diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f9b5d22e..4c9608fd 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ -patreon: ownh +patreon: xen42 custom: ["https://paypal.me/xen42"] diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 87a087a7..5a040682 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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/** diff --git a/.github/workflows/docs_build.yml b/.github/workflows/docs_build.yml index 7511a4bb..6d8e31e4 100644 --- a/.github/workflows/docs_build.yml +++ b/.github/workflows/docs_build.yml @@ -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 diff --git a/.github/workflows/release_build.yml b/.github/workflows/release_build.yml index d27381cc..c2aab503 100644 --- a/.github/workflows/release_build.yml +++ b/.github/workflows/release_build.yml @@ -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 diff --git a/.github/workflows/update_schemas.yml b/.github/workflows/update_schemas.yml index 9f9a4194..f570ec90 100644 --- a/.github/workflows/update_schemas.yml +++ b/.github/workflows/update_schemas.yml @@ -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/ diff --git a/NewHorizons/Assets/WarpDriveConfig.json b/NewHorizons/Assets/WarpDriveConfig.json index 1821c6e3..a9e90356 100644 --- a/NewHorizons/Assets/WarpDriveConfig.json +++ b/NewHorizons/Assets/WarpDriveConfig.json @@ -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" + } + } + ] + } } \ No newline at end of file diff --git a/NewHorizons/Assets/addon-manifest.json b/NewHorizons/Assets/addon-manifest.json index 363bc1bb..b5a71ba2 100644 --- a/NewHorizons/Assets/addon-manifest.json +++ b/NewHorizons/Assets/addon-manifest.json @@ -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", diff --git a/NewHorizons/Assets/bundles/newhorizons_private b/NewHorizons/Assets/bundles/newhorizons_private new file mode 100644 index 00000000..01e2c265 Binary files /dev/null and b/NewHorizons/Assets/bundles/newhorizons_private differ diff --git a/NewHorizons/Assets/newhorizons_private.manifest b/NewHorizons/Assets/bundles/newhorizons_private.manifest similarity index 81% rename from NewHorizons/Assets/newhorizons_private.manifest rename to NewHorizons/Assets/bundles/newhorizons_private.manifest index 71d62e91..847ea3aa 100644 --- a/NewHorizons/Assets/newhorizons_private.manifest +++ b/NewHorizons/Assets/bundles/newhorizons_private.manifest @@ -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: [] diff --git a/NewHorizons/Assets/newhorizons_public b/NewHorizons/Assets/bundles/newhorizons_public similarity index 100% rename from NewHorizons/Assets/newhorizons_public rename to NewHorizons/Assets/bundles/newhorizons_public diff --git a/NewHorizons/Assets/newhorizons_public.manifest b/NewHorizons/Assets/bundles/newhorizons_public.manifest similarity index 100% rename from NewHorizons/Assets/newhorizons_public.manifest rename to NewHorizons/Assets/bundles/newhorizons_public.manifest diff --git a/NewHorizons/Assets/newhorizons_private b/NewHorizons/Assets/newhorizons_private deleted file mode 100644 index f15bd4c7..00000000 Binary files a/NewHorizons/Assets/newhorizons_private and /dev/null differ diff --git a/NewHorizons/Assets/textures/blank_slide_reel.png b/NewHorizons/Assets/textures/blank_slide_reel.png new file mode 100644 index 00000000..3f9a5e2c Binary files /dev/null and b/NewHorizons/Assets/textures/blank_slide_reel.png differ diff --git a/NewHorizons/Assets/textures/inverted_blank_slide_reel.png b/NewHorizons/Assets/textures/inverted_blank_slide_reel.png new file mode 100644 index 00000000..4a394398 Binary files /dev/null and b/NewHorizons/Assets/textures/inverted_blank_slide_reel.png differ diff --git a/NewHorizons/Assets/translations/polish.json b/NewHorizons/Assets/translations/polish.json index d3d9bcda..2112eb79 100644 --- a/NewHorizons/Assets/translations/polish.json +++ b/NewHorizons/Assets/translations/polish.json @@ -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" } -} \ No newline at end of file +} diff --git a/NewHorizons/Assets/translations/portuguese_br.json b/NewHorizons/Assets/translations/portuguese_br.json index cac86858..279aa104 100644 --- a/NewHorizons/Assets/translations/portuguese_br.json +++ b/NewHorizons/Assets/translations/portuguese_br.json @@ -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 exploradora]]> está repousando por hora em: {0}]]>." + }, + "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." + } } -} \ No newline at end of file +} diff --git a/NewHorizons/Builder/Atmosphere/AirBuilder.cs b/NewHorizons/Builder/Atmosphere/AirBuilder.cs index 8d502f88..979bd933 100644 --- a/NewHorizons/Builder/Atmosphere/AirBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/AirBuilder.cs @@ -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) diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index ceef7fd5..3ad70e2f 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -66,7 +66,7 @@ namespace NewHorizons.Builder.Atmosphere } _qmBottomMeshGroup.variants = variants.ToArray(); } - if (_transparentCloud == null) _transparentCloud = Main.NHAssetBundle.LoadAsset("Assets/Resources/TransparentCloud.mat"); + if (_transparentCloud == null) _transparentCloud = AssetBundleUtilities.NHAssetBundle.LoadAsset("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 diff --git a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs index 8568f61b..fdc893c7 100644 --- a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs +++ b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs @@ -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); diff --git a/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs b/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs index 6672b15f..e1c6d41e 100644 --- a/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs +++ b/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs @@ -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()._material).DontDestroyOnLoad(); - if (_wallCollision == null) _wallCollision = Main.NHPrivateAssetBundle.LoadAsset("BrambleCollision"); + if (_wallCollision == null) _wallCollision = AssetBundleUtilities.NHPrivateAssetBundle.LoadAsset("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; diff --git a/NewHorizons/Builder/Body/DreamDimensionBuilder.cs b/NewHorizons/Builder/Body/DreamDimensionBuilder.cs new file mode 100644 index 00000000..9bd5c69c --- /dev/null +++ b/NewHorizons/Builder/Body/DreamDimensionBuilder.cs @@ -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(); + Delay.FireInNUpdates(() => + { + dreamDimension.Initialize(); + }, 4); + } + + } + + public static void MakeDreamSimulationMeshes(GameObject go) + { + if (gridMaterial == null) gridMaterial = SearchUtilities.FindResourceOfTypeAndName("Terrain_IP_DreamGrid_mat"); + if (waterMaterial == null) waterMaterial = SearchUtilities.FindResourceOfTypeAndName("Terrain_IP_DreamGrid_mat"); + + foreach (var mr in go.GetComponentsInChildren(true)) + { + if (mr.GetType() != typeof(MeshRenderer)) continue; + var mf = mr.GetComponent(); + if (mf == null) continue; + if (!CheckMeshCreationHeuristic(mr.gameObject, mr.sharedMaterials)) continue; + var simMesh = new GameObject("SimulationMesh").AddComponent(); + 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(); + 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; + } + } +} diff --git a/NewHorizons/Builder/Body/HeightMapBuilder.cs b/NewHorizons/Builder/Body/HeightMapBuilder.cs index 55b6f58c..48e15193 100644 --- a/NewHorizons/Builder/Body/HeightMapBuilder.cs +++ b/NewHorizons/Builder/Body/HeightMapBuilder.cs @@ -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("Assets/Shaders/SphereTextureWrapperTriplanar.shader"); + if (PlanetShader == null) PlanetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("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(); + if (superGroup != null) level2.gameObject.AddComponent()._superGroup = superGroup; } var cubeSphereSC = cubeSphere.AddComponent(); cubeSphereSC.radius = Mathf.Min(module.minHeight, module.maxHeight) * Mathf.Min(stretch.x, stretch.y, stretch.z); - var superGroup = planetGO.GetComponent(); - if (superGroup != null) cubeSphere.AddComponent()._superGroup = superGroup; - cubeSphere.SetActive(true); // Now that we've made the mesh we can delete the heightmap texture diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index c788c3b2..f1567659 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -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); diff --git a/NewHorizons/Builder/Body/RingBuilder.cs b/NewHorizons/Builder/Body/RingBuilder.cs index d7e18da0..b5c567b8 100644 --- a/NewHorizons/Builder/Body/RingBuilder.cs +++ b/NewHorizons/Builder/Body/RingBuilder.cs @@ -84,10 +84,10 @@ namespace NewHorizons.Builder.Body var ringMesh = ringMF.mesh; var ringMR = ringGO.AddComponent(); - if (RingShader == null) RingShader = Main.NHAssetBundle.LoadAsset("Assets/Shaders/Ring.shader"); - if (UnlitRingShader == null) UnlitRingShader = Main.NHAssetBundle.LoadAsset("Assets/Shaders/UnlitTransparent.shader"); - if (RingShader1Pixel == null) RingShader1Pixel = Main.NHAssetBundle.LoadAsset("Assets/Shaders/Ring1Pixel.shader"); - if (UnlitRingShader1Pixel == null) UnlitRingShader1Pixel = Main.NHAssetBundle.LoadAsset("Assets/Shaders/UnlitRing1Pixel.shader"); + if (RingShader == null) RingShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/Ring.shader"); + if (UnlitRingShader == null) UnlitRingShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/UnlitTransparent.shader"); + if (RingShader1Pixel == null) RingShader1Pixel = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/Ring1Pixel.shader"); + if (UnlitRingShader1Pixel == null) UnlitRingShader1Pixel = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/UnlitRing1Pixel.shader"); var mat = new Material(ring.unlit ? UnlitRingShader : RingShader); if (ringTexture.width == 1) diff --git a/NewHorizons/Builder/Body/SingularityBuilder.cs b/NewHorizons/Builder/Body/SingularityBuilder.cs index a961c9d5..eb8863c8 100644 --- a/NewHorizons/Builder/Body/SingularityBuilder.cs +++ b/NewHorizons/Builder/Body/SingularityBuilder.cs @@ -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(); wormholeVolume.TargetSolarSystem = targetStarSystem; + wormholeVolume.TargetSpawnID = targetSpawnID; } } else diff --git a/NewHorizons/Builder/Body/StellarRemnantBuilder.cs b/NewHorizons/Builder/Body/StellarRemnantBuilder.cs index 5dd00158..f34c3b67 100644 --- a/NewHorizons/Builder/Body/StellarRemnantBuilder.cs +++ b/NewHorizons/Builder/Body/StellarRemnantBuilder.cs @@ -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); diff --git a/NewHorizons/Builder/Body/WaterBuilder.cs b/NewHorizons/Builder/Body/WaterBuilder.cs index 8eba73c0..b53b4617 100644 --- a/NewHorizons/Builder/Body/WaterBuilder.cs +++ b/NewHorizons/Builder/Body/WaterBuilder.cs @@ -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); diff --git a/NewHorizons/Builder/General/AmbientLightBuilder.cs b/NewHorizons/Builder/General/AmbientLightBuilder.cs index d177679f..8484c26d 100644 --- a/NewHorizons/Builder/General/AmbientLightBuilder.cs +++ b/NewHorizons/Builder/General/AmbientLightBuilder.cs @@ -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("AmbientLight_QM"); + var baseCubemap = AssetBundleUtilities.NHPrivateAssetBundle.LoadAsset("AmbientLight_QM"); var cubemap = new Cubemap(baseCubemap.width, baseCubemap.format, baseCubemap.mipmapCount != 1); cubemap.name = key; cubemap.wrapMode = baseCubemap.wrapMode; diff --git a/NewHorizons/Builder/General/AstroObjectBuilder.cs b/NewHorizons/Builder/General/AstroObjectBuilder.cs index 45966762..15f00d62 100644 --- a/NewHorizons/Builder/General/AstroObjectBuilder.cs +++ b/NewHorizons/Builder/General/AstroObjectBuilder.cs @@ -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(); + 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( diff --git a/NewHorizons/Builder/General/DetectorBuilder.cs b/NewHorizons/Builder/General/DetectorBuilder.cs index 4a62d5b6..034a7e07 100644 --- a/NewHorizons/Builder/General/DetectorBuilder.cs +++ b/NewHorizons/Builder/General/DetectorBuilder.cs @@ -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; diff --git a/NewHorizons/Builder/General/GravityBuilder.cs b/NewHorizons/Builder/General/GravityBuilder.cs index 922cfee8..e358d1a8 100644 --- a/NewHorizons/Builder/General/GravityBuilder.cs +++ b/NewHorizons/Builder/General/GravityBuilder.cs @@ -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(); - SC.isTrigger = true; - SC.radius = gravityRadius; + var sphereCollider = gravityGO.AddComponent(); + sphereCollider.isTrigger = true; + sphereCollider.radius = gravityRadius; var owCollider = gravityGO.AddComponent(); 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; diff --git a/NewHorizons/Builder/General/MarkerBuilder.cs b/NewHorizons/Builder/General/MarkerBuilder.cs index bcb945a9..ad17b191 100644 --- a/NewHorizons/Builder/General/MarkerBuilder.cs +++ b/NewHorizons/Builder/General/MarkerBuilder.cs @@ -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(); + var module = config.MapMarker; + NHMapMarker mapMarker = body.AddComponent(); 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; } } } diff --git a/NewHorizons/Builder/General/RFVolumeBuilder.cs b/NewHorizons/Builder/General/RFVolumeBuilder.cs index f359a7e0..808a7314 100644 --- a/NewHorizons/Builder/General/RFVolumeBuilder.cs +++ b/NewHorizons/Builder/General/RFVolumeBuilder.cs @@ -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); } diff --git a/NewHorizons/Builder/General/SectorBuilder.cs b/NewHorizons/Builder/General/SectorBuilder.cs index 467ed91f..6ad8e462 100644 --- a/NewHorizons/Builder/General/SectorBuilder.cs +++ b/NewHorizons/Builder/General/SectorBuilder.cs @@ -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(); SS.SetCollisionMode(Shape.CollisionMode.Volume); diff --git a/NewHorizons/Builder/General/SpawnPointBuilder.cs b/NewHorizons/Builder/General/SpawnPointBuilder.cs index cc7feddf..a38802b6 100644 --- a/NewHorizons/Builder/General/SpawnPointBuilder.cs +++ b/NewHorizons/Builder/General/SpawnPointBuilder.cs @@ -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(); - 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(); + 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(); - ShipSpawn._isShipSpawn = true; - ShipSpawn._attachedBody = owRigidBody; - ShipSpawn._spawnLocation = SpawnLocation.None; + var shipSpawn = spawnGO.AddComponent(); + 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(); + } } } diff --git a/NewHorizons/Builder/Orbital/FocalPointBuilder.cs b/NewHorizons/Builder/Orbital/FocalPointBuilder.cs index 90aebab3..b3e63391 100644 --- a/NewHorizons/Builder/Orbital/FocalPointBuilder.cs +++ b/NewHorizons/Builder/Orbital/FocalPointBuilder.cs @@ -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) diff --git a/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs b/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs index ae3aff08..9a102f9f 100644 --- a/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs +++ b/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs @@ -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("Effects_SPA_OrbitLine_Dotted_mat"); if (_lineMaterial == null) _lineMaterial = SearchUtilities.FindResourceOfTypeAndName("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(); var lineRenderer = orbitGO.AddComponent(); @@ -47,7 +56,6 @@ namespace NewHorizons.Builder.Orbital else { orbitLine = orbitGO.AddComponent(); - (orbitLine as NHOrbitLine).SetFromParameters(astroObject); } @@ -94,8 +102,6 @@ namespace NewHorizons.Builder.Orbital orbitGO.SetActive(false); }; } - - return orbitLine; } } } diff --git a/NewHorizons/Builder/Props/Audio/SignalBuilder.cs b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs index b5fbfaf0..e3ba5aed 100644 --- a/NewHorizons/Builder/Props/Audio/SignalBuilder.cs +++ b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs @@ -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(); // 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()?._name == AstroObject.Name.DarkBramble) + { + audioSignal._outerFogWarpVolume = null; + } + else + { + audioSignal._outerFogWarpVolume = planetGO.GetComponentInChildren(); // shouldn't break non-bramble signals + } // If it can be heard regularly then we play it immediately owAudioSource.playOnAwake = !info.onlyAudibleToScope; diff --git a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs index 82e85a12..810b43d8 100644 --- a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs +++ b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs @@ -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; } diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 6a05cb09..2de1ccbd 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -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 _detailInfoToCorrespondingSpawnedGameObject = new(); private static readonly Dictionary<(Sector, string), (GameObject prefab, bool isItem)> _fixedPrefabCache = new(); private static GameObject _emptyPrefab; + private static readonly Dictionary _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(); } /// @@ -67,6 +69,8 @@ namespace NewHorizons.Builder.Props /// 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(); } 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()._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; + } } /// @@ -364,6 +395,12 @@ namespace NewHorizons.Builder.Props // Fix anglerfish speed on orbiting planets else if (component is AnglerfishController angler) { + if (planetGO?.GetComponent() 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().GetPrimaryBody().GetAttachedOWRigidbody()).magnitude; @@ -417,6 +454,20 @@ namespace NewHorizons.Builder.Props { component.gameObject.AddComponent(); } + // Add custom logic to NH-spawned rafts to handle fluid changes + else if (component is RaftController raft) + { + component.gameObject.AddComponent(); + } + 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()) + { + Component.DestroyImmediate(toggle); + } + } } /// @@ -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); } } + + /// + /// need component here to run after DreamLanternController.Awake + /// + [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(); + + // 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); + } + } } } diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 4daabe4a..8bd12c86 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -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(); + var dialogueObject = go.FindChild(info.pathToExistingDialogue); + if (dialogueObject == null) dialogueObject = SearchUtilities.Find(info.pathToExistingDialogue); + var existingDialogue = dialogueObject != null ? dialogueObject.GetComponent() : 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); + }); + } + + /// + /// Always call this after adding translations, else it won't update them properly + /// + /// + private static void DoDialogueOptionsListReplacement(XmlNode dialogueTree) + { + var optionsListsByName = new Dictionary(); + 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(); + 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(); + 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); + } } } diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs new file mode 100644 index 00000000..46144b6b --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var alarmTotem = _prefab.GetComponent(); + 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._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; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/DreamArrivalPointBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamArrivalPointBuilder.cs new file mode 100644 index 00000000..c2a5d261 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamArrivalPointBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var dreamArrivalPoint = _prefab.GetComponent(); + 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(); + arrivalPoint._sector = arrivalPoint.GetComponentInParent(); + arrivalPoint._location = DreamHandler.GetDreamArrivalLocation(info.id); + Locator.RegisterDreamArrivalPoint(arrivalPoint, arrivalPoint._location); + + return arrivalPointObj; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCampfireBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCampfireBuilder.cs new file mode 100644 index 00000000..8d05f73e --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCampfireBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var campfire = _prefab.GetComponentInChildren(); + 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(); + 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(); + if (streaming != null) + { + var targetArrivalPoint = Locator.GetDreamArrivalPoint(campfire._dreamArrivalLocation); + if (targetArrivalPoint != null) + { + var streamingGroup = targetArrivalPoint.transform.root.GetComponentInChildren(); + if (streamingGroup) + { + streaming._streamingGroup = streamingGroup; + } + } + } + }, 2); + + Locator.RegisterDreamCampfire(campfire, campfire._dreamArrivalLocation); + + return campfireObj; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs new file mode 100644 index 00000000..38387bf8 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs @@ -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 _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()._destroyOnDLCNotOwned = true; + var sensor = prefab.GetComponentInChildren(); + 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(); + + var sensor = candleObj.GetComponentInChildren(); + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; + + dreamCandle._startLit = info.startLit; + dreamCandle.SetLit(info.startLit, false, true); + + return candleObj; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs new file mode 100644 index 00000000..9a25dd12 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var zoomPoint = _prefab.GetComponentInChildren(); + zoomPoint._sector = null; + var sensor = _prefab.GetComponentInChildren(); + 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(); + zoomPoint._minActivationDistance = info.minDistance; + zoomPoint._arrivalDistance = info.arrivalDistance; + + var sensor = totemObj.GetComponentInChildren(); + sensor._detectionAngle = info.maxAngle; + sensor._maxDistance = info.maxDistance; + + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; + + return totemObj; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs new file mode 100644 index 00000000..b466e272 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var peephole = _mainPrefab.GetComponentInChildren(); + 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()._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(); + 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(); + peephole._viewingSector = viewingSector; + }, 2); + + return portholeObj; + } + } +} diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs new file mode 100644 index 00000000..69155147 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; + var projector = _prefab.GetComponent(); + 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(); + + if (!string.IsNullOrEmpty(info.pathToAlarmTotem)) + { + var alarmTotemObj = planetGO.transform.Find(info.pathToAlarmTotem); + if (alarmTotemObj != null) + { + var alarmTotem = alarmTotemObj.GetComponentInChildren(); + if (alarmTotem != null) + { + projector._alarmTotem = alarmTotem; + } + } + } + + if (info.pathsToDreamCandles != null) + { + var dreamCandles = new List(); + foreach (var pathToDreamCandles in info.pathsToDreamCandles) + { + if (string.IsNullOrEmpty(pathToDreamCandles)) continue; + var dreamCandleObj = planetGO.transform.Find(pathToDreamCandles); + if (dreamCandleObj != null) + { + dreamCandles.AddRange(dreamCandleObj.GetComponentsInChildren()); + } + } + projector._dreamCandles = dreamCandles.ToArray(); + } + + if (info.pathsToProjectionTotems != null) + { + var projectionTotems = new List(); + foreach (var pathToProjectionTotems in info.pathsToProjectionTotems) + { + if (string.IsNullOrEmpty(pathToProjectionTotems)) continue; + var projectionTotemObj = planetGO.transform.Find(pathToProjectionTotems); + if (projectionTotemObj != null) + { + projectionTotems.AddRange(projectionTotemObj.GetComponentsInChildren()); + } + } + projector._extinguishedProjectors = projectionTotems.ToArray(); + } + + if (info.pathsToProjectedObjects != null) + { + var projections = new List(); + foreach (var pathToProjectedObject in info.pathsToProjectedObjects) + { + if (string.IsNullOrEmpty(pathToProjectedObject)) continue; + var projectionObj = planetGO.transform.Find(pathToProjectedObject); + if (projectionObj != null) + { + projectionObj.gameObject.AddComponent(); + var projection = projectionObj.gameObject.AddComponent(); + 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; + } + } +} diff --git a/NewHorizons/Builder/Props/EyeOfTheUniverseBuilder.cs b/NewHorizons/Builder/Props/EyeOfTheUniverseBuilder.cs new file mode 100644 index 00000000..b8c84b62 --- /dev/null +++ b/NewHorizons/Builder/Props/EyeOfTheUniverseBuilder.cs @@ -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(); + 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(); + } + 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(); + 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(); + 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(); + collider.radius = info.interactRadius; + collider.isTrigger = true; + go.GetAddComponent(); + } + + go.GetAddComponent(); + var quantumInstrument = go.GetAddComponent(); + quantumInstrument._gatherWithScope = info.gatherWithScope; + ArrayHelpers.Append(ref quantumInstrument._deactivateObjects, go); + + var trigger = go.AddComponent(); + 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(); + + 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); + } + } + } + } +} diff --git a/NewHorizons/Builder/Props/ItemBuilder.cs b/NewHorizons/Builder/Props/ItemBuilder.cs index 78a866be..5d4dc7ba 100644 --- a/NewHorizons/Builder/Props/ItemBuilder.cs +++ b/NewHorizons/Builder/Props/ItemBuilder.cs @@ -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().radius = info.colliderRadius; + go.GetAddComponent(); + } socketGO.SetActive(true); socket._socketTransform = socketGO.transform; } diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 7795f81c..2ebf64fc 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -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()._destroyOnDLCNotOwned = true; - } + SlideReelWholePrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Whole"); + SlideReelWholePristinePrefab = NHPrivateAssetBundle.LoadAsset("Prefab_DW_Reel_Whole"); + SlideReelWholeRustedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Rusted_Whole"); + SlideReelWholeDestroyedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Destroyed_Whole"); + SlideReel8Prefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_8"); + SlideReel8PristinePrefab = NHPrivateAssetBundle.LoadAsset("Prefab_DW_Reel_8"); + SlideReel8RustedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Rusted_8"); + SlideReel8DestroyedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Destroyed_8"); + SlideReel7Prefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_7"); + SlideReel7PristinePrefab = NHPrivateAssetBundle.LoadAsset("Prefab_DW_Reel_7"); + SlideReel7RustedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Rusted_7"); + SlideReel7DestroyedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Destroyed_7"); + SlideReel6Prefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_6"); + SlideReel6PristinePrefab = NHPrivateAssetBundle.LoadAsset("Prefab_DW_Reel_6"); + SlideReel6RustedPrefab = NHPrivateAssetBundle.LoadAsset("Prefab_IP_Reel_Rusted_6"); + SlideReel6DestroyedPrefab = NHPrivateAssetBundle.LoadAsset("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(); slideReel.SetSector(sector); slideReel.SetVisible(true); - var slideCollectionContainer = slideReelObj.GetRequiredComponent(); + var toDestroy = slideReelObj.GetComponent(); + var slideCollectionContainer = slideReelObj.AddComponent(); + slideReel._slideCollectionContainer = slideCollectionContainer; + Component.DestroyImmediate(toDestroy); foreach (var renderer in slideReelObj.GetComponentsInChildren()) { @@ -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(true).transform.Find("Slides_Back").GetComponent(); + var slidesFront = slideReelObj.GetComponentInChildren(true).transform.Find("Slides_Front").GetComponent(); + + // 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(); - var slidesFront = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Front").GetComponent(); + var slidesBack = slideReelObj.GetComponentInChildren(true).transform.Find("Slides_Back").GetComponent(); + var slidesFront = slideReelObj.GetComponentInChildren(true).transform.Find("Slides_Front").GetComponent(); - // 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()._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(); autoProjector._sector = sector; - var slideCollectionContainer = autoProjector.GetRequiredComponent(); + var toDestroy = autoProjector.GetComponent(); + var slideCollectionContainer = autoProjector.gameObject.AddComponent(); + 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(); // 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(); - var slideCollectionContainer = g.AddComponent(); + var target = visionTorchTargetGO.AddComponent(); + var slideCollectionContainer = visionTorchTargetGO.AddComponent(); + slideCollectionContainer.doAsyncLoading = false; slideCollectionContainer.slideCollection = slideCollection; - target.slideCollection = g.AddComponent(); + target.slideCollection = visionTorchTargetGO.AddComponent(); 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._mindProjectorImageEffect = SearchUtilities.Find("Player_Body/PlayerCamera").GetComponent(); - + // 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(); + var slideCollectionContainer = standingTorch.AddComponent(); + slideCollectionContainer.doAsyncLoading = false; slideCollectionContainer.slideCollection = slideCollection; var mindSlideCollection = standingTorch.AddComponent(); @@ -307,21 +517,66 @@ namespace NewHorizons.Builder.Props return standingTorch; } - private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) + /// + /// start loading all the slide stuff we need async. + /// + /// the mod to load slides from + /// slides to load + /// where to assign the slide objects + /// should we load cached inverted images? + /// should we load cached atlas images? + /// should we load the original images? happens anyway if cache doesnt exist since atlas or inverted will need it + /// 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 + 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(); + 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(); + + slideCollectionContainer.conditionsToSet = info.conditionsToSet; + slideCollectionContainer.persistentConditionsToSet = info.persistentConditionsToSet; } } diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 86dcf826..579d5e21 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -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() 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 nextPass; + + public static void MakeGeneralProp(GameObject go, T prop, Action builder, Func errorMessage = null) where T : BasePropInfo + { + if (prop != null) + { + try + { + if (DoesParentExist(go, prop)) + { + builder(prop); + } + else + { + nextPass.Add(() => MakeGeneralProp(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()}{rename}{extra} for [{go.name}]:\n{ex}"); + } + } + } + + public static void MakeGeneralProps(GameObject go, IEnumerable props, Action builder, Func 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(); + + 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(), 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> propsByGroup = new Dictionary>(); - foreach (var detail in config.Props.details) - { - if (detail.quantumGroupID != null) - { - if (!propsByGroup.ContainsKey(detail.quantumGroupID)) propsByGroup[detail.quantumGroupID] = new List(); - 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(), 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; } } } diff --git a/NewHorizons/Builder/Props/QuantumBuilder.cs b/NewHorizons/Builder/Props/QuantumBuilder.cs index dff1d3a7..302270e8 100644 --- a/NewHorizons/Builder/Props/QuantumBuilder.cs +++ b/NewHorizons/Builder/Props/QuantumBuilder.cs @@ -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("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(); + 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(); 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(); + prop.go.SetActive(false); + var quantumObject = prop.go.AddComponent(); 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(); - // TODO: support _alignWithGravity? - if (prop.GetComponentInChildren() == null) AddBoundsVisibility(prop); - prop.SetActive(true); + if (prop.go.GetComponentInChildren() == 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(); + box.size = new Vector3(0.2f, 0.6f, 0.2f); + box.center = new Vector3(0, 0.3f, 0); + tracker.AddComponent(); + // Using a quantum object bc it can be locked by camera + var quantumObject = socket.gameObject.AddComponent(); + 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(); - foreach(var prop in propsInGroup) + foreach (var prop in propsInGroup) { prop.transform.parent = groupRoot.transform; var state = prop.AddComponent(); @@ -81,24 +178,23 @@ namespace NewHorizons.Builder.Props if (prop.GetComponentInChildren() != 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(); states.Add(state); - var boxBounds = GetBoundsOfSelfAndChildMeshes(template); + var boxBounds = BoundsUtilities.GetBoundsOfSelfAndChildMeshes(template); var boxShape = empty.AddComponent(); boxShape.center = boxBounds.center; boxShape.extents = boxBounds.size; - if (Main.Debug) empty.AddComponent(); - + empty.AddComponent(); empty.AddComponent(); } @@ -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(); 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(); - var skinnedMeshRenderers = g.GetComponentsInChildren(); - - 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(); - boxshapeReciever.g.AddComponent(); - if (Main.Debug) boxshapeReciever.g.AddComponent(); - - var fixer = boxshapeReciever.g.AddComponent(); - 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(); - 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(); - } - } - - /// - /// 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 - /// - 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); - } } } diff --git a/NewHorizons/Builder/Props/RaftBuilder.cs b/NewHorizons/Builder/Props/RaftBuilder.cs index 14ccf8d4..59f734bf 100644 --- a/NewHorizons/Builder/Props/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/RaftBuilder.cs @@ -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(); fluidDetector._alignmentFluid = waterVolume; fluidDetector._buoyancy.checkAgainstWaves = true; + // Rafts were unable to trigger docks because these were disabled for some reason + fluidDetector.GetComponent().enabled = true; + fluidDetector.GetComponent().enabled = true; // Light sensors foreach (var lightSensor in raftObject.GetComponentsInChildren()) @@ -73,6 +77,8 @@ namespace NewHorizons.Builder.Props sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated; } + var nhRaftController = raftObject.AddComponent(); + var achievementObject = new GameObject("AchievementVolume"); achievementObject.transform.SetParent(raftObject.transform, false); diff --git a/NewHorizons/Builder/Props/RemoteBuilder.cs b/NewHorizons/Builder/Props/RemoteBuilder.cs index 4ae0ac01..1d9ee2ef 100644 --- a/NewHorizons/Builder/Props/RemoteBuilder.cs +++ b/NewHorizons/Builder/Props/RemoteBuilder.cs @@ -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()._connectedPlatform = id; diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index e725bf8a..862f0629 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -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(); + } + 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); diff --git a/NewHorizons/Builder/Props/TornadoBuilder.cs b/NewHorizons/Builder/Props/TornadoBuilder.cs index c2d29093..098bf466 100644 --- a/NewHorizons/Builder/Props/TornadoBuilder.cs +++ b/NewHorizons/Builder/Props/TornadoBuilder.cs @@ -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()._fluidType = info.fluidType.ConvertToOW(FluidVolume.Type.CLOUD); - fluidGO.localScale = new Vector3(1, 2f, 1); + fluidGO.localPosition = Vector3.up * 4.8f; if (info.tint != null) { diff --git a/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs index 817084a5..4a78a3d6 100644 --- a/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs @@ -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; + + /// + /// What is this + /// public Vector2 a; + + /// + /// What is this + /// public Vector2 b; + + /// + /// What is this + /// public Vector2 startS; + + /// + /// What is this + /// public Vector2 endS; + + /// + /// What is this + /// public Vector2 skeletonScale; + + /// + /// What is this + /// public int numSkeletonPoints; + + /// + /// What is this + /// public float uvScale; - public float innerWidth; // width at the tip - public float outerWidth; // width at the base + + /// + /// Width at tip + /// + public float innerWidth; + + /// + /// Width at base + /// + 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, - - }; diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index 9ad3ba5c..18a09f87 100644 --- a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -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(cacheKey); var arranger = nomaiWallText.gameObject.AddComponent(); @@ -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(); + 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().sharedMaterial = mat; arc.transform.parent = conversationZone.transform; diff --git a/NewHorizons/Builder/Props/WarpPadBuilder.cs b/NewHorizons/Builder/Props/WarpPadBuilder.cs index 4342a374..84bc31c2 100644 --- a/NewHorizons/Builder/Props/WarpPadBuilder.cs +++ b/NewHorizons/Builder/Props/WarpPadBuilder.cs @@ -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().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().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(); computerLogger._warpReceiver = receiver; + computerLogger.Awake(); // Redo awake because OnReceiveWarpedBody doesn't get added to otherwise computerObject.SetActive(true); } diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 60e0e879..9d66447f 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -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 _astroObjectToShipLog = new(); + private static Dictionary _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().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().material; List 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(); 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 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().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(); + 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 } } diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index d224fffa..7f4d28f2 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -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"); diff --git a/NewHorizons/Builder/StarSystem/SkyboxBuilder.cs b/NewHorizons/Builder/StarSystem/SkyboxBuilder.cs index 368ec65d..5d4b8548 100644 --- a/NewHorizons/Builder/StarSystem/SkyboxBuilder.cs +++ b/NewHorizons/Builder/StarSystem/SkyboxBuilder.cs @@ -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; } diff --git a/NewHorizons/Builder/Volumes/ChangeStarSystemVolumeBuilder.cs b/NewHorizons/Builder/Volumes/ChangeStarSystemVolumeBuilder.cs index c3b8c5db..5557da98 100644 --- a/NewHorizons/Builder/Volumes/ChangeStarSystemVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/ChangeStarSystemVolumeBuilder.cs @@ -11,6 +11,7 @@ namespace NewHorizons.Builder.Volumes var volume = VolumeBuilder.Make(planetGO, sector, info); volume.TargetSolarSystem = info.targetStarSystem; + volume.TargetSpawnID = info.spawnPointID; return volume; } diff --git a/NewHorizons/Components/EOTE/DreamDimension.cs b/NewHorizons/Components/EOTE/DreamDimension.cs new file mode 100644 index 00000000..89eec750 --- /dev/null +++ b/NewHorizons/Components/EOTE/DreamDimension.cs @@ -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 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); + } + } +} diff --git a/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs b/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs new file mode 100644 index 00000000..839d286f --- /dev/null +++ b/NewHorizons/Components/EOTE/DreamWorldEndTimes.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/NewHorizons/Components/EOTE/LanternExtinguisher.cs b/NewHorizons/Components/EOTE/LanternExtinguisher.cs new file mode 100644 index 00000000..26d1b2a0 --- /dev/null +++ b/NewHorizons/Components/EOTE/LanternExtinguisher.cs @@ -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); + } + } + } +} diff --git a/NewHorizons/Components/EOTE/NHSlideCollection.cs b/NewHorizons/Components/EOTE/NHSlideCollection.cs new file mode 100644 index 00000000..6b13a833 --- /dev/null +++ b/NewHorizons/Components/EOTE/NHSlideCollection.cs @@ -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 _pathsBeingLoaded = new(); + /// + /// map of slide path to collections that have this path loaded. used to only unload slide when nothing else is using it + /// + public static Dictionary> _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().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; + } + } + } +} diff --git a/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs new file mode 100644 index 00000000..030139e4 --- /dev/null +++ b/NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs @@ -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; + } + } +} diff --git a/NewHorizons/Components/EyeOfTheUniverse/EyeAstroObject.cs b/NewHorizons/Components/EyeOfTheUniverse/EyeAstroObject.cs index c863d1e5..ab215fb4 100644 --- a/NewHorizons/Components/EyeOfTheUniverse/EyeAstroObject.cs +++ b/NewHorizons/Components/EyeOfTheUniverse/EyeAstroObject.cs @@ -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(); diff --git a/NewHorizons/Components/EyeOfTheUniverse/EyeMusicController.cs b/NewHorizons/Components/EyeOfTheUniverse/EyeMusicController.cs new file mode 100644 index 00000000..233b2f4c --- /dev/null +++ b/NewHorizons/Components/EyeOfTheUniverse/EyeMusicController.cs @@ -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 _loopSources = new(); + private List _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(); + } + + 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(); + if (signal != null) + { + signal.SetSignalActivation(false, 0f); + } + } + } + } +} diff --git a/NewHorizons/Components/EyeOfTheUniverse/InstrumentZone.cs b/NewHorizons/Components/EyeOfTheUniverse/InstrumentZone.cs new file mode 100644 index 00000000..37d0948f --- /dev/null +++ b/NewHorizons/Components/EyeOfTheUniverse/InstrumentZone.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace NewHorizons.Components.EyeOfTheUniverse +{ + /// + /// Class does nothing but is used with GetComponent to find instrument zones in the hierarchy + /// + public class InstrumentZone : MonoBehaviour + { + + } +} diff --git a/NewHorizons/Components/EyeOfTheUniverse/QuantumInstrumentTrigger.cs b/NewHorizons/Components/EyeOfTheUniverse/QuantumInstrumentTrigger.cs new file mode 100644 index 00000000..c927c36c --- /dev/null +++ b/NewHorizons/Components/EyeOfTheUniverse/QuantumInstrumentTrigger.cs @@ -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.OnFinishGather += OnFinishGather; + } + + private void OnDestroy() + { + _quantumInstrument.OnFinishGather -= OnFinishGather; + } + + private void OnFinishGather() + { + if (!string.IsNullOrEmpty(gatherCondition)) + { + DialogueConditionManager.SharedInstance.SetConditionState(gatherCondition, true); + } + } + } +} diff --git a/NewHorizons/Components/FixPhysics.cs b/NewHorizons/Components/FixPhysics.cs new file mode 100644 index 00000000..245f144a --- /dev/null +++ b/NewHorizons/Components/FixPhysics.cs @@ -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(); + _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(); + if (gravity != null) gravity.GetComponent().AddObjectToVolume(_body.GetComponentInChildren().gameObject); + } +} diff --git a/NewHorizons/Components/Fixers/PlayerShipAtmosphereDetectorFix.cs b/NewHorizons/Components/Fixers/PlayerShipAtmosphereDetectorFix.cs index 25d135c1..1eb440d9 100644 --- a/NewHorizons/Components/Fixers/PlayerShipAtmosphereDetectorFix.cs +++ b/NewHorizons/Components/Fixers/PlayerShipAtmosphereDetectorFix.cs @@ -14,7 +14,11 @@ internal class PlayerShipAtmosphereDetectorFix : MonoBehaviour public void Start() { _fluidDetector = Locator.GetPlayerCameraDetector().GetComponent(); - _shipAtmosphereVolume = Locator.GetShipBody().transform.Find("Volumes/ShipAtmosphereVolume").GetComponent(); + _shipAtmosphereVolume = Locator.GetShipBody()?.transform?.Find("Volumes/ShipAtmosphereVolume")?.GetComponent(); + if (_shipAtmosphereVolume == null) + { + Destroy(this); + } } public void Update() diff --git a/NewHorizons/Components/MaterialReplacer.cs b/NewHorizons/Components/MaterialReplacer.cs index 64b383c1..f402fd81 100644 --- a/NewHorizons/Components/MaterialReplacer.cs +++ b/NewHorizons/Components/MaterialReplacer.cs @@ -30,6 +30,13 @@ namespace NewHorizons.Components nnc._inactiveMaterial = materials[0]; nnc._activeMaterial = materials[1]; } + + NomaiLamp nl = GetComponentInParent(); + if (nl != null) + { + nl.enabled = true; + nl.Awake(); + } } } } diff --git a/NewHorizons/Components/NHMapMarker.cs b/NewHorizons/Components/NHMapMarker.cs new file mode 100644 index 00000000..501f18e7 --- /dev/null +++ b/NewHorizons/Components/NHMapMarker.cs @@ -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; + } + } + } +} diff --git a/NewHorizons/Components/Orbital/NHAstroObject.cs b/NewHorizons/Components/Orbital/NHAstroObject.cs index ed51060c..cb35f059 100644 --- a/NewHorizons/Components/Orbital/NHAstroObject.cs +++ b/NewHorizons/Components/Orbital/NHAstroObject.cs @@ -14,6 +14,11 @@ namespace NewHorizons.Components.Orbital public bool invulnerableToSun; public bool isVanilla; + /// + /// 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 + /// + public string modUniqueName; + public void SetOrbitalParametersFromConfig(OrbitModule orbit) { SetOrbitalParametersFromTrueAnomaly(orbit.eccentricity, orbit.semiMajorAxis, orbit.inclination, orbit.argumentOfPeriapsis, orbit.longitudeOfAscendingNode, orbit.trueAnomaly); diff --git a/NewHorizons/Components/Props/ConditionalObjectActivation.cs b/NewHorizons/Components/Props/ConditionalObjectActivation.cs index 122302cf..a0322ecc 100644 --- a/NewHorizons/Components/Props/ConditionalObjectActivation.cs +++ b/NewHorizons/Components/Props/ConditionalObjectActivation.cs @@ -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.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.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)); } diff --git a/NewHorizons/Components/Props/DreamSimulationMesh.cs b/NewHorizons/Components/Props/DreamSimulationMesh.cs new file mode 100644 index 00000000..8283ad51 --- /dev/null +++ b/NewHorizons/Components/Props/DreamSimulationMesh.cs @@ -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(); + sourceMeshRenderer = target.GetComponent(); + targetMeshFilter = gameObject.AddComponent(); + targetMeshRenderer = gameObject.AddComponent(); + + transform.SetParent(target.transform, false); + + streamingHandle = target.GetComponent(); + 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(); + } + } +} diff --git a/NewHorizons/Components/Props/NHItem.cs b/NewHorizons/Components/Props/NHItem.cs index b21db7ea..cdc783b5 100644 --- a/NewHorizons/Components/Props/NHItem.cs +++ b/NewHorizons/Components/Props/NHItem.cs @@ -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); } diff --git a/NewHorizons/Components/Props/NHItemSocket.cs b/NewHorizons/Components/Props/NHItemSocket.cs index bd40a96d..ce1cd913 100644 --- a/NewHorizons/Components/Props/NHItemSocket.cs +++ b/NewHorizons/Components/Props/NHItemSocket.cs @@ -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 diff --git a/NewHorizons/Components/Props/NHRaftController.cs b/NewHorizons/Components/Props/NHRaftController.cs new file mode 100644 index 00000000..be503eea --- /dev/null +++ b/NewHorizons/Components/Props/NHRaftController.cs @@ -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(); + 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; + } + } + } +} diff --git a/NewHorizons/Components/Props/NHSupernovaPlanetEffectController.cs b/NewHorizons/Components/Props/NHSupernovaPlanetEffectController.cs index 0f87cca3..7b08bddd 100644 --- a/NewHorizons/Components/Props/NHSupernovaPlanetEffectController.cs +++ b/NewHorizons/Components/Props/NHSupernovaPlanetEffectController.cs @@ -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); + } } } diff --git a/NewHorizons/Components/Quantum/QuantumPlanet.cs b/NewHorizons/Components/Quantum/QuantumPlanet.cs index 5e5a4ecf..d4da2a7e 100644 --- a/NewHorizons/Components/Quantum/QuantumPlanet.cs +++ b/NewHorizons/Components/Quantum/QuantumPlanet.cs @@ -22,6 +22,16 @@ namespace NewHorizons.Components.Quantum private OWRigidbody _rb; private OrbitLine _orbitLine; + public NHAstroObject astroObject + { + get + { + if (_astroObject == null) + _astroObject = GetComponent(); + 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(); _detector._activeVolumes = new List() { primaryBody.GetGravityVolume() }; if (_alignment != null) _alignment.SetTargetBody(primaryBody.GetComponent()); - _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) { diff --git a/NewHorizons/Components/Quantum/SnapshotLockableVisiblityObject.cs b/NewHorizons/Components/Quantum/SnapshotLockableVisiblityObject.cs new file mode 100644 index 00000000..ba139d66 --- /dev/null +++ b/NewHorizons/Components/Quantum/SnapshotLockableVisiblityObject.cs @@ -0,0 +1,78 @@ +using HarmonyLib; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components.Quantum; + +/// +/// A quantum object that does nothing but track if its been photographed +/// +/// Adapted from Escape Room (mod I (xen) worked on for Jam 4) +/// +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; + } +} diff --git a/NewHorizons/Components/Sectored/BrambleSectorController.cs b/NewHorizons/Components/Sectored/BrambleSectorController.cs index 3d48e53a..00077c25 100644 --- a/NewHorizons/Components/Sectored/BrambleSectorController.cs +++ b/NewHorizons/Components/Sectored/BrambleSectorController.cs @@ -33,13 +33,16 @@ namespace NewHorizons.Components.Sectored } private void Start() + { + DisableRenderers(); + } + + private void GetRenderers() { _renderers = gameObject.GetComponentsInChildren(); _tessellatedRenderers = gameObject.GetComponentsInChildren(); _colliders = gameObject.GetComponentsInChildren(); _lights = gameObject.GetComponentsInChildren(); - - 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; } } } diff --git a/NewHorizons/Components/Ship/ShipWarpController.cs b/NewHorizons/Components/Ship/ShipWarpController.cs index d1f8e62d..c9d4b8f5 100644 --- a/NewHorizons/Components/Ship/ShipWarpController.cs +++ b/NewHorizons/Components/Ship/ShipWarpController.cs @@ -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().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); } } } diff --git a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs index a1d466eb..cc5636eb 100644 --- a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs @@ -34,10 +34,13 @@ namespace NewHorizons.Components.ShipLog private int _nextCardIndex; + private HashSet _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().gameObject); + _cardTemplate = Instantiate(panRoot.GetComponentInChildren(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); diff --git a/NewHorizons/Components/VesselOrbLocker.cs b/NewHorizons/Components/Vessel/VesselOrbLocker.cs similarity index 59% rename from NewHorizons/Components/VesselOrbLocker.cs rename to NewHorizons/Components/Vessel/VesselOrbLocker.cs index a7343659..5021d738 100644 --- a/NewHorizons/Components/VesselOrbLocker.cs +++ b/NewHorizons/Components/Vessel/VesselOrbLocker.cs @@ -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(); + } } } diff --git a/NewHorizons/Components/VesselSingularityRoot.cs b/NewHorizons/Components/Vessel/VesselSingularityRoot.cs similarity index 100% rename from NewHorizons/Components/VesselSingularityRoot.cs rename to NewHorizons/Components/Vessel/VesselSingularityRoot.cs diff --git a/NewHorizons/Components/Vessel/VesselSpawnPoint.cs b/NewHorizons/Components/Vessel/VesselSpawnPoint.cs new file mode 100644 index 00000000..9bb01990 --- /dev/null +++ b/NewHorizons/Components/Vessel/VesselSpawnPoint.cs @@ -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(); + 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().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()); + } + } +} diff --git a/NewHorizons/Components/Volumes/BlackHoleWarpVolume.cs b/NewHorizons/Components/Volumes/BlackHoleWarpVolume.cs index bd99fee5..941bff2b 100644 --- a/NewHorizons/Components/Volumes/BlackHoleWarpVolume.cs +++ b/NewHorizons/Components/Volumes/BlackHoleWarpVolume.cs @@ -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; } } } diff --git a/NewHorizons/Components/Volumes/WarpVolume.cs b/NewHorizons/Components/Volumes/WarpVolume.cs index fa07d6e4..25892833 100644 --- a/NewHorizons/Components/Volumes/WarpVolume.cs +++ b/NewHorizons/Components/Volumes/WarpVolume.cs @@ -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; } } } diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index cfcc731b..66674575 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -41,6 +41,7 @@ namespace NewHorizons.External.Configs /// /// 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 /// public string subtitlePath = "subtitle.png"; } diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index f29c511a..6184ed36 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -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 /// - /// Unique name of your planet + /// Unique name of your planet. If not specified, the file name (without the extension) is used. /// - [Required] public string name; /// @@ -55,6 +52,13 @@ namespace NewHorizons.External.Configs /// public bool destroy; + /// + /// 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 + /// + [DefaultValue(true)] public bool trackForSolarSystemRadius = true; + /// /// A list of paths to child GameObjects to destroy on this planet /// @@ -93,6 +97,16 @@ namespace NewHorizons.External.Configs /// public CloakModule Cloak; + /// + /// Make this planet part of the dream world + /// + public DreamModule Dream; + + /// + /// Add features exclusive to the Eye of the Universe scene + /// + public EyeOfTheUniverseModule EyeOfTheUniverse; + /// /// Make this body into a focal point (barycenter) /// @@ -113,6 +127,11 @@ namespace NewHorizons.External.Configs /// public LavaModule Lava; + /// + /// Map marker properties of this body + /// + public MapMarkerModule MapMarker; + /// /// Describes this Body's orbit (or lack there of) /// @@ -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 existingGroups = new Dictionary(); - 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(); - 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 existingGroups = new Dictionary(); + 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(); + 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 } diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index 28ecc4dd..08d0d968 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -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 { + /// + /// Unique name of your system. If not specified, the file name (without the extension) is used. + /// + public string name; + + /// + /// When changing star systems are you allowed to bring items into this system? + /// + [DefaultValue(true)] + public bool allowOutsideItems = true; + /// /// In this system should the player be able to rotate their map camera freely or be stuck above the plane of the solar system? /// @@ -31,10 +43,29 @@ namespace NewHorizons.External.Configs public float farClipPlaneOverride; /// - /// 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` /// [DefaultValue(true)] public bool canEnterViaWarpDrive = true; + /// + /// 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. + /// + public string factRequiredForWarp; + + /// + /// Can you use the warp drive to leave this system? If you set `factRequiredToExitViaWarpDrive` + /// this will be true. + /// + [DefaultValue(true)] public bool canExitViaWarpDrive = true; + + /// + /// 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. + /// + public string factRequiredToExitViaWarpDrive; + /// /// Do you want a clean slate for this star system? Or will it be a modified version of the original. /// @@ -45,12 +76,6 @@ namespace NewHorizons.External.Configs /// [DefaultValue(true)] public bool enableTimeLoop = true; - /// - /// 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. - /// - public string factRequiredForWarp; - /// /// 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. /// @@ -82,11 +107,14 @@ namespace NewHorizons.External.Configs [Obsolete("travelAudioFilePath is deprecated, please use travelAudio instead")] public string travelAudioFilePath; - /// - /// 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. - /// + [Obsolete("travelAudio is deprecated, please use travelAudio instead")] public string travelAudio; + /// + /// Replace music that plays globally + /// + public GlobalMusicModule GlobalMusic; + /// /// Configure warping to this system with the vessel /// @@ -192,6 +220,45 @@ namespace NewHorizons.External.Configs public string backPath; } + [JsonObject] + public class GlobalMusicModule + { + /// + /// 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. + /// + public string travelAudio; + + /// + /// 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. + /// + public string endTimesAudio; + + /// + /// 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. + /// + public string endTimesDreamAudio; + + /// + /// 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. + /// + public string brambleDimensionAudio; + + /// + /// 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. + /// + public string finalEndTimesIntroAudio; + + /// + /// 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. + /// + public string finalEndTimesLoopAudio; + + /// + /// 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. + /// + 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; + } } } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/AsteroidBeltModule.cs b/NewHorizons/External/Modules/AsteroidBeltModule.cs index 29588801..d042a9fc 100644 --- a/NewHorizons/External/Modules/AsteroidBeltModule.cs +++ b/NewHorizons/External/Modules/AsteroidBeltModule.cs @@ -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; /// - /// Angle between the rings and the equatorial plane of the planet. + /// Angle between the belt and the equatorial plane of the planet. /// public float inclination; @@ -23,7 +23,7 @@ namespace NewHorizons.External.Modules [Range(0f, double.MaxValue)] public float innerRadius; /// - /// 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. /// public float longitudeOfAscendingNode; @@ -45,7 +45,7 @@ namespace NewHorizons.External.Modules [Range(0f, double.MaxValue)] public float outerRadius; /// - /// How the asteroids are generated + /// How the asteroids are generated, unless you supply a detail yourself using "assetBundle" and "path" /// public ProcGenModule procGen; @@ -53,5 +53,30 @@ namespace NewHorizons.External.Modules /// Number used to randomize asteroid positions /// public int randomSeed; + + /// + /// 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. + /// + public string assetBundle; + + /// + /// 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. + /// + public string path; + + /// + /// Surface gravity of the asteroids. + /// + [Range(0f, double.MaxValue)] + [DefaultValue(1)] + public float gravity = 1f; + + /// + /// Should the detail of the asteroid be randomly oriented, or should it point towards the center. + /// + [DefaultValue(true)] + public bool randomOrientation = true; } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index e318f372..5aba48a5 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -61,6 +61,7 @@ namespace NewHorizons.External.Modules /// /// 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. /// public string fogRampPath; @@ -107,6 +108,12 @@ namespace NewHorizons.External.Modules /// [DefaultValue(300f)] public float maxShockSpeed = 300f; + /// + /// Will the ship automatically try to orient itself to face upwards while in this volume? + /// + [DefaultValue(true)] + public bool allowShipAutoroll = true; + [JsonObject] public class CloudInfo { diff --git a/NewHorizons/External/Modules/BaseModule.cs b/NewHorizons/External/Modules/BaseModule.cs index 1e1c28d1..f57cb1fc 100644 --- a/NewHorizons/External/Modules/BaseModule.cs +++ b/NewHorizons/External/Modules/BaseModule.cs @@ -37,14 +37,11 @@ namespace NewHorizons.External.Modules public float groundSize; /// - /// 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) /// - public bool hasMapMarker; - - /// - /// Can this planet survive entering a star? - /// - public bool invulnerableToSun; + [DefaultValue(true)] + public bool hasFluidDetector = true; /// /// Do we show the minimap when walking around this planet? @@ -63,6 +60,8 @@ namespace NewHorizons.External.Modules /// /// 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 /// public float surfaceSize; @@ -71,6 +70,11 @@ namespace NewHorizons.External.Modules /// [DefaultValue(0)] public int gravityVolumePriority = 0; + /// + /// Optional. Overrides how far the player must be from the planet for their feet to automatically orient towards the ground. + /// + public int? gravityAlignmentRadiusOverride = null; + /// /// 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; diff --git a/NewHorizons/External/Modules/BrambleModule.cs b/NewHorizons/External/Modules/BrambleModule.cs index 3351c6f5..b10c984f 100644 --- a/NewHorizons/External/Modules/BrambleModule.cs +++ b/NewHorizons/External/Modules/BrambleModule.cs @@ -41,11 +41,12 @@ namespace NewHorizons.External.Modules /// /// The color of the fog inside this dimension. /// Leave blank for the default grayish color: (84, 83, 73) + /// The alpha value has no effect of the fog: Use fogDensity instead! /// public MColor fogTint; /// - /// The density of the fog inside this dimension. The default is 6. + /// The density of the fog inside this dimension. The default is 6. If you want no fog, set this to 0. /// [DefaultValue(6f)] public float fogDensity = 6f; diff --git a/NewHorizons/External/Modules/DreamModule.cs b/NewHorizons/External/Modules/DreamModule.cs new file mode 100644 index 00000000..c8b7fd2f --- /dev/null +++ b/NewHorizons/External/Modules/DreamModule.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class DreamModule + { + /// + /// Setting this value will make this body a dream world style dimension where its contents are only activated while entering it from a dream campfire. Disables the body's map marker. + /// + public bool inDreamWorld; + /// + /// Whether to generate simulation meshes (the models used in the "tronworld" or "matrix" view) for most objects on this planet by cloning the existing meshes and applying the simulation materials. Leave this off if you are building your own simulation meshes or using existing objects which have them. + /// + public bool generateSimulationMeshes; + } +} diff --git a/NewHorizons/External/Modules/EyeOfTheUniverseModule.cs b/NewHorizons/External/Modules/EyeOfTheUniverseModule.cs new file mode 100644 index 00000000..58e878c1 --- /dev/null +++ b/NewHorizons/External/Modules/EyeOfTheUniverseModule.cs @@ -0,0 +1,24 @@ +using NewHorizons.External.Modules.Props.EyeOfTheUniverse; +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class EyeOfTheUniverseModule + { + /// + /// Add custom travelers to the campfire sequence + /// + public EyeTravelerInfo[] eyeTravelers; + + /// + /// Add instrument zones which contain puzzles to gather a quantum instrument. You can parent other props to these with `parentPath` + /// + public InstrumentZoneInfo[] instrumentZones; + + /// + /// Add quantum instruments which cause their associated eye traveler to appear and instrument zones to disappear + /// + public QuantumInstrumentInfo[] quantumInstruments; + } +} diff --git a/NewHorizons/External/Modules/GeneralPropInfo.cs b/NewHorizons/External/Modules/GeneralPropInfo.cs index fc855f9a..ebe39ac1 100644 --- a/NewHorizons/External/Modules/GeneralPropInfo.cs +++ b/NewHorizons/External/Modules/GeneralPropInfo.cs @@ -5,27 +5,31 @@ using Newtonsoft.Json; namespace NewHorizons.External.Modules { [JsonObject] - public abstract class GeneralPointPropInfo + public abstract class BasePropInfo + { + /// + /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). + /// + public string parentPath; + + /// + /// An optional rename of this object + /// + public string rename; + } + + [JsonObject] + public abstract class GeneralPointPropInfo : BasePropInfo { /// /// Position of the object /// public MVector3 position; - /// - /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). - /// - public string parentPath; - /// /// Whether the positional and rotational coordinates are relative to parent instead of the root planet object. /// public bool isRelativeToParent; - - /// - /// An optional rename of this object - /// - public string rename; } [JsonObject] diff --git a/NewHorizons/External/Modules/MapMarkerModule.cs b/NewHorizons/External/Modules/MapMarkerModule.cs new file mode 100644 index 00000000..a1f91aad --- /dev/null +++ b/NewHorizons/External/Modules/MapMarkerModule.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class MapMarkerModule + { + /// + /// If the body should have a marker on the map screen. + /// + public bool enabled; + + /// + /// Lowest distance away from the body that the marker can be shown. This is automatically set to 0 for all bodies except focal points where it is 5,000. + /// + public float minDisplayDistanceOverride = -1; + + /// + /// Highest distance away from the body that the marker can be shown. For planets and focal points the automatic value is 50,000. Moons and planets in focal points are 5,000. Stars are 1E+10 (10,000,000,000). + /// + public float maxDisplayDistanceOverride = -1; + } +} \ No newline at end of file diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index b77c2f68..4a162bd8 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -54,7 +54,7 @@ namespace NewHorizons.External.Modules public DetailInfo[] proxyDetails; /// - /// Add rafts to this planet + /// Add rafts to this planet (requires Echoes of the Eye DLC) /// public RaftInfo[] rafts; @@ -64,15 +64,10 @@ namespace NewHorizons.External.Modules public ScatterInfo[] scatter; /// - /// Add slideshows (from the DLC) to the planet + /// Add slideshows to the planet (requires Echoes of the Eye DLC) /// public ProjectionInfo[] slideShows; - /// - /// A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject. - /// - public QuantumGroupInfo[] quantumGroups; - /// /// Add tornadoes to this planet /// @@ -123,8 +118,61 @@ namespace NewHorizons.External.Modules /// public ShuttleInfo[] shuttles; + /// + /// Add a socket quantum object to a planet. Define the position of multiple "sockets" and multiple objects that jump between sockets. + /// If the number of sockets equals the number of objects, they will shuffle around. + /// + public SocketQuantumGroupInfo[] socketQuantumGroups; + + /// + /// Add a state quantum object to a planet. Switches between displaying different objects in a single place. + /// + public StateQuantumGroupInfo[] stateQuantumGroups; + + /// + /// Add quantum lightning to a planet. When lightning strikes, a different detail object is shown. The lightning will take the first defined position/rotation for all objects. + /// + public LightningQuantumInfo[] lightningQuantumGroups; + + /// + /// Add campfires that allow you to enter the dream world/simulation (requires Echoes of the Eye DLC). Must be paired with a dream arrival point, which can be placed on this planet or elsewhere. + /// + public DreamCampfireInfo[] dreamCampfires; + + /// + /// Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire (requires Echoes of the Eye DLC). The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials. + /// + public DreamArrivalPointInfo[] dreamArrivalPoints; + + /// + /// Adds dream world grapple totems to this planet (requires Echoes of the Eye DLC). + /// + public GrappleTotemInfo[] grappleTotems; + + /// + /// Adds dream world alarm totems to this planet (requires Echoes of the Eye DLC). + /// + public AlarmTotemInfo[] alarmTotems; + + /// + /// Adds portholes (the windows you can peek through in the Stranger) to this planet (requires Echoes of the Eye DLC). + /// + public PortholeInfo[] portholes; + + /// + /// Adds dream world candles to this planet (requires Echoes of the Eye DLC). + /// + public DreamCandleInfo[] dreamCandles; + + /// + /// Adds dream world projection totems (requires Echoes of the Eye DLC). + /// + public ProjectionTotemInfo[] projectionTotems; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; + + [Obsolete("quantumGroups is deprecated. Use stateQuantumGroups or socketQuantumGroups instead.")] public QuantumGroupInfo[] quantumGroups; } } diff --git a/NewHorizons/External/Modules/Props/DetailInfo.cs b/NewHorizons/External/Modules/Props/DetailInfo.cs index 171f1b64..c16ff6b2 100644 --- a/NewHorizons/External/Modules/Props/DetailInfo.cs +++ b/NewHorizons/External/Modules/Props/DetailInfo.cs @@ -48,14 +48,13 @@ namespace NewHorizons.External.Modules.Props /// public MVector3 stretch; - /// - /// If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is - /// + [Obsolete("Use QuantumDetailInfo")] public string quantumGroupID; /// /// Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)? /// Also makes this detail visible on the map. + /// Keeping many props loaded is bad for performance so use this only when it's actually relevant /// Most logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided. /// public bool keepLoaded; diff --git a/NewHorizons/External/Modules/Props/Dialogue/AttentionPointInfo.cs b/NewHorizons/External/Modules/Props/Dialogue/AttentionPointInfo.cs new file mode 100644 index 00000000..5dc9a9d8 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Dialogue/AttentionPointInfo.cs @@ -0,0 +1,32 @@ +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.Dialogue +{ + [JsonObject] + public class AttentionPointInfo : GeneralPointPropInfo + { + /// + /// An additional offset to apply to apply when the camera looks at this attention point. + /// + public MVector3 offset; + } + + [JsonObject] + public class SwappedAttentionPointInfo : AttentionPointInfo + { + /// + /// The name of the dialogue node to activate this attention point for. If null or blank, activates for every node. + /// + public string dialogueNode; + /// + /// The index of the page in the current dialogue node to activate this attention point for, if the node has multiple pages. + /// + public int dialoguePage; + /// + /// The easing factor which determines how 'snappy' the camera is when looking at the attention point. + /// + [DefaultValue(1)] public float lookEasing = 1f; + } +} diff --git a/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs b/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs index b2687c3c..f79db190 100644 --- a/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs +++ b/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs @@ -28,6 +28,10 @@ namespace NewHorizons.External.Modules.Props.Dialogue /// If it's a Recorder this will also delete the existing dialogue already attached to that prop. /// /// If none of those components are present it will add a FacePlayerWhenTalking component. + /// + /// `pathToAnimController` also makes the dialogue into a child of the anim controller. This can be used with `isRelativeToParent` + /// to position the dialogue on relative to the speaker. If you also provide `parentPath`, that will instead override which object + /// is the parent, but the anim controller will otherwise function as expected. /// public string pathToAnimController; @@ -47,6 +51,16 @@ namespace NewHorizons.External.Modules.Props.Dialogue /// [DefaultValue(2f)] public float range = 2f; + /// + /// The point that the camera looks at when dialogue advances. + /// + public AttentionPointInfo attentionPoint; + + /// + /// Additional points that the camera looks at when dialogue advances through specific dialogue nodes and pages. + /// + public SwappedAttentionPointInfo[] swappedAttentionPoints; + /// /// Allows you to trigger dialogue from a distance when you walk into an area. /// diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs new file mode 100644 index 00000000..3537b978 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs @@ -0,0 +1,30 @@ +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class AlarmTotemInfo : GeneralPropInfo + { + /// + /// The maximum distance of the alarm's vision cone. + /// + [DefaultValue(45f)] public float sightDistance = 45; + + /// + /// The width of the alarm's vision cone in degrees. + /// + [DefaultValue(60f)] public float sightAngle = 60f; + + /// + /// Scales the visible vision cone in the simulation view (does not affect the actual vision cone detection). + /// + public MVector3 stretchVisionCone; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamArrivalPointInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamArrivalPointInfo.cs new file mode 100644 index 00000000..148ec55d --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamArrivalPointInfo.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class DreamArrivalPointInfo : GeneralPropInfo + { + /// + /// Unique ID for this dream-world arrival point + /// + public string id; + /// + /// Whether to generate simulation meshes (the models used in the "tronworld" or "matrix" view) for most objects on the current planet by cloning the existing meshes and applying the simulation materials. Leave this off if you are building your own simulation meshes or using existing objects which have them. + /// + public bool generateSimulationMeshes; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCampfireInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCampfireInfo.cs new file mode 100644 index 00000000..4b96d0ff --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCampfireInfo.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class DreamCampfireInfo : GeneralPropInfo + { + /// + /// Unique ID for this dream-world campfire + /// + public string id; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs new file mode 100644 index 00000000..10106fb8 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class DreamCandleInfo : GeneralPropInfo + { + /// + /// The type of dream candle this is. + /// + [DefaultValue(DreamCandleType.Ground)] public DreamCandleType type = DreamCandleType.Ground; + + /// + /// Whether the candle should start lit or extinguished. + /// + public bool startLit; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs new file mode 100644 index 00000000..07d1f764 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum DreamCandleType + { + [EnumMember(Value = @"ground")] Ground, + + [EnumMember(Value = @"groundSmall")] GroundSmall, + + [EnumMember(Value = @"groundLarge")] GroundLarge, + + [EnumMember(Value = @"groundSingle")] GroundSingle, + + [EnumMember(Value = @"wall")] Wall, + + [EnumMember(Value = @"wallLargeFlame")] WallLargeFlame, + + [EnumMember(Value = @"wallBigWick")] WallBigWick, + + [EnumMember(Value = @"standing")] Standing, + + [EnumMember(Value = @"pile")] Pile, + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs new file mode 100644 index 00000000..487e42d6 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class GrappleTotemInfo : GeneralPropInfo + { + /// + /// The minimum distance that the player must be from the grapple totem for it to activate. + /// + [DefaultValue(10f)] public float minDistance = 10f; + + /// + /// The distance from the grapple totem that the player will stop at when it activates. + /// + [DefaultValue(4f)] public float arrivalDistance = 4f; + + /// + /// The maximum angle in degrees allowed between the grapple totem's face and the player's lantern in order to activate the totem. + /// + [DefaultValue(45f)] public float maxAngle = 45f; + + /// + /// The maximum distance allowed between the grapple totem's face and the player's lantern in order to activate the totem. + /// + [DefaultValue(29f)] public float maxDistance = 29f; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs new file mode 100644 index 00000000..5f8659d3 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class PortholeInfo : GeneralPropInfo + { + /// + /// Fact IDs to reveal when peeking through the porthole. + /// + public string[] revealFacts; + + /// + /// The field of view of the porthole camera. + /// + [DefaultValue(90f)] public float fieldOfView = 90f; + + /// + /// The location of the camera when the player peeks through the porthole. Can be placed on a different planet. + /// + public PortholeTargetInfo target; + } + + [JsonObject] + public class PortholeTargetInfo : GeneralSolarSystemPropInfo + { + + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs index f56e2cac..e059c310 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs @@ -21,11 +21,43 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye } + [JsonConverter(typeof(StringEnumConverter))] + public enum SlideReelType + { + [EnumMember(Value = @"sixSlides")] SixSlides = 6, + + [EnumMember(Value = @"sevenSlides")] SevenSlides = 7, + + [EnumMember(Value = @"eightSlides")] EightSlides = 8, + + [EnumMember(Value = @"whole")] Whole = 9, + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum SlideReelCondition + { + [EnumMember(Value = @"antique")] Antique = 0, + + [EnumMember(Value = @"pristine")] Pristine = 1, + + [EnumMember(Value = @"rusted")] Rusted = 2, + } + /// /// The ship log facts revealed after finishing this slide reel. /// public string[] reveals; + /// + /// The dialogue conditions to set after finishing this slide reel. + /// + public string[] conditionsToSet; + + /// + /// The persistent conditions to set after finishing this slide reel. + /// + public string[] persistentConditionsToSet; + /// /// The ship log facts that make the reel play when they are displayed in the computer (by selecting entries or arrows). /// You should probably include facts from `reveals` here. @@ -43,6 +75,22 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// The type of object this is. /// [DefaultValue("slideReel")] public SlideShowType type = SlideShowType.SlideReel; - } + /// + /// Exclusive to the slide reel type. Model/mesh of the reel. Each model has a different number of slides on it. Whole has 7 slides but a full ring like 8. + /// + [DefaultValue("sevenSlides")] public SlideReelType reelModel = SlideReelType.SevenSlides; + + /// + /// Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel. + /// + [DefaultValue("antique")] public SlideReelCondition reelCondition = SlideReelCondition.Antique; + + /// + /// Set which slides appear on the slide reel model. Leave empty to default to the first few slides. + /// Takes a list of indices, i.e., to show the first 5 slides in reverse you would put [4, 3, 2, 1, 0]. + /// Index starts at 0. + /// + public int[] displaySlides; + } } diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs new file mode 100644 index 00000000..0852b7e9 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class ProjectionTotemInfo : GeneralPropInfo + { + /// + /// Whether the totem should start lit or extinguished. + /// + public bool startLit; + + /// + /// Whether the projection totem should be able to extinguished but not be able to be lit again with the artifact. Mainly useful if `startLit` is set to true. + /// + public bool extinguishOnly; + + /// + /// A relative path from this planet to an alarm totem that will be activated or deactivated based on whether this totem is lit. + /// + public string pathToAlarmTotem; + + /// + /// Relative paths from this planet to objects containing dream candles that will be activated or deactivated based on whether this totem is lit. All dream candles in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified. + /// + public string[] pathsToDreamCandles; + + /// + /// Relative paths from this planet to projection totems that will be deactivated if this totem is extinguished. All projection totems in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified. + /// + public string[] pathsToProjectionTotems; + + /// + /// Relative paths from this planet to objects that will appear or disappear when this totem is lit or extinguished. Some types of objects and effects are not supported and will remain visible and active. + /// + public string[] pathsToProjectedObjects; + + /// + /// If set, projected objects will be set to fully active or fully disabled instantly instead of smoothly fading lights/renderers/colliders. Use this if the normal behavior is insufficient for the objects you're using. + /// + public bool toggleProjectedObjectsActive; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/SlideInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/SlideInfo.cs index 26b6053d..0b007986 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/SlideInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/SlideInfo.cs @@ -84,5 +84,12 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// Set this to include ship log entry module. Base game default is "". /// public string reveal; + + // SlideRotationModule + + /// + /// Exclusive to slide reels. Whether this slide should rotate the reel item while inside a projector. + /// + [DefaultValue(true)] public bool rotate = true; } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/Props/EyeOfTheUniverse/EyeTravelerInfo.cs b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/EyeTravelerInfo.cs new file mode 100644 index 00000000..82f5abe8 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/EyeTravelerInfo.cs @@ -0,0 +1,50 @@ +using NewHorizons.External.Modules.Props.Audio; +using NewHorizons.External.Modules.Props.Dialogue; +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse +{ + [JsonObject] + public class EyeTravelerInfo : DetailInfo + { + /// + /// A unique ID to associate this traveler with their corresponding quantum instruments and instrument zones. Must be unique for each traveler. + /// + public string id; + + /// + /// If set, the player must know this ship log fact for this traveler (and their instrument zones and quantum instruments) to appear. The fact does not need to exist in the current star system; the player's save data will be checked directly. + /// + public string requiredFact; + + /// + /// If set, the player must have this persistent dialogue condition set for this traveler (and their instrument zones and quantum instruments) to appear. + /// + public string requiredPersistentCondition; + + /// + /// The dialogue condition that will trigger the traveler to start playing their instrument. Must be unique for each traveler. + /// + public string startPlayingCondition; + + /// + /// If specified, this dialogue condition must be set for the traveler to participate in the campfire song. Otherwise, the song will be able to start without them. + /// + public string participatingCondition; + + /// + /// The audio signal to use for the traveler while playing around the campfire (and also for their paired quantum instrument if another is not specified). The audio clip should be 16 measures at 92 BPM (approximately 42 seconds long). + /// + public SignalInfo signal; + + /// + /// The audio to use for the traveler during the finale of the campfire song. It should be 8 measures of the main loop at 92 BPM followed by 2 measures of fade-out (approximately 26 seconds long in total). Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string finaleAudio; + + /// + /// The dialogue to use for this traveler. If omitted, the first CharacterDialogueTree in the object will be used. + /// + public DialogueInfo dialogue; + } +} diff --git a/NewHorizons/External/Modules/Props/EyeOfTheUniverse/InstrumentZoneInfo.cs b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/InstrumentZoneInfo.cs new file mode 100644 index 00000000..047babb9 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/InstrumentZoneInfo.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse +{ + [JsonObject] + public class InstrumentZoneInfo : DetailInfo + { + /// + /// The unique ID of the Eye Traveler associated with this instrument zone. + /// + public string id; + } +} diff --git a/NewHorizons/External/Modules/Props/EyeOfTheUniverse/QuantumInstrumentInfo.cs b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/QuantumInstrumentInfo.cs new file mode 100644 index 00000000..1a686b9c --- /dev/null +++ b/NewHorizons/External/Modules/Props/EyeOfTheUniverse/QuantumInstrumentInfo.cs @@ -0,0 +1,40 @@ +using NewHorizons.External.Modules.Props.Audio; +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.EyeOfTheUniverse +{ + [JsonObject] + public class QuantumInstrumentInfo : DetailInfo + { + /// + /// The unique ID of the Eye Traveler associated with this quantum instrument. + /// + public string id; + + /// + /// A dialogue condition to set when gathering this quantum instrument. Use it in conjunction with `activationCondition` or `deactivationCondition` on other details. + /// + public string gatherCondition; + + /// + /// Allows gathering this quantum instrument using the zoomed-in signalscope, like Chert's bongos. + /// + public bool gatherWithScope; + + /// + /// The audio signal emitted by this quantum instrument. The fields `name`, `audio`, and `frequency` will be copied from the corresponding Eye Traveler's signal if not specified here. + /// + public SignalInfo signal; + + /// + /// The radius of the added sphere collider that will be used for interaction. + /// + [DefaultValue(0.5f)] public float interactRadius = 0.5f; + + /// + /// The furthest distance where the player can interact with this quantum instrument. + /// + [DefaultValue(2f)] public float interactRange = 2f; + } +} diff --git a/NewHorizons/External/Modules/Props/Item/ItemInfo.cs b/NewHorizons/External/Modules/Props/Item/ItemInfo.cs index 6307db11..23253119 100644 --- a/NewHorizons/External/Modules/Props/Item/ItemInfo.cs +++ b/NewHorizons/External/Modules/Props/Item/ItemInfo.cs @@ -44,6 +44,22 @@ namespace NewHorizons.External.Modules.Props.Item /// public MVector3 dropNormal; /// + /// A relative offset to apply to the item's position when holding it. The initial position varies for vanilla item types. + /// + public MVector3 holdOffset; + /// + /// A relative offset to apply to the item's rotation when holding it. + /// + public MVector3 holdRotation; + /// + /// A relative offset to apply to the item's position when placing it into a socket. + /// + public MVector3 socketOffset; + /// + /// A relative offset to apply to the item's rotation when placing it into a socket. + /// + public MVector3 socketRotation; + /// /// The audio to play when this item is picked up. Only applies to custom/non-vanilla item types. /// Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. /// diff --git a/NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs b/NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs index 76ac16f7..7294fbb0 100644 --- a/NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs +++ b/NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs @@ -1,13 +1,5 @@ -using NewHorizons.External.SerializableData; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; namespace NewHorizons.External.Modules.Props.Item { @@ -54,5 +46,10 @@ namespace NewHorizons.External.Modules.Props.Item /// A ship log fact to reveal when removing an item from this socket, or when the socket is empty. /// public string removalFact; + /// + /// Default collider radius when interacting with the socket + /// + [DefaultValue(0f)] + public float colliderRadius = 0f; } } diff --git a/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs b/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs new file mode 100644 index 00000000..1872b11e --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs @@ -0,0 +1,14 @@ +namespace NewHorizons.External.Modules.Props.Quantum; + +public class BaseQuantumGroupInfo +{ + /// + /// Optional name to help identify this group + /// + public string rename; + + /// + /// List of props which will be used in this quantum group + /// + public QuantumDetailInfo[] details; +} diff --git a/NewHorizons/External/Modules/Props/Quantum/LightningQuantumInfo.cs b/NewHorizons/External/Modules/Props/Quantum/LightningQuantumInfo.cs new file mode 100644 index 00000000..3c370dd1 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/LightningQuantumInfo.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules.Props.Quantum; + +[JsonObject] +public class LightningQuantumInfo : GeneralPropInfo +{ + /// + /// List of props which will be alternated between during flashes of lightning + /// + public DetailInfo[] details; +} diff --git a/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs b/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs new file mode 100644 index 00000000..304f1c89 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.Quantum; + +public class QuantumDetailInfo : DetailInfo +{ + public QuantumDetailInfo() { } + + public QuantumDetailInfo(DetailInfo info) + { + JsonConvert.PopulateObject(JsonConvert.SerializeObject(info), this); + } + + /// + /// When used in a quantum socket this object will randomize its rotation around the local Y axis. + /// + [DefaultValue(true)] public bool randomizeYRotation = true; + + /// + /// When used in a quantum socket this object will align to the nearest gravity volume. Else use the rotation of the quantum socket. + /// + [DefaultValue(true)] public bool alignWithGravity = true; +} diff --git a/NewHorizons/External/Modules/Props/Quantum/QuantumGroupInfo.cs b/NewHorizons/External/Modules/Props/Quantum/QuantumGroupInfo.cs index bb76c6e3..2c056fe3 100644 --- a/NewHorizons/External/Modules/Props/Quantum/QuantumGroupInfo.cs +++ b/NewHorizons/External/Modules/Props/Quantum/QuantumGroupInfo.cs @@ -1,9 +1,11 @@ using Newtonsoft.Json; +using System; using System.ComponentModel; namespace NewHorizons.External.Modules.Props.Quantum { [JsonObject] + [Obsolete] public class QuantumGroupInfo { /// @@ -34,6 +36,6 @@ namespace NewHorizons.External.Modules.Props.Quantum /// /// Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state /// - [DefaultValue(true)] public bool loop = true; + [DefaultValue(true)] public bool loop = true; } } diff --git a/NewHorizons/External/Modules/Props/Quantum/QuantumGroupType.cs b/NewHorizons/External/Modules/Props/Quantum/QuantumGroupType.cs index 8fb3a78f..36264cc0 100644 --- a/NewHorizons/External/Modules/Props/Quantum/QuantumGroupType.cs +++ b/NewHorizons/External/Modules/Props/Quantum/QuantumGroupType.cs @@ -1,10 +1,12 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using System; using System.Runtime.Serialization; namespace NewHorizons.External.Modules.Props.Quantum { [JsonConverter(typeof(StringEnumConverter))] + [Obsolete] public enum QuantumGroupType { [EnumMember(Value = @"sockets")] Sockets = 0, diff --git a/NewHorizons/External/Modules/Props/Quantum/SocketQuantumGroupInfo.cs b/NewHorizons/External/Modules/Props/Quantum/SocketQuantumGroupInfo.cs new file mode 100644 index 00000000..54475773 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/SocketQuantumGroupInfo.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules.Props.Quantum; + +[JsonObject] +public class SocketQuantumGroupInfo : BaseQuantumGroupInfo +{ + public QuantumSocketInfo[] sockets; +} diff --git a/NewHorizons/External/Modules/Props/Quantum/StateQuantumGroupInfo.cs b/NewHorizons/External/Modules/Props/Quantum/StateQuantumGroupInfo.cs new file mode 100644 index 00000000..4be2253b --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/StateQuantumGroupInfo.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.Quantum; + +[JsonObject] +public class StateQuantumGroupInfo : BaseQuantumGroupInfo +{ + /// + /// Optional. If this is true, then the states will be presented in order, rather than in a random order + /// + public bool sequential; + + /// + /// Optional. Only used if `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state + /// + [DefaultValue(true)] public bool loop = true; + + /// + /// Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states. + /// + public bool hasEmptyState; +} diff --git a/NewHorizons/External/Modules/Props/Remote/StoneInfo.cs b/NewHorizons/External/Modules/Props/Remote/ProjectionStoneInfo.cs similarity index 66% rename from NewHorizons/External/Modules/Props/Remote/StoneInfo.cs rename to NewHorizons/External/Modules/Props/Remote/ProjectionStoneInfo.cs index 0380b676..eeeab082 100644 --- a/NewHorizons/External/Modules/Props/Remote/StoneInfo.cs +++ b/NewHorizons/External/Modules/Props/Remote/ProjectionStoneInfo.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace NewHorizons.External.Modules.Props.Remote { [JsonObject] - public class StoneInfo : GeneralPropInfo + public class ProjectionStoneInfo : GeneralPropInfo { } diff --git a/NewHorizons/External/Modules/Props/Remote/RemoteInfo.cs b/NewHorizons/External/Modules/Props/Remote/RemoteInfo.cs index ab8e1faf..a9467892 100644 --- a/NewHorizons/External/Modules/Props/Remote/RemoteInfo.cs +++ b/NewHorizons/External/Modules/Props/Remote/RemoteInfo.cs @@ -23,11 +23,11 @@ namespace NewHorizons.External.Modules.Props.Remote /// /// Camera platform that the stones can project to and from /// - public PlatformInfo platform; + public RemotePlatformInfo platform; /// /// Projection stones /// - public StoneInfo[] stones; + public ProjectionStoneInfo[] stones; } } diff --git a/NewHorizons/External/Modules/Props/Remote/PlatformInfo.cs b/NewHorizons/External/Modules/Props/Remote/RemotePlatformInfo.cs similarity index 91% rename from NewHorizons/External/Modules/Props/Remote/PlatformInfo.cs rename to NewHorizons/External/Modules/Props/Remote/RemotePlatformInfo.cs index 5ad4afa9..e5b0cca7 100644 --- a/NewHorizons/External/Modules/Props/Remote/PlatformInfo.cs +++ b/NewHorizons/External/Modules/Props/Remote/RemotePlatformInfo.cs @@ -4,7 +4,7 @@ using System.ComponentModel; namespace NewHorizons.External.Modules.Props.Remote { [JsonObject] - public class PlatformInfo : GeneralPropInfo + public class RemotePlatformInfo : GeneralPropInfo { /// /// A ship log fact to reveal when the platform is connected to. diff --git a/NewHorizons/External/Modules/Props/ScatterInfo.cs b/NewHorizons/External/Modules/Props/ScatterInfo.cs index b53cd4e8..a03b2003 100644 --- a/NewHorizons/External/Modules/Props/ScatterInfo.cs +++ b/NewHorizons/External/Modules/Props/ScatterInfo.cs @@ -66,5 +66,10 @@ namespace NewHorizons.External.Modules.Props /// Should this detail stay loaded even if you're outside the sector (good for very large props) /// public bool keepLoaded; + + /// + /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). This parent should be at the position where you'd like to scatter (which would usually be zero). + /// + public string parentPath; } } diff --git a/NewHorizons/External/Modules/ShipLogModule.cs b/NewHorizons/External/Modules/ShipLogModule.cs index 10828598..3b9e5615 100644 --- a/NewHorizons/External/Modules/ShipLogModule.cs +++ b/NewHorizons/External/Modules/ShipLogModule.cs @@ -23,7 +23,7 @@ namespace NewHorizons.External.Modules public MapModeInfo mapMode; /// - /// A path to the folder where entry sprites are stored. + /// A path to the folder where entry sprites (.png) are stored. /// public string spriteFolder; @@ -61,7 +61,7 @@ namespace NewHorizons.External.Modules public float offset; /// - /// The path to the sprite to show when the planet is unexplored in map mode. + /// The path to the sprite (.png/.jpg/.exr) to show when the planet is unexplored in map mode. /// public string outlineSprite; @@ -71,7 +71,7 @@ namespace NewHorizons.External.Modules public bool remove; /// - /// The path to the sprite to show when the planet is revealed in map mode. + /// The path to the sprite (.png/.jpg/.exr) to show when the planet is revealed in map mode. /// public string revealedSprite; @@ -79,6 +79,12 @@ namespace NewHorizons.External.Modules /// Scale to apply to the planet in map mode. /// [DefaultValue(1f)] public float scale = 1f; + + /// + /// Can this ship log map mode entry be selected + /// Ex) Set to false for stars with no entries on them so they are skipped in navigation + /// + [DefaultValue(true)] public bool selectable = true; } [JsonObject] @@ -90,7 +96,7 @@ namespace NewHorizons.External.Modules public bool invisibleWhenHidden; /// - /// The sprite to show when the parent AstroBody is rumored/unexplored. + /// The sprite (.png/.jpg/.exr) to show when the parent AstroBody is rumored/unexplored. /// public string outlineSprite; @@ -100,7 +106,7 @@ namespace NewHorizons.External.Modules public MVector2 position; /// - /// The sprite to show when the parent AstroBody is revealed. + /// The sprite (.png/.jpg/.exr) to show when the parent AstroBody is revealed. /// public string revealedSprite; diff --git a/NewHorizons/External/Modules/SpawnModule.cs b/NewHorizons/External/Modules/SpawnModule.cs index f8af3fa1..3c62766a 100644 --- a/NewHorizons/External/Modules/SpawnModule.cs +++ b/NewHorizons/External/Modules/SpawnModule.cs @@ -1,4 +1,6 @@ +using NewHorizons.Builder.General; using NewHorizons.External.SerializableData; +using NewHorizons.Handlers; using Newtonsoft.Json; using System; @@ -9,12 +11,20 @@ namespace NewHorizons.External.Modules { /// /// If you want the player to spawn on the new body, set a value for this. + /// Different spawns can be unlocked with persistent conditions and facts /// - public PlayerSpawnPoint playerSpawn; + public PlayerSpawnPoint[] playerSpawnPoints; /// /// Required for the system to be accessible by warp drive. + /// Different spawns can be unlocked with persistent conditions and facts /// + public ShipSpawnPoint[] shipSpawnPoints; + + [Obsolete("Use playerSpawnPoints instead")] + public PlayerSpawnPoint playerSpawn; + + [Obsolete("Use shipSpawnPoints instead")] public ShipSpawnPoint shipSpawn; [Obsolete("playerSpawnPoint is deprecated. Use playerSpawn.position instead")] public MVector3 playerSpawnPoint; @@ -30,6 +40,56 @@ namespace NewHorizons.External.Modules /// Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0). /// public MVector3 offset; + + /// + /// Whether this planet's spawn point is the one the player/ship will initially spawn at, if multiple spawn points exist. + /// Do not use at the same time as makeDefaultIfFactRevealed or makeDefaultIfPersistentCondition + /// Spawns unlocked with this have lowest priority + /// + public bool isDefault; + + /// + /// If the given ship log fact is revealed, this spawn point will be used + /// Do not use at the same time as isDefault or makeDefaultIfPersistentCondition + /// Spawns unlocked with this have highest priority + /// + public string makeDefaultIfFactRevealed; + + /// + /// If the given persistent condition is true, this spawn point will be used + /// Do not use at the same time as isDefault or makeDefaultIfFactRevealed + /// Spawns unlocked with this have second highest priority + /// + public string makeDefaultIfPersistentCondition; + + /// + /// ID used to have a black hole or warp volume bring the player to this spawn specifically + /// + public string id; + + public int GetPriority() + { + if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(PlayerSpawnHandler.TargetSpawnID) && id == PlayerSpawnHandler.TargetSpawnID) + { + return 3; + } + if (!string.IsNullOrEmpty(makeDefaultIfFactRevealed) && ShipLogHandler.KnowsFact(makeDefaultIfFactRevealed)) + { + return 2; + } + if (!string.IsNullOrEmpty(makeDefaultIfPersistentCondition) && PlayerData.GetPersistentCondition(makeDefaultIfPersistentCondition)) + { + return 1; + } + if (isDefault) + { + return 0; + } + else + { + return -1; + } + } } [JsonObject] @@ -39,12 +99,6 @@ namespace NewHorizons.External.Modules /// If you spawn on a planet with no oxygen, you probably want to set this to true ;;) /// public bool startWithSuit; - /// - /// Whether this planet's spawn point is the one the player will initially spawn at, if multiple spawn points exist. - /// - public bool isDefault; - - } [JsonObject] diff --git a/NewHorizons/External/Modules/VariableSize/SingularityModule.cs b/NewHorizons/External/Modules/VariableSize/SingularityModule.cs index d85a399c..042cbe02 100644 --- a/NewHorizons/External/Modules/VariableSize/SingularityModule.cs +++ b/NewHorizons/External/Modules/VariableSize/SingularityModule.cs @@ -54,6 +54,12 @@ namespace NewHorizons.External.Modules.VariableSize /// public string targetStarSystem; + /// + /// If this is a black hole loading a new star system, set the ID of the spawn point you want to use + /// Otherwise, will use the default spawn + /// + public string spawnPointID; + /// /// Type of singularity (white hole or black hole) /// diff --git a/NewHorizons/External/Modules/VariableSize/WaterModule.cs b/NewHorizons/External/Modules/VariableSize/WaterModule.cs index 3443f162..d28ffb7c 100644 --- a/NewHorizons/External/Modules/VariableSize/WaterModule.cs +++ b/NewHorizons/External/Modules/VariableSize/WaterModule.cs @@ -26,5 +26,11 @@ namespace NewHorizons.External.Modules.VariableSize /// Tint of the water /// public MColor tint; + + /// + /// Will the ship automatically try to orient itself to face upwards while in this volume? + /// + [DefaultValue(true)] + public bool allowShipAutoroll = true; } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/ChangeStarSystemVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/ChangeStarSystemVolumeInfo.cs index 969a5945..24b492db 100644 --- a/NewHorizons/External/Modules/Volumes/VolumeInfos/ChangeStarSystemVolumeInfo.cs +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/ChangeStarSystemVolumeInfo.cs @@ -10,5 +10,11 @@ namespace NewHorizons.External.Modules.Volumes.VolumeInfos /// The star system that entering this volume will send you to. /// [DefaultValue("SolarSystem")] public string targetStarSystem; + + /// + /// ID assigned to a spawn point in the other system that the player will be sent to + /// Uses the default spawn if not set + /// + public string spawnPointID; } } diff --git a/NewHorizons/External/NewHorizonBody.cs b/NewHorizons/External/NewHorizonBody.cs index 6779e526..2016d8d0 100644 --- a/NewHorizons/External/NewHorizonBody.cs +++ b/NewHorizons/External/NewHorizonBody.cs @@ -3,6 +3,7 @@ using NewHorizons.Utility.Files; using NewHorizons.Utility.OWML; using OWML.Common; using System; +using System.IO; using System.Linq; using UnityEngine; @@ -15,8 +16,6 @@ namespace NewHorizons.External Config = config; Mod = mod; RelativePath = relativePath; - - Migrate(); } public PlanetConfig Config; @@ -32,8 +31,11 @@ namespace NewHorizons.External { var detailPaths = Config?.Props?.details?.Select(x => x.path) ?? Array.Empty(); return Config?.Cloak != null + || Config?.Dream != null || Config?.Props?.rafts != null || Config?.Props?.slideShows != null + || Config?.Props?.dreamArrivalPoints != null + || Config?.Props?.dreamCampfires != null || detailPaths.Any(x => x.StartsWith("RingWorld_Body") || x.StartsWith("DreamWorld_Body")); } catch @@ -99,12 +101,6 @@ namespace NewHorizons.External } } } - - // Because these guys put TWO spawn points - if (Mod.ModHelper.Manifest.UniqueName == "2walker2.Evacuation" && Config.name == "The Campground") - { - Config.Spawn.playerSpawn.isDefault = true; - } } #endregion diff --git a/NewHorizons/External/NewHorizonsData.cs b/NewHorizons/External/NewHorizonsData.cs index 63a62392..cedb624a 100644 --- a/NewHorizons/External/NewHorizonsData.cs +++ b/NewHorizons/External/NewHorizonsData.cs @@ -187,7 +187,11 @@ namespace NewHorizons.External public static void ReadOneTimePopup(string id) { - _activeProfile?.PopupsRead.Add(id); + // else it re-adds it each time + if (_activeProfile != null && !_activeProfile.PopupsRead.Contains(id)) + { + _activeProfile.PopupsRead.Add(id); + } } public static bool HasReadOneTimePopup(string id) diff --git a/NewHorizons/External/NewHorizonsSystem.cs b/NewHorizons/External/NewHorizonsSystem.cs index 0724a027..f4928f17 100644 --- a/NewHorizons/External/NewHorizonsSystem.cs +++ b/NewHorizons/External/NewHorizonsSystem.cs @@ -1,6 +1,6 @@ using NewHorizons.External.Configs; -using NewHorizons.External.Modules; using OWML.Common; +using System.Linq; namespace NewHorizons.External { @@ -8,10 +8,9 @@ namespace NewHorizons.External { public string UniqueID; public string RelativePath; - public SpawnModule Spawn = null; - public SpawnPoint SpawnPoint = null; public StarSystemConfig Config; public IModBehaviour Mod; + public bool HasShipSpawn; public NewHorizonsSystem(string uniqueID, StarSystemConfig config, string relativePath, IModBehaviour mod) { @@ -19,6 +18,12 @@ namespace NewHorizons.External Config = config; RelativePath = relativePath; Mod = mod; + + // Backwards compat + if (new string[] { "2walker2.OogaBooga", "2walker2.EndingIfYouWarpHereYouAreMean", "FeldsparSystem" }.Contains(uniqueID)) + { + config.canExitViaWarpDrive = false; + } } } } diff --git a/NewHorizons/External/SerializableData/MVector2.cs b/NewHorizons/External/SerializableData/MVector2.cs index 0e4aadd6..5ab03248 100644 --- a/NewHorizons/External/SerializableData/MVector2.cs +++ b/NewHorizons/External/SerializableData/MVector2.cs @@ -1,6 +1,5 @@ #region -using NewHorizons.Utility.DebugTools.Menu; using NewHorizons.Utility.OWML; using Newtonsoft.Json; using System; diff --git a/NewHorizons/External/SerializableData/MVector3.cs b/NewHorizons/External/SerializableData/MVector3.cs index 33100bc1..f61cb87b 100644 --- a/NewHorizons/External/SerializableData/MVector3.cs +++ b/NewHorizons/External/SerializableData/MVector3.cs @@ -27,6 +27,8 @@ namespace NewHorizons.External.SerializableData return new Vector3(vec.x, vec.y, vec.z); } + public float Length() => Mathf.Sqrt(x * x + y * y + z * z); + public override string ToString() => $"{x}, {y}, {z}"; } } diff --git a/NewHorizons/External/SerializableEnums/NHDeathType.cs b/NewHorizons/External/SerializableEnums/NHDeathType.cs index d01b9beb..f683e0ec 100644 --- a/NewHorizons/External/SerializableEnums/NHDeathType.cs +++ b/NewHorizons/External/SerializableEnums/NHDeathType.cs @@ -4,6 +4,27 @@ using System.Runtime.Serialization; namespace NewHorizons.External.SerializableEnums { + /// + /// Some special death types are: + /// + /// Supernova: Special death type used when the supernova hits you. You will not wake up if in the Dreamworld. + /// + /// Digestion: Death type used by anglerfish (and cut-content ghosts and water monster) + /// + /// Big bang: Special death type used at the end of the game + /// + /// Meditation: Special death type used when skipping to the next loop. You will not wake up if in the Dreamworld. + /// + /// Timeloop: Special death type used when the time loop ends. You will not wake up if in the Dreamworld. + /// + /// Blackhole: Special death type used by the ATP blackhole (and custom NH blackholes without whitehole destinations) + /// + /// Dream: Special DLC death type used to kill a player in the real world while in the Dreamworld (i.e., you will loop not wake up) + /// + /// DreamExplosion: Special DLC death type used by the prototype artifact to kill a player in the real world while in the Dreamworld (i.e., you will loop not wake up) + /// + /// CrushedByElevator: Similar to the Crushed death type, but much faster + /// [JsonConverter(typeof(StringEnumConverter))] public enum NHDeathType { diff --git a/NewHorizons/Handlers/DreamHandler.cs b/NewHorizons/Handlers/DreamHandler.cs new file mode 100644 index 00000000..9098170d --- /dev/null +++ b/NewHorizons/Handlers/DreamHandler.cs @@ -0,0 +1,32 @@ +using NewHorizons.Utility; +using NewHorizons.Utility.OWML; +using OWML.Utils; +using System; +using UnityEngine; + +namespace NewHorizons.Handlers +{ + public static class DreamHandler + { + public static DreamArrivalPoint.Location GetDreamArrivalLocation(string id) + { + try + { + if (EnumUtils.TryParse(id, out DreamArrivalPoint.Location location)) + { + return location; + } + else + { + NHLogger.LogVerbose($"Registering new dream arrival location [{id}]"); + return EnumUtilities.Create(id); + } + } + catch (Exception e) + { + NHLogger.LogError($"Couldn't load dream arrival location [{id}]:\n{e}"); + return DreamArrivalPoint.Location.Undefined; + } + } + } +} diff --git a/NewHorizons/Handlers/EyeDetailCacher.cs b/NewHorizons/Handlers/EyeDetailCacher.cs new file mode 100644 index 00000000..f4ff2ed0 --- /dev/null +++ b/NewHorizons/Handlers/EyeDetailCacher.cs @@ -0,0 +1,93 @@ +using NewHorizons.Utility; +using NewHorizons.Utility.OWML; +using System.Linq; + +namespace NewHorizons.Handlers; + +public static class EyeDetailCacher +{ + public static bool IsInitialized; + + public static void Init() + { + if (IsInitialized) return; + + SearchUtilities.ClearDontDestroyOnLoadCache(); + + IsInitialized = true; + + foreach (var body in Main.BodyDict["EyeOfTheUniverse"]) + { + NHLogger.LogVerbose($"{nameof(EyeDetailCacher)}: {body.Config.name}"); + if (body.Config?.EyeOfTheUniverse?.eyeTravelers != null) + { + foreach (var detail in body.Config.EyeOfTheUniverse.eyeTravelers) + { + if (!string.IsNullOrEmpty(detail.assetBundle)) continue; + + AddPathToCache(detail.path); + } + } + + if (body.Config?.EyeOfTheUniverse?.instrumentZones != null) + { + foreach (var detail in body.Config.EyeOfTheUniverse.instrumentZones) + { + if (!string.IsNullOrEmpty(detail.assetBundle)) continue; + + AddPathToCache(detail.path); + } + } + + if (body.Config?.EyeOfTheUniverse?.quantumInstruments != null) + { + foreach (var detail in body.Config.EyeOfTheUniverse.quantumInstruments) + { + if (!string.IsNullOrEmpty(detail.assetBundle)) continue; + + AddPathToCache(detail.path); + } + } + + if (body.Config?.Props?.details != null) + { + foreach (var detail in body.Config.Props.details) + { + if (!string.IsNullOrEmpty(detail.assetBundle)) continue; + + AddPathToCache(detail.path); + } + } + + if (body.Config?.Props?.scatter != null) + { + foreach (var scatter in body.Config.Props.scatter) + { + if (!string.IsNullOrEmpty(scatter.assetBundle)) continue; + + AddPathToCache(scatter.path); + } + } + } + } + + private static void AddPathToCache(string path) + { + NHLogger.LogVerbose($"{nameof(EyeDetailCacher)}: {path}"); + + if (string.IsNullOrEmpty(path)) return; + + var planet = path.Contains('/') ? path.Split('/').First() : string.Empty; + + if (planet != "EyeOfTheUniverse_Body" && planet != "Vessel_Body") + { + NHLogger.LogVerbose($"{nameof(EyeDetailCacher)}: Looking for {path}"); + var obj = SearchUtilities.Find(path); + if (obj != null) + { + NHLogger.LogVerbose($"{nameof(EyeDetailCacher)}: Added solar system asset to dont destroy on load cache for eye: {path}"); + SearchUtilities.AddToDontDestroyOnLoadCache(path, obj); + } + } + } +} diff --git a/NewHorizons/Handlers/EyeSceneHandler.cs b/NewHorizons/Handlers/EyeSceneHandler.cs index a0c925a9..1433e9ee 100644 --- a/NewHorizons/Handlers/EyeSceneHandler.cs +++ b/NewHorizons/Handlers/EyeSceneHandler.cs @@ -1,14 +1,66 @@ using NewHorizons.Builder.General; using NewHorizons.Components.EyeOfTheUniverse; using NewHorizons.Components.Stars; +using NewHorizons.External.Modules.Props.EyeOfTheUniverse; using NewHorizons.External.SerializableData; using NewHorizons.Utility; +using NewHorizons.Utility.OWML; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace NewHorizons.Handlers { public static class EyeSceneHandler { + private static Dictionary _eyeTravelers = new(); + private static EyeMusicController _eyeMusicController; + + public static void Init() + { + _eyeTravelers.Clear(); + _eyeMusicController = null; + } + + public static EyeMusicController GetMusicController() + { + return _eyeMusicController; + } + + public static EyeTravelerData GetEyeTravelerData(string id) + { + if (_eyeTravelers.TryGetValue(id, out EyeTravelerData traveler)) + { + return traveler; + } + return traveler; + } + + public static EyeTravelerData CreateEyeTravelerData(string id) + { + if (_eyeTravelers.TryGetValue(id, out EyeTravelerData traveler)) + { + return traveler; + } + traveler = new EyeTravelerData() + { + id = id, + info = null, + controller = null, + loopAudioSource = null, + finaleAudioSource = null, + instrumentZones = new(), + quantumInstruments = new(), + }; + _eyeTravelers[traveler.id] = traveler; + return traveler; + } + + public static List GetActiveCustomEyeTravelers() + { + return _eyeTravelers.Values.Where(t => t.requirementsMet).ToList(); + } + public static void OnSceneLoad() { // Create astro objects for eye and vessel because they didn't have them somehow. @@ -134,5 +186,142 @@ namespace NewHorizons.Handlers SunLightEffectsController.AddStar(starController); SunLightEffectsController.AddStarLight(sunLight); } + + public static void SetUpEyeCampfireSequence() + { + if (!GetActiveCustomEyeTravelers().Any()) return; + + _eyeMusicController = new GameObject("EyeMusicController").AddComponent(); + + var quantumCampsiteController = Object.FindObjectOfType(); + var cosmicInflationController = _eyeMusicController.CosmicInflationController; + + _eyeMusicController.RegisterFinaleSource(cosmicInflationController._travelerFinaleSource); + + foreach (var controller in quantumCampsiteController._travelerControllers) + { + _eyeMusicController.RegisterLoopSource(controller._signal.GetOWAudioSource()); + } + + foreach (var eyeTraveler in GetActiveCustomEyeTravelers()) + { + if (eyeTraveler.controller != null) + { + eyeTraveler.controller.gameObject.SetActive(false); + + ArrayHelpers.Append(ref quantumCampsiteController._travelerControllers, eyeTraveler.controller); + eyeTraveler.controller.OnStartPlaying += quantumCampsiteController.OnTravelerStartPlaying; + + ArrayHelpers.Append(ref cosmicInflationController._travelers, eyeTraveler.controller); + eyeTraveler.controller.OnStartPlaying += cosmicInflationController.OnTravelerStartPlaying; + + ArrayHelpers.Append(ref cosmicInflationController._inflationObjects, eyeTraveler.controller.transform); + } + else + { + NHLogger.LogError($"Missing Eye Traveler for ID \"{eyeTraveler.id}\""); + } + + if (eyeTraveler.loopAudioSource != null) + { + eyeTraveler.loopAudioSource.GetComponent().SetSignalActivation(false); + _eyeMusicController.RegisterLoopSource(eyeTraveler.loopAudioSource); + } + if (eyeTraveler.finaleAudioSource != null) + { + _eyeMusicController.RegisterFinaleSource(eyeTraveler.finaleAudioSource); + } + + foreach (var quantumInstrument in eyeTraveler.quantumInstruments) + { + ArrayHelpers.Append(ref quantumInstrument._activateObjects, eyeTraveler.controller.gameObject); + ArrayHelpers.Append(ref quantumInstrument._deactivateObjects, eyeTraveler.instrumentZones.Select(z => z.gameObject)); + + var ancestorInstrumentZone = quantumInstrument.GetComponentInParent(); + if (ancestorInstrumentZone == null) + { + // Quantum instrument is not a child of an instrument zone, so treat it like its own zone + quantumInstrument.gameObject.SetActive(false); + ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, quantumInstrument.gameObject); + + ArrayHelpers.Append(ref cosmicInflationController._inflationObjects, quantumInstrument.transform); + } + } + + foreach (var instrumentZone in eyeTraveler.instrumentZones) + { + instrumentZone.gameObject.SetActive(false); + ArrayHelpers.Append(ref quantumCampsiteController._instrumentZones, instrumentZone.gameObject); + + ArrayHelpers.Append(ref cosmicInflationController._inflationObjects, instrumentZone.transform); + } + } + + UpdateTravelerPositions(); + } + + public static void UpdateTravelerPositions() + { + if (!GetActiveCustomEyeTravelers().Any()) return; + + var quantumCampsiteController = Object.FindObjectOfType(); + + var travelers = new List() + { + quantumCampsiteController._travelerControllers[0].transform, // Riebeck + quantumCampsiteController._travelerControllers[2].transform, // Chert + quantumCampsiteController._travelerControllers[6].transform, // Esker + quantumCampsiteController._travelerControllers[1].transform, // Felspar + quantumCampsiteController._travelerControllers[3].transform, // Gabbro + }; + + if (quantumCampsiteController._hasMetSolanum) + { + travelers.Add(quantumCampsiteController._travelerControllers[4].transform); // Solanum + } + if (quantumCampsiteController._hasMetPrisoner) + { + travelers.Add(quantumCampsiteController._travelerControllers[5].transform); // Prisoner + } + + // Custom travelers (starting at index 7) + for (int i = 7; i < quantumCampsiteController._travelerControllers.Length; i++) + { + travelers.Add(quantumCampsiteController._travelerControllers[i].transform); + } + + var radius = 2f + 0.2f * travelers.Count; + var angle = Mathf.PI * 2f / travelers.Count; + var index = 0; + + foreach (var traveler in travelers) + { + // Esker isn't at height 0 so we have to do all this + var initialY = traveler.transform.position.y; + var newPos = quantumCampsiteController.transform.TransformPoint(new Vector3( + Mathf.Cos(angle * index) * radius, + 0f, + -Mathf.Sin(angle * index) * radius + )); + newPos.y = initialY; + traveler.transform.position = newPos; + var lookTarget = quantumCampsiteController.transform.position; + lookTarget.y = newPos.y; + traveler.transform.LookAt(lookTarget, traveler.transform.up); + index++; + } + } + + public class EyeTravelerData + { + public string id; + public EyeTravelerInfo info; + public TravelerEyeController controller; + public OWAudioSource loopAudioSource; + public OWAudioSource finaleAudioSource; + public List quantumInstruments = new(); + public List instrumentZones = new(); + public bool requirementsMet; + } } } diff --git a/NewHorizons/Handlers/FadeHandler.cs b/NewHorizons/Handlers/FadeHandler.cs index 41453488..9c15138a 100644 --- a/NewHorizons/Handlers/FadeHandler.cs +++ b/NewHorizons/Handlers/FadeHandler.cs @@ -5,6 +5,10 @@ using UnityEngine; namespace NewHorizons.Handlers { + /// + /// copied from LoadManager. + /// exists so we can do things after the fade without patching. + /// public static class FadeHandler { public static void FadeOut(float length) => Delay.StartCoroutine(FadeOutCoroutine(length)); @@ -19,11 +23,14 @@ namespace NewHorizons.Handlers while (Time.unscaledTime < endTime) { - LoadManager.s_instance._fadeImage.color = Color.Lerp(Color.clear, Color.black, (Time.unscaledTime - startTime) / length); + var t = Mathf.Clamp01((Time.unscaledTime - startTime) / length); + LoadManager.s_instance._fadeImage.color = Color.Lerp(Color.clear, Color.black, t); + AudioListener.volume = 1f - t; yield return new WaitForEndOfFrame(); } LoadManager.s_instance._fadeImage.color = Color.black; + AudioListener.volume = 0; yield return new WaitForEndOfFrame(); } diff --git a/NewHorizons/Handlers/HeldItemHandler.cs b/NewHorizons/Handlers/HeldItemHandler.cs new file mode 100644 index 00000000..5ff7e5bb --- /dev/null +++ b/NewHorizons/Handlers/HeldItemHandler.cs @@ -0,0 +1,235 @@ +using HarmonyLib; +using NewHorizons.Builder.Props; +using NewHorizons.External.Modules.Props; +using NewHorizons.Utility; +using NewHorizons.Utility.OWML; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace NewHorizons.Handlers; + +[HarmonyPatch] +public static class HeldItemHandler +{ + /// + /// Dictionary of system name to item path + /// If we travel to multiple systems within a single loop, this will hold the items we move between systems + /// + private static Dictionary> _pathOfItemTakenFromSystem = new(); + + public static bool WasAWCTakenFromATP => _pathOfItemTakenFromSystem.TryGetValue("SolarSystem", out var list) && list.Contains(ADVANCED_WARP_CORE); + + private static GameObject _currentlyHeldItem; + + /// + /// Track the path of any item we ever pick up, in case we take it out of the system + /// + private static Dictionary _trackedPaths = new(); + + private static bool _isInitialized = false; + private static bool _isSystemReady = false; + + /// + /// We keep our own reference to this because when Unload gets called it might have already been updated + /// + private static string _currentStarSystem; + + private const string ADVANCED_WARP_CORE = "TowerTwin_Body/Sector_TowerTwin/Sector_TimeLoopInterior/Interactables_TimeLoopInterior/WarpCoreSocket/Prefab_NOM_WarpCoreVessel"; + + [HarmonyPrefix, HarmonyPatch(typeof(ItemTool), nameof(ItemTool.Awake))] + private static void Init() + { + _trackedPaths.Clear(); + + _currentStarSystem = Main.Instance.CurrentStarSystem; + + if (!_isInitialized) + { + _isInitialized = true; + Main.Instance.OnChangeStarSystem.AddListener(OnStarSystemChanging); + Main.Instance.OnStarSystemLoaded.AddListener(OnSystemReady); + GlobalMessenger.AddListener("DeathSequenceComplete", OnDeathSequenceComplete); + } + } + + private static void OnDeathSequenceComplete() + { + NHLogger.Log("Player died, resetting held items"); + + // Destroy everything + _pathOfItemTakenFromSystem.Clear(); + GameObject.Destroy(_currentlyHeldItem); + } + + private static GameObject MakePerfectCopy(GameObject go) + { + var owItem = go.GetComponent(); + + var tempParent = new GameObject(); + tempParent.transform.position = new Vector3(100000, 0, 0); + var newObject = DetailBuilder.Make(tempParent, tempParent.AddComponent(), null, go, new DetailInfo() { keepLoaded = true }); + newObject.SetActive(false); + newObject.transform.parent = null; + newObject.name = go.name; + + var newOWItem = newObject.GetComponent(); + + NHLogger.Log($"Cloned {go.name}, original item has component: [{owItem != null}] new item has component: [{newOWItem != null}]"); + + return newObject; + } + + private static void TrackPath(string path) + { + if (!_pathOfItemTakenFromSystem.ContainsKey(Main.Instance.CurrentStarSystem)) + { + _pathOfItemTakenFromSystem[Main.Instance.CurrentStarSystem] = new(); + } + _pathOfItemTakenFromSystem[Main.Instance.CurrentStarSystem].Add(path); + } + + private static void OnStarSystemChanging(string newSystem) + { + // Double check we're still holding it + _currentlyHeldItem = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem()?.gameObject; + if (!Main.SystemDict[newSystem].Config.allowOutsideItems) + { + _currentlyHeldItem = null; + } + + if (_currentlyHeldItem != null) + { + // Track it so that when we return to this system we can delete the original + if (_trackedPaths.TryGetValue(_currentlyHeldItem, out var path)) + { + TrackPath(path); + } + + NHLogger.Log($"Scene unloaded, preserved inactive held item {_currentlyHeldItem.name}"); + // For some reason, the original will get destroyed no matter what we do. To avoid, we make a copy + _currentlyHeldItem = MakePerfectCopy(_currentlyHeldItem).DontDestroyOnLoad(); + } + + // If warping with a vessel, make sure to also track the path to the advanced warp core (assuming the player actually removed it) + if (Main.Instance.CurrentStarSystem == "SolarSystem" && Main.Instance.IsWarpingFromVessel) + { + // Making sure its actually gone + var warpCoreSocket = GameObject.FindObjectOfType()._warpCoreSocket; + if (!warpCoreSocket.IsSocketOccupied() || warpCoreSocket.GetWarpCoreType() != WarpCoreType.Vessel) + { + TrackPath(ADVANCED_WARP_CORE); + } + } + + _trackedPaths.Clear(); + _isSystemReady = false; + } + + private static void OnSystemReady(string _) + { + // If something was taken from this system during this life, remove it + if (_pathOfItemTakenFromSystem.TryGetValue(Main.Instance.CurrentStarSystem, out var paths)) + { + foreach (var path in paths) + { + // Have to wait two frames for the sockets to Awake and Start + Delay.FireInNUpdates(() => + { + try + { + NHLogger.Log($"Removing item that was taken from this system at {path}"); + var item = SearchUtilities.Find(path)?.GetComponent(); + // Make sure to update the socket it might be in so that it works + if (item.GetComponentInParent() is OWItemSocket socket) + { + socket.RemoveFromSocket(); + // Time loop core controller doesn't have time to hook up its events yet so we call this manually + if (path == ADVANCED_WARP_CORE) + { + var controller = GameObject.FindObjectOfType(); + controller.OpenCore(); + controller.OnSocketableRemoved(item); + } + NHLogger.Log($"Unsocketed {item.name}"); + } + item.gameObject.SetActive(false); + } + catch (Exception e) + { + NHLogger.LogError($"Failed to remove item at {path}: {e}"); + } + }, 2); + } + } + + // Give whatever item we were previously holding + if (_currentlyHeldItem != null) + { + NHLogger.Log($"Giving player held item {_currentlyHeldItem.name}"); + // Else its spawning the item inside the player and for that one frame it kills you + var newObject = MakePerfectCopy(_currentlyHeldItem); + + // We wait a bit because at some point after not something resets your held item to nothing + Delay.FireInNUpdates(() => + { + try + { + Locator.GetToolModeSwapper().GetItemCarryTool().PickUpItemInstantly(newObject.GetComponent()); + // For some reason picking something up messes up the input mode + if (PlayerState.AtFlightConsole()) + { + Locator.GetToolModeSwapper().UnequipTool(); + Locator.GetToolModeSwapper().OnEnterFlightConsole(Locator.GetShipBody()); + } + newObject.SetActive(true); + } + catch(Exception e) + { + NHLogger.LogError($"Failed to take item {newObject.name} to a new system: {e}"); + GameObject.Destroy(_currentlyHeldItem); + _currentlyHeldItem = null; + } + + }, 5); + } + + _isSystemReady = true; + } + + [HarmonyPrefix, HarmonyPatch(typeof(ItemTool), nameof(ItemTool.MoveItemToCarrySocket))] + private static void HeldItemChanged(ItemTool __instance, OWItem item) + { + if (!_isSystemReady) + { + return; + } + + if (item != null) + { + var path = item.transform.GetPath(); + if (!_trackedPaths.ContainsKey(item.gameObject)) + { + _trackedPaths[item.gameObject] = path; + } + } + + NHLogger.Log($"Player is now holding {item?.name ?? "nothing"}"); + _currentlyHeldItem = item?.gameObject; + } + + [HarmonyPostfix, HarmonyPatch(typeof(ItemTool))] + [HarmonyPatch(nameof(ItemTool.SocketItem))] + [HarmonyPatch(nameof(ItemTool.DropItem))] + [HarmonyPatch(nameof(ItemTool.StartUnsocketItem))] + private static void HeldItemChanged2(ItemTool __instance) + { + if (!_isSystemReady) + { + return; + } + + NHLogger.Log($"Player is now holding nothing"); + _currentlyHeldItem = null; + } +} diff --git a/NewHorizons/Handlers/InvulnerabilityHandler.cs b/NewHorizons/Handlers/InvulnerabilityHandler.cs index 9aa0e793..cacc9e19 100644 --- a/NewHorizons/Handlers/InvulnerabilityHandler.cs +++ b/NewHorizons/Handlers/InvulnerabilityHandler.cs @@ -1,36 +1,44 @@ using NewHorizons.Utility.OWML; using UnityEngine; +using UnityEngine.SceneManagement; namespace NewHorizons.Handlers { internal class InvulnerabilityHandler { - private static float _defaultImpactDeathSpeed = -1f; + /// + /// Used in patches + /// + public static bool Invincible { get; private set; } public static void MakeInvulnerable(bool invulnerable) { NHLogger.Log($"Toggling immortality: {invulnerable}"); + Invincible = invulnerable; var deathManager = GetDeathManager(); var resources = GetPlayerResouces(); if (invulnerable) { - if (_defaultImpactDeathSpeed == -1f) - _defaultImpactDeathSpeed = deathManager._impactDeathSpeed; - - deathManager._impactDeathSpeed = Mathf.Infinity; deathManager._invincible = true; + resources._invincible = true; } else { - deathManager._impactDeathSpeed = _defaultImpactDeathSpeed; resources._currentHealth = 100f; deathManager._invincible = false; + resources._invincible = false; } } private static DeathManager GetDeathManager() => GameObject.FindObjectOfType(); private static PlayerResources GetPlayerResouces() => GameObject.FindObjectOfType(); + + static InvulnerabilityHandler() + { + // If the scene unloads when Invincible is on it might not get turned off + SceneManager.sceneUnloaded += (_) => Invincible = false; + } } } diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index a778b8ad..6898382f 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -3,22 +3,23 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.General; using NewHorizons.Builder.Orbital; using NewHorizons.Builder.Props; +using NewHorizons.Builder.ShipLog; using NewHorizons.Builder.Volumes; using NewHorizons.Components.Orbital; using NewHorizons.Components.Quantum; using NewHorizons.Components.Stars; using NewHorizons.External; using NewHorizons.OtherMods.OWRichPresence; +using NewHorizons.Streaming; using NewHorizons.Utility; -using NewHorizons.Utility.OWML; using NewHorizons.Utility.OuterWilds; +using NewHorizons.Utility.OWML; +using Newtonsoft.Json; +using OWML.ModHelper; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; -using NewHorizons.Streaming; -using Newtonsoft.Json; -using NewHorizons.External.Modules.VariableSize; namespace NewHorizons.Handlers { @@ -86,6 +87,7 @@ namespace NewHorizons.Handlers } // Load all planets + _loadedBodies.Clear(); var toLoad = bodies.ToList(); var newPlanetGraph = new PlanetGraphHandler(toLoad); @@ -150,8 +152,18 @@ namespace NewHorizons.Handlers SingularityBuilder.PairAllSingularities(); } + private static List _loadedBodies = new(); + + /// + /// Returns false if it failed + /// + /// + /// + /// public static bool LoadBody(NewHorizonsBody body, bool defaultPrimaryToSun = false) { + if (_loadedBodies.Contains(body)) return true; + body.LoadCache(); // I don't remember doing this why is it exceptions what am I doing @@ -223,6 +235,7 @@ namespace NewHorizons.Handlers var rb = existingPlanet.GetComponent(); var sector = SectorBuilder.Make(existingPlanet, rb, GetSphereOfInfluence(body)); + sector._idString = body.Config.name; sector.name = $"Sector-{existingPlanet.GetComponentsInChildren().Count()}"; SharedGenerateBody(body, existingPlanet, sector, rb); @@ -305,6 +318,7 @@ namespace NewHorizons.Handlers } body.UnloadCache(true); + _loadedBodies.Add(body); return true; } @@ -344,6 +358,13 @@ namespace NewHorizons.Handlers // Do stuff that's shared between generating new planets and updating old ones go = SharedGenerateBody(body, go, sector, rb); + if (body.Config.ShipLog?.mapMode != null) + { + MapModeBuilder.TryReplaceExistingMapModeIcon(body, body.Mod as ModBehaviour, body.Config.ShipLog.mapMode); + } + + body.Object = go; + return go; } @@ -366,14 +387,15 @@ namespace NewHorizons.Handlers go.SetActive(false); body.Config.Base.showMinimap = false; - body.Config.Base.hasMapMarker = false; + body.Config.MapMarker.enabled = false; const float sphereOfInfluence = 2000f; var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence, body.Config); - var ao = AstroObjectBuilder.Make(go, null, body.Config, false); + var ao = AstroObjectBuilder.Make(go, null, body, false); var sector = SectorBuilder.Make(go, owRigidBody, sphereOfInfluence); + sector._idString = body.Config.name; ao._rootSector = sector; ao._type = AstroObject.Type.None; @@ -392,7 +414,7 @@ namespace NewHorizons.Handlers AstroObjectLocator.RegisterCustomAstroObject(ao); // Now that we're done move the planet into place - SetPositionFromVector(go, body.Config.Orbit.staticPosition); + SetPositionFromVector(go, body.Config.Orbit.staticPosition, body.Config.trackForSolarSystemRadius); NHLogger.LogVerbose($"Finished creating Bramble Dimension [{body.Config.name}]"); @@ -413,8 +435,8 @@ namespace NewHorizons.Handlers if (defaultPrimaryToSun) { NHLogger.LogError($"Couldn't find {body.Config.Orbit.primaryBody}, defaulting to center of solar system"); - // TODO: Make this work in other systems. We tried using Locator.GetCenterOfUniverse before but that doesn't work since its too early now - primaryBody = SearchUtilities.Find("Sun_Body")?.GetComponent(); + // Fix #933 not defaulting primary body + primaryBody = (SearchUtilities.Find("Sun_Body") ?? AstroObjectBuilder.CenterOfUniverse)?.GetComponent(); } else { @@ -447,9 +469,10 @@ namespace NewHorizons.Handlers var sphereOfInfluence = GetSphereOfInfluence(body); var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence, body.Config); - var ao = AstroObjectBuilder.Make(go, primaryBody, body.Config, false); + var ao = AstroObjectBuilder.Make(go, primaryBody, body, false); var sector = SectorBuilder.Make(go, owRigidBody, sphereOfInfluence * 2f); + sector._idString = body.Config.name; ao._rootSector = sector; if (body.Config.Base.surfaceGravity != 0) @@ -459,7 +482,7 @@ namespace NewHorizons.Handlers RFVolumeBuilder.Make(go, owRigidBody, sphereOfInfluence, body.Config.ReferenceFrame); - if (body.Config.Base.hasMapMarker) + if (body.Config.MapMarker.enabled) { MarkerBuilder.Make(go, body.Config.name, body.Config); } @@ -479,33 +502,23 @@ namespace NewHorizons.Handlers // Now that we're done move the planet into place if (body.Config.Orbit?.staticPosition != null) { - SetPositionFromVector(go, body.Config.Orbit.staticPosition); + SetPositionFromVector(go, body.Config.Orbit.staticPosition, body.Config.trackForSolarSystemRadius); } else { - UpdatePosition(go, body.Config.Orbit, primaryBody, ao); + UpdatePosition(go, body.Config.Orbit, primaryBody, ao, body.Config.trackForSolarSystemRadius); } // Have to do this after setting position var initialMotion = InitialMotionBuilder.Make(go, primaryBody, ao, owRigidBody, body.Config.Orbit); - // Spawning on other planets is a bit hacky so we do it last - if (body.Config.Spawn != null) - { - NHLogger.LogVerbose($"Making spawn point on {body.Config.name}"); - var spawnPoint = SpawnPointBuilder.Make(go, body.Config.Spawn, owRigidBody); - var isVanillaSystem = body.Config.starSystem == "SolarSystem" || body.Config.starSystem == "EyeOfTheUniverse"; - var needsSpawnPoint = Main.SystemDict[body.Config.starSystem].SpawnPoint == null || isVanillaSystem; - var isDefaultSpawn = body.Config.Spawn.playerSpawn?.isDefault ?? true; // Backwards compat - if (needsSpawnPoint || isDefaultSpawn) - { - Main.SystemDict[body.Config.starSystem].SpawnPoint = spawnPoint; - } - } - if (body.Config.Orbit.showOrbitLine && !body.Config.Orbit.isStatic) { - Delay.FireOnNextUpdate(() => OrbitlineBuilder.Make(body.Object, ao, body.Config.Orbit.isMoon, body.Config)); + // No map mode at eye + if (LoadManager.GetCurrentScene() != OWScene.EyeOfTheUniverse) + { + OrbitlineBuilder.Make(body.Object, body.Config.Orbit.isMoon, body.Config); + } } DetectorBuilder.Make(go, owRigidBody, primaryBody, ao, body.Config); @@ -589,6 +602,7 @@ namespace NewHorizons.Handlers var remnant = Main.BodyDict[body.Config.starSystem].Where(x => x.Config.name == body.Config.name && x.Config.isStellarRemnant).FirstOrDefault(); var remnantSector = SectorBuilder.Make(go, rb, sphereOfInfluence); + remnantSector._idString = body.Config.name; remnantSector.name = "CustomStellarRemnant"; SharedGenerateBody(remnant, go, remnantSector, rb); @@ -672,7 +686,7 @@ namespace NewHorizons.Handlers SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); } } - + if (body.Config.Atmosphere.fogSize != 0) { fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod); @@ -681,6 +695,18 @@ namespace NewHorizons.Handlers atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren(); } + if (body.Config.EyeOfTheUniverse != null) + { + if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") + { + EyeOfTheUniverseBuilder.Make(go, sector, body.Config.EyeOfTheUniverse, body); + } + else + { + NHLogger.LogWarning($"A mod creator (you?) has defined Eye of the Universe specific settings on a body [{body.Config.name}] that is not in the eye of the universe"); + } + } + if (body.Config.ParticleFields != null) { EffectsBuilder.Make(go, sector, body.Config); @@ -716,6 +742,11 @@ namespace NewHorizons.Handlers } } + if (Main.HasDLC) + { + DreamDimensionBuilder.Make(go, sector, body); + } + // Has to go last probably if (willHaveCloak) { @@ -727,6 +758,12 @@ namespace NewHorizons.Handlers SupernovaEffectBuilder.Make(go, sector, body.Config, body.Mod, procGen, ambientLight, fog, atmosphere, null, fog?._fogImpostor); } + if (body.Config.Spawn != null) + { + NHLogger.LogVerbose($"Making spawn point on {body.Config.name}"); + SpawnPointBuilder.Make(go, body.Config.Spawn, rb); + } + // We allow removing children afterwards so you can also take bits off of the modules you used if (body.Config.removeChildren != null) RemoveChildren(go, body); @@ -788,7 +825,7 @@ namespace NewHorizons.Handlers } // Just destroy the existing AO after copying everything over - var newAO = AstroObjectBuilder.Make(go, primary, body.Config, true); + var newAO = AstroObjectBuilder.Make(go, primary, body, true); newAO._gravityVolume = ao._gravityVolume; newAO._moon = ao._moon; newAO._name = ao._name; @@ -812,16 +849,29 @@ namespace NewHorizons.Handlers if (referenceFrame != null) referenceFrame._attachedAstroObject = newAO; // QM and stuff don't have orbit lines - var orbitLine = go.GetComponentInChildren()?.gameObject; - if (orbitLine != null) UnityEngine.Object.Destroy(orbitLine); + // Using the name as well since NH only creates the OrbitLine components next frame + var orbitLine = go.GetComponentInChildren()?.gameObject ?? go.transform.Find("Orbit")?.gameObject; + if (orbitLine != null) + { + UnityEngine.Object.Destroy(orbitLine); + } var isMoon = newAO.GetAstroObjectType() is AstroObject.Type.Moon or AstroObject.Type.Satellite or AstroObject.Type.SpaceStation; - if (body.Config.Orbit.showOrbitLine) OrbitlineBuilder.Make(go, newAO, isMoon, body.Config); + if (body.Config.Orbit.showOrbitLine) + { + // No map mode at eye + if (LoadManager.GetCurrentScene() != OWScene.EyeOfTheUniverse) + { + OrbitlineBuilder.Make(go, isMoon, body.Config); + } + } DetectorBuilder.SetDetector(primary, newAO, go.GetComponentInChildren()); // Get ready to move all the satellites var relativeMoonPositions = children.Select(x => x.transform.position - go.transform.position).ToArray(); + var relativeMoonMoonPositions = children.Select(x => AstroObjectLocator.GetChildren(x.GetComponent()) + .Select(childchild => (childchild?.transform?.position ?? Vector3.zero) - go.transform.position)).ToArray(); // If its tidally locked change the alignment var alignment = go.GetComponent(); @@ -831,7 +881,7 @@ namespace NewHorizons.Handlers } // Move the primary - UpdatePosition(go, body.Config.Orbit, primary, newAO); + UpdatePosition(go, body.Config.Orbit, primary, newAO, body.Config.trackForSolarSystemRadius); for (int i = 0; i < children.Count(); i++) { @@ -848,11 +898,15 @@ namespace NewHorizons.Handlers } else { + var j = 0; foreach (var childChild in AstroObjectLocator.GetChildren(childAO)) { - if (childChild == null) continue; - var dPos = childChild.transform.position - child.transform.position; - childChild.transform.position = go.transform.position + relativeMoonPositions[i] + dPos; + if (childChild != null) + { + var dPos = relativeMoonMoonPositions[i].ElementAt(j); + childChild.transform.position = go.transform.position + dPos; + } + j++; } // Make sure the moons get updated to the new AO childAO._primaryBody = newAO; @@ -880,6 +934,12 @@ namespace NewHorizons.Handlers } public static void UpdatePosition(GameObject go, IOrbitalParameters orbit, AstroObject primaryBody, AstroObject secondaryBody) + { + // Keeping old method signature because its the kind of method I think something somewhere might call + UpdatePosition(go, orbit, primaryBody, secondaryBody, true); + } + + public static void UpdatePosition(GameObject go, IOrbitalParameters orbit, AstroObject primaryBody, AstroObject secondaryBody, bool trackForSolarSystemRadius) { NHLogger.LogVerbose($"Placing [{secondaryBody?.name}] around [{primaryBody?.name}]"); @@ -889,15 +949,15 @@ namespace NewHorizons.Handlers var secondaryGravity = new Gravity(secondaryBody.GetGravityVolume()); var pos = orbit.GetOrbitalParameters(primaryGravity, secondaryGravity).InitialPosition + primaryBody.transform.position; - SetPositionFromVector(go, pos); + SetPositionFromVector(go, pos, trackForSolarSystemRadius); } else { - SetPositionFromVector(go, Vector3.zero); + SetPositionFromVector(go, Vector3.zero, trackForSolarSystemRadius); } } - public static void SetPositionFromVector(GameObject go, Vector3 position) + public static void SetPositionFromVector(GameObject go, Vector3 position, bool trackForSolarSystemRadius) { var rb = go.GetAttachedOWRigidbody(); if (rb) @@ -928,8 +988,8 @@ namespace NewHorizons.Handlers } // Uses the ratio of the interlopers furthest point to what the base game considers the edge of the solar system - var distanceToCenter = go.transform.position.magnitude * (24000 / 30000f); - if (distanceToCenter > SolarSystemRadius) + var distanceToCenter = go.transform.position.magnitude / (24000 / 30000f); + if (distanceToCenter > SolarSystemRadius && trackForSolarSystemRadius) { SolarSystemRadius = distanceToCenter; } diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index 7087c6fe..01b5fabe 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -70,6 +70,7 @@ namespace NewHorizons.Handlers if (Main.HasDLC) { StrangerRemoved(); + DreamWorldRemoved(); } // Put it back at the center of the universe after banishing it else there are weird graphical bugs @@ -90,6 +91,17 @@ namespace NewHorizons.Handlers } } + private static void DreamWorldRemoved() + { + // No you didn't + // Needs to stay alive so that custom Dreamworlds can use its Dreamworld controller + // We had a separate dreamworld controller solution before, but that broke Eyes of the Past somehow + Locator.GetAstroObject(AstroObject.Name.DreamWorld).gameObject.SetActive(true); + // We thought of disabling the children for consistency: However this broke the tronworld for some reason + // Basically, leaving the real Dreamworld in is fine since as long as you don't place your own custom dreamworld on top + // of it, you'll never have it appear when you dont want it to + } + private static void SunRemoved() { var sun = SearchUtilities.Find("Sun_Body").GetComponent(); @@ -207,6 +219,9 @@ namespace NewHorizons.Handlers case AstroObject.Name.RingWorld: StrangerRemoved(); break; + case AstroObject.Name.DreamWorld: + DreamWorldRemoved(); + break; } // Always delete the children diff --git a/NewHorizons/Handlers/PlanetGraphHandler.cs b/NewHorizons/Handlers/PlanetGraphHandler.cs index e58688a2..49d85c6d 100644 --- a/NewHorizons/Handlers/PlanetGraphHandler.cs +++ b/NewHorizons/Handlers/PlanetGraphHandler.cs @@ -126,11 +126,11 @@ namespace NewHorizons.Handlers private static bool DetermineIfChildOfFocal(NewHorizonsBody body, FocalPointNode node) { - var name = body.Config.name.ToLower(); - var primary = (body.Config.Orbit?.primaryBody ?? "").ToLower(); - var primaryName = node.primary.body.Config.name.ToLower(); - var secondaryName = node.secondary.body.Config.name.ToLower(); - return name != primaryName && name != secondaryName && (primary == node.body.Config.name.ToLower() || primary == primaryName || primary == secondaryName); + var name = body.Config.name.ToLowerInvariant(); + var primary = (body.Config.Orbit?.primaryBody ?? "").ToLowerInvariant(); + var primaryName = node.primary.body.Config.name.ToLowerInvariant(); + var secondaryName = node.secondary.body.Config.name.ToLowerInvariant(); + return name != primaryName && name != secondaryName && (primary == node.body.Config.name.ToLowerInvariant() || primary == primaryName || primary == secondaryName); } diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 40670ad8..51d8e0af 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -9,6 +9,11 @@ namespace NewHorizons.Handlers { public static class PlayerSpawnHandler { + /// + /// Set during the previous loop, force the player to spawn here + /// + public static string TargetSpawnID { get; set; } + public static void SetUpPlayerSpawn() { if (UsingCustomSpawn()) @@ -46,15 +51,46 @@ namespace NewHorizons.Handlers Delay.StartCoroutine(SpawnCoroutine(30)); } - var cloak = GetDefaultSpawn()?.GetAttachedOWRigidbody()?.GetComponentInChildren(); - if (cloak != null) + + // It was NREing in here when it was all ?. so explicit null checks + var spawn = GetDefaultSpawn(); + if (spawn != null) { - // Ensures it has invoked everything and actually placed the player in the cloaking field #671 - cloak._firstUpdate = true; + var attachedOWRigidBody = spawn.GetAttachedOWRigidbody(); + if (attachedOWRigidBody != null) + { + var cloak = attachedOWRigidBody.GetComponentInChildren(); + if (cloak != null) + { + // Ensures it has invoked everything and actually placed the player in the cloaking field #671 + cloak._firstUpdate = true; + } + } } // Spawn ship Delay.FireInNUpdates(SpawnShip, 30); + + if (UsingCustomSpawn() || shouldWarpInFromShip || shouldWarpInFromVessel) + { + // Have had bug reports (#1034, #975) where sometimes after spawning via vessel warp or ship warp you die from impact velocity after being flung + // Something weird must be happening with velocity. + // Try to correct it here after the ship is done spawning + Delay.FireInNUpdates(() => FixVelocity(shouldWarpInFromVessel, shouldWarpInFromShip), 31); + } + } + + private static void FixVelocity(bool shouldWarpInFromVessel, bool shouldWarpInFromShip) + { + var spawnOWRigidBody = GetDefaultSpawn().GetAttachedOWRigidbody(); + if (shouldWarpInFromVessel) spawnOWRigidBody = VesselWarpHandler.VesselSpawnPoint.GetAttachedOWRigidbody(); + if (shouldWarpInFromShip) spawnOWRigidBody = Locator.GetShipBody(); + + var spawnVelocity = spawnOWRigidBody.GetVelocity(); + var spawnAngularVelocity = spawnOWRigidBody.GetPointTangentialVelocity(Locator.GetPlayerBody().GetPosition()); + var velocity = spawnVelocity + spawnAngularVelocity; + + Locator.GetPlayerBody().SetVelocity(velocity); } public static void SpawnShip() @@ -137,30 +173,42 @@ namespace NewHorizons.Handlers private static IEnumerator SpawnCoroutine(int length) { + FixPlayerVelocity(); for(int i = 0; i < length; i++) { - FixPlayerVelocity(); + FixPlayerVelocity(false); // dont recenter universe here or else it spams and lags game yield return new WaitForEndOfFrame(); } + FixPlayerVelocity(); InvulnerabilityHandler.MakeInvulnerable(false); + + // Done spawning + TargetSpawnID = null; } - private static void FixPlayerVelocity() + private static void FixPlayerVelocity(bool recenter = true) { var playerBody = SearchUtilities.Find("Player_Body").GetAttachedOWRigidbody(); var resources = playerBody.GetComponent(); - SpawnBody(playerBody, GetDefaultSpawn()); + SpawnBody(playerBody, GetDefaultSpawn(), recenter: recenter); resources._currentHealth = 100f; } - public static void SpawnBody(OWRigidbody body, SpawnPoint spawn, Vector3? positionOverride = null) + public static void SpawnBody(OWRigidbody body, SpawnPoint spawn, Vector3? positionOverride = null, bool recenter = true) { var pos = positionOverride ?? spawn.transform.position; - body.WarpToPositionRotation(pos, spawn.transform.rotation); + if (recenter) + { + body.WarpToPositionRotation(pos, spawn.transform.rotation); + } + else + { + body.transform.SetPositionAndRotation(pos, spawn.transform.rotation); + } var spawnVelocity = spawn._attachedBody.GetVelocity(); var spawnAngularVelocity = spawn._attachedBody.GetPointTangentialVelocity(pos); @@ -191,8 +239,8 @@ namespace NewHorizons.Handlers return vector; } - public static bool UsingCustomSpawn() => Main.SystemDict[Main.Instance.CurrentStarSystem].SpawnPoint != null; + public static bool UsingCustomSpawn() => SpawnPointBuilder.PlayerSpawn != null; public static PlayerSpawner GetPlayerSpawner() => GameObject.FindObjectOfType(); - public static SpawnPoint GetDefaultSpawn() => Main.SystemDict[Main.Instance.CurrentStarSystem].SpawnPoint ?? GetPlayerSpawner().GetSpawnPoint(SpawnLocation.TimberHearth); + public static SpawnPoint GetDefaultSpawn() => SpawnPointBuilder.PlayerSpawn ?? GetPlayerSpawner().GetSpawnPoint(SpawnLocation.TimberHearth); } } diff --git a/NewHorizons/Handlers/RemoteHandler.cs b/NewHorizons/Handlers/RemoteHandler.cs index 28c52e9b..81c6c41e 100644 --- a/NewHorizons/Handlers/RemoteHandler.cs +++ b/NewHorizons/Handlers/RemoteHandler.cs @@ -2,6 +2,7 @@ using NewHorizons.Utility.OWML; using OWML.Utils; using System; using System.Collections.Generic; +using System.Diagnostics; namespace NewHorizons.Handlers @@ -33,24 +34,34 @@ namespace NewHorizons.Handlers return id.ToString(); } - public static NomaiRemoteCameraPlatform.ID GetPlatformID(string id) + public static bool TryGetPlatformID(string id, out NomaiRemoteCameraPlatform.ID platformID) { try { - NomaiRemoteCameraPlatform.ID platformID; - if (_customPlatformIDs.TryGetValue(id, out platformID) || EnumUtils.TryParse(id, out platformID)) + if (!(_customPlatformIDs.TryGetValue(id, out platformID) || EnumUtils.TryParse(id, out platformID))) { - return platformID; - } - else - { - return AddCustomPlatformID(id); + platformID = AddCustomPlatformID(id); } + return true; } catch (Exception e) { NHLogger.LogError($"Couldn't load platform id [{id}]:\n{e}"); - return NomaiRemoteCameraPlatform.ID.None; + platformID = NomaiRemoteCameraPlatform.ID.None; + return false; + } + } + + public static NomaiRemoteCameraPlatform.ID GetPlatformID(string id) + { + NomaiRemoteCameraPlatform.ID platformID = NomaiRemoteCameraPlatform.ID.None; + if (_customPlatformIDs.TryGetValue(id, out platformID) || EnumUtils.TryParse(id, out platformID)) + { + return platformID; + } + else + { + return AddCustomPlatformID(id); } } diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index 84cbb203..7f9a6e14 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -114,10 +114,8 @@ namespace NewHorizons.Handlers public static bool KnowsFact(string fact) { - // Works normally in the main system, else check save data directly - var shipLogManager = Locator.GetShipLogManager(); - if (Main.Instance.CurrentStarSystem == "SolarSystem" && shipLogManager != null) return shipLogManager.IsFactRevealed(fact); - else return PlayerData.GetShipLogFactSave(fact)?.revealOrder > -1; + // Use save data directly so stuff works between systems + return PlayerData.GetShipLogFactSave(fact)?.revealOrder > -1; } } } diff --git a/NewHorizons/Handlers/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs index b1ef9eea..27844c5b 100644 --- a/NewHorizons/Handlers/StarChartHandler.cs +++ b/NewHorizons/Handlers/StarChartHandler.cs @@ -16,6 +16,9 @@ namespace NewHorizons.Handlers private static Dictionary _starSystemToFactID; private static Dictionary _factIDToStarSystem; + private static bool _canExitViaWarpDrive; + private static string _factRequiredToExitViaWarpDrive; + private static NewHorizonsSystem[] _systems; public static void Init(NewHorizonsSystem[] systems) @@ -56,12 +59,21 @@ namespace NewHorizons.Handlers _starSystemToFactID = new Dictionary(); _factIDToStarSystem = new Dictionary(); + _factRequiredToExitViaWarpDrive = string.Empty; + foreach (NewHorizonsSystem system in _systems) { - if (system.Config.factRequiredForWarp != default) + if (system.Config.factRequiredForWarp != default && system.UniqueID != "SolarSystem") { RegisterFactForSystem(system.Config.factRequiredForWarp, system.UniqueID); } + + if (system.UniqueID == Main.Instance.CurrentStarSystem) + { + _factRequiredToExitViaWarpDrive = system.Config.factRequiredToExitViaWarpDrive; + _canExitViaWarpDrive = system.Config.canExitViaWarpDrive || !string.IsNullOrEmpty(_factRequiredToExitViaWarpDrive); + NHLogger.LogVerbose($"In system {system.UniqueID} can exit via warp drive? {system.Config.canExitViaWarpDrive} {_canExitViaWarpDrive} {_factRequiredToExitViaWarpDrive}"); + } } } @@ -88,6 +100,10 @@ namespace NewHorizons.Handlers /// public static bool HasUnlockedSystem(string system) { + // If warp drive is entirely disabled, then no + if (!CanExitViaWarpDrive()) + return false; + if (_starSystemToFactID == null || _starSystemToFactID.Count == 0) return true; @@ -99,6 +115,9 @@ namespace NewHorizons.Handlers return ShipLogHandler.KnowsFact(factID); } + public static bool CanExitViaWarpDrive() => Main.Instance.CurrentStarSystem == "SolarSystem" || (_canExitViaWarpDrive + && (string.IsNullOrEmpty(_factRequiredToExitViaWarpDrive) || ShipLogHandler.KnowsFact(_factRequiredToExitViaWarpDrive))); + /// /// Is it actually a valid warp target /// @@ -111,23 +130,63 @@ namespace NewHorizons.Handlers var canWarpTo = false; if (system.Equals("SolarSystem")) canWarpTo = true; else if (system.Equals("EyeOfTheUniverse")) canWarpTo = false; - else if (config.Spawn?.shipSpawn != null) canWarpTo = true; + else if (config.HasShipSpawn) canWarpTo = true; + + var canEnterViaWarpDrive = Main.SystemDict[system].Config.canEnterViaWarpDrive || system == "SolarSystem"; + + var canExitViaWarpDrive = CanExitViaWarpDrive(); + + // Make base system always ignore canExitViaWarpDrive + if (Main.Instance.CurrentStarSystem == "SolarSystem") + canExitViaWarpDrive = true; + + NHLogger.LogVerbose(canEnterViaWarpDrive, canExitViaWarpDrive, system, HasUnlockedSystem(system)); return canWarpTo - && Main.SystemDict[system].Config.canEnterViaWarpDrive + && canEnterViaWarpDrive + && canExitViaWarpDrive && system != Main.Instance.CurrentStarSystem && HasUnlockedSystem(system); } public static void OnRevealFact(string factID) { - if (_factIDToStarSystem.TryGetValue(factID, out var systemUnlocked)) + if (!string.IsNullOrEmpty(_factRequiredToExitViaWarpDrive) && factID == _factRequiredToExitViaWarpDrive) { - NHLogger.Log($"Just learned [{factID}] and unlocked [{systemUnlocked}]"); + _canExitViaWarpDrive = true; if (!Main.HasWarpDrive) + { + var flagActuallyAddedACard = false; + // Add all cards that now work + foreach (var starSystem in Main.SystemDict.Keys) + { + if (CanWarpToSystem(starSystem)) + { + ShipLogStarChartMode.AddSystemCard(starSystem); + flagActuallyAddedACard = true; + } + } + if (flagActuallyAddedACard) + { + Main.Instance.EnableWarpDrive(); + } + } + else + { + NHLogger.LogWarning("Warp drive was enabled before learning fact?"); + } + } + + if (_factIDToStarSystem != null && _factIDToStarSystem.TryGetValue(factID, out var systemUnlocked)) + { + var knowsWarpFact = string.IsNullOrEmpty(_factRequiredToExitViaWarpDrive) || ShipLogHandler.KnowsFact(_factRequiredToExitViaWarpDrive); + + NHLogger.Log($"Just learned [{factID}] and unlocked [{systemUnlocked}]"); + if (!Main.HasWarpDrive && knowsWarpFact) + { Main.Instance.EnableWarpDrive(); - if (ShipLogStarChartMode != null) - ShipLogStarChartMode.AddSystemCard(systemUnlocked); + } + ShipLogStarChartMode?.AddSystemCard(systemUnlocked); } } diff --git a/NewHorizons/Handlers/SubtitlesHandler.cs b/NewHorizons/Handlers/SubtitlesHandler.cs index d3fe3fb8..1a42bc79 100644 --- a/NewHorizons/Handlers/SubtitlesHandler.cs +++ b/NewHorizons/Handlers/SubtitlesHandler.cs @@ -1,3 +1,4 @@ +using NewHorizons.Utility; using NewHorizons.Utility.Files; using NewHorizons.Utility.OWML; using OWML.Common; @@ -31,6 +32,8 @@ namespace NewHorizons.Handlers private static List<(IModBehaviour mod, string filePath)> _additionalSubtitles = new(); + private CanvasGroup _titleCanvasGroup; + public static void RegisterAdditionalSubtitle(IModBehaviour mod, string filePath) { _additionalSubtitles.Add((mod, filePath)); @@ -64,6 +67,8 @@ namespace NewHorizons.Handlers var layout = GetComponent(); layout.minHeight = SUBTITLE_HEIGHT; + _titleCanvasGroup = SearchUtilities.Find("TitleCanvas").GetComponent(); + CheckForEOTE(); // We add our subtitles as a child object so that their sizing doesnt shift the layout of the main menu @@ -104,7 +109,7 @@ namespace NewHorizons.Handlers var tex = ImageUtilities.GetTexture(mod, filepath, false); if (tex == null) return; - var sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, Mathf.Max(SUBTITLE_HEIGHT, tex.height)), new Vector2(0.5f, 0.5f), 100.0f); + var sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100.0f); AddSubtitle(sprite); } @@ -137,6 +142,12 @@ namespace NewHorizons.Handlers return; } + // Fix subtitles start cycling before the main menu is visible #844 + if (_titleCanvasGroup.alpha < 1) + { + return; + } + if (pauseTimer > 0) { pauseTimer--; diff --git a/NewHorizons/Handlers/SystemCreationHandler.cs b/NewHorizons/Handlers/SystemCreationHandler.cs index 5936434a..db2951a3 100644 --- a/NewHorizons/Handlers/SystemCreationHandler.cs +++ b/NewHorizons/Handlers/SystemCreationHandler.cs @@ -8,6 +8,8 @@ using NewHorizons.Utility.OuterWilds; using UnityEngine; using Object = UnityEngine.Object; using NewHorizons.OtherMods; +using NewHorizons.Components.EOTE; +using Epic.OnlineServices.Presence; namespace NewHorizons.Handlers { @@ -46,9 +48,52 @@ namespace NewHorizons.Handlers TimeLoopUtilities.SetLoopDuration(system.Config.loopDuration); } - if (!string.IsNullOrEmpty(system.Config.travelAudio)) + if (system.Config.GlobalMusic != null) { - Delay.FireOnNextUpdate(() => AudioUtilities.SetAudioClip(Locator.GetGlobalMusicController()._travelSource, system.Config.travelAudio, system.Mod)); + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.travelAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.travelAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._travelSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.endTimesAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.endTimesAudio, system.Mod); + Delay.FireOnNextUpdate(() => { + Locator.GetGlobalMusicController().gameObject.GetAddComponent().SetEndTimesAudio(audioType); + Locator.GetGlobalMusicController()._endTimesSource.AssignAudioLibraryClip(audioType); + }); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.endTimesDreamAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.endTimesDreamAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController().gameObject.GetAddComponent().SetEndTimesDreamAudio(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.brambleDimensionAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.brambleDimensionAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._darkBrambleSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesIntroAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesIntroAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesIntroSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesLoopAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesLoopAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesLoopSource.AssignAudioLibraryClip(audioType)); + } + + if (!string.IsNullOrEmpty(system.Config.GlobalMusic.finalEndTimesBrambleDimensionAudio)) + { + var audioType = AudioTypeHandler.GetAudioType(system.Config.GlobalMusic.finalEndTimesBrambleDimensionAudio, system.Mod); + Delay.FireOnNextUpdate(() => Locator.GetGlobalMusicController()._finalEndTimesDarkBrambleSource.AssignAudioLibraryClip(audioType)); + } } } } diff --git a/NewHorizons/Handlers/TranslationHandler.cs b/NewHorizons/Handlers/TranslationHandler.cs index d8130aa8..43f787a0 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -2,9 +2,11 @@ using NewHorizons.External.Configs; using NewHorizons.Utility; using NewHorizons.Utility.OWML; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using static TextTranslation; namespace NewHorizons.Handlers { @@ -50,30 +52,46 @@ namespace NewHorizons.Handlers } // Get the translated text - if (dictionary.TryGetValue(language, out var table)) + if (TryGetTranslatedText(dictionary, language, text, out var translatedText)) { - if (table.TryGetValue(text, out var translatedText)) + return translatedText; + } + + if (warn) + { + NHLogger.LogVerbose($"Defaulting to english for {text}"); + } + + if (TryGetTranslatedText(dictionary, Language.ENGLISH, text, out translatedText)) + { + return translatedText; + } + + if (warn) + { + NHLogger.LogVerbose($"Defaulting to key for {text}"); + } + + return text; + } + + private static bool TryGetTranslatedText(Dictionary> dict, Language language, string text, out string translatedText) + { + if (dict.TryGetValue(language, out var table)) + { + if (table.TryGetValue(text, out translatedText)) { - return translatedText; + return true; } // Try without whitespace if its missing else if (table.TryGetValue(text.TruncateWhitespaceAndToLower(), out translatedText)) { - return translatedText; + return true; } } - if (warn) NHLogger.LogVerbose($"Defaulting to english for {text}"); - - // Try to default to English - if (dictionary.TryGetValue(TextTranslation.Language.ENGLISH, out var englishTable)) - if (englishTable.TryGetValue(text, out var englishText)) - return englishText; - - if (warn) NHLogger.LogVerbose($"Defaulting to key for {text}"); - - // Default to the key - return text; + translatedText = null; + return false; } public static void RegisterTranslation(TextTranslation.Language language, TranslationConfig config) @@ -155,6 +173,26 @@ namespace NewHorizons.Handlers TextTranslation.Get().m_table.theTable[key] = value; } + /// + /// Two dialogue nodes might share indentical text but they will have different prefixes. Still, we want to reuse that old text. + /// + /// + /// + /// + public static void ReuseDialogueTranslation(string rawText, string[] oldPrefixes, string[] newPrefixes) + { + var key = string.Join(string.Empty, newPrefixes) + rawText; + var existingKey = string.Join(string.Empty, oldPrefixes) + rawText; + if (TextTranslation.Get().m_table.theTable.TryGetValue(existingKey, out var existingTranslation)) + { + TextTranslation.Get().m_table.theTable[key] = existingTranslation; + } + else + { + NHLogger.LogWarning($"Couldn't find translation key {existingKey}"); + } + } + public static void AddShipLog(string rawText, params string[] rawPreText) { var key = string.Join(string.Empty, rawPreText) + rawText; @@ -171,7 +209,7 @@ namespace NewHorizons.Handlers { var uiTable = TextTranslation.Get().m_table.theUITable; - var text = GetTranslation(rawText, TextType.UI).ToUpper(); + var text = GetTranslation(rawText, TextType.UI).ToUpperFixed(); var key = uiTable.Keys.Max() + 1; try diff --git a/NewHorizons/Handlers/VesselWarpHandler.cs b/NewHorizons/Handlers/VesselWarpHandler.cs index 12859c3a..c5e622eb 100644 --- a/NewHorizons/Handlers/VesselWarpHandler.cs +++ b/NewHorizons/Handlers/VesselWarpHandler.cs @@ -1,3 +1,4 @@ +using NewHorizons.Builder.General; using NewHorizons.Builder.Props; using NewHorizons.Components; using NewHorizons.Components.EyeOfTheUniverse; @@ -5,7 +6,9 @@ using NewHorizons.Utility; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OWML; using UnityEngine; +using System.Collections; using static NewHorizons.Main; +using static NewHorizons.Utility.Files.AssetBundleUtilities; namespace NewHorizons.Handlers { @@ -41,7 +44,7 @@ namespace NewHorizons.Handlers { var vesselConfig = SystemDict[Instance.CurrentStarSystem].Config?.Vessel; var shouldSpawnOnVessel = IsVesselPresent() && (vesselConfig?.spawnOnVessel ?? false); - return !Instance.IsWarpingFromShip && (Instance.IsWarpingFromVessel || shouldSpawnOnVessel); + return !Instance.IsWarpingFromShip && (Instance.IsWarpingFromVessel || Instance.DidWarpFromVessel || shouldSpawnOnVessel); } public static void LoadVessel() @@ -54,20 +57,78 @@ namespace NewHorizons.Handlers } if (IsVesselPresentAndActive()) + { _vesselSpawnPoint = Instance.CurrentStarSystem == "SolarSystem" ? UpdateVessel() : CreateVessel(); + } else - _vesselSpawnPoint = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension").GetComponentInChildren(); + { + var vesselDimension = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension"); + var vesselDimensionSpawn = vesselDimension.GetComponentInChildren(true); + var vesselWarpController = vesselDimension.GetComponentInChildren(true); + + var defaultPlayerWarpPoint = new GameObject("DefaultPlayerWarpPos"); + defaultPlayerWarpPoint.transform.SetParent(vesselWarpController.transform, false); + defaultPlayerWarpPoint.transform.localPosition = new Vector3(0, -5.82f, -6.56f); + vesselWarpController._defaultPlayerWarpPoint = defaultPlayerWarpPoint.transform; + + var vesselSpawnObj = new GameObject("SPAWN_Vessel"); + vesselSpawnObj.transform.SetParent(vesselWarpController.transform.parent.parent, false); + vesselSpawnObj.transform.localPosition = new Vector3(-0.3f, -5.18f, -6.35f); + var vesselSpawnPoint = vesselSpawnObj.AddComponent(); + vesselSpawnPoint.WarpController = vesselWarpController; + vesselSpawnPoint._triggerVolumes = vesselDimensionSpawn._triggerVolumes; + _vesselSpawnPoint = vesselSpawnPoint; + } } public static void TeleportToVessel() { var playerSpawner = Object.FindObjectOfType(); - NHLogger.LogVerbose("Debug warping into vessel"); - playerSpawner.DebugWarp(_vesselSpawnPoint); + if (_vesselSpawnPoint is VesselSpawnPoint vesselSpawnPoint) + { + NHLogger.LogVerbose("Relative warping into vessel"); + vesselSpawnPoint.WarpPlayer(); + + // #1034 Vessel warp sometimes has the player get flung away into space and die + // We do what we do with regular spawns where we keep resetting their position to the right one while invincible until we're relatively certain + // that the spawning sequence is done + Delay.StartCoroutine(FixPlayerSpawning(25, vesselSpawnPoint)); + } + else + { + NHLogger.LogVerbose("Debug warping into vessel"); + playerSpawner.DebugWarp(_vesselSpawnPoint); + } Builder.General.SpawnPointBuilder.SuitUp(); + LoadDB(); + } + + private static IEnumerator FixPlayerSpawning(int frameInterval, VesselSpawnPoint vesselSpawn) + { + InvulnerabilityHandler.MakeInvulnerable(true); + + var frameCount = 0; + while (frameCount <= frameInterval) + { + vesselSpawn.WarpPlayer(); + frameCount++; + yield return null; // Wait for the next frame + } + + InvulnerabilityHandler.MakeInvulnerable(false); + var playerBody = SearchUtilities.Find("Player_Body").GetAttachedOWRigidbody(); + var resources = playerBody.GetComponent(); + resources._currentHealth = 100f; + } + + public static void LoadDB() + { if (Instance.CurrentStarSystem == "SolarSystem") { + //Deactivate lock since we aren't in timber anymore + GameObject.Destroy(SearchUtilities.Find("TimberHearth_Body/StreamingGroup_TH").GetComponent()); + // Deactivate village music because for some reason it still plays. SearchUtilities.Find("TimberHearth_Body/Sector_TH/Sector_Village/Volumes_Village/MusicVolume_Village").GetComponent().Deactivate(); @@ -81,7 +142,7 @@ namespace NewHorizons.Handlers } } - public static EyeSpawnPoint CreateVessel() + public static VesselSpawnPoint CreateVessel() { var system = SystemDict[Instance.CurrentStarSystem]; @@ -131,16 +192,23 @@ namespace NewHorizons.Handlers vesselWarpController._targetWarpPlatform._whiteHole.OnCollapse += vesselWarpController._targetWarpPlatform.OnWhiteHoleCollapse; GameObject blackHole = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension/Sector_VesselBridge/Interactibles_VesselBridge/BlackHole"); - GameObject newBlackHole = Object.Instantiate(blackHole, Vector3.zero, Quaternion.identity, singularityRoot.transform); + GameObject newBlackHole = Object.Instantiate(blackHole, singularityRoot.transform); + newBlackHole.transform.localPosition = Vector3.zero; + newBlackHole.transform.localRotation = Quaternion.identity; + newBlackHole.transform.localScale = Vector3.one; newBlackHole.name = "BlackHole"; vesselWarpController._blackHole = newBlackHole.GetComponentInChildren(); vesselWarpController._blackHoleOneShot = vesselWarpController._blackHole.transform.parent.Find("BlackHoleAudio_OneShot").GetComponent(); GameObject whiteHole = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension/Sector_VesselBridge/Interactibles_VesselBridge/WhiteHole"); - GameObject newWhiteHole = Object.Instantiate(whiteHole, Vector3.zero, Quaternion.identity, singularityRoot.transform); + GameObject newWhiteHole = Object.Instantiate(whiteHole, singularityRoot.transform); + newWhiteHole.transform.localPosition = Vector3.zero; + newWhiteHole.transform.localRotation = Quaternion.identity; + newWhiteHole.transform.localScale = Vector3.one; newWhiteHole.name = "WhiteHole"; vesselWarpController._whiteHole = newWhiteHole.GetComponentInChildren(); vesselWarpController._whiteHoleOneShot = vesselWarpController._whiteHole.transform.parent.Find("WhiteHoleAudio_OneShot").GetComponent(); + vesselWarpController._whiteHole._startActive = true; vesselObject.GetComponent()._labelID = (UITextType)TranslationHandler.AddUI("Vessel"); @@ -165,6 +233,17 @@ namespace NewHorizons.Handlers Object.Destroy(rfVolume.gameObject); } } + + if (hasParentBody) + { + foreach (OWRigidbody dynamicProp in vesselObject.GetComponentsInChildren(true)) + { + if (dynamicProp.GetComponent() == null) + { + dynamicProp.gameObject.AddComponent(); + } + } + } var attachWarpExitToVessel = system.Config.Vessel?.warpExit?.attachToVessel ?? false; var warpExitParent = vesselWarpController._targetWarpPlatform.transform.parent; @@ -186,20 +265,23 @@ namespace NewHorizons.Handlers } } - EyeSpawnPoint eyeSpawnPoint = vesselObject.GetComponentInChildren(true); + VesselSpawnPoint spawnPoint = vesselObject.GetComponentInChildren(true); if (ShouldSpawnAtVessel()) { - system.SpawnPoint = eyeSpawnPoint; + SpawnPointBuilder.OverridePlayerSpawn(spawnPoint); } vesselObject.SetActive(true); - Delay.FireOnNextUpdate(() => SetupWarpController(vesselWarpController)); + var power = vesselWarpController.transform.Find("PowerSwitchInterface"); + var orb = power.GetComponentInChildren(true); + // Run after Start (when pillar is lowered immediately) and then skip 100 frames so that the weird orb rising thing doesn't happen #958 + Delay.RunWhenAndInNUpdates(() => SetupWarpController(vesselWarpController, orb), () => !vesselWarpController._coordinateInterface._pillarRaised, 100); - return eyeSpawnPoint; + return spawnPoint; } - public static SpawnPoint UpdateVessel() + public static VesselSpawnPoint UpdateVessel() { var system = SystemDict[Instance.CurrentStarSystem]; @@ -207,7 +289,7 @@ namespace NewHorizons.Handlers var vectorSector = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension"); VesselObject = vectorSector; - var spawnPoint = vectorSector.GetComponentInChildren(); + var spawnPoint = vectorSector.GetComponentInChildren(true); VesselWarpController vesselWarpController = vectorSector.GetComponentInChildren(true); WarpController = vesselWarpController; @@ -219,12 +301,30 @@ namespace NewHorizons.Handlers vesselWarpController._whiteHoleOneShot = vesselWarpController._whiteHole.transform.parent.Find("WhiteHoleAudio_OneShot").GetComponent(); } - Delay.FireOnNextUpdate(() => SetupWarpController(vesselWarpController, true)); + vesselWarpController._whiteHole._startActive = true; + vesselWarpController._whiteHole.Stabilize(); - return spawnPoint; + var defaultPlayerWarpPoint = new GameObject("DefaultPlayerWarpPos"); + defaultPlayerWarpPoint.transform.SetParent(vesselWarpController.transform, false); + defaultPlayerWarpPoint.transform.localPosition = new Vector3(0, -5.82f, -6.56f); + vesselWarpController._defaultPlayerWarpPoint = defaultPlayerWarpPoint.transform; + + var vesselSpawnObj = new GameObject("SPAWN_Vessel"); + vesselSpawnObj.transform.SetParent(vesselWarpController.transform.parent.parent, false); + vesselSpawnObj.transform.localPosition = new Vector3(-0.3f, -5.18f, -6.35f); + var vesselSpawnPoint = vesselSpawnObj.AddComponent(); + vesselSpawnPoint.WarpController = vesselWarpController; + vesselSpawnPoint._triggerVolumes = spawnPoint._triggerVolumes; + + var power = vesselWarpController.transform.Find("PowerSwitchInterface"); + var orb = power.GetComponentInChildren(true); + // Run after Start (when pillar is lowered immediately) and then skip 100 frames so that the weird orb rising thing doesn't happen #958 + Delay.RunWhenAndInNUpdates(() => SetupWarpController(vesselWarpController, orb, true), () => !vesselWarpController._coordinateInterface._pillarRaised, 100); + + return vesselSpawnPoint; } - public static void SetupWarpController(VesselWarpController vesselWarpController, bool db = false) + public static void SetupWarpController(VesselWarpController vesselWarpController, NomaiInterfaceOrb orb, bool db = false) { if (db) { @@ -259,17 +359,33 @@ namespace NewHorizons.Handlers if (light.GetLight()) light.GetLight().enabled = true; } } - vesselWarpController._coreSocket.PlaceIntoSocket(newCore); + vesselWarpController._coreSocket._socketedItem = newCore; + newCore.SocketItem(vesselWarpController._coreSocket._socketTransform, vesselWarpController._coreSocket._sector); + newCore.PlaySocketAnimation(); + vesselWarpController._coreSocket.enabled = true; + vesselWarpController.SetPowered(true); break; } } } + else + { + foreach (NomaiLamp lamp in vesselWarpController.transform.root.GetComponentsInChildren(true)) + { + lamp._startOn = true; + lamp.Awake(); + } + } + vesselWarpController.OnSlotDeactivated(vesselWarpController._coordinatePowerSlot); - if (!db) vesselWarpController.OnSlotActivated(vesselWarpController._coordinatePowerSlot); vesselWarpController._gravityVolume.SetFieldMagnitude(vesselWarpController._origGravityMagnitude); vesselWarpController._coreCable.SetPowered(true); - vesselWarpController._coordinateCable.SetPowered(!db); vesselWarpController._warpPlatformCable.SetPowered(false); + orb.SetOrbPosition(vesselWarpController._coordinatePowerSlot.transform.position); + orb._occupiedSlot = vesselWarpController._coordinatePowerSlot; + orb._enterSlotTime = Time.time; + vesselWarpController.OnSlotActivated(vesselWarpController._coordinatePowerSlot); + vesselWarpController._coordinateCable.SetPowered(true); vesselWarpController._cageClosed = true; if (vesselWarpController._cageAnimator != null) { diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index 4dfd2d1f..350d0e2b 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -1,4 +1,3 @@ -using NewHorizons.Handlers; using OWML.Common; using System; using System.Collections.Generic; @@ -147,7 +146,8 @@ namespace NewHorizons void DefineStarSystem(string name, string config, IModBehaviour mod); /// - /// Allows creation of dialogue by directly passing the xml and dialogueInfo json contents as strings + /// Allows creation of dialogue by directly passing the xml and dialogueInfo json contents as strings. + /// Must be called at least 2 frames before entering dialogue if you're using ReuseDialogueOptionsFrom /// /// TextAsset name used for compatibility with voice mod. Just has to be a unique identifier. /// The contents of the dialogue xml file as a string @@ -215,5 +215,12 @@ namespace NewHorizons /// /// void AddSubtitle(IModBehaviour mod, string filePath); + + /// + /// Whatever system the player is warping to next, they will spawn at the spawn point with this ID + /// Gets reset after warping. Is also overriden by entering a system-changing black hole or warp volume by their `spawnPointID` + /// + /// + void SetNextSpawnID(string id); } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c8b714c7..468793aa 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -4,7 +4,9 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.General; using NewHorizons.Builder.Props; using NewHorizons.Builder.Props.Audio; +using NewHorizons.Builder.Props.EchoesOfTheEye; using NewHorizons.Builder.Props.TranslatorText; +using NewHorizons.Components.EOTE; using NewHorizons.Components.Fixers; using NewHorizons.Components.Ship; using NewHorizons.Components.SizeControllers; @@ -18,7 +20,6 @@ using NewHorizons.OtherMods.VoiceActing; using NewHorizons.Streaming; using NewHorizons.Utility; using NewHorizons.Utility.DebugTools; -using NewHorizons.Utility.DebugTools.Menu; using NewHorizons.Utility.Files; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OWML; @@ -39,16 +40,16 @@ namespace NewHorizons public class Main : ModBehaviour { - public static AssetBundle NHAssetBundle { get; private set; } - public static AssetBundle NHPrivateAssetBundle { get; private set; } public static Main Instance { get; private set; } // Settings public static bool Debug { get; private set; } + public static bool VisualizeQuantumObjects { get; private set; } public static bool VerboseLogs { get; private set; } - private static bool _useCustomTitleScreen; + public static bool SequentialPreCaching { get; private set; } + public static bool CustomTitleScreen { get; private set; } + public static string DefaultSystemOverride { get; private set; } private static bool _wasConfigured = false; - private static string _defaultSystemOverride; public static Dictionary SystemDict = new(); public static Dictionary> BodyDict = new(); @@ -57,9 +58,9 @@ namespace NewHorizons public static float SecondsElapsedInLoop = -1; - public static bool IsSystemReady { get; private set; } + public static bool IsSystemReady { get; private set; } - public string DefaultStarSystem => SystemDict.ContainsKey(_defaultSystemOverride) ? _defaultSystemOverride : _defaultStarSystem; + public string DefaultStarSystem => SystemDict.ContainsKey(DefaultSystemOverride) ? DefaultSystemOverride : _defaultStarSystem; public string CurrentStarSystem { get @@ -130,32 +131,33 @@ namespace NewHorizons var currentScene = SceneManager.GetActiveScene().name; - Debug = config.GetSettingsValue("Debug"); - VerboseLogs = config.GetSettingsValue("Verbose Logs"); + Debug = config.GetSettingsValue(nameof(Debug)); + VisualizeQuantumObjects = config.GetSettingsValue(nameof(VisualizeQuantumObjects)); + VerboseLogs = config.GetSettingsValue(nameof(VerboseLogs)); + SequentialPreCaching = config.GetSettingsValue(nameof(SequentialPreCaching)); if (currentScene == "SolarSystem") { DebugReload.UpdateReloadButton(); - DebugMenu.UpdatePauseMenuButton(); } if (VerboseLogs) NHLogger.UpdateLogLevel(NHLogger.LogType.Verbose); else if (Debug) NHLogger.UpdateLogLevel(NHLogger.LogType.Log); else NHLogger.UpdateLogLevel(NHLogger.LogType.Error); - var oldDefaultSystemOverride = _defaultSystemOverride; - _defaultSystemOverride = config.GetSettingsValue("Default System Override"); - if (oldDefaultSystemOverride != _defaultSystemOverride) + var oldDefaultSystemOverride = DefaultSystemOverride; + DefaultSystemOverride = config.GetSettingsValue(nameof(DefaultSystemOverride)); + if (oldDefaultSystemOverride != DefaultSystemOverride) { ResetCurrentStarSystem(); - NHLogger.Log($"Changed default star system override to {_defaultSystemOverride}"); + NHLogger.Log($"Changed default star system override to {DefaultSystemOverride}"); } - var wasUsingCustomTitleScreen = _useCustomTitleScreen; - _useCustomTitleScreen = config.GetSettingsValue("Custom title screen"); + var wasUsingCustomTitleScreen = CustomTitleScreen; + CustomTitleScreen = config.GetSettingsValue(nameof(CustomTitleScreen)); // Reload the title screen if this was updated on it // Don't reload if we haven't configured yet (called on game start) - if (wasUsingCustomTitleScreen != _useCustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured) + if (wasUsingCustomTitleScreen != CustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured) { NHLogger.LogVerbose("Reloading"); SceneManager.LoadScene("TitleScreen", LoadSceneMode.Single); @@ -171,7 +173,7 @@ namespace NewHorizons BodyDict["SolarSystem"] = new List(); BodyDict["EyeOfTheUniverse"] = new List(); // Keep this empty tho fr - SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig(), "", Instance) + SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig() { name = "SolarSystem" }, "", Instance) { Config = { @@ -187,7 +189,7 @@ namespace NewHorizons } } }; - SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig(), "", Instance) + SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig() { name = "EyeOfTheUniverse" }, "", Instance) { Config = { @@ -227,24 +229,19 @@ namespace NewHorizons // the campfire on the title screen calls this from RegisterShape before it gets patched, so we have to call it again. lol ShapeManager.Initialize(); + // Fix a thing (thanks jeff mobius) 1.1.15 updated the game over fonts to only include the characters they needed + for (int i = 0; i < TextTranslation.s_theTable.m_gameOverFonts.Length; i++) + { + var existingFont = TextTranslation.s_theTable.m_dynamicFonts[i]; + TextTranslation.s_theTable.m_gameOverFonts[i] = existingFont; + } + SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; GlobalMessenger.AddListener("PlayerDeath", OnDeath); GlobalMessenger.AddListener("WakeUp", OnWakeUp); - NHAssetBundle = ModHelper.Assets.LoadBundle("Assets/newhorizons_public"); - if (NHAssetBundle == null) - { - NHLogger.LogError("Couldn't find NHAssetBundle: The mod will likely not work."); - } - - NHPrivateAssetBundle = ModHelper.Assets.LoadBundle("Assets/newhorizons_private"); - if (NHPrivateAssetBundle == null) - { - NHLogger.LogError("Couldn't find NHPrivateAssetBundle: The mod will likely not work."); - } - VesselWarpHandler.Initialize(); ResetConfigs(resetTranslation: false); @@ -278,7 +275,6 @@ namespace NewHorizons { base.SetupPauseMenu(pauseMenu); DebugReload.InitializePauseMenu(pauseMenu); - DebugMenu.InitializePauseMenu(pauseMenu); } public void OnDestroy() @@ -334,6 +330,8 @@ namespace NewHorizons { try { + EyeDetailCacher.Init(); + AtmosphereBuilder.InitPrefabs(); BrambleDimensionBuilder.InitPrefabs(); BrambleNodeBuilder.InitPrefabs(); @@ -370,6 +368,8 @@ namespace NewHorizons ProjectionBuilder.InitPrefabs(); CloakBuilder.InitPrefab(); RaftBuilder.InitPrefab(); + DreamCampfireBuilder.InitPrefab(); + DreamArrivalPointBuilder.InitPrefab(); } WarpPadBuilder.InitPrefabs(); @@ -387,9 +387,11 @@ namespace NewHorizons else if (IsWarpingBackToEye) { IsWarpingBackToEye = false; - OWTime.Pause(OWTime.PauseType.Loading); - LoadManager.LoadSceneImmediate(OWScene.EyeOfTheUniverse); - OWTime.Unpause(OWTime.PauseType.Loading); + ManualOnStartSceneLoad(OWScene.EyeOfTheUniverse); + // LoadSceneImmediate doesn't cover the screen and you see the solar system for a frame without this + LoadManager.s_instance._fadeCanvas.enabled = true; + LoadManager.s_instance._fadeImage.color = Color.black; + LoadManager.LoadSceneImmediate(OWScene.EyeOfTheUniverse); return; } @@ -417,7 +419,7 @@ namespace NewHorizons IsChangingStarSystem = false; - if (isTitleScreen && _useCustomTitleScreen) + if (isTitleScreen && CustomTitleScreen) { try { @@ -434,6 +436,7 @@ namespace NewHorizons if (isEyeOfTheUniverse) { _playerAwake = true; + EyeSceneHandler.Init(); EyeSceneHandler.OnSceneLoad(); } @@ -524,6 +527,8 @@ namespace NewHorizons PlayerData.SaveLoopCount(2); PlayerData.SetPersistentCondition("LAUNCH_CODES_GIVEN", true); } + + if (shouldWarpInFromVessel) VesselWarpHandler.LoadDB(); } else if (isEyeOfTheUniverse) { @@ -531,6 +536,8 @@ namespace NewHorizons IsWarpingFromVessel = false; DidWarpFromVessel = false; DidWarpFromShip = false; + + EyeSceneHandler.SetUpEyeCampfireSequence(); } //Stop starfield from disappearing when there is no lights @@ -594,16 +601,15 @@ namespace NewHorizons { IsSystemReady = true; - // ShipWarpController will handle the invulnerability otherwise - if (!shouldWarpInFromShip) + // ShipWarpController or VesselWarpHandler will handle the invulnerability otherwise + if (!shouldWarpInFromShip && !shouldWarpInFromVessel) { Delay.FireOnNextUpdate(() => InvulnerabilityHandler.MakeInvulnerable(false)); } Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); Locator.GetPlayerBody().gameObject.AddComponent(); + if (HasDLC) Locator.GetPlayerBody().gameObject.AddComponent(); PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); @@ -627,10 +633,50 @@ namespace NewHorizons HasWarpDrive = true; } + /// + /// sometimes we call LoadSceneImmediate, which doesnt do the required event firing for mods to be happy. + /// this method emulates that via copying parts of LoadManager. + /// + public static void ManualOnStartSceneLoad(OWScene scene) + { + LoadManager.s_loadSceneJob = new LoadManager.LoadSceneJob(); + LoadManager.s_loadSceneJob.sceneToLoad = scene; + LoadManager.s_loadSceneJob.fadeType = LoadManager.FadeType.None; + LoadManager.s_loadSceneJob.fadeLength = 0; + LoadManager.s_loadSceneJob.pauseDuringFade = true; + LoadManager.s_loadSceneJob.asyncOperation = false; + LoadManager.s_loadSceneJob.skipPreLoadMemoryDump = false; + LoadManager.s_loadSceneJob.skipVsyncChange = false; + + LoadManager.s_loadingScene = LoadManager.s_loadSceneJob.sceneToLoad; + LoadManager.s_fadeType = LoadManager.s_loadSceneJob.fadeType; + LoadManager.s_fadeStartTime = Time.unscaledTime; + LoadManager.s_fadeLength = LoadManager.s_loadSceneJob.fadeLength; + LoadManager.s_pauseDuringFade = LoadManager.s_loadSceneJob.pauseDuringFade; + LoadManager.s_skipVsyncChange = LoadManager.s_loadSceneJob.skipVsyncChange; + + // cant fire events from outside of class without reflection + ((Delegate)AccessTools.Field(typeof(LoadManager), nameof(LoadManager.OnStartSceneLoad)).GetValue(null)) + .DynamicInvoke(LoadManager.s_currentScene, LoadManager.s_loadingScene); + + if (LoadManager.s_pauseDuringFade) + { + OWTime.Pause(OWTime.PauseType.Loading); + } + + LoadManager.s_loadSceneJob = null; + } #region Load - public void LoadStarSystemConfig(string starSystemName, StarSystemConfig starSystemConfig, string relativePath, IModBehaviour mod) + public void LoadStarSystemConfig(StarSystemConfig starSystemConfig, string relativePath, IModBehaviour mod) { + if (string.IsNullOrEmpty(starSystemConfig.name)) + { + starSystemConfig.name = Path.GetFileNameWithoutExtension(relativePath); + } + + var starSystemName = starSystemConfig.name; + starSystemConfig.Migrate(); starSystemConfig.FixCoordinates(); @@ -646,8 +692,25 @@ namespace NewHorizons if (SystemDict.ContainsKey(starSystemName)) { - if (string.IsNullOrEmpty(SystemDict[starSystemName].Config.travelAudio) && SystemDict[starSystemName].Config.Skybox == null) + // Both changing the Mod and RelativePath are weird and will likely cause incompat issues if two mods both affected the same system + // It's mostly just to fix up the config compared to the default one NH makes for the base StarSystem + + if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null) + { SystemDict[starSystemName].Mod = mod; + } + + // If a mod contains a change to the default system, set the relative path. + // Warning: If multiple systems make changes to the default system, only the relativePath will be set to the last mod loaded. + if (string.IsNullOrEmpty(SystemDict[starSystemName].RelativePath)) + { + SystemDict[starSystemName].RelativePath = relativePath; + } + else + { + NHLogger.LogWarning($"Two (or more) mods are making system changes to {starSystemName} which may result in errors"); + } + SystemDict[starSystemName].Config.Merge(starSystemConfig); } else @@ -683,13 +746,12 @@ namespace NewHorizons foreach (var file in systemFiles) { - var starSystemName = Path.GetFileNameWithoutExtension(file); - - NHLogger.LogVerbose($"Loading system {starSystemName}"); - var relativePath = file.Replace(folder, ""); + + NHLogger.LogVerbose($"Loading system {Path.GetFileNameWithoutExtension(relativePath)}"); + var starSystemConfig = mod.ModHelper.Storage.Load(relativePath, false); - LoadStarSystemConfig(starSystemName, starSystemConfig, relativePath, mod); + LoadStarSystemConfig(starSystemConfig, relativePath, mod); } } if (Directory.Exists(planetsFolder)) @@ -718,9 +780,6 @@ namespace NewHorizons if (body != null) { - // Wanna track the spawn point of each system - if (body.Config.Spawn != null) SystemDict[body.Config.starSystem].Spawn = body.Config.Spawn; - // Add the new planet to the planet dictionary if (!BodyDict.ContainsKey(body.Config.starSystem)) BodyDict[body.Config.starSystem] = new List(); BodyDict[body.Config.starSystem].Add(body); @@ -787,7 +846,7 @@ namespace NewHorizons { if (language is TextTranslation.Language.UNKNOWN or TextTranslation.Language.TOTAL) continue; - var relativeFile = Path.Combine("translations", language.ToString().ToLower() + ".json"); + var relativeFile = Path.Combine("translations", language.ToString().ToLowerInvariant() + ".json"); if (File.Exists(Path.Combine(folder, relativeFile))) { @@ -838,11 +897,7 @@ namespace NewHorizons { if (!SystemDict.ContainsKey(config.starSystem)) { - // Since we didn't load it earlier there shouldn't be a star system config - var starSystemConfig = mod.ModHelper.Storage.Load(Path.Combine("systems", config.starSystem + ".json"), false); - if (starSystemConfig == null) starSystemConfig = new StarSystemConfig(); - else NHLogger.LogWarning($"Loaded system config for {config.starSystem}. Why wasn't this loaded earlier?"); - + var starSystemConfig = new StarSystemConfig() { name = config.starSystem }; starSystemConfig.Migrate(); starSystemConfig.FixCoordinates(); @@ -853,10 +908,22 @@ namespace NewHorizons BodyDict.Add(config.starSystem, new List()); } + // Fall back to file name if name not given + if (!string.IsNullOrEmpty(relativePath) && string.IsNullOrEmpty(config.name)) + { + config.name = Path.GetFileNameWithoutExtension(relativePath); + } + // Has to happen after we make sure theres a system config config.Validate(); config.Migrate(); + // Check if this system can be warped to + if (config.Spawn?.shipSpawnPoints?.Any() ?? false) + { + SystemDict[config.starSystem].HasShipSpawn = true; + } + return new NewHorizonsBody(config, mod, relativePath); } @@ -870,6 +937,10 @@ namespace NewHorizons { _defaultStarSystem = defaultSystem; } + if (LoadManager.GetCurrentScene() != OWScene.SolarSystem && LoadManager.GetCurrentScene() != OWScene.EyeOfTheUniverse) + { + CurrentStarSystem = _defaultStarSystem; + } } #endregion Load @@ -907,6 +978,7 @@ namespace NewHorizons IsWarpingFromVessel = vessel; DidWarpFromVessel = false; OnChangeStarSystem?.Invoke(newStarSystem); + VesselWarpController.s_relativeLocationSaved = false; NHLogger.Log($"Warping to {newStarSystem}"); if (warp && ShipWarpController) ShipWarpController.WarpOut(); @@ -922,7 +994,8 @@ namespace NewHorizons } else { - PlayerData.SaveEyeCompletion(); // So that the title screen doesn't keep warping you back to eye + if (!IsWarpingBackToEye) + PlayerData.SaveEyeCompletion(); // So that the title screen doesn't keep warping you back to eye if (SystemDict[CurrentStarSystem].Config.enableTimeLoop) SecondsElapsedInLoop = TimeLoop.GetSecondsElapsed(); else SecondsElapsedInLoop = -1; @@ -940,13 +1013,44 @@ namespace NewHorizons { // Slide reel unloading is tied to being removed from the sector, so we do that here to prevent a softlock Locator.GetPlayerSectorDetector().RemoveFromAllSectors(); + ManualOnStartSceneLoad(sceneToLoad); // putting it before fade breaks ship warp effect cuz pause LoadManager.LoadSceneImmediate(sceneToLoad); }); } } + /// + /// Exclusively for + /// + internal void ChangeCurrentStarSystemVesselAsync(string newStarSystem) + { + if (IsChangingStarSystem) return; + + if (LoadManager.GetCurrentScene() == OWScene.SolarSystem || LoadManager.GetCurrentScene() == OWScene.EyeOfTheUniverse) + { + IsWarpingFromShip = false; + IsWarpingFromVessel = true; + DidWarpFromVessel = false; + OnChangeStarSystem?.Invoke(newStarSystem); + + NHLogger.Log($"Vessel warping to {newStarSystem}"); + IsChangingStarSystem = true; + WearingSuit = PlayerState.IsWearingSuit(); + + PlayerData.SaveEyeCompletion(); // So that the title screen doesn't keep warping you back to eye + + if (SystemDict[CurrentStarSystem].Config.enableTimeLoop) SecondsElapsedInLoop = TimeLoop.GetSecondsElapsed(); + else SecondsElapsedInLoop = -1; + + CurrentStarSystem = newStarSystem; + + LoadManager.LoadSceneAsync(OWScene.SolarSystem, false, LoadManager.FadeType.ToBlack); + } + } + void OnDeath(DeathType _) { + VesselWarpController.s_relativeLocationSaved = false; // We reset the solar system on death if (!IsChangingStarSystem) { @@ -958,13 +1062,17 @@ namespace NewHorizons private void ResetCurrentStarSystem() { - if (SystemDict.ContainsKey(_defaultSystemOverride)) + if (SystemDict.ContainsKey(DefaultSystemOverride)) { - CurrentStarSystem = _defaultSystemOverride; + CurrentStarSystem = DefaultSystemOverride; - if (BodyDict.TryGetValue(_defaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null)) + // #738 - Sometimes the override will not support spawning regularly, so always warp in if possible + if (SystemDict[DefaultSystemOverride].Config.Vessel?.spawnOnVessel == true) + { + IsWarpingFromVessel = true; + } + else if (BodyDict.TryGetValue(DefaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawnPoints?.Any() ?? false)) { - // #738 - Sometimes the override will not support spawning regularly, so always warp in if possible IsWarpingFromShip = true; } else @@ -975,9 +1083,9 @@ namespace NewHorizons else { // Ignore first load because it doesn't even know what systems we have - if (!_firstLoad && !string.IsNullOrEmpty(_defaultSystemOverride)) + if (!_firstLoad && !string.IsNullOrEmpty(DefaultSystemOverride)) { - NHLogger.LogError($"The given default system override {_defaultSystemOverride} is invalid - no system exists with that name"); + NHLogger.LogError($"The given default system override {DefaultSystemOverride} is invalid - no system exists with that name"); } CurrentStarSystem = _defaultStarSystem; diff --git a/NewHorizons/NewHorizons.csproj b/NewHorizons/NewHorizons.csproj index e8abb757..5ae0cd1c 100644 --- a/NewHorizons/NewHorizons.csproj +++ b/NewHorizons/NewHorizons.csproj @@ -15,8 +15,8 @@ none - - + + diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 6a165fa2..348991c0 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -143,9 +143,11 @@ namespace NewHorizons public object QueryBody(Type outType, string bodyName, string jsonPath) { var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName); - return planet == null - ? null - : QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); + if (planet == null){ + NHLogger.LogError($"Could not find planet with body name {bodyName}."); + return null; + } + return QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); } public T QueryBody(string bodyName, string jsonPath) @@ -259,7 +261,8 @@ namespace NewHorizons public void DefineStarSystem(string name, string config, IModBehaviour mod) { var starSystemConfig = JsonConvert.DeserializeObject(config); - Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); + starSystemConfig.name = name; + Main.Instance.LoadStarSystemConfig(starSystemConfig, null, mod); } public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) @@ -310,6 +313,7 @@ namespace NewHorizons var system = new StarSystemConfig() { + name = starSystem, entryPositions = entryPositions? .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) .ToArray(), @@ -318,7 +322,7 @@ namespace NewHorizons .ToArray() }; - Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); + Main.Instance.LoadStarSystemConfig(system, null, mod); RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); } @@ -338,5 +342,7 @@ namespace NewHorizons public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); public void AddSubtitle(IModBehaviour mod, string filePath) => SubtitlesHandler.RegisterAdditionalSubtitle(mod, filePath); + + public void SetNextSpawnID(string id) => PlayerSpawnHandler.TargetSpawnID = id; } } diff --git a/NewHorizons/Patches/DialoguePatches/CharacterDialogueTreePatches.cs b/NewHorizons/Patches/DialoguePatches/CharacterDialogueTreePatches.cs index 1ef6c7b3..d30cc7b7 100644 --- a/NewHorizons/Patches/DialoguePatches/CharacterDialogueTreePatches.cs +++ b/NewHorizons/Patches/DialoguePatches/CharacterDialogueTreePatches.cs @@ -24,7 +24,10 @@ public static class CharacterDialogueTreePatches private static void OnAttachPlayerToPoint(this CharacterDialogueTree characterDialogueTree, OWRigidbody rigidbody) { - characterDialogueTree.EndConversation(); + if (characterDialogueTree.InConversation()) + { + characterDialogueTree.EndConversation(); + } } [HarmonyPostfix] diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs new file mode 100644 index 00000000..5ec80d5d --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs @@ -0,0 +1,21 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(AlarmTotem))] + public static class AlarmTotemPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(AlarmTotem.SetFaceOpen))] + public static void AlarmTotem_SetFaceOpen(AlarmTotem __instance, bool open) + { + // This method is unused in the base game and sets the rotations incorrectly (-90f instead of 90f); this corrects that + __instance._rightFaceCover.localEulerAngles = Vector3.up * (open ? 90f : 0f); + __instance._leftFaceCover.localEulerAngles = Vector3.up * (open ? -90f : 0f); + } + } +} diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/DreamworldControllerPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/DreamworldControllerPatches.cs index 4119fd0a..3d37b4b2 100644 --- a/NewHorizons/Patches/EchoesOfTheEyePatches/DreamworldControllerPatches.cs +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/DreamworldControllerPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Components.EOTE; using System.Collections.Generic; using System.Reflection.Emit; using UnityEngine; @@ -26,5 +27,59 @@ namespace NewHorizons.Patches.EchoesOfTheEyePatches .Repeat(matcher => matcher.RemoveInstructions(5)) .InstructionEnumeration(); } + + [HarmonyPrefix] + [HarmonyPatch(nameof(DreamWorldController.EnterDreamWorld))] + public static bool DreamWorldController_EnterDreamWorld(DreamWorldController __instance, DreamCampfire dreamCampfire, DreamArrivalPoint arrivalPoint) + { + // If we are arriving in a custom dream dimension, activate it immediately + var dimension = arrivalPoint.GetAttachedOWRigidbody().GetComponent(); + if (dimension != null) + { + dimension.SetActive(true); + } + + // Make the body/sector/volume where the player 'wakes up' in the dream match the body where the arrival point is located if it isn't the vanilla DreamWorld, or reset it if it is + var dreamWorldAO = Locator.GetAstroObject(AstroObject.Name.DreamWorld); + if (arrivalPoint.GetAttachedOWRigidbody() == dreamWorldAO.GetOWRigidbody()) + { + __instance._dreamBody = dreamWorldAO.GetAttachedOWRigidbody(); + __instance._dreamWorldSector = dreamWorldAO.GetRootSector(); + __instance._dreamWorldVolume = __instance._dreamWorldSector.transform.Find("Volumes_DreamWorld").Find("DreamWorldVolume").GetComponent(); + } + else + { + var arrivalAO = arrivalPoint.GetComponentInParent(); + __instance._dreamBody = arrivalAO.GetAttachedOWRigidbody(); + __instance._dreamWorldSector = arrivalAO.GetRootSector(); + __instance._dreamWorldVolume = arrivalAO._gravityVolume.GetOWTriggerVolume(); + } + + // Make the 'bubble' around the artifact load correctly when the destination body isn't the vanilla DreamWorld + __instance._primarySimulationRoot.GetComponent().SetSector(__instance._dreamWorldSector); + + // if the player's already in the dream world, we want to wake up at the first campfire we slept at, so don't override those values + if (PlayerState.InDreamWorld()) + { + __instance._dreamArrivalPoint = arrivalPoint; + __instance._enteringDream = true; + return false; + } + + // Make the body where the player 'wakes up' out of the dream match the body where the campfire is located if it isn't the Stranger ("RingWorld"), or reset it if it is + var ringWorldAO = Locator.GetAstroObject(AstroObject.Name.RingWorld); + if (dreamCampfire.GetAttachedOWRigidbody() == ringWorldAO.GetOWRigidbody()) + { + __instance._planetBody = ringWorldAO.GetAttachedOWRigidbody(); + __instance._ringWorldController = ringWorldAO.GetComponent(); + } + else + { + __instance._planetBody = dreamCampfire.GetAttachedOWRigidbody(); + __instance._ringWorldController = null; + } + + return true; + } } } diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs new file mode 100644 index 00000000..38503ef4 --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs @@ -0,0 +1,117 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(LanternZoomPoint))] + public static class LanternZoomPointPatches + { + // Patching all methods that assume the player is holding an artifact (_playerLantern) to add null checks so they'll work outside of the dream world + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.Update))] + public static bool LanternZoomPoint_Update(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + if (!__instance.enabled) + { + return false; + } + if (__instance._state != LanternZoomPoint.State.RetroZoom) + { + if (__instance._playerLantern != null) + { + __instance._playerLantern.GetLanternController().MoveTowardFocus(1f, 2f); + } + } + if (__instance._state == LanternZoomPoint.State.LookAt && Time.time > __instance._stateChangeTime + 0.4f) + { + __instance.ChangeState(LanternZoomPoint.State.ZoomIn); + __instance.StartZoomIn(); + } + else if (__instance._state == LanternZoomPoint.State.ZoomIn) + { + __instance.UpdateZoomIn(); + } + if (__instance._state == LanternZoomPoint.State.RetroZoom) + { + __instance.UpdateRetroZoom(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.UpdateRetroZoom))] + public static bool LanternZoomPoint_UpdateRetroZoom(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + float num = Mathf.InverseLerp(__instance._stateChangeTime, __instance._stateChangeTime + 1.2f, Time.time); + float focus = Mathf.Pow(Mathf.SmoothStep(0f, 1f, 1f - num), 0.2f); + if (__instance._playerLantern != null) + { + __instance._playerLantern.GetLanternController().SetFocus(focus); + } + float t = __instance._retroZoomCurve.Evaluate(num); + float targetFieldOfView = Mathf.Lerp(__instance._startFOV, Locator.GetPlayerCameraController().GetOrigFieldOfView(), t); + Locator.GetPlayerCameraController().SetTargetFieldOfView(targetFieldOfView); + float d = __instance._imageHalfWidth / Mathf.Tan(Locator.GetPlayerCamera().fieldOfView * 0.017453292f * 0.5f); + Vector3 vector = __instance._startLocalPos - __instance._endLocalPos; + __instance._attachPoint.transform.localPosition = __instance._endLocalPos + vector.normalized * d; + if (num >= 1f) + { + __instance.FinishRetroZoom(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.FinishRetroZoom))] + public static bool LanternZoomPoint_FinishRetroZoom(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + __instance.ChangeState(LanternZoomPoint.State.Idle); + __instance.enabled = false; + __instance._attachPoint.DetachPlayer(); + GlobalMessenger.FireEvent("PlayerRepositioned"); + if (__instance._playerLantern != null) + { + __instance._playerLantern.ForceUnfocus(); + __instance._playerLantern.enabled = true; + __instance._playerLantern = null; + } + OWInput.ChangeInputMode(InputMode.Character); + __instance._lightController.FadeTo(0f, 1f); + Locator.GetPlayerController().SetColliderActivation(true); + Locator.GetPlayerTransform().GetComponent().BreakLock(); + Locator.GetDreamWorldController().SetActiveZoomPoint(null); + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.OnDetectLight))] + public static bool LanternZoomPoint_OnDetectLight(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + if (__instance._state == LanternZoomPoint.State.Idle && !PlayerState.IsAttached() && Time.time > __instance._stateChangeTime + 1f && Vector3.Distance(__instance.transform.position, Locator.GetPlayerCamera().transform.position) > __instance._minActivationDistance) + { + __instance._playerLantern = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem() as DreamLanternItem; + Locator.GetDreamWorldController().SetActiveZoomPoint(__instance); + __instance._attachPoint.transform.position = Locator.GetPlayerTransform().position; + __instance._attachPoint.transform.rotation = Locator.GetPlayerTransform().rotation; + __instance._attachPoint.AttachPlayer(); + Locator.GetPlayerTransform().GetComponent().LockOn(__instance.transform, 5f, false, 1f); + OWInput.ChangeInputMode(InputMode.None); + if (__instance._playerLantern != null) + { + __instance._playerLantern.enabled = false; + } + __instance.ChangeState(LanternZoomPoint.State.LookAt); + __instance.enabled = true; + } + return false; + } + } +} diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs new file mode 100644 index 00000000..f0b6df44 --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs @@ -0,0 +1,69 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(Peephole))] + public static class PeepholePatches + { + static List _previousSectors = new List(); + + [HarmonyPrefix] + [HarmonyPatch(nameof(Peephole.SwitchToPeepholeCamera))] + public static void Peephole_SwitchToPeepholeCamera_Prefix() + { + _previousSectors.Clear(); + _previousSectors.AddRange(Locator.GetPlayerSectorDetector()._sectorList); + } + + + [HarmonyPostfix] + [HarmonyPatch(nameof(Peephole.SwitchToPeepholeCamera))] + public static void Peephole_SwitchToPeepholeCamera(Peephole __instance) + { + if (__instance._viewingSector) + { + var sector = __instance._viewingSector; + while (sector._parentSector != null) + { + sector = sector._parentSector; + + if (!_previousSectors.Contains(sector)) + { + sector.AddOccupant(Locator.GetPlayerSectorDetector()); + } + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(nameof(Peephole.SwitchToPlayerCamera))] + public static void Peephole_SwitchToPlayerCamera(Peephole __instance) + { + if (__instance._viewingSector) + { + var sector = __instance._viewingSector; + + if (_previousSectors.Contains(sector)) + { + sector.AddOccupant(Locator.GetPlayerSectorDetector()); + } + + while (sector._parentSector != null) + { + sector = sector._parentSector; + + if (!_previousSectors.Contains(sector)) + { + sector.RemoveOccupant(Locator.GetPlayerSectorDetector()); + } + } + } + + _previousSectors.Clear(); + } + } +} diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs index a0b40a99..48a08f08 100644 --- a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Components.Props; using UnityEngine; namespace NewHorizons.Patches.EchoesOfTheEyePatches @@ -63,7 +64,8 @@ namespace NewHorizons.Patches.EchoesOfTheEyePatches if (__instance._playerInEffectsRange) { // All this to change what fluidVolume we use on this line - float num = __instance._fluidDetector.InFluidType(FluidVolume.Type.WATER) ? __instance._fluidDetector._alignmentFluid.GetFractionSubmerged(__instance._fluidDetector) : 0f; + FluidVolume volume = __instance._fluidDetector._alignmentFluid; + float num = __instance._fluidDetector.InFluidType(FluidVolume.Type.WATER) && volume != null ? volume.GetFractionSubmerged(__instance._fluidDetector) : 0f; bool allowMovement = num > 0.25f && num < 1f; __instance._effectsController.UpdateMovementAudio(allowMovement, __instance._lightSensors); __instance._effectsController.UpdateGroundedAudio(__instance._fluidDetector); @@ -72,5 +74,31 @@ namespace NewHorizons.Patches.EchoesOfTheEyePatches return false; } + + [HarmonyPostfix] + [HarmonyPatch(nameof(RaftController.UpdateMoveToTarget))] + public static void UpdateMoveToTarget(RaftController __instance) + { + // If it has a riverFluid volume then its a regular stranger one + if (__instance._movingToTarget && __instance._riverFluid == null) + { + OWRigidbody raftBody = __instance._raftBody; + OWRigidbody origParentBody = __instance._raftBody.GetOrigParentBody(); + Transform transform = origParentBody.transform; + Vector3 vector = transform.TransformPoint(__instance._targetLocalPosition); + + // Base game threshold has this at 1f (after doing smoothstep on it) + // For whatever reason it never hits that for NH planets (probably since they're moving so much compared to the steady velocity of the Stranger) + // Might break for somebody with a wacky spinning planet in which case we can adjust this or add some kind of fallback (i.e., wait x seconds and then just say its there) + // Fixes #1005 + if (__instance.currentDistanceLerp > 0.999f) + { + raftBody.SetPosition(vector); + raftBody.SetRotation(transform.rotation * __instance._targetLocalRotation); + __instance.StopMovingToTarget(); + __instance.OnArriveAtTarget.Invoke(); + } + } + } } } diff --git a/NewHorizons/Patches/EyeScenePatches/CosmicInflationControllerPatches.cs b/NewHorizons/Patches/EyeScenePatches/CosmicInflationControllerPatches.cs new file mode 100644 index 00000000..2363140c --- /dev/null +++ b/NewHorizons/Patches/EyeScenePatches/CosmicInflationControllerPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using NewHorizons.Handlers; +using NewHorizons.Utility.OWML; +using System.Linq; + +namespace NewHorizons.Patches.EyeScenePatches +{ + [HarmonyPatch(typeof(CosmicInflationController))] + public static class CosmicInflationControllerPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(CosmicInflationController.UpdateFormation))] + public static void CosmicInflationController_UpdateFormation(CosmicInflationController __instance) + { + if (__instance._waitForCrossfade && EyeSceneHandler.GetActiveCustomEyeTravelers().Any()) + { + NHLogger.Log($"Hijacking finale cross-fade, NH will handle it"); + __instance._waitForCrossfade = false; + __instance._waitForMusicEnd = true; + EyeSceneHandler.GetMusicController().TransitionToFinale(); + } + } + } +} diff --git a/NewHorizons/Patches/EyeScenePatches/QuantumCampsiteControllerPatches.cs b/NewHorizons/Patches/EyeScenePatches/QuantumCampsiteControllerPatches.cs new file mode 100644 index 00000000..542c310d --- /dev/null +++ b/NewHorizons/Patches/EyeScenePatches/QuantumCampsiteControllerPatches.cs @@ -0,0 +1,88 @@ +using HarmonyLib; +using NewHorizons.Handlers; +using NewHorizons.Utility.OWML; +using System.Linq; + +namespace NewHorizons.Patches.EyeScenePatches +{ + [HarmonyPatch(typeof(QuantumCampsiteController))] + public static class QuantumCampsiteControllerPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(QuantumCampsiteController.Start))] + public static void QuantumCampsiteController_Start() + { + EyeSceneHandler.UpdateTravelerPositions(); + } + + [HarmonyPostfix] + [HarmonyPatch(nameof(QuantumCampsiteController.ActivateRemainingInstrumentZones))] + public static void QuantumCampsiteController_ActivateRemainingInstrumentZones(QuantumCampsiteController __instance) + { + // We modify this array when registering a custom instrument zone but the vanilla method only activates the first 6 + for (int i = 6; i < __instance._instrumentZones.Length; i++) + { + __instance._instrumentZones[i].SetActive(true); + } + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(QuantumCampsiteController.AreAllTravelersGathered))] + public static bool QuantumCampsiteController_AreAllTravelersGathered(QuantumCampsiteController __instance, ref bool __result) + { + if (!EyeSceneHandler.GetActiveCustomEyeTravelers().Any()) + { + return true; + } + bool gatheredAllHearthianTravelers = __instance._travelerControllers.Take(4).All(t => t.gameObject.activeInHierarchy); + if (!gatheredAllHearthianTravelers) + { + __result = false; + return false; + } + bool needsSolanum = __instance._hasMetSolanum; + bool gatheredSolanum = __instance._travelerControllers[QuantumCampsiteController.SOLANUM_INDEX].gameObject.activeInHierarchy; + if (needsSolanum && !gatheredSolanum) + { + __result = false; + return false; + } + bool needsPrisoner = __instance._hasMetPrisoner && !__instance._hasErasedPrisoner; + bool gatheredPrisoner = __instance._travelerControllers[QuantumCampsiteController.PRISONER_INDEX].gameObject.activeInHierarchy; + if (needsPrisoner && !gatheredPrisoner) + { + __result = false; + return false; + } + foreach (var traveler in EyeSceneHandler.GetActiveCustomEyeTravelers()) + { + bool needsTraveler = true; + if (!string.IsNullOrEmpty(traveler.info.participatingCondition)) + { + needsTraveler = DialogueConditionManager.SharedInstance.GetConditionState(traveler.info.participatingCondition); + } + bool gatheredTraveler = traveler.controller.gameObject.activeInHierarchy; + if (needsTraveler && !gatheredTraveler) + { + __result = false; + return false; + } + } + __result = true; + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(QuantumCampsiteController.OnTravelerStartPlaying))] + public static void OnTravelerStartPlaying(QuantumCampsiteController __instance) + { + if (!__instance._hasJamSessionStarted && EyeSceneHandler.GetActiveCustomEyeTravelers().Any()) + { + NHLogger.Log($"NH is handling Eye sequence music"); + // Jam session is starting, start our custom music handler + EyeSceneHandler.GetMusicController().StartPlaying(); + } + // Letting the original method run in case mods have patched TravelerEyeController.OnStartCosmicJamSession() + } + } +} diff --git a/NewHorizons/Patches/EyeScenePatches/TravelerEyeControllerPatches.cs b/NewHorizons/Patches/EyeScenePatches/TravelerEyeControllerPatches.cs new file mode 100644 index 00000000..d31fca50 --- /dev/null +++ b/NewHorizons/Patches/EyeScenePatches/TravelerEyeControllerPatches.cs @@ -0,0 +1,26 @@ +using HarmonyLib; +using NewHorizons.Handlers; +using System.Linq; + +namespace NewHorizons.Patches.EyeScenePatches +{ + [HarmonyPatch(typeof(TravelerEyeController))] + public static class TravelerEyeControllerPatches + { + [HarmonyPrefix] + [HarmonyPatch(nameof(TravelerEyeController.OnStartCosmicJamSession))] + public static bool TravelerEyeController_OnStartCosmicJamSession(TravelerEyeController __instance) + { + if (!EyeSceneHandler.GetActiveCustomEyeTravelers().Any()) + { + return true; + } + // Not starting the loop audio here; EyeMusicController will handle that + if (__instance._signal != null) + { + __instance._signal.GetOWAudioSource().SetLocalVolume(0f); + } + return false; + } + } +} diff --git a/NewHorizons/Patches/GlobalMusicControllerPatches.cs b/NewHorizons/Patches/GlobalMusicControllerPatches.cs index 776cb8e8..9697beb7 100644 --- a/NewHorizons/Patches/GlobalMusicControllerPatches.cs +++ b/NewHorizons/Patches/GlobalMusicControllerPatches.cs @@ -1,10 +1,14 @@ -using HarmonyLib; +using HarmonyLib; +using NewHorizons.Components.EOTE; +using NewHorizons.Utility; +using System.Collections.Generic; +using System.Reflection.Emit; using UnityEngine; namespace NewHorizons.Patches; [HarmonyPatch(typeof(GlobalMusicController))] -public class GlobalMusicControllerPatches +public static class GlobalMusicControllerPatches { private static AudioDetector _audioDetector; @@ -21,7 +25,7 @@ public class GlobalMusicControllerPatches PlayerState.AtFlightConsole() && !PlayerState.IsHullBreached() && !__instance._playingFinalEndTimes && - _audioDetector._activeVolumes.Count == 0; // change - don't play if in another audio volume + _audioDetector._activeVolumes.Count <= 1; // change - don't play if in another audio volume other than ambient var playing = __instance._darkBrambleSource.isPlaying && !__instance._darkBrambleSource.IsFadingOut(); if (shouldBePlaying && !playing) @@ -33,6 +37,93 @@ public class GlobalMusicControllerPatches __instance._darkBrambleSource.FadeOut(5f); } + return false; + } + + /// + /// Replaces any 85f with + /// + [HarmonyTranspiler] + [HarmonyPatch(nameof(GlobalMusicController.UpdateEndTimesMusic))] + public static IEnumerable GlobalMusicController_UpdateEndTimesMusic(IEnumerable instructions, ILGenerator generator) + { + return new CodeMatcher(instructions, generator).MatchForward(true, + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Ble_Un) + ).Advance(-1).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).MatchForward(true, + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Bge_Un) + ).Advance(-1).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).MatchForward(false, + new CodeMatch(OpCodes.Ldc_R4, 85f), + new CodeMatch(OpCodes.Call), + new CodeMatch(OpCodes.Sub) + ).RemoveInstruction().Insert( + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(NewHorizonsExtensions), nameof(NewHorizonsExtensions.GetSecondsBeforeSupernovaPlayTime))) + ).InstructionEnumeration(); + } + + // Custom end times for dreamworld + + [HarmonyPrefix] + [HarmonyPatch(nameof(GlobalMusicController.OnEnterDreamWorld))] + public static bool GlobalMusicController_OnEnterDreamWorld(GlobalMusicController __instance) + { + if (__instance._playingFinalEndTimes) + { + __instance._finalEndTimesIntroSource.Stop(); + __instance._finalEndTimesLoopSource.Stop(); + __instance._finalEndTimesDarkBrambleSource.FadeIn(1f); + } + else + { + if (__instance.TryGetComponent(out DreamWorldEndTimes dreamWorldEndTimes)) + { + dreamWorldEndTimes.AssignEndTimesDream(__instance._endTimesSource); + } + else + { + __instance._endTimesSource.Stop(); + __instance._endTimesSource.AssignAudioLibraryClip(AudioType.EndOfTime_Dream); + } + __instance._playingEndTimes = false; + } + + return false; + } + + + [HarmonyPrefix] + [HarmonyPatch(nameof(GlobalMusicController.OnExitDreamWorld))] + public static bool GlobalMusicController_OnExitDreamWorld(GlobalMusicController __instance) + { + if (__instance._playingFinalEndTimes) + { + __instance._finalEndTimesLoopSource.FadeIn(1f); + __instance._finalEndTimesDarkBrambleSource.Stop(); + } + else + { + if (__instance.TryGetComponent(out DreamWorldEndTimes dreamWorldEndTimes)) + { + dreamWorldEndTimes.AssignEndTimes(__instance._endTimesSource); + } + else + { + __instance._endTimesSource.Stop(); + __instance._endTimesSource.AssignAudioLibraryClip(AudioType.EndOfTime); + } + __instance._playingEndTimes = false; + } + return false; } } diff --git a/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs b/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs index c99ae6fe..df51f4bb 100644 --- a/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs +++ b/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs @@ -14,11 +14,11 @@ namespace NewHorizons.Patches.HUDPatches bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside()); bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside; - bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; - bool insideCloak = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside; + bool insideIPMatches = Locator.GetCloakFieldController() == null || Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; + bool insideCloakMatches = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside; bool sameInterference = InterferenceHandler.IsPlayerSameAsShip(); - __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIP && insideCloak && sameInterference; + __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIPMatches && insideCloakMatches && sameInterference; if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); diff --git a/NewHorizons/Patches/LocatorPatches.cs b/NewHorizons/Patches/LocatorPatches.cs index b1d32bd8..f5080b04 100644 --- a/NewHorizons/Patches/LocatorPatches.cs +++ b/NewHorizons/Patches/LocatorPatches.cs @@ -1,4 +1,6 @@ using HarmonyLib; +using System.Collections.Generic; +using System.Linq; namespace NewHorizons.Patches { @@ -108,5 +110,12 @@ namespace NewHorizons.Patches _mapSatellite = null; _sunStation = null; } + + [HarmonyPostfix] + [HarmonyPatch(nameof(Locator.GetAudioSignals))] + public static void Locator_GetAudioSignals(ref List __result) + { + __result = __result.Where(signal => signal.IsActive()).ToList(); + } } } diff --git a/NewHorizons/Patches/PlayerImpactAudioPatches.cs b/NewHorizons/Patches/PlayerImpactAudioPatches.cs new file mode 100644 index 00000000..f0028e0f --- /dev/null +++ b/NewHorizons/Patches/PlayerImpactAudioPatches.cs @@ -0,0 +1,16 @@ +using HarmonyLib; +using NewHorizons.Handlers; + +namespace NewHorizons.Patches; + +[HarmonyPatch] +public static class PlayerImpactAudioPatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerImpactAudio), nameof(PlayerImpactAudio.OnImpact))] + public static bool PlayerImpactAudio_OnImpact() + { + // DeathManager and PlayerResources _invincible stops player dying but you still hear the impact sounds which is annoying so we disable them + return !InvulnerabilityHandler.Invincible; + } +} diff --git a/NewHorizons/Patches/PlayerPatches/PlayerBreathingAudioPatches.cs b/NewHorizons/Patches/PlayerPatches/PlayerBreathingAudioPatches.cs new file mode 100644 index 00000000..a550b504 --- /dev/null +++ b/NewHorizons/Patches/PlayerPatches/PlayerBreathingAudioPatches.cs @@ -0,0 +1,17 @@ +using HarmonyLib; + +namespace NewHorizons.Patches.PlayerPatches +{ + [HarmonyPatch(typeof(PlayerBreathingAudio))] + public static class PlayerBreathingAudioPatches + { + [HarmonyPrefix] + [HarmonyPatch(nameof(PlayerBreathingAudio.OnWakeUp))] + public static bool PlayerBreathingAudio_OnWakeUp(PlayerBreathingAudio __instance) + { + if (Main.Instance.IsWarpingFromShip || Main.Instance.IsWarpingFromVessel || Main.Instance.DidWarpFromShip || Main.Instance.DidWarpFromVessel) + return false; + return true; + } + } +} diff --git a/NewHorizons/Patches/PlayerPatches/PlayerCameraEffectControllerPatches.cs b/NewHorizons/Patches/PlayerPatches/PlayerCameraEffectControllerPatches.cs new file mode 100644 index 00000000..26933ce5 --- /dev/null +++ b/NewHorizons/Patches/PlayerPatches/PlayerCameraEffectControllerPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using NewHorizons.Utility; +using NewHorizons.Utility.OWML; +using UnityEngine; + +namespace NewHorizons.Patches.PlayerPatches +{ + [HarmonyPatch(typeof(PlayerCameraEffectController))] + public static class PlayerCameraEffectControllerPatches + { + [HarmonyPrefix] + [HarmonyPatch(nameof(PlayerCameraEffectController.OnStartOfTimeLoop))] + public static bool PlayerCameraEffectController_OnStartOfTimeLoop(PlayerCameraEffectController __instance, int loopCount) + { + if (__instance.gameObject.CompareTag("MainCamera") && (Main.Instance.IsWarpingFromShip || Main.Instance.IsWarpingFromVessel || Main.Instance.DidWarpFromShip || Main.Instance.DidWarpFromVessel)) + { + __instance.CloseEyesImmediate(); + GlobalMessenger.FireEvent("WakeUp"); + return false; + } + return true; + } + } +} diff --git a/NewHorizons/Patches/RigidbodyPatches.cs b/NewHorizons/Patches/RigidbodyPatches.cs index 22ad16dc..963a6406 100644 --- a/NewHorizons/Patches/RigidbodyPatches.cs +++ b/NewHorizons/Patches/RigidbodyPatches.cs @@ -1,5 +1,5 @@ using HarmonyLib; -using NewHorizons.Utility; +using OWML.Utils; using System.Collections.Generic; using UnityEngine; diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs index abfc67a8..dd1654bd 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs @@ -31,7 +31,7 @@ namespace NewHorizons.Patches.ShipLogPatches { // Custom astro objects might have no entries, in this case they will be permanently hidden // Just treat it as if it were revealed - if (__instance._entries.Count == 0) + if (__instance._entries == null || __instance._entries.Count == 0) { __instance._state = ShipLogEntry.State.Explored; __instance._imageObj.SetActive(true); diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs index b76d61bb..5fe2684b 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs @@ -129,5 +129,18 @@ namespace NewHorizons.Patches.ShipLogPatches return false; } } + + [HarmonyPrefix] + [HarmonyPatch(nameof(ShipLogManager.AddEntry))] + public static bool ShipLogManager_AddEntry(ShipLogManager __instance, ShipLogEntry entry) + { + if (__instance._entryDict.TryGetValue(entry.GetID(), out var existing)) + { + NHLogger.LogVerbose($"Merging duplicate shiplog entry: {entry.GetID()}"); + RumorModeBuilder.MergeEntries(__instance, entry, existing); + return false; + } + return true; + } } } diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs index e6fd50ad..46411832 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs @@ -1,5 +1,6 @@ using HarmonyLib; using NewHorizons.Builder.ShipLog; +using NewHorizons.Components.Orbital; using NewHorizons.Handlers; using NewHorizons.Utility; using NewHorizons.Utility.OWML; @@ -19,6 +20,29 @@ namespace NewHorizons.Patches.ShipLogPatches GameObject panRoot = SearchUtilities.Find(ShipLogHandler.PAN_ROOT_PATH); GameObject sunObject = SearchUtilities.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, __instance._astroObjects, sunObject.layer); + // Remove anything that isnt meant to be selectable + var flagRemovedUnselectable = false; + for (int i = 0; i < navMatrix.Length; i++) + { + for (int j = 0; j < navMatrix[i].Length; j++) + { + if (!(MapModeBuilder.GetMapModeInfoForAstroObject(navMatrix[i][j])?.selectable ?? true)) + { + flagRemovedUnselectable = true; + navMatrix[i][j].UpdateState(); + navMatrix[i][j] = null; + } + } + } + if (flagRemovedUnselectable) + { + navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).ToArray(); + for (var index = 0; index < navMatrix.Length; index++) + { + navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + } + } + if (navMatrix == null || navMatrix.Length <= 1) { NHLogger.LogWarning("Skipping Map Mode Generation."); diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs new file mode 100644 index 00000000..1da6531e --- /dev/null +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogSlideReelPatches.cs @@ -0,0 +1,37 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; + +namespace NewHorizons.Patches.ShipLogPatches; + +[HarmonyPatch] +public static class ShipLogSlideReelPatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogSlideProjector), nameof(ShipLogSlideProjector.CheckStreamingTexturesAvailable))] + public static bool ShipLogSlideProjector_CheckStreamingTexturesAvailable(ShipLogSlideProjector __instance, ref bool __result) + { + if (__instance._collectionIndex >= 0 && __instance._collectionIndex < __instance._slideCollections.Count && + __instance._slideCollections[__instance._collectionIndex] is NHSlideCollection) + { + __result = true; + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogSlideProjector), nameof(ShipLogSlideProjector.UnloadCurrentStreamingTextures))] + public static bool ShipLogSlideProjector_UnloadCurrentStreamingTextures(ShipLogSlideProjector __instance) + { + if (__instance._collectionIndex >= 0 && __instance._collectionIndex < __instance._slideCollections.Count && + __instance._slideCollections[__instance._collectionIndex] is NHSlideCollection collection) + { + for (int i = 0; i < collection.slides.Length; i++) + { + collection.UnloadSlide(i); + } + return false; + } + return true; + } +} diff --git a/NewHorizons/Patches/ShipLogPatches/SlideCollectionContainerPatches.cs b/NewHorizons/Patches/ShipLogPatches/SlideCollectionContainerPatches.cs deleted file mode 100644 index d3c4cae7..00000000 --- a/NewHorizons/Patches/ShipLogPatches/SlideCollectionContainerPatches.cs +++ /dev/null @@ -1,30 +0,0 @@ -using HarmonyLib; - -namespace NewHorizons.Patches.ShipLogPatches -{ - [HarmonyPatch(typeof(SlideCollectionContainer))] - public static class SlideCollectionContainerPatches - { - [HarmonyPrefix] - [HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))] - public static bool SlideCollectionContainer_Initialize(SlideCollectionContainer __instance) - { - 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); - } - return false; - } - } -} \ No newline at end of file diff --git a/NewHorizons/Patches/SignalPatches/AudioSignalPatches.cs b/NewHorizons/Patches/SignalPatches/AudioSignalPatches.cs index 9f1aacfe..a886b827 100644 --- a/NewHorizons/Patches/SignalPatches/AudioSignalPatches.cs +++ b/NewHorizons/Patches/SignalPatches/AudioSignalPatches.cs @@ -2,6 +2,7 @@ using HarmonyLib; using NewHorizons.Builder.Props.Audio; using NewHorizons.External; using NewHorizons.Handlers; +using NewHorizons.Utility; using System; using UnityEngine; @@ -10,6 +11,13 @@ namespace NewHorizons.Patches.SignalPatches [HarmonyPatch(typeof(AudioSignal))] public static class AudioSignalPatches { + [HarmonyPostfix] + [HarmonyPatch(nameof(AudioSignal.IsActive))] + public static void AudioSignal_IsActive(AudioSignal __instance, ref bool __result) + { + __result = __result && __instance.gameObject.activeInHierarchy; + } + [HarmonyPrefix] [HarmonyPatch(nameof(AudioSignal.SignalNameToString))] public static bool AudioSignal_SignalNameToString(SignalName name, ref string __result) @@ -17,7 +25,7 @@ namespace NewHorizons.Patches.SignalPatches var customSignalName = SignalBuilder.GetCustomSignalName(name); if (!string.IsNullOrEmpty(customSignalName)) { - __result = TranslationHandler.GetTranslation(customSignalName, TranslationHandler.TextType.UI, false).ToUpper(); + __result = TranslationHandler.GetTranslation(customSignalName, TranslationHandler.TextType.UI, false).ToUpperFixed(); return false; } return true; @@ -68,7 +76,7 @@ namespace NewHorizons.Patches.SignalPatches var customName = SignalBuilder.GetCustomFrequencyName(frequency); if (!string.IsNullOrEmpty(customName)) { - if (NewHorizonsData.KnowsFrequency(customName)) __result = TranslationHandler.GetTranslation(customName, TranslationHandler.TextType.UI, false).ToUpper(); + if (NewHorizonsData.KnowsFrequency(customName)) __result = TranslationHandler.GetTranslation(customName, TranslationHandler.TextType.UI, false).ToUpperFixed(); else __result = UITextLibrary.GetString(UITextType.SignalFreqUnidentified); return false; } diff --git a/NewHorizons/Patches/SignalPatches/SignalscopeReticleControllerPatches.cs b/NewHorizons/Patches/SignalPatches/SignalscopeReticleControllerPatches.cs new file mode 100644 index 00000000..c9ec099a --- /dev/null +++ b/NewHorizons/Patches/SignalPatches/SignalscopeReticleControllerPatches.cs @@ -0,0 +1,20 @@ +using HarmonyLib; + +namespace NewHorizons.Patches.SignalPatches +{ + [HarmonyPatch(typeof(SignalscopeReticleController))] + public static class SignalScopeReticleControllerPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(SignalscopeReticleController.UpdateBrackets))] + public static void SignalscopeReticleController_UpdateBrackets(SignalscopeReticleController __instance) + { + var listSignals = Locator.GetAudioSignals(); + for (int i = listSignals.Count; i < __instance._clonedLeftBrackets.Count; i++) + { + __instance._clonedLeftBrackets[i].enabled = false; + __instance._clonedRightBrackets[i].enabled = false; + } + } + } +} diff --git a/NewHorizons/Patches/TimeLoopPatches.cs b/NewHorizons/Patches/TimeLoopPatches.cs index 5628fa15..d2b266fe 100644 --- a/NewHorizons/Patches/TimeLoopPatches.cs +++ b/NewHorizons/Patches/TimeLoopPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Handlers; namespace NewHorizons.Patches { @@ -14,5 +15,16 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(GlobalMusicController), nameof(GlobalMusicController.UpdateEndTimesMusic))] [HarmonyPatch(typeof(TimeLoop), nameof(TimeLoop.Update))] public static bool DisableWithoutTimeLoop() => Main.Instance.TimeLoopEnabled; + + [HarmonyPostfix] + [HarmonyPatch(typeof(TimeLoop), nameof(TimeLoop.Start))] + public static void TimeLoop_Start(TimeLoop __instance) + { + // If we took the AWC out of the main system make sure to disable time loop + if (Main.Instance.CurrentStarSystem != "SolarSystem" && HeldItemHandler.WasAWCTakenFromATP) + { + TimeLoop.SetTimeLoopEnabled(false); + } + } } } diff --git a/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs b/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs index 63986a2a..a9d1d1ca 100644 --- a/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs +++ b/NewHorizons/Patches/ToolPatches/ToolModeSwapperPatches.cs @@ -21,9 +21,8 @@ namespace NewHorizons.Patches.ToolPatches mode == ToolMode.Probe || mode == ToolMode.SignalScope || mode == ToolMode.Translator; - var isInShip = UnityEngine.Object.FindObjectOfType()?._playerAtFlightConsole ?? false; - if (!isInShip && isHoldingVisionTorch && swappingToRestrictedTool) return false; + if (!PlayerState.AtFlightConsole() && isHoldingVisionTorch && swappingToRestrictedTool) return false; return true; } diff --git a/NewHorizons/Patches/WarpPatches/InputManagerPatches.cs b/NewHorizons/Patches/WarpPatches/InputManagerPatches.cs new file mode 100644 index 00000000..0a41f168 --- /dev/null +++ b/NewHorizons/Patches/WarpPatches/InputManagerPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; + +namespace NewHorizons.Patches.WarpPatches; + +[HarmonyPatch(typeof(InputManager))] +public static class InputManagerPatches +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(InputManager.ChangeInputMode))] + public static bool InputManager_ChangeInputMode(InputManager __instance, InputMode mode) + { + // Can't use player state because it is updated after this method is called + var atFlightConsole = Locator.GetPlayerCameraController()?._shipController?.IsPlayerAtFlightConsole() ?? false; + // If we're flying the ship don't let it break our input by changing us to another input mode + if (atFlightConsole && mode == InputMode.Character) + { + return false; + } + else + { + return true; + } + } +} diff --git a/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs b/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs index 559f43d0..e62d1d75 100644 --- a/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs +++ b/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs @@ -1,6 +1,9 @@ using HarmonyLib; +using NewHorizons.Components.Ship; +using NewHorizons.Handlers; using NewHorizons.Utility; using NewHorizons.Utility.OuterWilds; +using NewHorizons.Utility.OWML; namespace NewHorizons.Patches.WarpPatches { @@ -20,6 +23,10 @@ namespace NewHorizons.Patches.WarpPatches if (!Main.Instance.IsWarpingFromVessel) PlayerData.SaveWarpedToTheEye(TimeLoopUtilities.GetVanillaSecondsRemaining()); + // This is totally letting us see the interior of bramble when warping + Locator.GetPlayerSectorDetector().RemoveFromAllSectors(); + // This is a very jank workaround to stop us seeing all that #957 + Locator.GetPlayerCamera().farClipPlane = 0; LoadManager.EnableAsyncLoadTransition(); return false; @@ -64,10 +71,10 @@ namespace NewHorizons.Patches.WarpPatches if (canWarpToEye || canWarpToStarSystem && targetSystem == "EyeOfTheUniverse") { Main.Instance.CurrentStarSystem = "EyeOfTheUniverse"; - LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, false, LoadManager.FadeType.ToWhite); + LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, false, LoadManager.FadeType.ToBlack);// Mobius had the fade set to white. Doesn't look that good because of the loadng screen being black. } - else if (canWarpToStarSystem) - Main.Instance.ChangeCurrentStarSystem(targetSystem, false, true); + else if (canWarpToStarSystem && targetSystem != "EyeOfTheUniverse") + Main.Instance.ChangeCurrentStarSystemVesselAsync(targetSystem); __instance._blackHoleOneShot.PlayOneShot(AudioType.VesselSingularityCreate); GlobalMessenger.FireEvent("StartVesselWarp"); } diff --git a/NewHorizons/Schemas/addon_manifest_schema.json b/NewHorizons/Schemas/addon_manifest_schema.json index dec9022f..a1e1d34a 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -36,7 +36,7 @@ }, "subtitlePath": { "type": "string", - "description": "The path to the addons subtitle for the main menu.\nDefaults to \"subtitle.png\"." + "description": "The path to the addons subtitle for the main menu.\nDefaults to \"subtitle.png\".\nThe dimensions of the Echoes of the Eye subtitle is 669 x 67, so aim for that size" }, "$schema": { "type": "string", diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 28dd5fcc..7336433e 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -4,14 +4,10 @@ "type": "object", "description": "Describes a celestial body to generate", "additionalProperties": false, - "required": [ - "name" - ], "properties": { "name": { "type": "string", - "description": "Unique name of your planet", - "minLength": 1 + "description": "Unique name of your planet. If not specified, the file name (without the extension) is used." }, "starSystem": { "type": "string", @@ -35,6 +31,11 @@ "type": "boolean", "description": "`true` if you want to delete this planet" }, + "trackForSolarSystemRadius": { + "type": "boolean", + "description": "Do we track the position of this body when calculating the solar system radius?\n`true` if you want the map zoom speed, map panning distance/speed, map camera farclip plane,\nand autopilot-returning-to-solar-system to adjust to this planet's orbit", + "default": true + }, "removeChildren": { "type": "array", "description": "A list of paths to child GameObjects to destroy on this planet", @@ -69,6 +70,14 @@ "description": "Add a cloaking field to this planet", "$ref": "#/definitions/CloakModule" }, + "Dream": { + "description": "Make this planet part of the dream world", + "$ref": "#/definitions/DreamModule" + }, + "EyeOfTheUniverse": { + "description": "Add features exclusive to the Eye of the Universe scene", + "$ref": "#/definitions/EyeOfTheUniverseModule" + }, "FocalPoint": { "description": "Make this body into a focal point (barycenter)", "$ref": "#/definitions/FocalPointModule" @@ -85,6 +94,10 @@ "description": "Add lava to this planet", "$ref": "#/definitions/LavaModule" }, + "MapMarker": { + "description": "Map marker properties of this body", + "$ref": "#/definitions/MapMarkerModule" + }, "Orbit": { "description": "Describes this Body's orbit (or lack there of)", "$ref": "#/definitions/OrbitModule" @@ -271,7 +284,7 @@ }, "inclination": { "type": "number", - "description": "Angle between the rings and the equatorial plane of the planet.", + "description": "Angle between the belt and the equatorial plane of the planet.", "format": "float" }, "innerRadius": { @@ -282,7 +295,7 @@ }, "longitudeOfAscendingNode": { "type": "number", - "description": "Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.", + "description": "Angle defining the point where the belt rises up from the planet's equatorial plane if inclination is nonzero.", "format": "float" }, "maxSize": { @@ -306,13 +319,33 @@ "minimum": 0.0 }, "procGen": { - "description": "How the asteroids are generated", + "description": "How the asteroids are generated, unless you supply a detail yourself using \"assetBundle\" and \"path\"", "$ref": "#/definitions/ProcGenModule" }, "randomSeed": { "type": "integer", "description": "Number used to randomize asteroid positions", "format": "int32" + }, + "assetBundle": { + "type": "string", + "description": "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.\nThis is a relative filepath to an asset-bundle to load the prefab defined in `path` from." + }, + "path": { + "type": "string", + "description": "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.\nThis is either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle. " + }, + "gravity": { + "type": "number", + "description": "Surface gravity of the asteroids.", + "format": "float", + "default": 1, + "minimum": 0.0 + }, + "randomOrientation": { + "type": "boolean", + "description": "Should the detail of the asteroid be randomly oriented, or should it point towards the center.", + "default": true } } }, @@ -366,7 +399,7 @@ }, "fogRampPath": { "type": "string", - "description": "Relative filepath to the fog color ramp texture, if you put fog.\nx axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top)." + "description": "Relative filepath to the fog color ramp texture, if you put fog.\nx axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top).\nOptional. If you set fogTint, a default fog ramp will be tinted for you." }, "hasOxygen": { "type": "boolean", @@ -405,6 +438,11 @@ "description": "Maximum speed that your ship can go in the atmosphere where flames will appear at their brightest.", "format": "float", "default": 300.0 + }, + "allowShipAutoroll": { + "type": "boolean", + "description": "Will the ship automatically try to orient itself to face upwards while in this volume?", + "default": true } } }, @@ -543,13 +581,10 @@ "description": "Radius of a simple sphere used as the ground for the planet. If you want to use more complex terrain, leave this as\n0.", "format": "float" }, - "hasMapMarker": { + "hasFluidDetector": { "type": "boolean", - "description": "If the body should have a marker on the map screen." - }, - "invulnerableToSun": { - "type": "boolean", - "description": "Can this planet survive entering a star?" + "description": "Is this planet able to detect fluid volumes? Disabling this means that entering a star or lava volume will not destroy this planet\nMay 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)", + "default": true }, "showMinimap": { "type": "boolean", @@ -568,7 +603,7 @@ }, "surfaceSize": { "type": "number", - "description": "A scale height used for a number of things. Should be the approximate radius of the body.", + "description": "A scale height used for a number of things. Should be the approximate radius of the body.\n\nAffected settings include: Base sector size, proxy body scaling, surface gravity", "format": "float" }, "gravityVolumePriority": { @@ -577,6 +612,14 @@ "format": "int32", "default": 0 }, + "gravityAlignmentRadiusOverride": { + "type": [ + "integer", + "null" + ], + "description": "Optional. Overrides how far the player must be from the planet for their feet to automatically orient towards the ground.", + "format": "int32" + }, "pushable": { "type": "boolean", "description": "Apply physics to this planet when you bump into it. Will have a spherical collider the size of surfaceSize. \nFor custom colliders they have to all be convex and you can leave surface size as 0.\nThis is meant for stuff like satellites which are relatively simple and can be de-orbited.\nIf you are using an orbit line but a tracking line, it will be removed when the planet is bumped in to." @@ -621,12 +664,12 @@ "additionalProperties": false, "properties": { "fogTint": { - "description": "The color of the fog inside this dimension. \nLeave blank for the default grayish color: (84, 83, 73)", + "description": "The color of the fog inside this dimension. \nLeave blank for the default grayish color: (84, 83, 73)\nThe alpha value has no effect of the fog: Use fogDensity instead!", "$ref": "#/definitions/MColor" }, "fogDensity": { "type": "number", - "description": "The density of the fog inside this dimension. The default is 6.", + "description": "The density of the fog inside this dimension. The default is 6. If you want no fog, set this to 0.", "format": "float", "default": 6.0 }, @@ -692,14 +735,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -797,6 +840,888 @@ } } }, + "DreamModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "inDreamWorld": { + "type": "boolean", + "description": "Setting this value will make this body a dream world style dimension where its contents are only activated while entering it from a dream campfire. Disables the body's map marker." + }, + "generateSimulationMeshes": { + "type": "boolean", + "description": "Whether to generate simulation meshes (the models used in the \"tronworld\" or \"matrix\" view) for most objects on this planet by cloning the existing meshes and applying the simulation materials. Leave this off if you are building your own simulation meshes or using existing objects which have them." + } + } + }, + "EyeOfTheUniverseModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "eyeTravelers": { + "type": "array", + "description": "Add custom travelers to the campfire sequence", + "items": { + "$ref": "#/definitions/EyeTravelerInfo" + } + }, + "instrumentZones": { + "type": "array", + "description": "Add instrument zones which contain puzzles to gather a quantum instrument. You can parent other props to these with `parentPath` ", + "items": { + "$ref": "#/definitions/InstrumentZoneInfo" + } + }, + "quantumInstruments": { + "type": "array", + "description": "Add quantum instruments which cause their associated eye traveler to appear and instrument zones to disappear", + "items": { + "$ref": "#/definitions/QuantumInstrumentInfo" + } + } + } + }, + "EyeTravelerInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle. \nIf empty, will make an empty game object. This can be useful for adding other props to it as its children." + }, + "removeChildren": { + "type": "array", + "description": "A list of children to remove from this detail", + "items": { + "type": "string" + } + }, + "removeComponents": { + "type": "boolean", + "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." + }, + "scale": { + "type": "number", + "description": "Scale the prop", + "format": "float", + "default": 1.0 + }, + "stretch": { + "description": "Scale each axis of the prop. Overrides `scale`.", + "$ref": "#/definitions/MVector3" + }, + "keepLoaded": { + "type": "boolean", + "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nKeeping many props loaded is bad for performance so use this only when it's actually relevant\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." + }, + "hasPhysics": { + "type": "boolean", + "description": "Should this object dynamically move around?\nThis tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others." + }, + "physicsMass": { + "type": "number", + "description": "The mass of the physics object.\nMost pushable props use the default value, which matches the player mass.", + "format": "float", + "default": 0.001 + }, + "physicsRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for physics collision.\nIf there's already good colliders on the detail, you can make this 0.", + "format": "float", + "default": 1.0 + }, + "physicsSuspendUntilImpact": { + "type": "boolean", + "description": "If true, this detail will stay still until it touches something.\nGood for zero-g props.", + "default": false + }, + "ignoreSun": { + "type": "boolean", + "description": "Set to true if this object's lighting should ignore the effects of sunlight" + }, + "activationCondition": { + "type": "string", + "description": "Activates this game object when the dialogue condition is met" + }, + "deactivationCondition": { + "type": "string", + "description": "Deactivates this game object when the dialogue condition is met" + }, + "blinkWhenActiveChanged": { + "type": "boolean", + "description": "Should the player close their eyes while the activation state changes. Only relevant if activationCondition or deactivationCondition are set.", + "default": true + }, + "item": { + "description": "Should this detail be treated as an interactible item", + "$ref": "#/definitions/ItemInfo" + }, + "itemSocket": { + "description": "Should this detail be treated as a socket for an interactible item", + "$ref": "#/definitions/ItemSocketInfo" + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "A unique ID to associate this traveler with their corresponding quantum instruments and instrument zones. Must be unique for each traveler." + }, + "requiredFact": { + "type": "string", + "description": "If set, the player must know this ship log fact for this traveler (and their instrument zones and quantum instruments) to appear. The fact does not need to exist in the current star system; the player's save data will be checked directly." + }, + "requiredPersistentCondition": { + "type": "string", + "description": "If set, the player must have this persistent dialogue condition set for this traveler (and their instrument zones and quantum instruments) to appear." + }, + "startPlayingCondition": { + "type": "string", + "description": "The dialogue condition that will trigger the traveler to start playing their instrument. Must be unique for each traveler." + }, + "participatingCondition": { + "type": "string", + "description": "If specified, this dialogue condition must be set for the traveler to participate in the campfire song. Otherwise, the song will be able to start without them." + }, + "signal": { + "description": "The audio signal to use for the traveler while playing around the campfire (and also for their paired quantum instrument if another is not specified). The audio clip should be 16 measures at 92 BPM (approximately 42 seconds long).", + "$ref": "#/definitions/SignalInfo" + }, + "finaleAudio": { + "type": "string", + "description": "The audio to use for the traveler during the finale of the campfire song. It should be 8 measures of the main loop at 92 BPM followed by 2 measures of fade-out (approximately 26 seconds long in total). Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "dialogue": { + "description": "The dialogue to use for this traveler. If omitted, the first CharacterDialogueTree in the object will be used.", + "$ref": "#/definitions/DialogueInfo" + } + } + }, + "ItemInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the item to be displayed in the UI. Defaults to the name of the detail object." + }, + "itemType": { + "type": "string", + "description": "The type of the item, which determines its orientation when held and what sockets it fits into. This can be a custom string, or a vanilla ItemType (Scroll, WarpCore, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch). Defaults to the item name." + }, + "interactRange": { + "type": "number", + "description": "The furthest distance where the player can interact with this item. Defaults to two meters, same as most vanilla items. Set this to zero to disable all interaction by default.", + "format": "float", + "default": 2.0 + }, + "colliderRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for collision and hover detection.\nIf there's already a collider on the detail, you can make this 0.", + "format": "float", + "default": 0.5 + }, + "droppable": { + "type": "boolean", + "description": "Whether the item can be dropped. Defaults to true.", + "default": true + }, + "dropOffset": { + "description": "A relative offset to apply to the item's position when dropping it on the ground.", + "$ref": "#/definitions/MVector3" + }, + "dropNormal": { + "description": "The direction the item will be oriented when dropping it on the ground. Defaults to up (0, 1, 0).", + "$ref": "#/definitions/MVector3" + }, + "holdOffset": { + "description": "A relative offset to apply to the item's position when holding it. The initial position varies for vanilla item types.", + "$ref": "#/definitions/MVector3" + }, + "holdRotation": { + "description": "A relative offset to apply to the item's rotation when holding it.", + "$ref": "#/definitions/MVector3" + }, + "socketOffset": { + "description": "A relative offset to apply to the item's position when placing it into a socket.", + "$ref": "#/definitions/MVector3" + }, + "socketRotation": { + "description": "A relative offset to apply to the item's rotation when placing it into a socket.", + "$ref": "#/definitions/MVector3" + }, + "pickupAudio": { + "type": "string", + "description": "The audio to play when this item is picked up. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "dropAudio": { + "type": "string", + "description": "The audio to play when this item is dropped. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "socketAudio": { + "type": "string", + "description": "The audio to play when this item is inserted into a socket. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "unsocketAudio": { + "type": "string", + "description": "The audio to play when this item is removed from a socket. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "pickupCondition": { + "type": "string", + "description": "A dialogue condition to set when picking up this item." + }, + "clearPickupConditionOnDrop": { + "type": "boolean", + "description": "Whether the pickup condition should be cleared when dropping the item. Defaults to true.", + "default": true + }, + "pickupFact": { + "type": "string", + "description": "A ship log fact to reveal when picking up this item." + }, + "pathToInitialSocket": { + "type": "string", + "description": "A relative path from the planet to a socket that this item will be automatically inserted into." + } + } + }, + "ItemSocketInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "socketPath": { + "type": "string", + "description": "The relative path to a child game object of this detail that will act as the socket point for the item. Will be used instead of this socket's positioning info if set." + }, + "itemType": { + "type": "string", + "description": "The type of item allowed in this socket. This can be a custom string, or a vanilla ItemType (Scroll, WarpCode, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch)." + }, + "interactRange": { + "type": "number", + "description": "The furthest distance where the player can interact with this item socket. Defaults to two meters, same as most vanilla item sockets. Set this to zero to disable all interaction by default.", + "format": "float", + "default": 2.0 + }, + "useGiveTakePrompts": { + "type": "boolean", + "description": "Whether to use \"Give Item\" / \"Take Item\" prompts instead of \"Insert Item\" / \"Remove Item\"." + }, + "insertCondition": { + "type": "string", + "description": "A dialogue condition to set when inserting an item into this socket." + }, + "clearInsertConditionOnRemoval": { + "type": "boolean", + "description": "Whether the insert condition should be cleared when removing the socketed item. Defaults to true.", + "default": true + }, + "insertFact": { + "type": "string", + "description": "A ship log fact to reveal when inserting an item into this socket." + }, + "removalCondition": { + "type": "string", + "description": "A dialogue condition to set when removing an item from this socket, or when the socket is empty." + }, + "clearRemovalConditionOnInsert": { + "type": "boolean", + "description": "Whether the removal condition should be cleared when inserting a socketed item. Defaults to true.", + "default": true + }, + "removalFact": { + "type": "string", + "description": "A ship log fact to reveal when removing an item from this socket, or when the socket is empty." + }, + "colliderRadius": { + "type": "number", + "description": "Default collider radius when interacting with the socket", + "format": "float", + "default": 0.0 + } + } + }, + "SignalInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "audio": { + "type": "string", + "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "minDistance": { + "type": "number", + "description": "At this distance the sound is at its loudest.", + "format": "float" + }, + "maxDistance": { + "type": "number", + "description": "The sound will drop off by this distance (Note: for signals, this only effects when it is heard aloud and not via the signalscope).", + "format": "float", + "default": 5.0 + }, + "volume": { + "type": "number", + "description": "How loud the sound will play", + "format": "float", + "default": 0.5 + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "detectionRadius": { + "type": "number", + "description": "How close the player must get to the signal to detect it. This is when you get the \"Unknown Signal Detected\"\nnotification.", + "format": "float", + "minimum": 0.0 + }, + "frequency": { + "type": "string", + "description": "The frequency ID of the signal. The built-in game values are `Default`, `Traveler`, `Quantum`, `EscapePod`,\n`Statue`, `WarpCore`, `HideAndSeek`, and `Radio`. You can also put a custom value." + }, + "identificationRadius": { + "type": "number", + "description": "How close the player must get to the signal to identify it. This is when you learn its name.", + "format": "float", + "default": 10.0, + "minimum": 0.0 + }, + "insideCloak": { + "type": "boolean", + "description": "Only set to `true` if you are putting this signal inside a cloaking field." + }, + "name": { + "type": "string", + "description": "The unique ID of the signal." + }, + "onlyAudibleToScope": { + "type": "boolean", + "description": "`false` if the player can hear the signal without equipping the signal-scope.", + "default": true + }, + "reveals": { + "type": "string", + "description": "A ship log fact to reveal when the signal is identified.", + "default": "" + }, + "sourceRadius": { + "type": "number", + "description": "Radius of the sphere giving off the signal.", + "format": "float", + "default": 1.0 + } + } + }, + "DialogueInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "blockAfterPersistentCondition": { + "type": "string", + "description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue\ntriggers that you want to have happen only once." + }, + "lookAtRadius": { + "type": "number", + "description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set\nto 0, they will only look at you when spoken to.", + "format": "float" + }, + "pathToAnimController": { + "type": "string", + "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's\nCharacterAnimController, TravelerController, TravelerEyeController (eye of the universe), FacePlayerWhenTalking, \nHearthianRecorderEffects or SolanumAnimController.\n\nIf it's a Recorder this will also delete the existing dialogue already attached to that prop.\n\nIf none of those components are present it will add a FacePlayerWhenTalking component.\n\n`pathToAnimController` also makes the dialogue into a child of the anim controller. This can be used with `isRelativeToParent`\nto position the dialogue on relative to the speaker. If you also provide `parentPath`, that will instead override which object \nis the parent, but the anim controller will otherwise function as expected." + }, + "pathToExistingDialogue": { + "type": "string", + "description": "If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here" + }, + "radius": { + "type": "number", + "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a\nremoteTrigger, you can set this to 0 to make the dialogue only trigger remotely.", + "format": "float" + }, + "range": { + "type": "number", + "description": "Distance from radius the prompt appears", + "format": "float", + "default": 2.0 + }, + "attentionPoint": { + "description": "The point that the camera looks at when dialogue advances.", + "$ref": "#/definitions/AttentionPointInfo" + }, + "swappedAttentionPoints": { + "type": "array", + "description": "Additional points that the camera looks at when dialogue advances through specific dialogue nodes and pages.", + "items": { + "$ref": "#/definitions/SwappedAttentionPointInfo" + } + }, + "remoteTrigger": { + "description": "Allows you to trigger dialogue from a distance when you walk into an area.", + "$ref": "#/definitions/RemoteTriggerInfo" + }, + "xmlFile": { + "type": "string", + "description": "Relative path to the xml file defining the dialogue." + }, + "flashlightToggle": { + "description": "What type of flashlight toggle to do when dialogue is interacted with", + "default": "none", + "$ref": "#/definitions/FlashlightToggle" + } + } + }, + "AttentionPointInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "offset": { + "description": "An additional offset to apply to apply when the camera looks at this attention point.", + "$ref": "#/definitions/MVector3" + } + } + }, + "SwappedAttentionPointInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "offset": { + "description": "An additional offset to apply to apply when the camera looks at this attention point.", + "$ref": "#/definitions/MVector3" + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "dialogueNode": { + "type": "string", + "description": "The name of the dialogue node to activate this attention point for. If null or blank, activates for every node." + }, + "dialoguePage": { + "type": "integer", + "description": "The index of the page in the current dialogue node to activate this attention point for, if the node has multiple pages.", + "format": "int32" + }, + "lookEasing": { + "type": "number", + "description": "The easing factor which determines how 'snappy' the camera is when looking at the attention point.", + "format": "float", + "default": 1 + } + } + }, + "RemoteTriggerInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "radius": { + "type": "number", + "description": "The radius of the remote trigger volume.", + "format": "float" + }, + "prereqCondition": { + "type": "string", + "description": "This condition must be met for the remote trigger volume to trigger." + } + } + }, + "FlashlightToggle": { + "type": "string", + "description": "", + "x-enumNames": [ + "TurnOff", + "TurnOffThenOn", + "None" + ], + "enum": [ + "turnOff", + "turnOffThenOn", + "none" + ] + }, + "InstrumentZoneInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle. \nIf empty, will make an empty game object. This can be useful for adding other props to it as its children." + }, + "removeChildren": { + "type": "array", + "description": "A list of children to remove from this detail", + "items": { + "type": "string" + } + }, + "removeComponents": { + "type": "boolean", + "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." + }, + "scale": { + "type": "number", + "description": "Scale the prop", + "format": "float", + "default": 1.0 + }, + "stretch": { + "description": "Scale each axis of the prop. Overrides `scale`.", + "$ref": "#/definitions/MVector3" + }, + "keepLoaded": { + "type": "boolean", + "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nKeeping many props loaded is bad for performance so use this only when it's actually relevant\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." + }, + "hasPhysics": { + "type": "boolean", + "description": "Should this object dynamically move around?\nThis tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others." + }, + "physicsMass": { + "type": "number", + "description": "The mass of the physics object.\nMost pushable props use the default value, which matches the player mass.", + "format": "float", + "default": 0.001 + }, + "physicsRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for physics collision.\nIf there's already good colliders on the detail, you can make this 0.", + "format": "float", + "default": 1.0 + }, + "physicsSuspendUntilImpact": { + "type": "boolean", + "description": "If true, this detail will stay still until it touches something.\nGood for zero-g props.", + "default": false + }, + "ignoreSun": { + "type": "boolean", + "description": "Set to true if this object's lighting should ignore the effects of sunlight" + }, + "activationCondition": { + "type": "string", + "description": "Activates this game object when the dialogue condition is met" + }, + "deactivationCondition": { + "type": "string", + "description": "Deactivates this game object when the dialogue condition is met" + }, + "blinkWhenActiveChanged": { + "type": "boolean", + "description": "Should the player close their eyes while the activation state changes. Only relevant if activationCondition or deactivationCondition are set.", + "default": true + }, + "item": { + "description": "Should this detail be treated as an interactible item", + "$ref": "#/definitions/ItemInfo" + }, + "itemSocket": { + "description": "Should this detail be treated as a socket for an interactible item", + "$ref": "#/definitions/ItemSocketInfo" + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "The unique ID of the Eye Traveler associated with this instrument zone." + } + } + }, + "QuantumInstrumentInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle. \nIf empty, will make an empty game object. This can be useful for adding other props to it as its children." + }, + "removeChildren": { + "type": "array", + "description": "A list of children to remove from this detail", + "items": { + "type": "string" + } + }, + "removeComponents": { + "type": "boolean", + "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." + }, + "scale": { + "type": "number", + "description": "Scale the prop", + "format": "float", + "default": 1.0 + }, + "stretch": { + "description": "Scale each axis of the prop. Overrides `scale`.", + "$ref": "#/definitions/MVector3" + }, + "keepLoaded": { + "type": "boolean", + "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nKeeping many props loaded is bad for performance so use this only when it's actually relevant\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." + }, + "hasPhysics": { + "type": "boolean", + "description": "Should this object dynamically move around?\nThis tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others." + }, + "physicsMass": { + "type": "number", + "description": "The mass of the physics object.\nMost pushable props use the default value, which matches the player mass.", + "format": "float", + "default": 0.001 + }, + "physicsRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for physics collision.\nIf there's already good colliders on the detail, you can make this 0.", + "format": "float", + "default": 1.0 + }, + "physicsSuspendUntilImpact": { + "type": "boolean", + "description": "If true, this detail will stay still until it touches something.\nGood for zero-g props.", + "default": false + }, + "ignoreSun": { + "type": "boolean", + "description": "Set to true if this object's lighting should ignore the effects of sunlight" + }, + "activationCondition": { + "type": "string", + "description": "Activates this game object when the dialogue condition is met" + }, + "deactivationCondition": { + "type": "string", + "description": "Deactivates this game object when the dialogue condition is met" + }, + "blinkWhenActiveChanged": { + "type": "boolean", + "description": "Should the player close their eyes while the activation state changes. Only relevant if activationCondition or deactivationCondition are set.", + "default": true + }, + "item": { + "description": "Should this detail be treated as an interactible item", + "$ref": "#/definitions/ItemInfo" + }, + "itemSocket": { + "description": "Should this detail be treated as a socket for an interactible item", + "$ref": "#/definitions/ItemSocketInfo" + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "The unique ID of the Eye Traveler associated with this quantum instrument." + }, + "gatherCondition": { + "type": "string", + "description": "A dialogue condition to set when gathering this quantum instrument. Use it in conjunction with `activationCondition` or `deactivationCondition` on other details." + }, + "gatherWithScope": { + "type": "boolean", + "description": "Allows gathering this quantum instrument using the zoomed-in signalscope, like Chert's bongos." + }, + "signal": { + "description": "The audio signal emitted by this quantum instrument. The fields `name`, `audio`, and `frequency` will be copied from the corresponding Eye Traveler's signal if not specified here.", + "$ref": "#/definitions/SignalInfo" + }, + "interactRadius": { + "type": "number", + "description": "The radius of the added sphere collider that will be used for interaction.", + "format": "float", + "default": 0.5 + }, + "interactRange": { + "type": "number", + "description": "The furthest distance where the player can interact with this quantum instrument.", + "format": "float", + "default": 2.0 + } + } + }, "FocalPointModule": { "type": "object", "additionalProperties": false, @@ -1022,6 +1947,26 @@ } } }, + "MapMarkerModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "If the body should have a marker on the map screen." + }, + "minDisplayDistanceOverride": { + "type": "number", + "description": "Lowest distance away from the body that the marker can be shown. This is automatically set to 0 for all bodies except focal points where it is 5,000.", + "format": "float" + }, + "maxDisplayDistanceOverride": { + "type": "number", + "description": "Highest distance away from the body that the marker can be shown. For planets and focal points the automatic value is 50,000. Moons and planets in focal points are 5,000. Stars are 1E+10 (10,000,000,000).", + "format": "float" + } + } + }, "OrbitModule": { "type": "object", "additionalProperties": false, @@ -1098,7 +2043,6 @@ "type": "number", "description": "The semi-major axis of the ellipse that is the body's orbit. For a circular orbit this is the radius.", "format": "float", - "default": 5000.0, "minimum": 0.0 }, "inclination": { @@ -1178,7 +2122,7 @@ }, "rafts": { "type": "array", - "description": "Add rafts to this planet", + "description": "Add rafts to this planet (requires Echoes of the Eye DLC)", "items": { "$ref": "#/definitions/RaftInfo" } @@ -1192,18 +2136,11 @@ }, "slideShows": { "type": "array", - "description": "Add slideshows (from the DLC) to the planet", + "description": "Add slideshows to the planet (requires Echoes of the Eye DLC)", "items": { "$ref": "#/definitions/ProjectionInfo" } }, - "quantumGroups": { - "type": "array", - "description": "A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject.", - "items": { - "$ref": "#/definitions/QuantumGroupInfo" - } - }, "tornados": { "type": "array", "description": "Add tornadoes to this planet", @@ -1273,6 +2210,76 @@ "items": { "$ref": "#/definitions/ShuttleInfo" } + }, + "socketQuantumGroups": { + "type": "array", + "description": "Add a socket quantum object to a planet. Define the position of multiple \"sockets\" and multiple objects that jump between sockets. \nIf the number of sockets equals the number of objects, they will shuffle around.", + "items": { + "$ref": "#/definitions/SocketQuantumGroupInfo" + } + }, + "stateQuantumGroups": { + "type": "array", + "description": "Add a state quantum object to a planet. Switches between displaying different objects in a single place.", + "items": { + "$ref": "#/definitions/StateQuantumGroupInfo" + } + }, + "lightningQuantumGroups": { + "type": "array", + "description": "Add quantum lightning to a planet. When lightning strikes, a different detail object is shown. The lightning will take the first defined position/rotation for all objects.", + "items": { + "$ref": "#/definitions/LightningQuantumInfo" + } + }, + "dreamCampfires": { + "type": "array", + "description": "Add campfires that allow you to enter the dream world/simulation (requires Echoes of the Eye DLC). Must be paired with a dream arrival point, which can be placed on this planet or elsewhere.", + "items": { + "$ref": "#/definitions/DreamCampfireInfo" + } + }, + "dreamArrivalPoints": { + "type": "array", + "description": "Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire (requires Echoes of the Eye DLC). The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials.", + "items": { + "$ref": "#/definitions/DreamArrivalPointInfo" + } + }, + "grappleTotems": { + "type": "array", + "description": "Adds dream world grapple totems to this planet (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/GrappleTotemInfo" + } + }, + "alarmTotems": { + "type": "array", + "description": "Adds dream world alarm totems to this planet (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/AlarmTotemInfo" + } + }, + "portholes": { + "type": "array", + "description": "Adds portholes (the windows you can peek through in the Stranger) to this planet (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/PortholeInfo" + } + }, + "dreamCandles": { + "type": "array", + "description": "Adds dream world candles to this planet (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/DreamCandleInfo" + } + }, + "projectionTotems": { + "type": "array", + "description": "Adds dream world projection totems (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/ProjectionTotemInfo" + } } } }, @@ -1295,14 +2302,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -1336,13 +2343,9 @@ "description": "Scale each axis of the prop. Overrides `scale`.", "$ref": "#/definitions/MVector3" }, - "quantumGroupID": { - "type": "string", - "description": "If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is" - }, "keepLoaded": { "type": "boolean", - "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." + "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nKeeping many props loaded is bad for performance so use this only when it's actually relevant\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." }, "hasPhysics": { "type": "boolean", @@ -1392,263 +2395,6 @@ } } }, - "ItemInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "The name of the item to be displayed in the UI. Defaults to the name of the detail object." - }, - "itemType": { - "type": "string", - "description": "The type of the item, which determines its orientation when held and what sockets it fits into. This can be a custom string, or a vanilla ItemType (Scroll, WarpCore, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch). Defaults to the item name." - }, - "interactRange": { - "type": "number", - "description": "The furthest distance where the player can interact with this item. Defaults to two meters, same as most vanilla items. Set this to zero to disable all interaction by default.", - "format": "float", - "default": 2.0 - }, - "colliderRadius": { - "type": "number", - "description": "The radius that the added sphere collider will use for collision and hover detection.\nIf there's already a collider on the detail, you can make this 0.", - "format": "float", - "default": 0.5 - }, - "droppable": { - "type": "boolean", - "description": "Whether the item can be dropped. Defaults to true.", - "default": true - }, - "dropOffset": { - "description": "A relative offset to apply to the item's position when dropping it on the ground.", - "$ref": "#/definitions/MVector3" - }, - "dropNormal": { - "description": "The direction the item will be oriented when dropping it on the ground. Defaults to up (0, 1, 0).", - "$ref": "#/definitions/MVector3" - }, - "pickupAudio": { - "type": "string", - "description": "The audio to play when this item is picked up. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "dropAudio": { - "type": "string", - "description": "The audio to play when this item is dropped. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "socketAudio": { - "type": "string", - "description": "The audio to play when this item is inserted into a socket. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "unsocketAudio": { - "type": "string", - "description": "The audio to play when this item is removed from a socket. Only applies to custom/non-vanilla item types.\nCan be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "pickupCondition": { - "type": "string", - "description": "A dialogue condition to set when picking up this item." - }, - "clearPickupConditionOnDrop": { - "type": "boolean", - "description": "Whether the pickup condition should be cleared when dropping the item. Defaults to true.", - "default": true - }, - "pickupFact": { - "type": "string", - "description": "A ship log fact to reveal when picking up this item." - }, - "pathToInitialSocket": { - "type": "string", - "description": "A relative path from the planet to a socket that this item will be automatically inserted into." - } - } - }, - "ItemSocketInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "rotation": { - "description": "Rotation of the object", - "$ref": "#/definitions/MVector3" - }, - "alignRadial": { - "type": [ - "boolean", - "null" - ], - "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." - }, - "position": { - "description": "Position of the object", - "$ref": "#/definitions/MVector3" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, - "isRelativeToParent": { - "type": "boolean", - "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." - }, - "rename": { - "type": "string", - "description": "An optional rename of this object" - }, - "socketPath": { - "type": "string", - "description": "The relative path to a child game object of this detail that will act as the socket point for the item. Will be used instead of this socket's positioning info if set." - }, - "itemType": { - "type": "string", - "description": "The type of item allowed in this socket. This can be a custom string, or a vanilla ItemType (Scroll, WarpCode, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch)." - }, - "interactRange": { - "type": "number", - "description": "The furthest distance where the player can interact with this item socket. Defaults to two meters, same as most vanilla item sockets. Set this to zero to disable all interaction by default.", - "format": "float", - "default": 2.0 - }, - "useGiveTakePrompts": { - "type": "boolean", - "description": "Whether to use \"Give Item\" / \"Take Item\" prompts instead of \"Insert Item\" / \"Remove Item\"." - }, - "insertCondition": { - "type": "string", - "description": "A dialogue condition to set when inserting an item into this socket." - }, - "clearInsertConditionOnRemoval": { - "type": "boolean", - "description": "Whether the insert condition should be cleared when removing the socketed item. Defaults to true.", - "default": true - }, - "insertFact": { - "type": "string", - "description": "A ship log fact to reveal when inserting an item into this socket." - }, - "removalCondition": { - "type": "string", - "description": "A dialogue condition to set when removing an item from this socket, or when the socket is empty." - }, - "clearRemovalConditionOnInsert": { - "type": "boolean", - "description": "Whether the removal condition should be cleared when inserting a socketed item. Defaults to true.", - "default": true - }, - "removalFact": { - "type": "string", - "description": "A ship log fact to reveal when removing an item from this socket, or when the socket is empty." - } - } - }, - "DialogueInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "Position of the object", - "$ref": "#/definitions/MVector3" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, - "isRelativeToParent": { - "type": "boolean", - "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." - }, - "rename": { - "type": "string", - "description": "An optional rename of this object" - }, - "blockAfterPersistentCondition": { - "type": "string", - "description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue\ntriggers that you want to have happen only once." - }, - "lookAtRadius": { - "type": "number", - "description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set\nto 0, they will only look at you when spoken to.", - "format": "float" - }, - "pathToAnimController": { - "type": "string", - "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's\nCharacterAnimController, TravelerController, TravelerEyeController (eye of the universe), FacePlayerWhenTalking, \nHearthianRecorderEffects or SolanumAnimController.\n\nIf it's a Recorder this will also delete the existing dialogue already attached to that prop.\n\nIf none of those components are present it will add a FacePlayerWhenTalking component." - }, - "pathToExistingDialogue": { - "type": "string", - "description": "If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here" - }, - "radius": { - "type": "number", - "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a\nremoteTrigger, you can set this to 0 to make the dialogue only trigger remotely.", - "format": "float" - }, - "range": { - "type": "number", - "description": "Distance from radius the prompt appears", - "format": "float", - "default": 2.0 - }, - "remoteTrigger": { - "description": "Allows you to trigger dialogue from a distance when you walk into an area.", - "$ref": "#/definitions/RemoteTriggerInfo" - }, - "xmlFile": { - "type": "string", - "description": "Relative path to the xml file defining the dialogue." - }, - "flashlightToggle": { - "description": "What type of flashlight toggle to do when dialogue is interacted with", - "default": "none", - "$ref": "#/definitions/FlashlightToggle" - } - } - }, - "RemoteTriggerInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "Position of the object", - "$ref": "#/definitions/MVector3" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, - "isRelativeToParent": { - "type": "boolean", - "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." - }, - "rename": { - "type": "string", - "description": "An optional rename of this object" - }, - "radius": { - "type": "number", - "description": "The radius of the remote trigger volume.", - "format": "float" - }, - "prereqCondition": { - "type": "string", - "description": "This condition must be met for the remote trigger volume to trigger." - } - } - }, - "FlashlightToggle": { - "type": "string", - "description": "", - "x-enumNames": [ - "TurnOff", - "TurnOffThenOn", - "None" - ], - "enum": [ - "turnOff", - "turnOffThenOn", - "none" - ] - }, "EntryLocationInfo": { "type": "object", "additionalProperties": false, @@ -1657,14 +2403,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -1698,14 +2444,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -1777,14 +2523,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -1946,14 +2692,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2030,6 +2776,10 @@ "keepLoaded": { "type": "boolean", "description": "Should this detail stay loaded even if you're outside the sector (good for very large props)" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector). This parent should be at the position where you'd like to scatter (which would usually be zero)." } } }, @@ -2052,14 +2802,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2071,6 +2821,20 @@ "type": "string" } }, + "conditionsToSet": { + "type": "array", + "description": "The dialogue conditions to set after finishing this slide reel.", + "items": { + "type": "string" + } + }, + "persistentConditionsToSet": { + "type": "array", + "description": "The persistent conditions to set after finishing this slide reel.", + "items": { + "type": "string" + } + }, "playWithShipLogFacts": { "type": "array", "description": "The ship log facts that make the reel play when they are displayed in the computer (by selecting entries or arrows).\nYou should probably include facts from `reveals` here.\nIf you only specify a rumor fact, then it would only play in its ship log entry if this has revealed only\nrumor facts because an entry with revealed explore facts doesn't display rumor facts.", @@ -2089,6 +2853,24 @@ "description": "The type of object this is.", "default": "slideReel", "$ref": "#/definitions/SlideShowType" + }, + "reelModel": { + "description": "Exclusive to the slide reel type. Model/mesh of the reel. Each model has a different number of slides on it. Whole has 7 slides but a full ring like 8.", + "default": "sevenSlides", + "$ref": "#/definitions/SlideReelType" + }, + "reelCondition": { + "description": "Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel.", + "default": "antique", + "$ref": "#/definitions/SlideReelCondition" + }, + "displaySlides": { + "type": "array", + "description": "Set which slides appear on the slide reel model. Leave empty to default to the first few slides.\nTakes a list of indices, i.e., to show the first 5 slides in reverse you would put [4, 3, 2, 1, 0].\nIndex starts at 0.", + "items": { + "type": "integer", + "format": "int32" + } } } }, @@ -2154,6 +2936,11 @@ "reveal": { "type": "string", "description": "Ship log fact revealed when viewing this slide.\nSet this to include ship log entry module. Base game default is \"\"." + }, + "rotate": { + "type": "boolean", + "description": "Exclusive to slide reels. Whether this slide should rotate the reel item while inside a projector.", + "default": true } } }, @@ -2173,90 +2960,35 @@ "standingVisionTorch" ] }, - "QuantumGroupInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "description": "What type of group this is: does it define a list of states a single quantum object could take or a list of sockets one or more quantum objects could share?", - "$ref": "#/definitions/QuantumGroupType" - }, - "id": { - "type": "string", - "description": "A unique string used by props (that are marked as quantum) use to refer back to this group" - }, - "sockets": { - "type": "array", - "description": "Only required if type is `sockets`. This lists all the possible locations for any props assigned to this group.", - "items": { - "$ref": "#/definitions/QuantumSocketInfo" - } - }, - "hasEmptyState": { - "type": "boolean", - "description": "Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states." - }, - "sequential": { - "type": "boolean", - "description": "Optional. Only used if type is `states`. If this is true, then the states will be presented in order, rather than in a random order" - }, - "loop": { - "type": "boolean", - "description": "Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state", - "default": true - } - } - }, - "QuantumGroupType": { + "SlideReelType": { "type": "string", "description": "", "x-enumNames": [ - "Sockets", - "States" + "SixSlides", + "SevenSlides", + "EightSlides", + "Whole" ], "enum": [ - "sockets", - "states" + "sixSlides", + "sevenSlides", + "eightSlides", + "whole" ] }, - "QuantumSocketInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "rotation": { - "description": "Rotation of the object", - "$ref": "#/definitions/MVector3" - }, - "alignRadial": { - "type": [ - "boolean", - "null" - ], - "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." - }, - "position": { - "description": "Position of the object", - "$ref": "#/definitions/MVector3" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, - "isRelativeToParent": { - "type": "boolean", - "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." - }, - "rename": { - "type": "string", - "description": "An optional rename of this object" - }, - "probability": { - "type": "number", - "description": "The probability any props that are part of this group will occupy this socket", - "format": "float", - "default": 1.0 - } - } + "SlideReelCondition": { + "type": "string", + "description": "", + "x-enumNames": [ + "Antique", + "Pristine", + "Rusted" + ], + "enum": [ + "antique", + "pristine", + "rusted" + ] }, "TornadoInfo": { "type": "object", @@ -2277,14 +3009,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2445,14 +3177,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2515,14 +3247,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2558,6 +3290,10 @@ "type": "string", "description": "If you want a black hole to load a new star system scene, put its name here." }, + "spawnPointID": { + "type": "string", + "description": "If this is a black hole loading a new star system, set the ID of the spawn point you want to use\nOtherwise, will use the default spawn" + }, "type": { "description": "Type of singularity (white hole or black hole)", "$ref": "#/definitions/SingularityType" @@ -2587,90 +3323,6 @@ "whiteHole" ] }, - "SignalInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "audio": { - "type": "string", - "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "minDistance": { - "type": "number", - "description": "At this distance the sound is at its loudest.", - "format": "float" - }, - "maxDistance": { - "type": "number", - "description": "The sound will drop off by this distance (Note: for signals, this only effects when it is heard aloud and not via the signalscope).", - "format": "float", - "default": 5.0 - }, - "volume": { - "type": "number", - "description": "How loud the sound will play", - "format": "float", - "default": 0.5 - }, - "position": { - "description": "Position of the object", - "$ref": "#/definitions/MVector3" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, - "isRelativeToParent": { - "type": "boolean", - "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." - }, - "rename": { - "type": "string", - "description": "An optional rename of this object" - }, - "detectionRadius": { - "type": "number", - "description": "How close the player must get to the signal to detect it. This is when you get the \"Unknown Signal Detected\"\nnotification.", - "format": "float", - "minimum": 0.0 - }, - "frequency": { - "type": "string", - "description": "The frequency ID of the signal. The built-in game values are `Default`, `Traveler`, `Quantum`, `EscapePod`,\n`Statue`, `WarpCore`, `HideAndSeek`, and `Radio`. You can also put a custom value." - }, - "identificationRadius": { - "type": "number", - "description": "How close the player must get to the signal to identify it. This is when you learn its name.", - "format": "float", - "default": 10.0, - "minimum": 0.0 - }, - "insideCloak": { - "type": "boolean", - "description": "Only set to `true` if you are putting this signal inside a cloaking field." - }, - "name": { - "type": "string", - "description": "The unique ID of the signal." - }, - "onlyAudibleToScope": { - "type": "boolean", - "description": "`false` if the player can hear the signal without equipping the signal-scope.", - "default": true - }, - "reveals": { - "type": "string", - "description": "A ship log fact to reveal when the signal is identified.", - "default": "" - }, - "sourceRadius": { - "type": "number", - "description": "Radius of the sphere giving off the signal.", - "format": "float", - "default": 1.0 - } - } - }, "RemoteInfo": { "type": "object", "additionalProperties": false, @@ -2689,13 +3341,13 @@ }, "platform": { "description": "Camera platform that the stones can project to and from", - "$ref": "#/definitions/PlatformInfo" + "$ref": "#/definitions/RemotePlatformInfo" }, "stones": { "type": "array", "description": "Projection stones", "items": { - "$ref": "#/definitions/StoneInfo" + "$ref": "#/definitions/ProjectionStoneInfo" } } } @@ -2719,14 +3371,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2779,7 +3431,7 @@ } } }, - "PlatformInfo": { + "RemotePlatformInfo": { "type": "object", "additionalProperties": false, "properties": { @@ -2798,14 +3450,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2825,7 +3477,7 @@ } } }, - "StoneInfo": { + "ProjectionStoneInfo": { "type": "object", "additionalProperties": false, "properties": { @@ -2844,14 +3496,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2881,14 +3533,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2926,14 +3578,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -2963,14 +3615,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3016,14 +3668,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3094,14 +3746,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3141,14 +3793,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3185,14 +3837,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3203,6 +3855,669 @@ } } }, + "SocketQuantumGroupInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rename": { + "type": "string", + "description": "Optional name to help identify this group" + }, + "details": { + "type": "array", + "description": "List of props which will be used in this quantum group", + "items": { + "$ref": "#/definitions/QuantumDetailInfo" + } + }, + "sockets": { + "type": "array", + "items": { + "$ref": "#/definitions/QuantumSocketInfo" + } + } + } + }, + "QuantumDetailInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle. \nIf empty, will make an empty game object. This can be useful for adding other props to it as its children." + }, + "removeChildren": { + "type": "array", + "description": "A list of children to remove from this detail", + "items": { + "type": "string" + } + }, + "removeComponents": { + "type": "boolean", + "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." + }, + "scale": { + "type": "number", + "description": "Scale the prop", + "format": "float", + "default": 1.0 + }, + "stretch": { + "description": "Scale each axis of the prop. Overrides `scale`.", + "$ref": "#/definitions/MVector3" + }, + "keepLoaded": { + "type": "boolean", + "description": "Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?\nAlso makes this detail visible on the map.\nKeeping many props loaded is bad for performance so use this only when it's actually relevant\nMost logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided." + }, + "hasPhysics": { + "type": "boolean", + "description": "Should this object dynamically move around?\nThis tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others." + }, + "physicsMass": { + "type": "number", + "description": "The mass of the physics object.\nMost pushable props use the default value, which matches the player mass.", + "format": "float", + "default": 0.001 + }, + "physicsRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for physics collision.\nIf there's already good colliders on the detail, you can make this 0.", + "format": "float", + "default": 1.0 + }, + "physicsSuspendUntilImpact": { + "type": "boolean", + "description": "If true, this detail will stay still until it touches something.\nGood for zero-g props.", + "default": false + }, + "ignoreSun": { + "type": "boolean", + "description": "Set to true if this object's lighting should ignore the effects of sunlight" + }, + "activationCondition": { + "type": "string", + "description": "Activates this game object when the dialogue condition is met" + }, + "deactivationCondition": { + "type": "string", + "description": "Deactivates this game object when the dialogue condition is met" + }, + "blinkWhenActiveChanged": { + "type": "boolean", + "description": "Should the player close their eyes while the activation state changes. Only relevant if activationCondition or deactivationCondition are set.", + "default": true + }, + "item": { + "description": "Should this detail be treated as an interactible item", + "$ref": "#/definitions/ItemInfo" + }, + "itemSocket": { + "description": "Should this detail be treated as a socket for an interactible item", + "$ref": "#/definitions/ItemSocketInfo" + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "randomizeYRotation": { + "type": "boolean", + "description": "When used in a quantum socket this object will randomize its rotation around the local Y axis.", + "default": true + }, + "alignWithGravity": { + "type": "boolean", + "description": "When used in a quantum socket this object will align to the nearest gravity volume. Else use the rotation of the quantum socket.", + "default": true + } + } + }, + "QuantumSocketInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "probability": { + "type": "number", + "description": "The probability any props that are part of this group will occupy this socket", + "format": "float", + "default": 1.0 + } + } + }, + "StateQuantumGroupInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rename": { + "type": "string", + "description": "Optional name to help identify this group" + }, + "details": { + "type": "array", + "description": "List of props which will be used in this quantum group", + "items": { + "$ref": "#/definitions/QuantumDetailInfo" + } + }, + "sequential": { + "type": "boolean", + "description": "Optional. If this is true, then the states will be presented in order, rather than in a random order" + }, + "loop": { + "type": "boolean", + "description": "Optional. Only used if `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state", + "default": true + }, + "hasEmptyState": { + "type": "boolean", + "description": "Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states." + } + } + }, + "LightningQuantumInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "details": { + "type": "array", + "description": "List of props which will be alternated between during flashes of lightning", + "items": { + "$ref": "#/definitions/DetailInfo" + } + } + } + }, + "DreamCampfireInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "Unique ID for this dream-world campfire" + } + } + }, + "DreamArrivalPointInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "Unique ID for this dream-world arrival point" + }, + "generateSimulationMeshes": { + "type": "boolean", + "description": "Whether to generate simulation meshes (the models used in the \"tronworld\" or \"matrix\" view) for most objects on the current planet by cloning the existing meshes and applying the simulation materials. Leave this off if you are building your own simulation meshes or using existing objects which have them." + } + } + }, + "GrappleTotemInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "minDistance": { + "type": "number", + "description": "The minimum distance that the player must be from the grapple totem for it to activate.", + "format": "float", + "default": 10.0 + }, + "arrivalDistance": { + "type": "number", + "description": "The distance from the grapple totem that the player will stop at when it activates.", + "format": "float", + "default": 4.0 + }, + "maxAngle": { + "type": "number", + "description": "The maximum angle in degrees allowed between the grapple totem's face and the player's lantern in order to activate the totem.", + "format": "float", + "default": 45.0 + }, + "maxDistance": { + "type": "number", + "description": "The maximum distance allowed between the grapple totem's face and the player's lantern in order to activate the totem.", + "format": "float", + "default": 29.0 + } + } + }, + "AlarmTotemInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "sightDistance": { + "type": "number", + "description": "The maximum distance of the alarm's vision cone.", + "format": "float", + "default": 45.0 + }, + "sightAngle": { + "type": "number", + "description": "The width of the alarm's vision cone in degrees.", + "format": "float", + "default": 60.0 + }, + "stretchVisionCone": { + "description": "Scales the visible vision cone in the simulation view (does not affect the actual vision cone detection).", + "$ref": "#/definitions/MVector3" + } + } + }, + "PortholeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "revealFacts": { + "type": "array", + "description": "Fact IDs to reveal when peeking through the porthole.", + "items": { + "type": "string" + } + }, + "fieldOfView": { + "type": "number", + "description": "The field of view of the porthole camera.", + "format": "float", + "default": 90.0 + }, + "target": { + "description": "The location of the camera when the player peeks through the porthole. Can be placed on a different planet.", + "$ref": "#/definitions/PortholeTargetInfo" + } + } + }, + "PortholeTargetInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "parentBody": { + "type": "string", + "description": "The name of the planet that will be used with `parentPath`. Must be set if `parentPath` is set." + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + } + } + }, + "DreamCandleInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "type": { + "description": "The type of dream candle this is.", + "default": "Ground", + "$ref": "#/definitions/DreamCandleType" + }, + "startLit": { + "type": "boolean", + "description": "Whether the candle should start lit or extinguished." + } + } + }, + "DreamCandleType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Ground", + "GroundSmall", + "GroundLarge", + "GroundSingle", + "Wall", + "WallLargeFlame", + "WallBigWick", + "Standing", + "Pile" + ], + "enum": [ + "ground", + "groundSmall", + "groundLarge", + "groundSingle", + "wall", + "wallLargeFlame", + "wallBigWick", + "standing", + "pile" + ] + }, + "ProjectionTotemInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "startLit": { + "type": "boolean", + "description": "Whether the totem should start lit or extinguished." + }, + "extinguishOnly": { + "type": "boolean", + "description": "Whether the projection totem should be able to extinguished but not be able to be lit again with the artifact. Mainly useful if `startLit` is set to true." + }, + "pathToAlarmTotem": { + "type": "string", + "description": "A relative path from this planet to an alarm totem that will be activated or deactivated based on whether this totem is lit." + }, + "pathsToDreamCandles": { + "type": "array", + "description": "Relative paths from this planet to objects containing dream candles that will be activated or deactivated based on whether this totem is lit. All dream candles in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified.", + "items": { + "type": "string" + } + }, + "pathsToProjectionTotems": { + "type": "array", + "description": "Relative paths from this planet to projection totems that will be deactivated if this totem is extinguished. All projection totems in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified.", + "items": { + "type": "string" + } + }, + "pathsToProjectedObjects": { + "type": "array", + "description": "Relative paths from this planet to objects that will appear or disappear when this totem is lit or extinguished. Some types of objects and effects are not supported and will remain visible and active.", + "items": { + "type": "string" + } + }, + "toggleProjectedObjectsActive": { + "type": "boolean", + "description": "If set, projected objects will be set to fully active or fully disabled instantly instead of smoothly fading lights/renderers/colliders. Use this if the normal behavior is insufficient for the objects you're using." + } + } + }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false, @@ -3337,7 +4652,7 @@ }, "spriteFolder": { "type": "string", - "description": "A path to the folder where entry sprites are stored." + "description": "A path to the folder where entry sprites (.png) are stored." }, "xmlFile": { "type": "string", @@ -3375,7 +4690,7 @@ }, "outlineSprite": { "type": "string", - "description": "The path to the sprite to show when the planet is unexplored in map mode." + "description": "The path to the sprite (.png/.jpg/.exr) to show when the planet is unexplored in map mode." }, "remove": { "type": "boolean", @@ -3383,13 +4698,18 @@ }, "revealedSprite": { "type": "string", - "description": "The path to the sprite to show when the planet is revealed in map mode." + "description": "The path to the sprite (.png/.jpg/.exr) to show when the planet is revealed in map mode." }, "scale": { "type": "number", "description": "Scale to apply to the planet in map mode.", "format": "float", "default": 1.0 + }, + "selectable": { + "type": "boolean", + "description": "Can this ship log map mode entry be selected\nEx) Set to false for stars with no entries on them so they are skipped in navigation", + "default": true } } }, @@ -3403,7 +4723,7 @@ }, "outlineSprite": { "type": "string", - "description": "The sprite to show when the parent AstroBody is rumored/unexplored." + "description": "The sprite (.png/.jpg/.exr) to show when the parent AstroBody is rumored/unexplored." }, "position": { "description": "The position (relative to the parent) to place the detail.", @@ -3411,7 +4731,7 @@ }, "revealedSprite": { "type": "string", - "description": "The sprite to show when the parent AstroBody is revealed." + "description": "The sprite (.png/.jpg/.exr) to show when the parent AstroBody is revealed." }, "rotation": { "type": "number", @@ -3455,13 +4775,19 @@ "type": "object", "additionalProperties": false, "properties": { - "playerSpawn": { - "description": "If you want the player to spawn on the new body, set a value for this.", - "$ref": "#/definitions/PlayerSpawnPoint" + "playerSpawnPoints": { + "type": "array", + "description": "If you want the player to spawn on the new body, set a value for this.\nDifferent spawns can be unlocked with persistent conditions and facts", + "items": { + "$ref": "#/definitions/PlayerSpawnPoint" + } }, - "shipSpawn": { - "description": "Required for the system to be accessible by warp drive.", - "$ref": "#/definitions/ShipSpawnPoint" + "shipSpawnPoints": { + "type": "array", + "description": "Required for the system to be accessible by warp drive.\nDifferent spawns can be unlocked with persistent conditions and facts", + "items": { + "$ref": "#/definitions/ShipSpawnPoint" + } } } }, @@ -3473,6 +4799,22 @@ "description": "Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0).", "$ref": "#/definitions/MVector3" }, + "isDefault": { + "type": "boolean", + "description": "Whether this planet's spawn point is the one the player/ship will initially spawn at, if multiple spawn points exist.\nDo not use at the same time as makeDefaultIfFactRevealed or makeDefaultIfPersistentCondition\nSpawns unlocked with this have lowest priority" + }, + "makeDefaultIfFactRevealed": { + "type": "string", + "description": "If the given ship log fact is revealed, this spawn point will be used\nDo not use at the same time as isDefault or makeDefaultIfPersistentCondition\nSpawns unlocked with this have highest priority" + }, + "makeDefaultIfPersistentCondition": { + "type": "string", + "description": "If the given persistent condition is true, this spawn point will be used\nDo not use at the same time as isDefault or makeDefaultIfFactRevealed\nSpawns unlocked with this have second highest priority" + }, + "id": { + "type": "string", + "description": "ID used to have a black hole or warp volume bring the player to this spawn specifically" + }, "rotation": { "description": "Rotation of the object", "$ref": "#/definitions/MVector3" @@ -3488,14 +4830,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3503,10 +4845,6 @@ "startWithSuit": { "type": "boolean", "description": "If you spawn on a planet with no oxygen, you probably want to set this to true ;;)" - }, - "isDefault": { - "type": "boolean", - "description": "Whether this planet's spawn point is the one the player will initially spawn at, if multiple spawn points exist." } } }, @@ -3518,6 +4856,22 @@ "description": "Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0).", "$ref": "#/definitions/MVector3" }, + "isDefault": { + "type": "boolean", + "description": "Whether this planet's spawn point is the one the player/ship will initially spawn at, if multiple spawn points exist.\nDo not use at the same time as makeDefaultIfFactRevealed or makeDefaultIfPersistentCondition\nSpawns unlocked with this have lowest priority" + }, + "makeDefaultIfFactRevealed": { + "type": "string", + "description": "If the given ship log fact is revealed, this spawn point will be used\nDo not use at the same time as isDefault or makeDefaultIfPersistentCondition\nSpawns unlocked with this have highest priority" + }, + "makeDefaultIfPersistentCondition": { + "type": "string", + "description": "If the given persistent condition is true, this spawn point will be used\nDo not use at the same time as isDefault or makeDefaultIfFactRevealed\nSpawns unlocked with this have second highest priority" + }, + "id": { + "type": "string", + "description": "ID used to have a black hole or warp volume bring the player to this spawn specifically" + }, "rotation": { "description": "Rotation of the object", "$ref": "#/definitions/MVector3" @@ -3533,14 +4887,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -3744,6 +5098,11 @@ "tint": { "description": "Tint of the water", "$ref": "#/definitions/MColor" + }, + "allowShipAutoroll": { + "type": "boolean", + "description": "Will the ship automatically try to orient itself to face upwards while in this volume?", + "default": true } } }, @@ -4013,14 +5372,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4111,14 +5470,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4181,14 +5540,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4202,7 +5561,7 @@ }, "NHDeathType": { "type": "string", - "description": "", + "description": "Some special death types are:\n\nSupernova: Special death type used when the supernova hits you. You will not wake up if in the Dreamworld. \n\nDigestion: Death type used by anglerfish (and cut-content ghosts and water monster)\n\nBig bang: Special death type used at the end of the game\n\nMeditation: Special death type used when skipping to the next loop. You will not wake up if in the Dreamworld. \n\nTimeloop: Special death type used when the time loop ends. You will not wake up if in the Dreamworld. \n\nBlackhole: Special death type used by the ATP blackhole (and custom NH blackholes without whitehole destinations)\n\nDream: Special DLC death type used to kill a player in the real world while in the Dreamworld (i.e., you will loop not wake up)\n\nDreamExplosion: Special DLC death type used by the prototype artifact to kill a player in the real world while in the Dreamworld (i.e., you will loop not wake up)\n\nCrushedByElevator: Similar to the Crushed death type, but much faster", "x-enumNames": [ "Default", "Impact", @@ -4264,14 +5623,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4315,14 +5674,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4358,14 +5717,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4392,14 +5751,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4463,14 +5822,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4520,14 +5879,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4643,14 +6002,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4683,14 +6042,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4733,14 +6092,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4777,14 +6136,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4849,14 +6208,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4903,14 +6262,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4943,14 +6302,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4983,14 +6342,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -4999,6 +6358,10 @@ "type": "string", "description": "The star system that entering this volume will send you to.", "default": "SolarSystem" + }, + "spawnPointID": { + "type": "string", + "description": "ID assigned to a spawn point in the other system that the player will be sent to\nUses the default spawn if not set" } } }, @@ -5016,14 +6379,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" diff --git a/NewHorizons/Schemas/dialogue_schema.xsd b/NewHorizons/Schemas/dialogue_schema.xsd index 55f1a632..2b458cd5 100644 --- a/NewHorizons/Schemas/dialogue_schema.xsd +++ b/NewHorizons/Schemas/dialogue_schema.xsd @@ -1,4 +1,4 @@ - + @@ -8,7 +8,7 @@ - + The name of the character, used for the interaction prompt. Set to `SIGN` for the prompt @@ -16,7 +16,7 @@ - + The different nodes of this dialogue tree @@ -30,7 +30,7 @@ - + The name of this dialogue node @@ -45,34 +45,35 @@ - + When used with multiple Dialogues, the node will choose a random one to show - + The dialogue to show to the player - - - - A list of options to show to the player once the character is done talking - - - - + Facts to reveal when the player goes through this dialogue node + + + + Set a new persistent condition that will last indefinitely in the current save, unless cancelled + or deleted + + + @@ -80,11 +81,10 @@ - + - Set a new persistent condition that will last indefinitely in the current save, unless cancelled - or deleted + Disable a set persistent condition from the current save @@ -95,7 +95,7 @@ - + The name of the `DialogueNode` to go to after this node. Mutually exclusive with @@ -103,13 +103,20 @@ + + + + A list of options to show to the player once the character is done talking + + + - + A page of dialogue to show to the player @@ -122,7 +129,7 @@ - + The ID of a fact to reveal @@ -135,19 +142,33 @@ - + Options the player can select from + + + + Name of another DialogueNode whose options you want to repeat to avoid having to copy paste + + + + + + + Require a ship log fact to be known to show this option + + + @@ -162,55 +183,48 @@ - + Require a (single-loop) condition to be met to show this option - + Hide this option if a (single-loop) condition has been met - - - - Require a ship log fact to be known to show this option - - - - + The text to show for this option - - - - Set a condition when this option is chosen - - - - - - - Cancel a condition when this option is chosen - - - - + The name of the `DialogueNode` to go to when this option is selected + + + + Set a condition when this option is chosen + + + + + + + Cancel a condition when this option is chosen + + + diff --git a/NewHorizons/Schemas/shiplog_schema.xsd b/NewHorizons/Schemas/shiplog_schema.xsd index c702c4ea..213cc157 100644 --- a/NewHorizons/Schemas/shiplog_schema.xsd +++ b/NewHorizons/Schemas/shiplog_schema.xsd @@ -1,18 +1,18 @@ - + - + ID of the planet these entries are for - + A set of entries that belong to this planet @@ -29,52 +29,59 @@ - + The ID of this entry - + Name of this entry - + The curiosity this entry belongs to - + Whether this entry is a curiosity - + Whether to hide the "More To Explore" text on this entry - + + + + When the parent of this entry is determining whether its "More To Explore" text should appear, this child entry will be ignored. + + + + Ignore more to explore if a persistent condition is `true` - + - If this fact is revealed, show the Alt picture + If this fact is revealed, show the Alt picture. Alt photos use the same file name as default but suffixed with "_alt" @@ -105,35 +112,35 @@ - + The ID of this rumor fact - + The source of this rumor, this draws a line in detective mode - + Displays on the card in detective mode if no ExploreFacts have been revealed on the parent entry - + Priority over other RumorFacts to appear as the entry card's title - + Whether to hide the "More to explore" on this rumor fact @@ -147,14 +154,14 @@ - + The ID of this explore fact - + Whether to hide the "More to explore" text for this fact @@ -168,14 +175,14 @@ - + The text content for this fact - + Display alt-text given a certain fact is revealed @@ -183,14 +190,14 @@ - + The text to display if the condition is met - + The condition that needs to be fulfilled to have the alt text be displayed diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index e80caae9..d1466958 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -5,6 +5,15 @@ "description": "Configuration for a specific star system", "additionalProperties": false, "properties": { + "name": { + "type": "string", + "description": "Unique name of your system. If not specified, the file name (without the extension) is used." + }, + "allowOutsideItems": { + "type": "boolean", + "description": "When changing star systems are you allowed to bring items into this system?", + "default": true + }, "freeMapAngle": { "type": "boolean", "description": "In this system should the player be able to rotate their map camera freely or be stuck above the plane of the solar system?" @@ -20,9 +29,22 @@ }, "canEnterViaWarpDrive": { "type": "boolean", - "description": "Whether this system can be warped to via the warp drive. If you set factRequiredForWarp, this will be true.", + "description": "Whether this system can be warped to via the warp drive. If you set `factRequiredForWarp`, this will be true.\nDoes NOT effect the base SolarSystem. For that, see `canExitViaWarpDrive` and `factRequiredToExitViaWarpDrive`", "default": true }, + "factRequiredForWarp": { + "type": "string", + "description": "The FactID that must be revealed before it can be warped to. Don't set `canEnterViaWarpDrive` to `false` if\nyou're using this, because it will be overwritten." + }, + "canExitViaWarpDrive": { + "type": "boolean", + "description": "Can you use the warp drive to leave this system? If you set `factRequiredToExitViaWarpDrive`\nthis will be true.", + "default": true + }, + "factRequiredToExitViaWarpDrive": { + "type": "string", + "description": "The FactID that must be revealed for you to warp back to the main solar system from here. Don't set `canWarpHome`\nto `false` if you're using this, because it will be overwritten." + }, "destroyStockPlanets": { "type": "boolean", "description": "Do you want a clean slate for this star system? Or will it be a modified version of the original.", @@ -33,10 +55,6 @@ "description": "Should the time loop be enabled in this system?", "default": true }, - "factRequiredForWarp": { - "type": "string", - "description": "The FactID that must be revealed before it can be warped to. Don't set `canEnterViaWarpDrive` to `false` if\nyou're using this, because it will be overwritten." - }, "loopDuration": { "type": "number", "description": "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.", @@ -59,9 +77,9 @@ "type": "boolean", "description": "Set to `true` if you want the player to stay in this star system if they die in it." }, - "travelAudio": { - "type": "string", - "description": "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." + "GlobalMusic": { + "description": "Replace music that plays globally", + "$ref": "#/definitions/GlobalMusicModule" }, "Vessel": { "description": "Configure warping to this system with the vessel", @@ -143,6 +161,40 @@ } } }, + "GlobalMusicModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "travelAudio": { + "type": "string", + "description": "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." + }, + "endTimesAudio": { + "type": "string", + "description": "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." + }, + "endTimesDreamAudio": { + "type": "string", + "description": "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." + }, + "brambleDimensionAudio": { + "type": "string", + "description": "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." + }, + "finalEndTimesIntroAudio": { + "type": "string", + "description": "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." + }, + "finalEndTimesLoopAudio": { + "type": "string", + "description": "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." + }, + "finalEndTimesBrambleDimensionAudio": { + "type": "string", + "description": "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." + } + } + }, "VesselModule": { "type": "object", "additionalProperties": false, @@ -246,14 +298,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" @@ -301,14 +353,14 @@ "description": "Position of the object", "$ref": "#/definitions/MVector3" }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." - }, "isRelativeToParent": { "type": "boolean", "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "rename": { "type": "string", "description": "An optional rename of this object" diff --git a/NewHorizons/Schemas/text_schema.xsd b/NewHorizons/Schemas/text_schema.xsd index 297e21cd..797497f4 100644 --- a/NewHorizons/Schemas/text_schema.xsd +++ b/NewHorizons/Schemas/text_schema.xsd @@ -27,27 +27,27 @@ - + The id of this text block - + The id of the parent text block - + - + - + The text to show for this option @@ -58,17 +58,17 @@ - + - + - + Facts to reveal when the player goes through this dialogue node diff --git a/NewHorizons/Utility/CollectionUtilities.cs b/NewHorizons/Utility/CollectionUtilities.cs index c70f7507..d98ab60f 100644 --- a/NewHorizons/Utility/CollectionUtilities.cs +++ b/NewHorizons/Utility/CollectionUtilities.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace NewHorizons.Utility { public static class CollectionUtilities { - public static T KeyByValue(Dictionary dict, W val) + public static T KeyByValue(Dictionary dict, W val, T defaultValue = default) { - T key = default; + T key = defaultValue; foreach (KeyValuePair pair in dict) { if (EqualityComparer.Default.Equals(pair.Value, val)) diff --git a/NewHorizons/Utility/DebugTools/DebugPropPlacer.cs b/NewHorizons/Utility/DebugTools/DebugPropPlacer.cs deleted file mode 100644 index 4a107eaf..00000000 --- a/NewHorizons/Utility/DebugTools/DebugPropPlacer.cs +++ /dev/null @@ -1,311 +0,0 @@ -using NewHorizons.Builder.Props; -using NewHorizons.External.Configs; -using NewHorizons.External.Modules.Props; -using NewHorizons.Handlers; -using NewHorizons.Utility.Files; -using NewHorizons.Utility.OWML; -using NewHorizons.Utility.OuterWilds; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.InputSystem; - -namespace NewHorizons.Utility.DebugTools -{ - - // - // The prop placer. Doesn't interact with any files, just places and tracks props. - // - - [RequireComponent(typeof(DebugRaycaster))] - class DebugPropPlacer : MonoBehaviour - { - private struct PropPlacementData - { - public AstroObject body; - public string system; - public GameObject gameObject; - public DetailInfo detailInfo; - } - - // VASE - public static readonly string DEFAULT_OBJECT = "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District1/Props_HangingCity_District1/OtherComponentsGroup/Props_HangingCity_Building_10/Prefab_NOM_VaseThin"; - - public string currentObject { get; private set; } // path of the prop to be placed - private bool hasAddedCurrentObjectToRecentsList = false; - private List props = new List(); - private List deletedProps = new List(); - private DebugRaycaster _rc; - - public static HashSet RecentlyPlacedProps = new HashSet(); - - public static bool active = false; - public GameObject mostRecentlyPlacedPropGO { get { return props.Count() <= 0 ? null : props[props.Count() - 1].gameObject; } } - public string mostRecentlyPlacedPropPath { get { return props.Count() <= 0 ? "" : props[props.Count() - 1].detailInfo.path; } } - - private ScreenPrompt _placePrompt; - private ScreenPrompt _undoPrompt; - private ScreenPrompt _redoPrompt; - - private void Awake() - { - _rc = this.GetRequiredComponent(); - currentObject = DEFAULT_OBJECT; - - _placePrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_PLACE", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.G)); - _undoPrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_UNDO", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.Minus)); - _redoPrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_REDO", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.Equals)); - - Locator.GetPromptManager().AddScreenPrompt(_placePrompt, PromptPosition.UpperRight, false); - Locator.GetPromptManager().AddScreenPrompt(_undoPrompt, PromptPosition.UpperRight, false); - Locator.GetPromptManager().AddScreenPrompt(_redoPrompt, PromptPosition.UpperRight, false); - } - - private void OnDestroy() - { - var promptManager = Locator.GetPromptManager(); - if (promptManager == null) return; - promptManager.RemoveScreenPrompt(_placePrompt, PromptPosition.UpperRight); - promptManager.RemoveScreenPrompt(_undoPrompt, PromptPosition.UpperRight); - promptManager.RemoveScreenPrompt(_redoPrompt, PromptPosition.UpperRight); - } - - private void Update() - { - UpdatePromptVisibility(); - if (!Main.Debug) return; - if (!active) return; - - if (Keyboard.current[Key.G].wasReleasedThisFrame) - { - PlaceObject(); - } - - if (Keyboard.current[Key.Minus].wasReleasedThisFrame) - { - DeleteLast(); - } - - if (Keyboard.current[Key.Equals].wasReleasedThisFrame) - { - UndoDelete(); - } - } - - public void UpdatePromptVisibility() - { - var visible = !OWTime.IsPaused() && Main.Debug && active; - _placePrompt.SetVisibility(visible); - _undoPrompt.SetVisibility(visible && props.Count > 0); - _redoPrompt.SetVisibility(visible && deletedProps.Count > 0); - } - - public void SetCurrentObject(string s) - { - currentObject = s; - hasAddedCurrentObjectToRecentsList = false; - } - - internal void PlaceObject() - { - DebugRaycastData data = _rc.Raycast(); - PlaceObject(data); - - //TODO: use DropItem logic to make props not clip through the ground when placed - //public virtual void DropItem(Vector3 position, Vector3 normal, Transform parent, Sector sector, IItemDropTarget customDropTarget) - - if (!hasAddedCurrentObjectToRecentsList) - { - hasAddedCurrentObjectToRecentsList = true; - - if (!RecentlyPlacedProps.Contains(currentObject)) - { - RecentlyPlacedProps.Add(currentObject); - } - } - } - - public void PlaceObject(DebugRaycastData data) - { - // TODO: implement sectors - // if this hits a sector, store that sector and add a config file option for it - - if (data.hitBodyGameObject == null) - { - NHLogger.LogError($"Failed to place object {currentObject} on nothing."); - return; - } - - try - { - if (currentObject == "" || currentObject == null) - { - SetCurrentObject(DEFAULT_OBJECT); - } - - var planetGO = data.hitBodyGameObject; - - if (!planetGO.name.EndsWith("_Body")) - { - NHLogger.LogWarning("Cannot place object on non-body object: " + data.hitBodyGameObject.name); - } - - var sector = planetGO.GetComponentInChildren(); - var prefab = SearchUtilities.Find(currentObject); - var detailInfo = new DetailInfo() - { - position = data.pos, - rotation = data.rot.eulerAngles, - }; - var prop = DetailBuilder.Make(planetGO, sector, null, prefab, detailInfo); - - var body = data.hitBodyGameObject.GetComponent(); - if (body != null) RegisterProp(body, prop); - } - catch - { - NHLogger.LogError($"Failed to place object {currentObject} on body ${data.hitBodyGameObject} at location ${data.pos}."); - } - } - - - - public static string GetAstroObjectName(string bodyName) - { - var astroObject = AstroObjectLocator.GetAstroObject(bodyName); - if (astroObject == null) return null; - - var astroObjectName = astroObject.name; - - return astroObjectName; - } - - public void FindAndRegisterPropsFromConfig(PlanetConfig config, List pathsList = null) - { - if (config.starSystem != Main.Instance.CurrentStarSystem) return; - - var planet = AstroObjectLocator.GetAstroObject(config.name); - - if (planet == null) return; - if (config.Props == null || config.Props.details == null) return; - - var astroObject = AstroObjectLocator.GetAstroObject(config.name); - - foreach (var detail in config.Props.details) - { - var spawnedProp = DetailBuilder.GetSpawnedGameObjectByDetailInfo(detail); - - if (spawnedProp == null) - { - NHLogger.LogError("No spawned prop found for " + detail.path); - continue; - } - - var data = RegisterProp_WithReturn(astroObject, spawnedProp, detail.path, detail); - - // note: we do not support placing props from assetbundles, so they will not be added to the - // selectable list of placed props - if (detail.assetBundle == null && !RecentlyPlacedProps.Contains(data.detailInfo.path)) - { - if (pathsList != null) pathsList.Add(data.detailInfo.path); - } - } - } - - public void RegisterProp(AstroObject body, GameObject prop) - { - RegisterProp_WithReturn(body, prop); - } - - private PropPlacementData RegisterProp_WithReturn(AstroObject body, GameObject prop, string propPath = null, DetailInfo detailInfo = null) - { - if (Main.Debug) - { - // TOOD: make this prop an item - } - - //var body = AstroObjectLocator.GetAstroObject(bodyGameObjectName); - - NHLogger.LogVerbose($"Adding prop to {Main.Instance.CurrentStarSystem}::{body.name}"); - - - detailInfo = detailInfo == null ? new DetailInfo() : detailInfo; - detailInfo.path = propPath == null ? currentObject : propPath; - - PropPlacementData data = new PropPlacementData - { - body = body, - gameObject = prop, - system = Main.Instance.CurrentStarSystem, - detailInfo = detailInfo - }; - - props.Add(data); - return data; - } - - public Dictionary GetPropsConfigByBody() - { - var groupedProps = props - .GroupBy(p => p.system + "." + p.body) - .Select(grp => grp.ToList()) - .ToList(); - - Dictionary propConfigs = new(); - - foreach (List bodyProps in groupedProps) - { - if (bodyProps == null || bodyProps.Count == 0) continue; - if (bodyProps[0].body == null) continue; - var body = bodyProps[0].body; - NHLogger.LogVerbose("getting prop group for body " + body.name); - //string bodyName = GetAstroObjectName(bodyProps[0].body); - - DetailInfo[] infoArray = new DetailInfo[bodyProps.Count]; - propConfigs[body] = infoArray; - - for (int i = 0; i < bodyProps.Count; i++) - { - var prop = bodyProps[i]; - var rootTransform = prop.gameObject.transform.root; - - // Objects are parented to the sector and not to the planet - // However, raycasted positions are reported relative to the root game object - // Normally these two are the same, but there are some notable exceptions (ex, floating islands) - // So we can't use local position/rotation here, we have to inverse transform the global position/rotation relative to root object - prop.detailInfo.position = rootTransform.InverseTransformPoint(prop.gameObject.transform.position); - prop.detailInfo.scale = prop.gameObject.transform.localScale.x; - if (!prop.detailInfo.alignRadial.GetValueOrDefault()) prop.detailInfo.rotation = rootTransform.InverseTransformRotation(prop.gameObject.transform.rotation).eulerAngles; - - infoArray[i] = prop.detailInfo; - } - } - - return propConfigs; - } - - public void DeleteLast() - { - if (props.Count <= 0) return; - - PropPlacementData last = props[props.Count - 1]; - props.RemoveAt(props.Count - 1); - - last.gameObject.SetActive(false); - - deletedProps.Add(last); - } - - public void UndoDelete() - { - if (deletedProps.Count <= 0) return; - - PropPlacementData last = deletedProps[deletedProps.Count - 1]; - deletedProps.RemoveAt(deletedProps.Count - 1); - - last.gameObject.SetActive(true); - - props.Add(last); - } - } -} diff --git a/NewHorizons/Utility/DebugTools/DebugReload.cs b/NewHorizons/Utility/DebugTools/DebugReload.cs index cd067d98..97a40178 100644 --- a/NewHorizons/Utility/DebugTools/DebugReload.cs +++ b/NewHorizons/Utility/DebugTools/DebugReload.cs @@ -15,7 +15,7 @@ namespace NewHorizons.Utility.DebugTools public static void InitializePauseMenu(IPauseMenuManager pauseMenu) { - _reloadButton = pauseMenu.MakeSimpleButton(TranslationHandler.GetTranslation("Reload Configs", TranslationHandler.TextType.UI).ToUpper(), 3, true); + _reloadButton = pauseMenu.MakeSimpleButton(TranslationHandler.GetTranslation("Reload Configs", TranslationHandler.TextType.UI).ToUpperFixed(), 3, true); _reloadButton.OnSubmitAction += ReloadConfigs; UpdateReloadButton(); } @@ -43,10 +43,21 @@ namespace NewHorizons.Utility.DebugTools NHLogger.LogWarning("Error While Reloading"); } + Main.Instance.ForceClearCaches = true; + + SearchUtilities.Find("/PauseMenu/PauseMenuManagers").GetComponent().OnSkipToNextTimeLoop(); - Main.Instance.ForceClearCaches = true; - Main.Instance.ChangeCurrentStarSystem(Main.Instance.CurrentStarSystem); + if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") + { + Main.Instance.IsWarpingBackToEye = true; + EyeDetailCacher.IsInitialized = false; + Main.Instance.ChangeCurrentStarSystem("SolarSystem"); + } + else + { + Main.Instance.ChangeCurrentStarSystem(Main.Instance.CurrentStarSystem, Main.Instance.DidWarpFromShip, Main.Instance.DidWarpFromVessel); + } Main.SecondsElapsedInLoop = -1f; } diff --git a/NewHorizons/Utility/DebugTools/Menu/DebugMenu.cs b/NewHorizons/Utility/DebugTools/Menu/DebugMenu.cs deleted file mode 100644 index 92e77893..00000000 --- a/NewHorizons/Utility/DebugTools/Menu/DebugMenu.cs +++ /dev/null @@ -1,321 +0,0 @@ -using NewHorizons.External.Configs; -using NewHorizons.Handlers; -using NewHorizons.Utility.Files; -using NewHorizons.Utility.OWML; -using Newtonsoft.Json; -using OWML.Common; -using OWML.Common.Menus; -using OWML.Utils; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using UnityEngine; - -namespace NewHorizons.Utility.DebugTools.Menu -{ - class DebugMenu : MonoBehaviour - { - private static SubmitAction pauseMenuButton; - - public GUIStyle _editorMenuStyle; - public GUIStyle _tabBarStyle; - public GUIStyle _submenuStyle; - internal Vector2 EditorMenuSize = new Vector2(600, 900); - bool menuOpen = false; - static bool openMenuOnPause; - - // Menu params - internal static IModBehaviour loadedMod = null; - internal Dictionary loadedConfigFiles = new Dictionary(); - private bool saveButtonUnlocked = false; - private Vector2 recentModListScrollPosition = Vector2.zero; - - // Submenus - private List submenus; - private int activeSubmenu = 0; - - private static DebugMenu _instance; - - internal static JsonSerializerSettings jsonSettings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore, - Formatting = Formatting.Indented, - }; - - private void Awake() - { - submenus = new List() - { - new DebugMenuPropPlacer(), - new DebugMenuShipLogs(), - }; - - submenus.ForEach((submenu) => submenu.OnAwake(this)); - } - - private void Start() - { - _instance = this; - - Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuOpened += OnOpenMenu; - Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuClosed += OnCloseMenu; - Main.Instance.OnChangeStarSystem.AddListener(OnChangeStarSystem); - - InitMenu(); - - if (loadedMod != null) - { - LoadMod(loadedMod); - } - } - - public void OnDestroy() - { - Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuOpened -= OnOpenMenu; - Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuClosed -= OnCloseMenu; - Main.Instance.OnChangeStarSystem.RemoveListener(OnChangeStarSystem); - } - - private void OnChangeStarSystem(string _) - { - if (saveButtonUnlocked) - { - SaveLoadedConfigsForRecentSystem(); - saveButtonUnlocked = false; - } - } - - public static void InitializePauseMenu(IPauseMenuManager pauseMenu) - { - pauseMenuButton = pauseMenu.MakeSimpleButton(TranslationHandler.GetTranslation("Toggle Dev Tools Menu", TranslationHandler.TextType.UI).ToUpper(), 3, true); - _instance?.InitMenu(); - } - - public static void UpdatePauseMenuButton() - { - pauseMenuButton?.SetButtonVisible(Main.Debug); - } - - private void OnOpenMenu() { menuOpen = openMenuOnPause; } - - private void ToggleMenu() { menuOpen = !menuOpen; openMenuOnPause = !openMenuOnPause; } - - private void OnCloseMenu() { menuOpen = false; } - - private void OnGUI() - { - if (!menuOpen) return; - if (!Main.Debug) return; - - Vector2 menuPosition = new Vector2(10, 40); - - GUILayout.BeginArea(new Rect(menuPosition.x, menuPosition.y, EditorMenuSize.x, EditorMenuSize.y), _editorMenuStyle); - - // Continue working on existing mod - GUILayout.Label("Name of your mod"); - if (loadedMod == null) - { - recentModListScrollPosition = GUILayout.BeginScrollView(recentModListScrollPosition, GUILayout.Width(EditorMenuSize.x), GUILayout.Height(100)); - - foreach (var mod in Main.MountedAddons) - { - if (GUILayout.Button(mod.ModHelper.Manifest.UniqueName)) - { - LoadMod(mod); - submenus[activeSubmenu].GainActive(); - } - } - - GUILayout.EndScrollView(); - } - else - { - GUILayout.Label(loadedMod.ModHelper.Manifest.UniqueName); - } - - GUILayout.Space(5); - - // Save your work - if (loadedMod != null) - { - GUILayout.BeginHorizontal(); - if (GUILayout.Button(saveButtonUnlocked ? " O " : " | ", GUILayout.ExpandWidth(false))) - { - saveButtonUnlocked = !saveButtonUnlocked; - } - GUI.enabled = saveButtonUnlocked; - if (GUILayout.Button("Update your mod's configs")) - { - SaveLoadedConfigsForRecentSystem(); - saveButtonUnlocked = false; - } - GUI.enabled = true; - GUILayout.EndHorizontal(); - - if (GUILayout.Button("Print config changes for your mod")) - { - PrintLoadedConfigChangesForRecentSystem(); - saveButtonUnlocked = false; - } - } - - GUILayout.Space(20); - - // Draw submenu stuff - if (loadedMod != null) - { - GUILayout.BeginHorizontal(_tabBarStyle); - GUILayout.Space(5); - for (int i = 0; i < submenus.Count; i++) - { - GUI.enabled = i != activeSubmenu; - var style = i == activeSubmenu ? _submenuStyle : _tabBarStyle; - if (GUILayout.Button(" " + submenus[i].SubmenuName() + " ", style, GUILayout.ExpandWidth(false))) - { - GUI.enabled = true; - submenus[activeSubmenu].LoseActive(); - activeSubmenu = i; - submenus[activeSubmenu].GainActive(); - - } - GUI.enabled = true; - - // if (i < submenus.Count-1) GUILayout.Label("|", GUILayout.ExpandWidth(false)); - } - GUILayout.EndHorizontal(); - - GUILayout.BeginVertical(_submenuStyle); - GUILayout.Space(10); - submenus[activeSubmenu].OnGUI(this); - GUILayout.EndVertical(); - } - - GUILayout.EndArea(); - } - - private void LoadMod(IModBehaviour mod) - { - loadedMod = mod; - submenus.ForEach(submenu => submenu.OnBeginLoadMod(this)); - - var folder = loadedMod.ModHelper.Manifest.ModFolderPath; - - var bodiesForThisMod = Main.BodyDict.Values.SelectMany(x => x).Where(x => x.Mod == loadedMod).ToList(); - foreach (var body in bodiesForThisMod) - { - if (body.RelativePath == null) - { - NHLogger.LogWarning($"Error loading config for {body.Config.name} in {body.Config.starSystem}"); - continue; - } - - loadedConfigFiles[Path.Combine(folder, body.RelativePath)] = body.Config; - submenus.ForEach(submenu => submenu.LoadConfigFile(this, body.Config)); - } - } - - private void SaveLoadedConfigsForRecentSystem() - { - submenus.ForEach(submenu => submenu.PreSave(this)); - - var backupFolderName = $"configBackups\\{DateTime.Now.ToString("yyyyMMddTHHmmss")}\\"; - - NHLogger.Log($"Potentially saving {loadedConfigFiles.Keys.Count} files"); - - foreach (var filePath in loadedConfigFiles.Keys) - { - NHLogger.LogVerbose($"Possibly Saving... {loadedConfigFiles[filePath].name} @ {filePath}"); - - if (loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) continue; - - var relativePath = filePath.Replace(loadedMod.ModHelper.Manifest.ModFolderPath, ""); - - var json = loadedConfigFiles[filePath].ToSerializedJson(); - - try - { - var path = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, backupFolderName, relativePath); - NHLogger.LogVerbose($"Backing up... {relativePath} to {path}"); - var oldPath = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, relativePath); - var directoryName = Path.GetDirectoryName(path); - Directory.CreateDirectory(directoryName); - - if (File.Exists(oldPath)) - File.WriteAllBytes(path, File.ReadAllBytes(oldPath)); - else - File.WriteAllText(path, json); - } - catch (Exception e) - { - NHLogger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e}"); - } - - try - { - NHLogger.Log($"Saving... {relativePath} to {filePath}"); - var path = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, relativePath); - var directoryName = Path.GetDirectoryName(path); - Directory.CreateDirectory(directoryName); - - File.WriteAllText(path, json); - } - catch (Exception e) - { - NHLogger.LogError($"Failed to save file {relativePath}:\n{e}"); - } - } - } - - private void PrintLoadedConfigChangesForRecentSystem() - { - foreach(DebugSubmenu menu in submenus) - { - menu.PrintNewConfigSection(this); - } - } - - private void InitMenu() - { - if (_editorMenuStyle != null) return; - - UpdatePauseMenuButton(); - - // TODO: figure out how to clear this event list so that we don't pile up useless instances of the DebugMenu that can't get garbage collected - pauseMenuButton.OnSubmitAction += ToggleMenu; - - submenus.ForEach(submenu => submenu.OnInit(this)); - - _editorMenuStyle = new GUIStyle - { - normal = - { - background = ImageUtilities.MakeSolidColorTexture(1, 1, Color.black) - } - }; - - _tabBarStyle = new GUIStyle - { - normal = - { - background = ImageUtilities.MakeSolidColorTexture(1, 1, new Color(0.2f, 0.2f, 0.2f, 1)), - textColor = Color.white - }, - fontStyle = FontStyle.Bold, - fontSize = 16 - }; - - _submenuStyle = new GUIStyle - { - normal = - { - background = ImageUtilities.MakeSolidColorTexture(1, 1, new Color(0.1f, 0.1f, 0.1f, 1)), - textColor = Color.white - }, - fontStyle = FontStyle.Bold, - fontSize = 16 - }; - } - } -} diff --git a/NewHorizons/Utility/DebugTools/Menu/DebugMenuPropPlacer.cs b/NewHorizons/Utility/DebugTools/Menu/DebugMenuPropPlacer.cs deleted file mode 100644 index 74d67169..00000000 --- a/NewHorizons/Utility/DebugTools/Menu/DebugMenuPropPlacer.cs +++ /dev/null @@ -1,401 +0,0 @@ -using HarmonyLib; -using NewHorizons.External.Configs; -using NewHorizons.External.Modules; -using NewHorizons.Utility.Geometry; -using NewHorizons.Utility.OWML; -using NewHorizons.Utility.OuterWilds; -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -namespace NewHorizons.Utility.DebugTools.Menu -{ - class DebugMenuPropPlacer : DebugSubmenu - { - private HashSet favoriteProps = new HashSet(); - private List propsLoadedFromConfig = new List(); - public static readonly char separatorCharacter = '☧'; // since no chars are illegal in game object names, I picked one that's extremely unlikely to be used to be a separator - private static readonly string favoritePropsPlayerPrefKey = "FavoriteProps"; - - internal DebugPropPlacer _dpp; - internal DebugRaycaster _drc; - - // misc - private GameObject mostRecentlyPlacedProp; - private Vector3 mostRecentlyPlacedPropSphericalPos; - - // menu params - private Vector2 recentPropsScrollPosition = Vector2.zero; - private bool propsCollapsed = false; - private bool propPositioningCollapsed = false; - private Vector3 propPosDelta = new Vector3(0.1f, 0.1f, 0.1f); - private Vector3 propRotDelta = new Vector3(0.1f, 0.1f, 0.1f); - private Vector3 propSphericalPosDelta = new Vector3(0.1f, 0.1f, 0.1f); - private float propRotationAboutLocalUpDelta = 0.1f; - private float propScaleDelta = 0.1f; - - internal override string SubmenuName() - { - return "Prop Placer"; - } - - internal override void OnInit(DebugMenu menu) - { - _dpp = menu.GetComponent(); - _drc = menu.GetComponent(); - } - - internal override void OnAwake(DebugMenu menu) - { - _dpp = menu.GetComponent(); - _drc = menu.GetComponent(); - LoadFavoriteProps(); - } - - internal override void OnBeginLoadMod(DebugMenu debugMenu) - { - - } - - internal override void GainActive() - { - DebugPropPlacer.active = true; - } - - internal override void LoseActive() - { - DebugPropPlacer.active = false; - } - - internal override void LoadConfigFile(DebugMenu menu, PlanetConfig config) - { - _dpp.FindAndRegisterPropsFromConfig(config, propsLoadedFromConfig); - } - - private void LoadFavoriteProps() - { - string favoritePropsPlayerPref = PlayerPrefs.GetString(favoritePropsPlayerPrefKey); - - if (favoritePropsPlayerPref == null || favoritePropsPlayerPref == "") return; - - var favoritePropPaths = favoritePropsPlayerPref.Split(separatorCharacter); - foreach (string favoriteProp in favoritePropPaths) - { - DebugPropPlacer.RecentlyPlacedProps.Add(favoriteProp); - this.favoriteProps.Add(favoriteProp); - } - } - - internal override void OnGUI(DebugMenu menu) - { - // - // DebugPropPlacer - // - GUILayout.Label("Currently placing: "); - _dpp.SetCurrentObject(GUILayout.TextArea(_dpp.currentObject)); - - GUILayout.Space(5); - GUILayout.Space(5); - - - var arrow = propsCollapsed ? " > " : " v "; - if (GUILayout.Button(arrow + "Recently placed objects", menu._tabBarStyle)) propsCollapsed = !propsCollapsed; - if (!propsCollapsed) DrawPropsList(menu); - GUILayout.Space(5); - - if (_dpp.mostRecentlyPlacedPropGO != null) - { - arrow = propPositioningCollapsed ? " > " : " v "; - if (GUILayout.Button(arrow + "Position last placed prop", menu._tabBarStyle)) propPositioningCollapsed = !propPositioningCollapsed; - if (!propPositioningCollapsed) DrawPropsAdustmentControls(menu); - } - } - - private void DrawPropsAdustmentControls(DebugMenu menu) - { - var propPath = _dpp.mostRecentlyPlacedPropPath; - var propPathElements = propPath[propPath.Length - 1] == '/' - ? propPath.Substring(0, propPath.Length - 1).Split('/') - : propPath.Split('/'); - string propName = propPathElements[propPathElements.Length - 1]; - GUILayout.Label($"Reposition {propName}: "); - - - Vector3 latestPropPosDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localPosition, propPosDelta, out propPosDelta, "x", "y", "z"); - _dpp.mostRecentlyPlacedPropGO.transform.localPosition += latestPropPosDelta; - if (latestPropPosDelta != Vector3.zero) mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero); - - //GUILayout.Space(5); - //Vector3 latestPropRotDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles, propRotDelta, out propRotDelta, "x", "y", "z"); - //_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles += latestPropRotDelta; - - GUILayout.Space(5); - GUILayout.Space(5); - - - if (mostRecentlyPlacedProp != _dpp.mostRecentlyPlacedPropGO) - { - mostRecentlyPlacedProp = _dpp.mostRecentlyPlacedPropGO; - mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero); - } - - Vector3 latestPropSphericalPosDelta = VectorInput(mostRecentlyPlacedPropSphericalPos, propSphericalPosDelta, out propSphericalPosDelta, "lat ", "lon ", "height"); - if (latestPropSphericalPosDelta != Vector3.zero) - { - SetSphericalPosition(mostRecentlyPlacedProp, mostRecentlyPlacedPropSphericalPos + latestPropSphericalPosDelta); - mostRecentlyPlacedPropSphericalPos = mostRecentlyPlacedPropSphericalPos + latestPropSphericalPosDelta; - } - - GUILayout.Space(5); - GUILayout.Space(5); - - - GUILayout.BeginHorizontal(); - GUILayout.Label("Rotate about up: ", GUILayout.Width(50)); - float deltaRot = 0; - if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaRot += propRotationAboutLocalUpDelta; - if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaRot -= propRotationAboutLocalUpDelta; - propRotationAboutLocalUpDelta = float.Parse(GUILayout.TextField(propRotationAboutLocalUpDelta + "", GUILayout.Width(100))); - - if (deltaRot != 0) - { - Transform astroObject = mostRecentlyPlacedProp.transform.parent.parent; - mostRecentlyPlacedProp.transform.RotateAround(mostRecentlyPlacedProp.transform.position, mostRecentlyPlacedProp.transform.up, deltaRot); - } - GUILayout.EndHorizontal(); - - - GUILayout.BeginHorizontal(); - GUILayout.Label("scale: ", GUILayout.Width(50)); - var scaleString = mostRecentlyPlacedProp.transform.localScale.x + ""; - var newScaleString = GUILayout.TextField(scaleString, GUILayout.Width(50)); - var parsedScaleString = mostRecentlyPlacedProp.transform.localScale.x; try { parsedScaleString = float.Parse(newScaleString); } catch { } - float deltaScale = scaleString == newScaleString ? 0 : parsedScaleString - mostRecentlyPlacedProp.transform.localScale.x; - if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaScale += propScaleDelta; - if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaScale -= propScaleDelta; - propScaleDelta = float.Parse(GUILayout.TextField(propScaleDelta + "", GUILayout.Width(100))); - - if (deltaScale != 0) - { - float newScale = mostRecentlyPlacedProp.transform.localScale.x + deltaScale; - mostRecentlyPlacedProp.transform.localScale = new Vector3(newScale, newScale, newScale); - } - GUILayout.EndHorizontal(); - } - private Vector3 DeltaSphericalPosition(GameObject prop, Vector3 deltaSpherical) - { - Transform astroObject = prop.transform.parent.parent; - Transform sector = prop.transform.parent; - Vector3 originalLocalPos = astroObject.InverseTransformPoint(prop.transform.position); // parent is the sector, this gives localPos relative to the astroobject (what the DetailBuilder asks for) - Vector3 sphericalPos = CoordinateUtilities.CartesianToSpherical(originalLocalPos); - - if (deltaSpherical == Vector3.zero) return sphericalPos; - Vector3 newSpherical = sphericalPos + deltaSpherical; - - Vector3 finalLocalPosition = CoordinateUtilities.SphericalToCartesian(newSpherical); - Vector3 finalAbsolutePosition = astroObject.TransformPoint(finalLocalPosition); - prop.transform.localPosition = prop.transform.parent.InverseTransformPoint(finalAbsolutePosition); - // prop.transform.rotation = Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * prop.transform.rotation; - - // first, rotate the object by the astroObject's rotation, that means anything afterwards is relative to this rotation (ie, we can pretend the astroObject has 0 rotation) - // then, rotate by the difference in position, basically accounting for the curvature of a sphere - // then re-apply the local rotations of the hierarchy down to the prop (apply the sector local rotation, then the prop local rotation) - - // since we're doing all rotation relative to the astro object, we start with its absolute rotation - // then we apply the rotation about the astroobject using FromTooRotation - // then we reapply the local rotations down through the hierarchy - prop.transform.rotation = astroObject.rotation * Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * sector.localRotation * prop.transform.localRotation; - - return newSpherical; - } - - // DB_EscapePodDimension_Body/Sector_EscapePodDimension/Interactables_EscapePodDimension/InnerWarp_ToAnglerNest - // DB_ExitOnlyDimension_Body/Sector_ExitOnlyDimension/Interactables_ExitOnlyDimension/InnerWarp_ToExitOnly // need to change the colors - // DB_HubDimension_Body/Sector_HubDimension/Interactables_HubDimension/InnerWarp_ToCluster // need to delete the child "Signal_Harmonica" - - private Vector3 SetSphericalPosition(GameObject prop, Vector3 newSpherical) - { - Transform astroObject = prop.transform.parent.parent; - Transform sector = prop.transform.parent; - Vector3 originalLocalPos = astroObject.InverseTransformPoint(prop.transform.position); // parent is the sector, this gives localPos relative to the astroobject (what the DetailBuilder asks for) - Vector3 sphericalPos = CoordinateUtilities.CartesianToSpherical(originalLocalPos); - - if (newSpherical == sphericalPos) return sphericalPos; - - Vector3 finalLocalPosition = CoordinateUtilities.SphericalToCartesian(newSpherical); - Vector3 finalAbsolutePosition = astroObject.TransformPoint(finalLocalPosition); - prop.transform.localPosition = prop.transform.parent.InverseTransformPoint(finalAbsolutePosition); - NHLogger.Log("new position: " + prop.transform.localPosition); - - var onlyChangingRAndRIsNegative = false; - - // first, rotate the object by the astroObject's rotation, that means anything afterwards is relative to this rotation (ie, we can pretend the astroObject has 0 rotation) - // then, rotate by the difference in position, basically accounting for the curvature of a sphere - // then re-apply the local rotations of the hierarchy down to the prop (apply the sector local rotation, then the prop local rotation) - - // since we're doing all rotation relative to the astro object, we start with its absolute rotation - // then we apply the rotation about the astroobject using FromTooRotation - // then we reapply the local rotations down through the hierarchy - Vector3 originalLocalPos_ForcedPositiveR = CoordinateUtilities.SphericalToCartesian(new Vector3(sphericalPos.x, sphericalPos.y, Mathf.Abs(sphericalPos.z))); - Vector3 finalLocalPos_ForcedPositiveR = CoordinateUtilities.SphericalToCartesian(new Vector3(newSpherical.x, newSpherical.y, Mathf.Abs(newSpherical.z))); - if (!onlyChangingRAndRIsNegative) prop.transform.rotation = astroObject.rotation * Quaternion.FromToRotation(originalLocalPos_ForcedPositiveR.normalized, finalLocalPos_ForcedPositiveR.normalized) * sector.localRotation * prop.transform.localRotation; - - return newSpherical; - } - - private Vector3 VectorInput(Vector3 input, Vector3 deltaControls, out Vector3 deltaControlsOut, string labelX, string labelY, string labelZ) - { - var dx = deltaControls.x; - var dy = deltaControls.y; - var dz = deltaControls.z; - - // x - GUILayout.BeginHorizontal(); - GUILayout.Label(labelX + ": ", GUILayout.Width(50)); - var xString = input.x + ""; - var newXString = GUILayout.TextField(xString, GUILayout.Width(50)); - var parsedXString = input.x; try { parsedXString = float.Parse(newXString); } catch { } - float deltaX = xString == newXString ? 0 : parsedXString - input.x; - if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaX += dx; - if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaX -= dx; - dx = float.Parse(GUILayout.TextField(dx + "", GUILayout.Width(100))); - GUILayout.EndHorizontal(); - - // y - GUILayout.BeginHorizontal(); - GUILayout.Label(labelY + ": ", GUILayout.Width(50)); - var yString = input.y + ""; - var newYString = GUILayout.TextField(yString, GUILayout.Width(50)); - var parsedYString = input.y; try { parsedYString = float.Parse(newYString); } catch { } - float deltaY = yString == newYString ? 0 : parsedYString - input.y; - if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaY += dy; - if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaY -= dy; - dy = float.Parse(GUILayout.TextField(dy + "", GUILayout.Width(100))); - GUILayout.EndHorizontal(); - - // z - GUILayout.BeginHorizontal(); - GUILayout.Label(labelZ + ": ", GUILayout.Width(50)); - var zString = input.z + ""; - var newZString = GUILayout.TextField(zString, GUILayout.Width(50)); - var parsedZString = input.z; try { parsedZString = float.Parse(newZString); } catch { } - float deltaZ = zString == newZString ? 0 : parsedZString - input.z; - if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaZ += dz; - if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaZ -= dz; - dz = float.Parse(GUILayout.TextField(dz + "", GUILayout.Width(100))); - GUILayout.EndHorizontal(); - - deltaControlsOut = new Vector3(dx, dy, dz); - return new Vector3(deltaX, deltaY, deltaZ); - } - - private void DrawPropsList(DebugMenu menu) - { - // List of recently placed objects - recentPropsScrollPosition = GUILayout.BeginScrollView(recentPropsScrollPosition, GUILayout.Width(menu.EditorMenuSize.x), GUILayout.Height(500)); - foreach (string propPath in DebugPropPlacer.RecentlyPlacedProps) - { - GUILayout.BeginHorizontal(); - - var propPathElements = propPath[propPath.Length - 1] == '/' - ? propPath.Substring(0, propPath.Length - 1).Split('/') - : propPath.Split('/'); - string propName = propPathElements[propPathElements.Length - 1]; - - string favoriteButtonIcon = favoriteProps.Contains(propPath) ? "★" : "☆"; - if (GUILayout.Button(favoriteButtonIcon, GUILayout.ExpandWidth(false))) - { - if (favoriteProps.Contains(propPath)) - { - favoriteProps.Remove(propPath); - } - else - { - favoriteProps.Add(propPath); - } - - string[] favoritePropsArray = favoriteProps.ToArray(); - PlayerPrefs.SetString(favoritePropsPlayerPrefKey, string.Join(separatorCharacter + "", favoritePropsArray)); - } - - if (GUILayout.Button(propName)) - { - _dpp.SetCurrentObject(propPath); - } - - GUILayout.EndHorizontal(); - } - GUILayout.EndScrollView(); - } - - internal override void PreSave(DebugMenu menu) - { - UpdateLoadedConfigsForRecentSystem(menu); - } - - private void UpdateLoadedConfigsForRecentSystem(DebugMenu menu) - { - var newDetails = _dpp.GetPropsConfigByBody(); - - var newDetailsCountsByPlanet = string.Join(", ", newDetails.Keys.Select(x => $"{x.name} ({newDetails[x].Length})")); - NHLogger.Log($"Updating config files. New Details Counts by planet: {newDetailsCountsByPlanet}"); - - var planetToConfigPath = new Dictionary(); - - // Get all configs - foreach (var filePath in menu.loadedConfigFiles.Keys) - { - NHLogger.LogVerbose($"Potentially updating copy of config at {filePath}"); - NHLogger.LogVerbose($"{menu.loadedConfigFiles[filePath].name} {AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name)?.name}"); - NHLogger.LogVerbose($"{menu.loadedConfigFiles[filePath].name}"); - - if (menu.loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) return; - if (menu.loadedConfigFiles[filePath].name == null || AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name) == null) - { - NHLogger.LogWarning("Failed to update copy of config at " + filePath); - continue; - } - - var astroObject = AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name); - planetToConfigPath[astroObject] = filePath; - - if (!newDetails.ContainsKey(astroObject)) continue; - - if (menu.loadedConfigFiles[filePath].Props == null) menu.loadedConfigFiles[filePath].Props = new PropModule(); - menu.loadedConfigFiles[filePath].Props.details = newDetails[astroObject]; - - NHLogger.Log($"Successfully updated copy of config file for {astroObject.name}"); - } - - // find all new planets that do not yet have config paths - var planetsThatDoNotHaveConfigFiles = newDetails.Keys.Where(x => !planetToConfigPath.ContainsKey(x)).ToList(); - foreach (var astroObject in planetsThatDoNotHaveConfigFiles) - { - NHLogger.Log("Fabricating new config file for " + astroObject.name); - - var filepath = $"planets/{Main.Instance.CurrentStarSystem}/{astroObject.name}.json"; - - var config = new PlanetConfig(); - config.starSystem = Main.Instance.CurrentStarSystem; - config.name = astroObject._name == AstroObject.Name.CustomString ? astroObject.GetCustomName() : astroObject._name.ToString(); - config.Props = new PropModule(); - config.Props.details = newDetails[astroObject]; - - menu.loadedConfigFiles[filepath] = config; - } - } - - internal override void PrintNewConfigSection(DebugMenu menu) - { - foreach (var body in _dpp.GetPropsConfigByBody()) - { - var json = body.Value.Join( - detail => "\t" + JsonConvert.SerializeObject(detail, DebugMenu.jsonSettings), - ",\n" - ); - NHLogger.Log($"{body.Key.name} ({body.Value.Length})\n[\n{json}\n]"); - } - } - } -} diff --git a/NewHorizons/Utility/DebugTools/Menu/DebugMenuShipLogs.cs b/NewHorizons/Utility/DebugTools/Menu/DebugMenuShipLogs.cs deleted file mode 100644 index 77053a88..00000000 --- a/NewHorizons/Utility/DebugTools/Menu/DebugMenuShipLogs.cs +++ /dev/null @@ -1,81 +0,0 @@ -using HarmonyLib; -using NewHorizons.External.Configs; -using NewHorizons.External.Modules; -using NewHorizons.External.SerializableData; -using NewHorizons.Utility.OWML; -using Newtonsoft.Json; -using System.Linq; -using UnityEngine; - -namespace NewHorizons.Utility.DebugTools.Menu -{ - class DebugMenuShipLogs : DebugSubmenu - { - string entryPositionsText = ""; - - internal override void GainActive() - { - - } - internal override void LoseActive() - { - - } - - internal override void LoadConfigFile(DebugMenu menu, PlanetConfig config) - { - - } - - - internal override void OnAwake(DebugMenu menu) - { - } - - internal override void OnBeginLoadMod(DebugMenu debugMenu) - { - } - - private string GetEntryPositionsJSON() => - Resources - .FindObjectsOfTypeAll() - .Join( - entry => JsonConvert.SerializeObject(new ShipLogModule.EntryPositionInfo - { - id = entry.name, - position = new MVector2(entry.transform.localPosition.x, entry.transform.localPosition.y) - }, DebugMenu.jsonSettings), - ",\n" - ); - - internal override void OnGUI(DebugMenu menu) - { - if (GUILayout.Button("Print Ship Log Positions")) - { - entryPositionsText = GetEntryPositionsJSON(); - } - - GUILayout.TextArea(entryPositionsText); - } - - internal override void OnInit(DebugMenu menu) - { - - } - - internal override void PreSave(DebugMenu menu) - { - - } - - internal override string SubmenuName() - { - return "Ship Log"; - } - - internal override void PrintNewConfigSection(DebugMenu menu) - { - NHLogger.Log(GetEntryPositionsJSON()); - } - } -} diff --git a/NewHorizons/Utility/DebugTools/Menu/DebugSubmenu.cs b/NewHorizons/Utility/DebugTools/Menu/DebugSubmenu.cs deleted file mode 100644 index 0bcc9099..00000000 --- a/NewHorizons/Utility/DebugTools/Menu/DebugSubmenu.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NewHorizons.External.Configs; - -namespace NewHorizons.Utility.DebugTools.Menu -{ - abstract class DebugSubmenu - { - internal abstract void OnAwake(DebugMenu menu); - internal abstract void OnGUI(DebugMenu menu); - internal abstract void PrintNewConfigSection(DebugMenu menu); - internal abstract void PreSave(DebugMenu menu); - internal abstract void OnInit(DebugMenu menu); - internal abstract void LoadConfigFile(DebugMenu menu, PlanetConfig config); - internal abstract void OnBeginLoadMod(DebugMenu debugMenu); - - internal abstract string SubmenuName(); - - internal abstract void GainActive(); - internal abstract void LoseActive(); - - } -} diff --git a/NewHorizons/Utility/Files/AssetBundleUtilities.cs b/NewHorizons/Utility/Files/AssetBundleUtilities.cs index 104d7393..87170770 100644 --- a/NewHorizons/Utility/Files/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/Files/AssetBundleUtilities.cs @@ -1,5 +1,6 @@ using NewHorizons.Utility.OWML; using OWML.Common; +using OWML.ModHelper; using System; using System.Collections.Generic; using System.IO; @@ -14,6 +15,25 @@ namespace NewHorizons.Utility.Files private static readonly List _loadingBundles = new(); + public static AssetBundle NHAssetBundle { get; private set; } + public static AssetBundle NHPrivateAssetBundle { get; private set; } + + static AssetBundleUtilities() + { + NHAssetBundle = LoadRequiredBundle("Assets/bundles/newhorizons_public"); + NHPrivateAssetBundle = LoadRequiredBundle("Assets/bundles/newhorizons_private"); + } + + private static AssetBundle LoadRequiredBundle(string path) + { + var bundle = Main.Instance.ModHelper.Assets.LoadBundle(path); + if (bundle == null) + { + NHLogger.LogError($"Couldn't find [{Path.GetFileName(path)}]: Some features of NH will not work."); + } + return bundle; + } + public static void ClearCache() { foreach (var pair in AssetBundles) @@ -155,6 +175,42 @@ namespace NewHorizons.Utility.Files } } } + + // for dream world underwater fog + foreach (var ruleset in prefab.GetComponentsInChildren(true)) + { + var material = ruleset._material; + if (material == null) continue; + + var replacementShader = Shader.Find(material.shader.name); + if (replacementShader == null) continue; + + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else + { + material.shader = replacementShader; + } + } + // for raft splash + foreach (var fluidDetector in prefab.GetComponentsInChildren(true)) + { + if (fluidDetector._splashEffects == null) continue; + foreach (var splashEffect in fluidDetector._splashEffects) + { + if (splashEffect == null) continue; + if (splashEffect.splashPrefab == null) continue; + AssetBundleUtilities.ReplaceShaders(splashEffect.splashPrefab); + } + } } } } \ No newline at end of file diff --git a/NewHorizons/Utility/Files/AudioUtilities.cs b/NewHorizons/Utility/Files/AudioUtilities.cs index ca01b44a..fe09d46e 100644 --- a/NewHorizons/Utility/Files/AudioUtilities.cs +++ b/NewHorizons/Utility/Files/AudioUtilities.cs @@ -121,7 +121,9 @@ namespace NewHorizons.Utility.Files } else { - return dh.audioClip; + var audioClip = dh.audioClip; + audioClip.name = Path.GetFileNameWithoutExtension(path); + return audioClip; } } } @@ -140,7 +142,9 @@ namespace NewHorizons.Utility.Files } else { - return DownloadHandlerAudioClip.GetContent(www); + var audioClip = DownloadHandlerAudioClip.GetContent(www); + audioClip.name = Path.GetFileNameWithoutExtension(path); + return audioClip; } } } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index ea0ceaf6..09ff831f 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -1,14 +1,10 @@ -using HarmonyLib; +using NewHorizons.Builder.Props; using NewHorizons.Utility.OWML; using OWML.Common; using System; -using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; using UnityEngine; -using UnityEngine.Events; -using UnityEngine.Networking; namespace NewHorizons.Utility.Files { @@ -17,9 +13,13 @@ namespace NewHorizons.Utility.Files // key is path + applied effects private static readonly Dictionary _textureCache = new(); public static bool CheckCachedTexture(string key, out Texture existingTexture) => _textureCache.TryGetValue(key, out existingTexture); - public static void TrackCachedTexture(string key, Texture texture) => _textureCache.Add(key, texture); + public static void TrackCachedTexture(string key, Texture texture) => _textureCache.Add(key, texture); // dont reinsert cuz that causes memory leak! - private static string GetKey(string path) => path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length); + public static string GetKey(IModBehaviour mod, string filename) + => GetKey(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename)); + + public static string GetKey(string path) => + path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length + 1).Replace('\\', '/'); public static bool IsTextureLoaded(IModBehaviour mod, string filename) { @@ -28,8 +28,12 @@ namespace NewHorizons.Utility.Files return _textureCache.ContainsKey(key); } + #region obsolete // needed for backwards compat :P + // idk what mod used it + [Obsolete] public static Texture2D GetTexture(IModBehaviour mod, string filename, bool useMipmaps, bool wrap) => GetTexture(mod, filename, useMipmaps, wrap, false); + #endregion // bug: cache only considers file path, not wrap/mips/linear. oh well public static Texture2D GetTexture(IModBehaviour mod, string filename, bool useMipmaps = true, bool wrap = false, bool linear = false) { @@ -43,7 +47,7 @@ namespace NewHorizons.Utility.Files var key = GetKey(path); if (_textureCache.TryGetValue(key, out var existingTexture)) { - NHLogger.LogVerbose($"Already loaded image at path: {path}"); + //NHLogger.LogVerbose($"Already loaded image at path: {path}"); return (Texture2D)existingTexture; } @@ -67,10 +71,28 @@ namespace NewHorizons.Utility.Files } } + /// + /// Not sure why the other method takes in the texture as well + /// + /// + public static void DeleteTexture(string key) + { + if (_textureCache.ContainsKey(key)) + { + UnityEngine.Object.Destroy(_textureCache[key]); + _textureCache.Remove(key); + } + } + public static void DeleteTexture(IModBehaviour mod, string filename, Texture2D texture) { var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); var key = GetKey(path); + DeleteTexture(key, texture); + } + + public static void DeleteTexture(string key, Texture2D texture) + { if (_textureCache.ContainsKey(key)) { if (_textureCache[key] == texture) @@ -96,12 +118,22 @@ namespace NewHorizons.Utility.Files } /// - /// used specifically for projected slides. - /// also adds a border (to prevent weird visual bug) and makes the texture linear (otherwise the projected image is too bright). + /// used to fix projection slides + /// adds a border (to prevent weird visual bug) and does some magic with gamma correction and inverting /// - public static Texture2D Invert(Texture2D texture) + public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath) { var key = $"{texture.name} > invert"; + var cachedPath = ""; + + // If we're going to end up caching the texture we must make sure it will end up using the same key + // Not sure why we check if the originalPath is null but it did that before so + if (!string.IsNullOrEmpty(originalPath)) + { + cachedPath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); + key = GetKey(cachedPath); + } + if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; var pixels = texture.GetPixels(); @@ -120,13 +152,20 @@ namespace NewHorizons.Utility.Files } else { - pixels[i].r = 1f - pixels[i].r; - pixels[i].g = 1f - pixels[i].g; - pixels[i].b = 1f - pixels[i].b; + // convert gamma to linear, then invert + // outer wilds will invert, then convert linear to gamma (reversing the process) + pixels[i].r = 1f - Mathf.GammaToLinearSpace(pixels[i].r); + pixels[i].g = 1f - Mathf.GammaToLinearSpace(pixels[i].g); + pixels[i].b = 1f - Mathf.GammaToLinearSpace(pixels[i].b); } } - var newTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount != 1, true); + // making this linear makes vanilla reel atlas match vanilla reels. + // however, it also makes it darker than the source image. + // for custom slides this is unintuitive. + // people will be like "wtf why is it darker than my image?" + // see https://github.com/Outer-Wilds-New-Horizons/new-horizons/pull/986#issuecomment-2449223761 for comparisons + var newTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount != 1); newTexture.name = key; newTexture.SetPixels(pixels); newTexture.Apply(); @@ -135,18 +174,26 @@ namespace NewHorizons.Utility.Files _textureCache.Add(key, newTexture); + // Since doing this is expensive we cache the results to the disk + // Preloading cached values is done in ProjectionBuilder + if (!string.IsNullOrEmpty(cachedPath)) + { + NHLogger.LogVerbose($"Caching inverted image to {cachedPath}"); + Directory.CreateDirectory(Path.GetDirectoryName(cachedPath)); + File.WriteAllBytes(cachedPath, newTexture.EncodeToPNG()); + } + return newTexture; } - public static Texture2D MakeReelTexture(Texture2D[] textures) + public static Texture2D MakeReelTexture(IModBehaviour mod, Texture2D[] textures, string uniqueSlideReelID) { - var key = $"SlideReelAtlas of {textures.Join(x => x.name)}"; - if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; + if (_textureCache.TryGetValue(uniqueSlideReelID, out var existingTexture)) return (Texture2D)existingTexture; var size = 256; var texture = new Texture2D(size * 4, size * 4, TextureFormat.ARGB32, false); - texture.name = key; + texture.name = uniqueSlideReelID; var fillPixels = new Color[size * size * 4 * 4]; for (int xIndex = 0; xIndex < 4; xIndex++) @@ -160,9 +207,9 @@ namespace NewHorizons.Utility.Files { for (int j = 0; j < size; j++) { - var colour = Color.black; + var colour = Color.clear; - if (srcTexture) + if (srcTexture != null) { var srcX = i * srcTexture.width / (float)size; var srcY = j * srcTexture.height / (float)size; @@ -188,7 +235,14 @@ namespace NewHorizons.Utility.Files texture.SetPixels(fillPixels); texture.Apply(); - _textureCache.Add(key, texture); + _textureCache.Add(uniqueSlideReelID, texture); + + // Since doing this is expensive we cache the results to the disk + // Preloading cached values is done in ProjectionBuilder + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.AtlasSlideReelCacheFolder, $"{uniqueSlideReelID}.png"); + NHLogger.LogVerbose($"Caching atlas image to {path}"); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllBytes(path, texture.EncodeToPNG()); return texture; } @@ -408,6 +462,9 @@ namespace NewHorizons.Utility.Files } public static Texture2D MakeSolidColorTexture(int width, int height, Color color) { + var key = $"{color} {width} {height}"; + if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; + var pixels = new Color[width * height]; for (int i = 0; i < pixels.Length; i++) @@ -418,6 +475,9 @@ namespace NewHorizons.Utility.Files var newTexture = new Texture2D(width, height); newTexture.SetPixels(pixels); newTexture.Apply(); + + _textureCache.Add(key, newTexture); + return newTexture; } @@ -429,96 +489,5 @@ namespace NewHorizons.Utility.Files sprite.name = texture.name; return sprite; } - - // Modified from https://stackoverflow.com/a/69141085/9643841 - public class AsyncImageLoader : MonoBehaviour - { - public List<(int index, string path)> PathsToLoad { get; private set; } = new(); - - public class ImageLoadedEvent : UnityEvent { } - public ImageLoadedEvent imageLoadedEvent = new(); - - private readonly object _lockObj = new(); - - public bool FinishedLoading { get; private set; } - private int _loadedCount = 0; - - // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, - // and make use of that for at least for projector stuff (require player to be in the same sector as the slides - // for them to start loading, and unload when the player leaves) - - void Start() - { - imageLoadedEvent.AddListener(OnImageLoaded); - foreach (var (index, path) in PathsToLoad) - { - StartCoroutine(DownloadTexture(path, index)); - } - } - - private void OnImageLoaded(Texture texture, int index) - { - lock (_lockObj) - { - _loadedCount++; - - if (_loadedCount >= PathsToLoad.Count) - { - NHLogger.LogVerbose($"Finished loading all textures for {gameObject.name} (one was {PathsToLoad.FirstOrDefault()}"); - FinishedLoading = true; - } - } - } - - IEnumerator DownloadTexture(string url, int index) - { - var key = GetKey(url); - lock (_textureCache) - { - if (_textureCache.TryGetValue(key, out var existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - imageLoadedEvent?.Invoke((Texture2D)existingTexture, index); - yield break; - } - } - - using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); - - yield return uwr.SendWebRequest(); - - var hasError = uwr.error != null && uwr.error != ""; - - if (hasError) - { - NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}"); - } - else - { - var texture = new Texture2D(2, 2, TextureFormat.RGBA32, false); - texture.name = key; - texture.wrapMode = TextureWrapMode.Clamp; - - var handler = (DownloadHandlerTexture)uwr.downloadHandler; - texture.LoadImage(handler.data); - - lock (_textureCache) - { - if (_textureCache.TryGetValue(key, out var existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - Destroy(texture); - texture = (Texture2D)existingTexture; - } - else - { - _textureCache.Add(key, texture); - } - - imageLoadedEvent?.Invoke(texture, index); - } - } - } - } } } diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs new file mode 100644 index 00000000..e088e9d9 --- /dev/null +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -0,0 +1,218 @@ +using NewHorizons.Builder.Props; +using NewHorizons.Utility.OWML; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Networking; +using UnityEngine.SceneManagement; + +namespace NewHorizons.Utility.Files; + +/// +/// Modified from https://stackoverflow.com/a/69141085/9643841 +/// +public class SlideReelAsyncImageLoader +{ + public List<(int index, string path)> PathsToLoad { get; private set; } = new(); + + private Dictionary _loadedTextures = new(); + + public class ImageLoadedEvent : UnityEvent { } + public ImageLoadedEvent imageLoadedEvent = new(); + + public bool FinishedLoading { get; private set; } + private int _loadedCount = 0; + + /// + /// If we are loading images where: + /// 1) The slide reel cache does not exist + /// 2) The loader would only use the images to make the cache + /// Then we want to delete them immediately after loading, in order to save memory + /// + public bool deleteTexturesWhenDone = false; + + // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, + // and make use of that for at least for projector stuff (require player to be in the same sector as the slides + // for them to start loading, and unload when the player leaves) + // also remember this for ship logs!!! lol + + private bool _started; + private bool _clamp; + + /// + /// start loading the images a frame later + /// + /// sets wrapMode + /// load all slides one at a time vs at the same time + public void Start(bool clamp, bool sequential) + { + if (_started) return; + + _clamp = clamp; + + _started = true; + + if (SingletonSlideReelAsyncImageLoader.Instance == null) + { + Main.Instance.gameObject.AddComponent(); + } + + NHLogger.LogVerbose("Loading new slide reel"); + imageLoadedEvent.AddListener(OnImageLoaded); + SingletonSlideReelAsyncImageLoader.Instance.Load(this, sequential); + } + + private void OnImageLoaded(Texture texture, int index, string originalPath) + { + _loadedCount++; + + var key = ImageUtilities.GetKey(originalPath); + _loadedTextures[key] = texture as Texture2D; + + if (_loadedCount >= PathsToLoad.Count) + { + NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}"); + FinishedLoading = true; + + if (deleteTexturesWhenDone) + { + DeleteLoadedImages(); + } + } + } + + private void DeleteLoadedImages() + { + foreach (var (key, texture) in _loadedTextures) + { + ImageUtilities.DeleteTexture(key, texture); + } + } + + private IEnumerator DownloadTextures() + { + foreach (var (index, path) in PathsToLoad) + { + NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}"); + + yield return DownloadTexture(path, index); + } + } + + private IEnumerator DownloadTexture(string url, int index) + { + var key = ImageUtilities.GetKey(url); + if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) + { + NHLogger.LogVerbose($"Already loaded image {index}:{url} with key {key}"); + imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url); + yield break; + } + + using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); + + yield return uwr.SendWebRequest(); + + var hasError = uwr.error != null && uwr.error != ""; + + if (hasError) + { + NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}"); + if (url.Contains(ProjectionBuilder.CurrentSlideReelFolder)) + { + NHLogger.LogError("Missing image in SlideReelCache: If you are a dev, try deleting the folder so that New Horizons can regenerate the cache. If you are a player: do that and then complain to the mod dev."); + } + } + else + { + var texture = DownloadHandlerTexture.GetContent(uwr); + texture.name = key; + if (_clamp) + { + texture.wrapMode = TextureWrapMode.Clamp; + } + + if (ImageUtilities.CheckCachedTexture(key, out existingTexture)) + { + // the image could be loaded by something else by the time we're done doing async stuff + NHLogger.LogVerbose($"Already loaded image {index}:{url}"); + GameObject.Destroy(texture); + texture = (Texture2D)existingTexture; + } + else + { + ImageUtilities.TrackCachedTexture(key, texture); + } + + var time = DateTime.Now; + imageLoadedEvent?.Invoke(texture, index, url); + + NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); + } + } + + private class SingletonSlideReelAsyncImageLoader : MonoBehaviour + { + public static SingletonSlideReelAsyncImageLoader Instance { get; private set; } + + private Queue _loaders = new(); + + private bool _isLoading; + + public void Awake() + { + Instance = this; + SceneManager.sceneUnloaded += OnSceneUnloaded; + } + + private void OnSceneUnloaded(Scene _) + { + StopAllCoroutines(); + _loaders.Clear(); + _isLoading = false; + } + + public void Load(SlideReelAsyncImageLoader loader, bool sequential) + { + // Delay at least one frame to let things subscribe to the event before it fires + Delay.FireOnNextUpdate(() => + { + if (sequential && Main.SequentialPreCaching) + { + // Sequential + _loaders.Enqueue(loader); + if (!_isLoading) + { + StartCoroutine(LoadAllSequential()); + } + } + else + { + foreach (var (index, path) in loader.PathsToLoad) + { + NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}"); + + StartCoroutine(loader.DownloadTexture(path, index)); + } + } + }); + } + + private IEnumerator LoadAllSequential() + { + NHLogger.Log("Loading slide reels"); + _isLoading = true; + while (_loaders.Count > 0) + { + var loader = _loaders.Dequeue(); + yield return loader.DownloadTextures(); + NHLogger.Log($"Finished a slide reel, {_loaders.Count} left"); + } + _isLoading = false; + NHLogger.Log("Done loading slide reels"); + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/Geometry/BoundsUtilities.cs b/NewHorizons/Utility/Geometry/BoundsUtilities.cs new file mode 100644 index 00000000..9f60ce0d --- /dev/null +++ b/NewHorizons/Utility/Geometry/BoundsUtilities.cs @@ -0,0 +1,77 @@ +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Utility.Geometry; + +public static class BoundsUtilities +{ + private struct BoxShapeReciever + { + public MeshFilter meshFilter; + public SkinnedMeshRenderer skinnedMeshRenderer; + public GameObject gameObject; + } + + public static void AddBoundsVisibility(GameObject gameObject) + { + var meshFilters = gameObject.GetComponentsInChildren(); + var skinnedMeshRenderers = gameObject.GetComponentsInChildren(); + + var boxShapeRecievers = meshFilters + .Select(meshFilter => new BoxShapeReciever() { meshFilter = meshFilter, gameObject = meshFilter.gameObject }) + .Concat( + skinnedMeshRenderers.Select(skinnedMeshRenderer => new BoxShapeReciever() { skinnedMeshRenderer = skinnedMeshRenderer, gameObject = skinnedMeshRenderer.gameObject }) + ) + .ToList(); + + foreach (var boxshapeReciever in boxShapeRecievers) + { + var box = boxshapeReciever.gameObject.AddComponent(); + boxshapeReciever.gameObject.AddComponent(); + if (Main.Debug) + { + boxshapeReciever.gameObject.AddComponent(); + } + + var fixer = boxshapeReciever.gameObject.AddComponent(); + fixer.shape = box; + fixer.meshFilter = boxshapeReciever.meshFilter; + fixer.skinnedMeshRenderer = boxshapeReciever.skinnedMeshRenderer; + } + } + + // BUG: ignores skinned guys. this coincidentally makes it work without BoxShapeFixer + public static Bounds GetBoundsOfSelfAndChildMeshes(GameObject gameObject) + { + var meshFilters = gameObject.GetComponentsInChildren(); + var corners = meshFilters.SelectMany(meshFilter => GetMeshCorners(meshFilter, gameObject)).ToList(); + + var bounds = new Bounds(corners[0], Vector3.zero); + corners.ForEach(bounds.Encapsulate); + + return bounds; + } + + public static Vector3[] GetMeshCorners(MeshFilter meshFilter, GameObject relativeTo = null) + { + var bounds = meshFilter.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(meshFilter.transform.TransformPoint).ToArray(); + + if (relativeTo == null) return globalCorners; + + return globalCorners.Select(relativeTo.transform.InverseTransformPoint).ToArray(); + } +} diff --git a/NewHorizons/Utility/Geometry/BoxShapeFinder.cs b/NewHorizons/Utility/Geometry/BoxShapeFinder.cs new file mode 100644 index 00000000..1c0173a8 --- /dev/null +++ b/NewHorizons/Utility/Geometry/BoxShapeFinder.cs @@ -0,0 +1,39 @@ +using NewHorizons.Utility.OWML; +using UnityEngine; + +namespace NewHorizons.Utility.Geometry; + +/// +/// 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 +/// +public class BoxShapeFixer : MonoBehaviour +{ + public BoxShape shape; + public MeshFilter meshFilter; + public SkinnedMeshRenderer skinnedMeshRenderer; + + public 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); + } +} diff --git a/NewHorizons/Utility/Geometry/BoxShapeVisualizer.cs b/NewHorizons/Utility/Geometry/BoxShapeVisualizer.cs index 77dbf6cf..039e7bdc 100644 --- a/NewHorizons/Utility/Geometry/BoxShapeVisualizer.cs +++ b/NewHorizons/Utility/Geometry/BoxShapeVisualizer.cs @@ -4,16 +4,19 @@ namespace NewHorizons.Utility.Geometry { public class BoxShapeVisualizer : MonoBehaviour { - BoxShape box; + private BoxShape _box; - void Awake() + public void Awake() { - box = GetComponent(); + _box = GetComponent(); } - void OnRenderObject() + public void OnRenderObject() { - Popcron.Gizmos.Cube(transform.TransformPoint(box.center), transform.rotation, Vector3.Scale(box.size, transform.lossyScale)); + if (Main.Debug && Main.VisualizeQuantumObjects) + { + Popcron.Gizmos.Cube(transform.TransformPoint(_box.center), transform.rotation, Vector3.Scale(_box.size, transform.lossyScale)); + } } } } diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index 9ded956b..9f4da941 100644 --- a/NewHorizons/Utility/NewHorizonExtensions.cs +++ b/NewHorizons/Utility/NewHorizonExtensions.cs @@ -1,3 +1,4 @@ +using HarmonyLib; using NewHorizons.External.Configs; using NewHorizons.External.Modules.VariableSize; using NewHorizons.External.SerializableData; @@ -7,6 +8,7 @@ using Newtonsoft.Json; using OWML.Utils; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -69,20 +71,94 @@ namespace NewHorizons.Utility return 0; } + public static string ToLanguageName(this TextTranslation.Language language) + { + switch (language) + { + case TextTranslation.Language.UNKNOWN: + case TextTranslation.Language.TOTAL: + case TextTranslation.Language.ENGLISH: + return "English"; + case TextTranslation.Language.SPANISH_LA: + return "Spanish"; + case TextTranslation.Language.GERMAN: + return "German"; + case TextTranslation.Language.FRENCH: + return "French"; + case TextTranslation.Language.ITALIAN: + return "Italian"; + case TextTranslation.Language.POLISH: + return "Polish"; + case TextTranslation.Language.PORTUGUESE_BR: + return "Portuguese (Brazil)"; + case TextTranslation.Language.JAPANESE: + return "Japanese"; + case TextTranslation.Language.RUSSIAN: + return "Russian"; + case TextTranslation.Language.CHINESE_SIMPLE: + return "Chinese (Simplified)"; + case TextTranslation.Language.KOREAN: + return "Korean"; + case TextTranslation.Language.TURKISH: + return "Turkish"; + default: + return language.ToString().Replace("_", " ").ToTitleCase(); + } + } + + public static CultureInfo ToCultureInfo(this TextTranslation.Language language) + { + return CultureInfo.GetCultures(CultureTypes.AllCultures).FirstOrDefault(culture => + { + var name = language.ToLanguageName(); + return culture.EnglishName == name || culture.NativeName.ToTitleCase() == name; + }) ?? CultureInfo.CurrentCulture; + } + + public static string ToUpperFixed(this string str) + { + return str.ToUpper(TextTranslation.Get().m_language); + } + + public static string ToLowerFixed(this string str) + { + return str.ToLower(TextTranslation.Get().m_language); + } + + public static string ToUpper(this string str, TextTranslation.Language language) + { + return str.ToUpper(language.ToCultureInfo()); + } + + public static string ToLower(this string str, TextTranslation.Language language) + { + return str.ToLower(language.ToCultureInfo()); + } + public static string ToCamelCase(this string str) { StringBuilder strBuilder = new StringBuilder(str); - strBuilder[0] = strBuilder[0].ToString().ToLower().ToCharArray()[0]; + strBuilder[0] = strBuilder[0].ToString().ToLowerInvariant().ToCharArray()[0]; return strBuilder.ToString(); } public static string ToTitleCase(this string str) { - StringBuilder strBuilder = new StringBuilder(str); - strBuilder[0] = strBuilder[0].ToString().ToUpper().ToCharArray()[0]; + StringBuilder strBuilder = new StringBuilder(str.ToLowerInvariant()); + strBuilder[0] = strBuilder[0].ToString().ToUpperInvariant().ToCharArray()[0]; return strBuilder.ToString(); } + public static string ToLowercaseNamingConvention(this string str, string separation = " ") + { + var r = new Regex(@" + (?<=[A-Z])(?=[A-Z][a-z]) | + (?<=[^A-Z])(?=[A-Z]) | + (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); + + return r.Replace(str, separation).ToLower(); + } + public static void CopyPropertiesFrom(this object destination, object source) { // If any this null throw an exception @@ -307,40 +383,6 @@ namespace NewHorizons.Utility return curve; } - // From QSB - public static void RaiseEvent(this T instance, string eventName, params object[] args) - { - const BindingFlags flags = BindingFlags.Instance - | BindingFlags.Static - | BindingFlags.Public - | BindingFlags.NonPublic - | BindingFlags.DeclaredOnly; - if (typeof(T) - .GetField(eventName, flags)? - .GetValue(instance) is not MulticastDelegate multiDelegate) - { - return; - } - - multiDelegate.SafeInvoke(args); - } - - // From QSB - public static void SafeInvoke(this MulticastDelegate multicast, params object[] args) - { - foreach (var del in multicast.GetInvocationList()) - { - try - { - del.DynamicInvoke(args); - } - catch (TargetInvocationException ex) - { - NHLogger.LogError($"Error invoking delegate! {ex.InnerException}"); - } - } - } - public static List GetChildNodes(this XmlNode parentNode, string tagName) { return parentNode.ChildNodes.Cast().Where(node => node.LocalName == tagName).ToList(); @@ -348,7 +390,7 @@ namespace NewHorizons.Utility public static XmlNode GetChildNode(this XmlNode parentNode, string tagName) { - return parentNode.ChildNodes.Cast().First(node => node.LocalName == tagName); + return parentNode.ChildNodes.Cast().FirstOrDefault(node => node.LocalName == tagName); } public static string TruncateWhitespaceAndToLower(this string text) @@ -356,5 +398,66 @@ namespace NewHorizons.Utility // return Regex.Replace(text.Trim(), @"[^\S\r\n]+", "GUH"); return Regex.Replace(text.Trim(), @"\s+", " ").ToLowerInvariant(); } + + public static void Stabilize(this SingularityController singularity) + { + singularity._state = SingularityController.State.Stable; + singularity._timer = 0f; + singularity._baseRadius = singularity._targetRadius; + singularity._currentRadius = singularity._targetRadius; + singularity._renderer.SetActivation(active: true); + singularity._renderer.SetMaterialProperty(singularity._propID_Radius, singularity._targetRadius); + if (singularity._owAmbientSource != null) singularity._owAmbientSource.FadeIn(0.5f); + singularity.enabled = true; + } + + public static void OpenEyesImmediate(this PlayerCameraEffectController playerCameraEffectController) + { + playerCameraEffectController._lastOpenness = 1; + playerCameraEffectController._wakeCurve = playerCameraEffectController._fastWakeCurve; + playerCameraEffectController._isOpeningEyes = false; + playerCameraEffectController._isClosingEyes = false; + playerCameraEffectController._eyeAnimDuration = 0; + playerCameraEffectController._eyeAnimStartTime = Time.time; + playerCameraEffectController._owCamera.postProcessingSettings.eyeMask.openness = 1; + playerCameraEffectController._owCamera.postProcessingSettings.bloom.threshold = playerCameraEffectController._owCamera.postProcessingSettings.bloomDefault.threshold; + playerCameraEffectController._owCamera.postProcessingSettings.eyeMaskEnabled = false; + } + + public static void CloseEyesImmediate(this PlayerCameraEffectController playerCameraEffectController) + { + playerCameraEffectController._lastOpenness = 0f; + playerCameraEffectController._wakeCurve = playerCameraEffectController._fastWakeCurve; + playerCameraEffectController._isOpeningEyes = false; + playerCameraEffectController._isClosingEyes = false; + playerCameraEffectController._eyeAnimDuration = 0; + playerCameraEffectController._eyeAnimStartTime = Time.time; + playerCameraEffectController._owCamera.postProcessingSettings.eyeMask.openness = 0f; + playerCameraEffectController._owCamera.postProcessingSettings.bloom.threshold = 0f; + playerCameraEffectController._owCamera.postProcessingSettings.eyeMaskEnabled = true; + } + + public static float GetSecondsBeforeSupernovaPlayTime(this GlobalMusicController globalMusicController) + { + var clip = globalMusicController._endTimesSource.audioLibraryClip; + if (clip == AudioType.EndOfTime || clip == AudioType.EndOfTime_Dream) + return GlobalMusicController.secondsBeforeSupernovaPlayTime; + return globalMusicController._endTimesSource.clip.length; + } + + public static CodeMatcher LogInstructions(this CodeMatcher matcher, string prefix) + { + matcher.InstructionEnumeration().LogInstructions(prefix); + return matcher; + } + + public static IEnumerable LogInstructions(this IEnumerable instructions, string prefix) + { + var message = prefix; + foreach (var instruction in instructions) + message += $"\n{instruction}"; + Debug.LogError(message); + return instructions; + } } } diff --git a/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs b/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs index b1aba93e..9e3a8a01 100644 --- a/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs +++ b/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Globalization; using System.Linq; using NewHorizons.Components.Orbital; using NewHorizons.Handlers; @@ -33,7 +34,7 @@ namespace NewHorizons.Utility.OuterWilds } // Else check stock names - var stringID = name.ToUpper().Replace(" ", "_").Replace("'", ""); + var stringID = name.ToUpperInvariant().Replace(" ", "_").Replace("'", ""); if (stringID.Equals("ATTLEROCK")) stringID = "TIMBER_MOON"; if (stringID.Equals("HOLLOWS_LANTERN")) stringID = "VOLCANIC_MOON"; if (stringID.Equals("ASH_TWIN")) stringID = "TOWER_TWIN"; @@ -42,7 +43,7 @@ namespace NewHorizons.Utility.OuterWilds if (stringID.Equals("EYE") || stringID.Equals("EYEOFTHEUNIVERSE")) stringID = "EYE_OF_THE_UNIVERSE"; string key; - if (stringID.ToUpper().Replace("_", "").Equals("MAPSATELLITE")) + if (stringID.ToUpperInvariant().Replace("_", "").Equals("MAPSATELLITE")) { key = AstroObject.Name.MapSatellite.ToString(); } @@ -163,6 +164,9 @@ namespace NewHorizons.Utility.OuterWilds .Select(x => x.gameObject) .Where(x => x.name == "SS_Debris_Body")); break; + case AstroObject.Name.Eye: + otherChildren.Add(SearchUtilities.Find("Vessel_Body")); + break; // Just in case GetChildren runs before sun station's name is changed case AstroObject.Name.CustomString: if (primary._customName.Equals("Sun Station")) diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 9ed77826..36e656e6 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -9,9 +9,24 @@ namespace NewHorizons.Utility { public static class SearchUtilities { + private static readonly Dictionary DontDestroyOnLoadCachedGameObjects = new Dictionary(); private static readonly Dictionary CachedGameObjects = new Dictionary(); private static readonly Dictionary CachedRootGameObjects = new Dictionary(); + public static void AddToDontDestroyOnLoadCache(string path, GameObject go) + { + DontDestroyOnLoadCachedGameObjects[path] = go.InstantiateInactive().DontDestroyOnLoad(); + } + + public static void ClearDontDestroyOnLoadCache() + { + foreach (var go in DontDestroyOnLoadCachedGameObjects.Values) + { + GameObject.Destroy(go); + } + DontDestroyOnLoadCachedGameObjects.Clear(); + } + public static void ClearCache() { NHLogger.LogVerbose("Clearing search cache"); @@ -96,6 +111,8 @@ namespace NewHorizons.Utility /// public static GameObject Find(string path, bool warn = true) { + if (DontDestroyOnLoadCachedGameObjects.TryGetValue(path, out var gameObject)) return gameObject; + if (CachedGameObjects.TryGetValue(path, out var go)) return go; // 1: normal find diff --git a/NewHorizons/default-config.json b/NewHorizons/default-config.json index 146eb9fe..4f3239c2 100644 --- a/NewHorizons/default-config.json +++ b/NewHorizons/default-config.json @@ -1,9 +1,44 @@ { - "enabled": true, - "settings": { - "Debug": false, - "Custom title screen": true, - "Default System Override": "", - "Verbose Logs": false - } + "enabled": true, + "settings": { + "CustomTitleScreen": { + "title": "Custom Title Screen", + "type": "toggle", + "value": true, + "tooltip": "Displays planets from installed mods on the title screen." + }, + "DebugSeparator": { + "type": "separator" + }, + "Debug": { + "title": "Debug", + "type": "toggle", + "value": false, + "tooltip": "Enables the debug raycast, visible quantum object colliders, and debug options menu." + }, + "VisualizeQuantumObjects": { + "title": "Visualize Quantum Objects", + "type": "toggle", + "value": false, + "tooltip": "Draws boundaries around quantum objects when Debug mode is on." + }, + "VerboseLogs": { + "title": "Verbose Logs", + "type": "toggle", + "value": false, + "tooltip": "Makes logs much more detailed. Useful when debugging." + }, + "SequentialPreCaching": { + "title": "Sequential Pre-caching", + "type": "toggle", + "value": false, + "tooltip": "This is a debug option intended for mod creators. Prevents running out of memory when computing slide reel caches, but is slower and will cause in-game lag." + }, + "DefaultSystemOverride": { + "title": "Default System Override", + "type": "text", + "value": "", + "tooltip": "Forces the player to spawn in this system after death. Must match the unique name of a system, eg, xen.NewHorizonsExamples" + } + } } \ No newline at end of file diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 6ce4e38f..2f76acba 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -1,13 +1,13 @@ { "$schema": "https://raw.githubusercontent.com/amazingalek/owml/master/schemas/manifest_schema.json", "filename": "NewHorizons.dll", - "author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends", + "author": "xen, Bwc9876, JohnCorby, MegaPiggy, Trifid, and friends", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.19.7", - "owmlVersion": "2.10.3", + "version": "1.26.1", + "owmlVersion": "2.12.1", "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], - "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ], + "conflicts": [ "PacificEngine.OW_CommonResources" ], "pathsToPreserve": [ "planets", "systems", "translations" ], - "donateLink": "https://www.patreon.com/ownh" + "donateLink": "https://www.patreon.com/xen42" } diff --git a/README.md b/README.md index 49cfb2ff..75307d4f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![new horizons thumbnail 2](https://user-images.githubusercontent.com/22628069/154112130-b777f618-245f-44c9-9408-e11141fc5fde.png) -[![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Downh%26type%3Dpatrons&style=flat)](https://patreon.com/ownh) +[![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dxen42%26type%3Dpatrons&style=flat)](https://patreon.com/xen42) [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/paypalme/xen42) ![Current version](https://img.shields.io/github/manifest-json/v/xen-42/outer-wilds-new-horizons?color=gree&filename=NewHorizons%2Fmanifest.json) ![Downloads](https://img.shields.io/github/downloads/xen-42/outer-wilds-new-horizons/total) @@ -26,8 +26,9 @@ Check the ship's log for how to use your warp drive to travel between star syste ## Incompatible mods -- Quantum Space Buddies. -- OW Randomizer. +New Horizons conflicts with the mod Common Resources. This mod is a requirement for other mods such as Cheats Mod (we recommend you use the [Cheat and Debug Menu](https://outerwildsmods.com/mods/cheatanddebugmenu/) mod instead) and OW Randomizer. + +Why do these two mods conflict? Common Resources is a mod which reimplements many of the game's features underneath the hood, for one reason or another. For instance, it completely overhauls how the orbits of planets work, as this is a requirement for it to support OW Randomizer. It does this even when you are only using Cheats Mod. In particular, having CR installed seems to, for whatever reason, break character dialogue introduced by New Horizons. As CR is no longer actively maintained, it is unlikely this issue will be resolved any time soon. ## Supported Mods @@ -40,12 +41,12 @@ New Horizons has optional support for a few other mods: ## Features - Load planet meshes or details from asset bundles -- Use our [template Unity project](https://github.com/xen-42/outer-wilds-unity-template) to create assets for use in NH, including all game scripts recovered using UtinyRipper -- Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature accessible via the ship's log -- Remove or edit existing planets, including what they orbit around -- Create custom planets from heightmaps/texturemaps +- Use our [template Unity project](https://github.com/ow-mods/outer-wilds-unity-template) to create assets for use in NH, including all game scripts recovered using UtinyRipper +- Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature found in the ship's log +- Remove or edit existing planets, including their orbits +- Create custom planets from heightmaps/texturemaps with support for triplanar mapping - Create stars (and supernovae), comets, asteroid belts, satellites, quantum planets/moons, and custom Dark Bramble dimensions. -- Add stock planet features to custom ones, such as geysers, cloak fields, meteor-launching volcanoes, rafts, tornados, and Dark Bramble seeds/nodes. +- Add stock planet features to custom ones, such as geysers, cloaking fields, meteor-launching volcanoes, rafts, tornados, and Dark Bramble seeds/nodes. - Binary orbits - Signalscope signals and custom frequencies - Surface scatter: rocks, trees, etc, using in-game models, or custom ones @@ -86,6 +87,7 @@ Translation credits: - Spanish: Ciborgm9, Ink, GayCoffee - French: xen - Japanese: TRSasasusu +- Portuguese: avengerx, loco-choco New Horizons was based off [Marshmallow](https://github.com/misternebula/Marshmallow) was made by: diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index 3874aa87..00e37f02 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -81,13 +81,10 @@ public static class SchemaExporter switch (_title) { case "Celestial Body Schema": - schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; schema.Definitions["NomaiTextType"].Enumeration.Remove("cairn"); schema.Definitions["NomaiTextType"].EnumerationNames.Remove("Cairn"); schema.Definitions["NomaiTextType"].Enumeration.Remove("cairnVariant"); schema.Definitions["NomaiTextType"].EnumerationNames.Remove("CairnVariant"); - schema.Definitions["QuantumGroupType"].Enumeration.Remove("FailedValidation"); - schema.Definitions["QuantumGroupType"].EnumerationNames.Remove("FailedValidation"); schema.Definitions["StellarRemnantType"].Enumeration.Remove("Pulsar"); schema.Definitions["StellarRemnantType"].EnumerationNames.Remove("Pulsar"); break; diff --git a/SchemaExporter/SchemaExporter.csproj b/SchemaExporter/SchemaExporter.csproj index e4e5c0f6..726f05dd 100644 --- a/SchemaExporter/SchemaExporter.csproj +++ b/SchemaExporter/SchemaExporter.csproj @@ -20,7 +20,7 @@ - + diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 6e7191ae..f70ba561 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -8,10 +8,10 @@ Thank you for your interest in contributing to NH Docs! We are excited to have y Pages in the NH docs are all markdown files. The folder with all the pages is in `src/content/docs`. -- `index.mdx` Is a special file that is the home page of the docs. This is a markdown X file that allows us to use Astro components in the markdown. -- `start-here` is the folder that contains all the pages for the start here section of the docs. -- `getting-started` is the folder that contains all the pages for the getting started section of the docs. -- `reference` is the folder that contains all the pages for the api section of the docs. +- `index.mdx` Is a special file that is the home page of the docs. This is a markdown X file that allows us to use Astro components in the markdown. +- `start-here` is the folder that contains all the pages for the start here section of the docs. +- `getting-started` is the folder that contains all the pages for the getting started section of the docs. +- `reference` is the folder that contains all the pages for the api section of the docs. Finally, the `schemas` folder contains all the schema pages. You might notice that the schema folder is not present in GitHub. This is because the schema pages are auto-generated from the schema files in `../NewHorizons/Schemas`. In order to edit these you need to edit the C# class they correspond to. More info in the main contributing document found one folder up. @@ -23,10 +23,10 @@ One thing to note is the section fenced with `---` at the top of each page. This If you open this folder (`docs` not the entire repo), VSCode should prompt you to install the recommended extensions. If it doesn't, you can install them manually. The recommended extensions are: -- astro-build.astro-vscode -- davidanson.vscode-markdownlint -- yzhang.markdown-all-in-one -- esbenp.prettier-vscode +- astro-build.astro-vscode +- davidanson.vscode-markdownlint +- yzhang.markdown-all-in-one +- esbenp.prettier-vscode ## How To Add Pages @@ -48,8 +48,8 @@ Your images will be automatically optimized when the site is built. If you want to get a local copy of the site running, you'll need a few programs -- [Node.js](https://nodejs.org/en/) -- [PNPM](https://pnpm.io/) +- [Node.js](https://nodejs.org/en/) +- [PNPM](https://pnpm.io/) Once you have these installed, you can run the following commands to get the site running locally: diff --git a/docs/package.json b/docs/package.json index 2d8ea72f..a7c8ec9d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,18 +12,18 @@ "format": "prettier --write ." }, "dependencies": { - "@astrojs/starlight": "^0.19.1", - "astro": "4.4.1", + "@astrojs/starlight": "^0.30.3", + "astro": "5.1.3", "rehype-external-links": "^3.0.0", - "sharp": "^0.33.2" + "sharp": "^0.33.5" }, "devDependencies": { - "@apidevtools/json-schema-ref-parser": "^11.1.0", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.1.3", - "fast-xml-parser": "^4.3.4", - "prettier": "^3.2.5", - "prettier-plugin-astro": "^0.13.0", + "@apidevtools/json-schema-ref-parser": "^11.7.3", + "eslint": "^9.17.0", + "eslint-plugin-prettier": "^5.2.1", + "fast-xml-parser": "^4.5.1", + "prettier": "^3.4.2", + "prettier-plugin-astro": "^0.14.1", "xml-js": "^1.6.11" } } diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 75ffd0cc..7954ba13 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -1,2533 +1,1650 @@ -lockfileVersion: "6.0" +lockfileVersion: "9.0" settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - "@astrojs/starlight": - specifier: ^0.19.1 - version: 0.19.1(astro@4.4.1) - astro: - specifier: 4.4.1 - version: 4.4.1 - rehype-external-links: - specifier: ^3.0.0 - version: 3.0.0 - sharp: - specifier: ^0.33.2 - version: 0.33.2 - -devDependencies: - "@apidevtools/json-schema-ref-parser": - specifier: ^11.1.0 - version: 11.1.0 - eslint: - specifier: ^8.56.0 - version: 8.56.0 - eslint-plugin-prettier: - specifier: ^5.1.3 - version: 5.1.3(eslint@8.56.0)(prettier@3.2.5) - fast-xml-parser: - specifier: ^4.3.4 - version: 4.3.4 - prettier: - specifier: ^3.2.5 - version: 3.2.5 - prettier-plugin-astro: - specifier: ^0.13.0 - version: 0.13.0 - xml-js: - specifier: ^1.6.11 - version: 1.6.11 +importers: + .: + dependencies: + "@astrojs/starlight": + specifier: ^0.30.3 + version: 0.30.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3)) + astro: + specifier: 5.1.3 + version: 5.1.3(rollup@4.30.0)(typescript@5.6.3) + rehype-external-links: + specifier: ^3.0.0 + version: 3.0.0 + sharp: + specifier: ^0.33.5 + version: 0.33.5 + devDependencies: + "@apidevtools/json-schema-ref-parser": + specifier: ^11.7.3 + version: 11.7.3 + eslint: + specifier: ^9.17.0 + version: 9.17.0 + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(eslint@9.17.0)(prettier@3.4.2) + fast-xml-parser: + specifier: ^4.5.1 + version: 4.5.1 + prettier: + specifier: ^3.4.2 + version: 3.4.2 + prettier-plugin-astro: + specifier: ^0.14.1 + version: 0.14.1 + xml-js: + specifier: ^1.6.11 + version: 1.6.11 packages: - /@aashutoshrathi/word-wrap@1.2.6: + "@apidevtools/json-schema-ref-parser@11.7.3": resolution: { - integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - } - engines: { node: ">=0.10.0" } - dev: true - - /@ampproject/remapping@2.2.1: - resolution: - { - integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - } - engines: { node: ">=6.0.0" } - dependencies: - "@jridgewell/gen-mapping": 0.3.3 - "@jridgewell/trace-mapping": 0.3.22 - dev: false - - /@apidevtools/json-schema-ref-parser@11.1.0: - resolution: - { - integrity: sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag== + integrity: sha512-WApSdLdXEBb/1FUPca2lteASewEfpjEYJ8oXZP+0gExK5qSfsEKBKcA+WjY6Q4wvXwyv0+W6Kvc372pSceib9w== } engines: { node: ">= 16" } - dependencies: - "@jsdevtools/ono": 7.1.3 - "@types/json-schema": 7.0.15 - "@types/lodash.clonedeep": 4.5.9 - js-yaml: 4.1.0 - lodash.clonedeep: 4.5.0 - dev: true - /@astrojs/compiler@1.8.2: + "@astrojs/compiler@2.10.3": resolution: { - integrity: sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw== + integrity: sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw== } - dev: true - /@astrojs/compiler@2.5.3: + "@astrojs/internal-helpers@0.4.2": resolution: { - integrity: sha512-jzj01BRv/fmo+9Mr2FhocywGzEYiyiP2GVHje1ziGNU6c97kwhYGsnvwMkHrncAy9T9Vi54cjaMK7UE4ClX4vA== + integrity: sha512-EdDWkC3JJVcpGpqJAU/5hSk2LKXyG3mNGkzGoAuyK+xoPHbaVdSuIWoN1QTnmK3N/gGfaaAfM8gO2KDCAW7S3w== } - dev: false - /@astrojs/internal-helpers@0.2.1: + "@astrojs/markdown-remark@6.0.1": resolution: { - integrity: sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A== + integrity: sha512-CTSYijj25NfxgZi15TU3CwPwgyD1/7yA3FcdcNmB9p94nydupiUbrIiq3IqeTp2m5kCVzxbPZeC7fTwEOaNyGw== } - dev: false - /@astrojs/markdown-remark@4.2.1: + "@astrojs/mdx@4.0.3": resolution: { - integrity: sha512-2RQBIwrq+2qPYtp99bH+eL5hfbK0BoxXla85lHsRpIX/IsGqFrPX6pXI2cbWPihBwGbKCdxS6uZNX2QerZWwpQ== + integrity: sha512-8HcuyNG/KgYUAQWVzKFkboXcTOBCW6aQ0WK0Er/iSmVSF0y3yimg4/3QSt+Twv9dogpwIHL+E8iBJKqieFv4+g== } - dependencies: - "@astrojs/prism": 3.0.0 - github-slugger: 2.0.0 - import-meta-resolve: 4.0.0 - mdast-util-definitions: 6.0.0 - rehype-raw: 7.0.0 - rehype-stringify: 10.0.0 - remark-gfm: 4.0.0 - remark-parse: 11.0.0 - remark-rehype: 11.1.0 - remark-smartypants: 2.1.0 - shikiji: 0.9.19 - unified: 11.0.4 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@astrojs/mdx@2.1.1(astro@4.4.1): - resolution: - { - integrity: sha512-AgGFdE7HOGmoFooGvMSatkA9FiSKwyVW7ImHot/bXJ6uAbFfu6iG2ht18Cf1pT22Hda/6iSCGWusFvBv0/EnKQ== - } - engines: { node: ">=18.14.1" } + engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } peerDependencies: - astro: ^4.0.0 - dependencies: - "@astrojs/markdown-remark": 4.2.1 - "@mdx-js/mdx": 3.0.1 - acorn: 8.11.3 - astro: 4.4.1 - es-module-lexer: 1.4.1 - estree-util-visit: 2.0.0 - github-slugger: 2.0.0 - gray-matter: 4.0.3 - hast-util-to-html: 9.0.0 - kleur: 4.1.5 - rehype-raw: 7.0.0 - remark-gfm: 4.0.0 - remark-smartypants: 2.1.0 - source-map: 0.7.4 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: false + astro: ^5.0.0 - /@astrojs/prism@3.0.0: + "@astrojs/prism@3.2.0": resolution: { - integrity: sha512-g61lZupWq1bYbcBnYZqdjndShr/J3l/oFobBKPA3+qMat146zce3nz2kdO4giGbhYDt4gYdhmoBz0vZJ4sIurQ== + integrity: sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw== } - engines: { node: ">=18.14.1" } - dependencies: - prismjs: 1.29.0 - dev: false + engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } - /@astrojs/sitemap@3.1.0: + "@astrojs/sitemap@3.2.1": resolution: { - integrity: sha512-eAlD3ISaESByanYtr08yfwIYNZdLEvF8shSne5SaxSdrvtK7fS8UQTF0GpDFUpU/NOyqwB7YmndjlTplhYLP1A== + integrity: sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA== } - dependencies: - sitemap: 7.1.1 - zod: 3.22.4 - dev: false - /@astrojs/starlight@0.19.1(astro@4.4.1): + "@astrojs/starlight@0.30.3": resolution: { - integrity: sha512-HcrgWAdOJRVRhiOf4TySzpCek/6N56jq5Iz0ahDTE5Fv2tHHA2cLIRAOvVF+iPNVZa349hqv6QSCmzUHA2JK4Q== + integrity: sha512-HbGYYIR2Rnrvvc2jD0dUpp8zUzv3jQYtG5im3aulDgE4Jo21Ahw0yXlb/Y134G3LALLbqhImmlbt/h/nDV3yMA== } peerDependencies: - astro: ^4.2.7 - dependencies: - "@astrojs/mdx": 2.1.1(astro@4.4.1) - "@astrojs/sitemap": 3.1.0 - "@pagefind/default-ui": 1.0.4 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - astro: 4.4.1 - astro-expressive-code: 0.32.4(astro@4.4.1) - bcp-47: 2.1.0 - hast-util-select: 6.0.2 - hastscript: 8.0.0 - mdast-util-directive: 3.0.0 - mdast-util-to-markdown: 2.1.0 - pagefind: 1.0.4 - rehype: 13.0.1 - remark-directive: 3.0.0 - unified: 11.0.4 - unist-util-remove: 4.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: false + astro: ^5.0.0 - /@astrojs/telemetry@3.0.4: + "@astrojs/telemetry@3.2.0": resolution: { - integrity: sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ== + integrity: sha512-wxhSKRfKugLwLlr4OFfcqovk+LIFtKwLyGPqMsv+9/ibqqnW3Gv7tBhtKEb0gAyUAC4G9BTVQeQahqnQAhd6IQ== } - engines: { node: ">=18.14.1" } - dependencies: - ci-info: 3.9.0 - debug: 4.3.4 - dlv: 1.1.3 - dset: 3.1.3 - is-docker: 3.0.0 - is-wsl: 3.1.0 - which-pm-runs: 1.1.0 - transitivePeerDependencies: - - supports-color - dev: false + engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0 } - /@babel/code-frame@7.23.5: + "@babel/helper-string-parser@7.25.9": resolution: { - integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== } engines: { node: ">=6.9.0" } - dependencies: - "@babel/highlight": 7.23.4 - chalk: 2.4.2 - dev: false - /@babel/compat-data@7.23.5: + "@babel/helper-validator-identifier@7.25.9": resolution: { - integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== } engines: { node: ">=6.9.0" } - dev: false - /@babel/core@7.23.9: + "@babel/parser@7.26.3": resolution: { - integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - } - engines: { node: ">=6.9.0" } - dependencies: - "@ampproject/remapping": 2.2.1 - "@babel/code-frame": 7.23.5 - "@babel/generator": 7.23.6 - "@babel/helper-compilation-targets": 7.23.6 - "@babel/helper-module-transforms": 7.23.3(@babel/core@7.23.9) - "@babel/helpers": 7.23.9 - "@babel/parser": 7.23.9 - "@babel/template": 7.23.9 - "@babel/traverse": 7.23.9 - "@babel/types": 7.23.9 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/generator@7.23.6: - resolution: - { - integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - "@jridgewell/gen-mapping": 0.3.3 - "@jridgewell/trace-mapping": 0.3.22 - jsesc: 2.5.2 - dev: false - - /@babel/helper-annotate-as-pure@7.22.5: - resolution: - { - integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-compilation-targets@7.23.6: - resolution: - { - integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/compat-data": 7.23.5 - "@babel/helper-validator-option": 7.23.5 - browserslist: 4.23.0 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: false - - /@babel/helper-environment-visitor@7.22.20: - resolution: - { - integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - } - engines: { node: ">=6.9.0" } - dev: false - - /@babel/helper-function-name@7.23.0: - resolution: - { - integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/template": 7.23.9 - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-hoist-variables@7.22.5: - resolution: - { - integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-module-imports@7.22.15: - resolution: - { - integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9): - resolution: - { - integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0 - dependencies: - "@babel/core": 7.23.9 - "@babel/helper-environment-visitor": 7.22.20 - "@babel/helper-module-imports": 7.22.15 - "@babel/helper-simple-access": 7.22.5 - "@babel/helper-split-export-declaration": 7.22.6 - "@babel/helper-validator-identifier": 7.22.20 - dev: false - - /@babel/helper-plugin-utils@7.22.5: - resolution: - { - integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - } - engines: { node: ">=6.9.0" } - dev: false - - /@babel/helper-simple-access@7.22.5: - resolution: - { - integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-split-export-declaration@7.22.6: - resolution: - { - integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@babel/helper-string-parser@7.23.4: - resolution: - { - integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - } - engines: { node: ">=6.9.0" } - dev: false - - /@babel/helper-validator-identifier@7.22.20: - resolution: - { - integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - } - engines: { node: ">=6.9.0" } - dev: false - - /@babel/helper-validator-option@7.23.5: - resolution: - { - integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - } - engines: { node: ">=6.9.0" } - dev: false - - /@babel/helpers@7.23.9: - resolution: - { - integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/template": 7.23.9 - "@babel/traverse": 7.23.9 - "@babel/types": 7.23.9 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/highlight@7.23.4: - resolution: - { - integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/helper-validator-identifier": 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: false - - /@babel/parser@7.23.9: - resolution: - { - integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== + integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== } engines: { node: ">=6.0.0" } hasBin: true - dependencies: - "@babel/types": 7.23.9 - dev: false - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9): + "@babel/runtime@7.26.0": resolution: { - integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== } engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - dependencies: - "@babel/core": 7.23.9 - "@babel/helper-plugin-utils": 7.22.5 - dev: false - /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.23.9): + "@babel/types@7.26.3": resolution: { - integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== } engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - dependencies: - "@babel/core": 7.23.9 - "@babel/helper-annotate-as-pure": 7.22.5 - "@babel/helper-module-imports": 7.22.15 - "@babel/helper-plugin-utils": 7.22.5 - "@babel/plugin-syntax-jsx": 7.23.3(@babel/core@7.23.9) - "@babel/types": 7.23.9 - dev: false - /@babel/template@7.23.9: + "@ctrl/tinycolor@4.1.0": resolution: { - integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== + integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ== } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/code-frame": 7.23.5 - "@babel/parser": 7.23.9 - "@babel/types": 7.23.9 - dev: false + engines: { node: ">=14" } - /@babel/traverse@7.23.9: + "@emnapi/runtime@1.3.1": resolution: { - integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== + integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/code-frame": 7.23.5 - "@babel/generator": 7.23.6 - "@babel/helper-environment-visitor": 7.22.20 - "@babel/helper-function-name": 7.23.0 - "@babel/helper-hoist-variables": 7.22.5 - "@babel/helper-split-export-declaration": 7.22.6 - "@babel/parser": 7.23.9 - "@babel/types": 7.23.9 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: false - /@babel/types@7.23.9: + "@esbuild/aix-ppc64@0.21.5": resolution: { - integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== - } - engines: { node: ">=6.9.0" } - dependencies: - "@babel/helper-string-parser": 7.23.4 - "@babel/helper-validator-identifier": 7.22.20 - to-fast-properties: 2.0.0 - dev: false - - /@ctrl/tinycolor@3.6.1: - resolution: - { - integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== - } - engines: { node: ">=10" } - dev: false - - /@emnapi/runtime@0.45.0: - resolution: - { - integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w== - } - requiresBuild: true - dependencies: - tslib: 2.6.2 - dev: false - optional: true - - /@esbuild/aix-ppc64@0.19.12: - resolution: - { - integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== + integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== } engines: { node: ">=12" } cpu: [ppc64] os: [aix] - requiresBuild: true - dev: false - optional: true - /@esbuild/android-arm64@0.19.12: + "@esbuild/aix-ppc64@0.24.2": resolution: { - integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== + integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [aix] + + "@esbuild/android-arm64@0.21.5": + resolution: + { + integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== } engines: { node: ">=12" } cpu: [arm64] os: [android] - requiresBuild: true - dev: false - optional: true - /@esbuild/android-arm@0.19.12: + "@esbuild/android-arm64@0.24.2": resolution: { - integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== + integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [android] + + "@esbuild/android-arm@0.21.5": + resolution: + { + integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== } engines: { node: ">=12" } cpu: [arm] os: [android] - requiresBuild: true - dev: false - optional: true - /@esbuild/android-x64@0.19.12: + "@esbuild/android-arm@0.24.2": resolution: { - integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== + integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + } + engines: { node: ">=18" } + cpu: [arm] + os: [android] + + "@esbuild/android-x64@0.21.5": + resolution: + { + integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== } engines: { node: ">=12" } cpu: [x64] os: [android] - requiresBuild: true - dev: false - optional: true - /@esbuild/darwin-arm64@0.19.12: + "@esbuild/android-x64@0.24.2": resolution: { - integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== + integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + } + engines: { node: ">=18" } + cpu: [x64] + os: [android] + + "@esbuild/darwin-arm64@0.21.5": + resolution: + { + integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== } engines: { node: ">=12" } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@esbuild/darwin-x64@0.19.12: + "@esbuild/darwin-arm64@0.24.2": resolution: { - integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== + integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [darwin] + + "@esbuild/darwin-x64@0.21.5": + resolution: + { + integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== } engines: { node: ">=12" } cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@esbuild/freebsd-arm64@0.19.12: + "@esbuild/darwin-x64@0.24.2": resolution: { - integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== + integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [darwin] + + "@esbuild/freebsd-arm64@0.21.5": + resolution: + { + integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== } engines: { node: ">=12" } cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: false - optional: true - /@esbuild/freebsd-x64@0.19.12: + "@esbuild/freebsd-arm64@0.24.2": resolution: { - integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== + integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [freebsd] + + "@esbuild/freebsd-x64@0.21.5": + resolution: + { + integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== } engines: { node: ">=12" } cpu: [x64] os: [freebsd] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-arm64@0.19.12: + "@esbuild/freebsd-x64@0.24.2": resolution: { - integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== + integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + } + engines: { node: ">=18" } + cpu: [x64] + os: [freebsd] + + "@esbuild/linux-arm64@0.21.5": + resolution: + { + integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== } engines: { node: ">=12" } cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-arm@0.19.12: + "@esbuild/linux-arm64@0.24.2": resolution: { - integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== + integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [linux] + + "@esbuild/linux-arm@0.21.5": + resolution: + { + integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== } engines: { node: ">=12" } cpu: [arm] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-ia32@0.19.12: + "@esbuild/linux-arm@0.24.2": resolution: { - integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== + integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + } + engines: { node: ">=18" } + cpu: [arm] + os: [linux] + + "@esbuild/linux-ia32@0.21.5": + resolution: + { + integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== } engines: { node: ">=12" } cpu: [ia32] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-loong64@0.19.12: + "@esbuild/linux-ia32@0.24.2": resolution: { - integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== + integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [linux] + + "@esbuild/linux-loong64@0.21.5": + resolution: + { + integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== } engines: { node: ">=12" } cpu: [loong64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-mips64el@0.19.12: + "@esbuild/linux-loong64@0.24.2": resolution: { - integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== + integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + } + engines: { node: ">=18" } + cpu: [loong64] + os: [linux] + + "@esbuild/linux-mips64el@0.21.5": + resolution: + { + integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== } engines: { node: ">=12" } cpu: [mips64el] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-ppc64@0.19.12: + "@esbuild/linux-mips64el@0.24.2": resolution: { - integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== + integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + } + engines: { node: ">=18" } + cpu: [mips64el] + os: [linux] + + "@esbuild/linux-ppc64@0.21.5": + resolution: + { + integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== } engines: { node: ">=12" } cpu: [ppc64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-riscv64@0.19.12: + "@esbuild/linux-ppc64@0.24.2": resolution: { - integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== + integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [linux] + + "@esbuild/linux-riscv64@0.21.5": + resolution: + { + integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== } engines: { node: ">=12" } cpu: [riscv64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-s390x@0.19.12: + "@esbuild/linux-riscv64@0.24.2": resolution: { - integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== + integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + } + engines: { node: ">=18" } + cpu: [riscv64] + os: [linux] + + "@esbuild/linux-s390x@0.21.5": + resolution: + { + integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== } engines: { node: ">=12" } cpu: [s390x] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/linux-x64@0.19.12: + "@esbuild/linux-s390x@0.24.2": resolution: { - integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== + integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + } + engines: { node: ">=18" } + cpu: [s390x] + os: [linux] + + "@esbuild/linux-x64@0.21.5": + resolution: + { + integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== } engines: { node: ">=12" } cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@esbuild/netbsd-x64@0.19.12: + "@esbuild/linux-x64@0.24.2": resolution: { - integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== + integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + } + engines: { node: ">=18" } + cpu: [x64] + os: [linux] + + "@esbuild/netbsd-arm64@0.24.2": + resolution: + { + integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [netbsd] + + "@esbuild/netbsd-x64@0.21.5": + resolution: + { + integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== } engines: { node: ">=12" } cpu: [x64] os: [netbsd] - requiresBuild: true - dev: false - optional: true - /@esbuild/openbsd-x64@0.19.12: + "@esbuild/netbsd-x64@0.24.2": resolution: { - integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== + integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + } + engines: { node: ">=18" } + cpu: [x64] + os: [netbsd] + + "@esbuild/openbsd-arm64@0.24.2": + resolution: + { + integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openbsd] + + "@esbuild/openbsd-x64@0.21.5": + resolution: + { + integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== } engines: { node: ">=12" } cpu: [x64] os: [openbsd] - requiresBuild: true - dev: false - optional: true - /@esbuild/sunos-x64@0.19.12: + "@esbuild/openbsd-x64@0.24.2": resolution: { - integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== + integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [openbsd] + + "@esbuild/sunos-x64@0.21.5": + resolution: + { + integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== } engines: { node: ">=12" } cpu: [x64] os: [sunos] - requiresBuild: true - dev: false - optional: true - /@esbuild/win32-arm64@0.19.12: + "@esbuild/sunos-x64@0.24.2": resolution: { - integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== + integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + } + engines: { node: ">=18" } + cpu: [x64] + os: [sunos] + + "@esbuild/win32-arm64@0.21.5": + resolution: + { + integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== } engines: { node: ">=12" } cpu: [arm64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@esbuild/win32-ia32@0.19.12: + "@esbuild/win32-arm64@0.24.2": resolution: { - integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== + integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [win32] + + "@esbuild/win32-ia32@0.21.5": + resolution: + { + integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== } engines: { node: ">=12" } cpu: [ia32] os: [win32] - requiresBuild: true - dev: false - optional: true - /@esbuild/win32-x64@0.19.12: + "@esbuild/win32-ia32@0.24.2": resolution: { - integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== + integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [win32] + + "@esbuild/win32-x64@0.21.5": + resolution: + { + integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== } engines: { node: ">=12" } cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + "@esbuild/win32-x64@0.24.2": resolution: { - integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + } + engines: { node: ">=18" } + cpu: [x64] + os: [win32] + + "@eslint-community/eslint-utils@4.4.1": + resolution: + { + integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== } engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.56.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.10.0: + "@eslint-community/regexpp@4.12.1": resolution: { - integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== } engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - dev: true - /@eslint/eslintrc@2.1.4: + "@eslint/config-array@0.19.1": resolution: { - integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@eslint/js@8.56.0: + "@eslint/core@0.9.1": resolution: { - integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== + integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dev: true + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@expressive-code/core@0.32.4: + "@eslint/eslintrc@3.2.0": resolution: { - integrity: sha512-S0OwgZCy29OCcwFUBTLDrShUovIUWZcQn3EvSoKsGfzf/wTisK7XqZ1uH0Y7Mlof3Hf9uJMjOhJZvxTLtQUdSQ== + integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== } - dependencies: - "@ctrl/tinycolor": 3.6.1 - hast-util-to-html: 8.0.4 - hastscript: 7.2.0 - postcss: 8.4.35 - postcss-nested: 6.0.1(postcss@8.4.35) - dev: false + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@expressive-code/plugin-frames@0.32.4: + "@eslint/js@9.17.0": resolution: { - integrity: sha512-XOQrLqlVEy5JbqsBhDcSJQinceQ5j/Z8cE0/27Lnlcj4oXRdiQNjMVtstC/xZUeWEbm+FI9ZZP4Z9yihol61Aw== + integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w== } - dependencies: - "@expressive-code/core": 0.32.4 - hastscript: 7.2.0 - dev: false + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@expressive-code/plugin-shiki@0.32.4: + "@eslint/object-schema@2.1.5": resolution: { - integrity: sha512-zZzTXFFTpG+fmBG6C+4KzIyh1nFPdn4gLJ8E9LhBVufmRkn3gZplkE99lulfillsKyUZTRw3+dC3xYZWEZKzPw== + integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ== } - dependencies: - "@expressive-code/core": 0.32.4 - shikiji: 0.8.7 - dev: false + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@expressive-code/plugin-text-markers@0.32.4: + "@eslint/plugin-kit@0.2.4": resolution: { - integrity: sha512-lFlo3uwTp7vUmfXtLPn2aXs0CPFqdFvKiR3y8gtNzmBeYWPqVahF4RFUCN9ZpztCmXp5V8p2ADvNHzoNwCBwzA== + integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg== } - dependencies: - "@expressive-code/core": 0.32.4 - hastscript: 7.2.0 - unist-util-visit-parents: 5.1.3 - dev: false + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /@humanwhocodes/config-array@0.11.14: + "@expressive-code/core@0.38.3": resolution: { - integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + integrity: sha512-s0/OtdRpBONwcn23O8nVwDNQqpBGKscysejkeBkwlIeHRLZWgiTVrusT5Idrdz1d8cW5wRk9iGsAIQmwDPXgJg== } - engines: { node: ">=10.10.0" } - dependencies: - "@humanwhocodes/object-schema": 2.0.2 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - /@humanwhocodes/module-importer@1.0.1: + "@expressive-code/plugin-frames@0.38.3": + resolution: + { + integrity: sha512-qL2oC6FplmHNQfZ8ZkTR64/wKo9x0c8uP2WDftR/ydwN/yhe1ed7ZWYb8r3dezxsls+tDokCnN4zYR594jbpvg== + } + + "@expressive-code/plugin-shiki@0.38.3": + resolution: + { + integrity: sha512-kqHnglZeesqG3UKrb6e9Fq5W36AZ05Y9tCREmSN2lw8LVTqENIeCIkLDdWtQ5VoHlKqwUEQFTVlRehdwoY7Gmw== + } + + "@expressive-code/plugin-text-markers@0.38.3": + resolution: + { + integrity: sha512-dPK3+BVGTbTmGQGU3Fkj3jZ3OltWUAlxetMHI6limUGCWBCucZiwoZeFM/WmqQa71GyKRzhBT+iEov6kkz2xVA== + } + + "@humanfs/core@0.19.1": + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + } + engines: { node: ">=18.18.0" } + + "@humanfs/node@0.16.6": + resolution: + { + integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + } + engines: { node: ">=18.18.0" } + + "@humanwhocodes/module-importer@1.0.1": resolution: { integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== } engines: { node: ">=12.22" } - dev: true - /@humanwhocodes/object-schema@2.0.2: + "@humanwhocodes/retry@0.3.1": resolution: { - integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== + integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== } - dev: true + engines: { node: ">=18.18" } - /@img/sharp-darwin-arm64@0.33.2: + "@humanwhocodes/retry@0.4.1": resolution: { - integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w== + integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== } - engines: + engines: { node: ">=18.18" } + + "@img/sharp-darwin-arm64@0.33.5": + resolution: { - glibc: ">=2.26", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [darwin] + + "@img/sharp-darwin-x64@0.33.5": + resolution: + { + integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [darwin] + + "@img/sharp-libvips-darwin-arm64@1.0.4": + resolution: + { + integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== } cpu: [arm64] os: [darwin] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-darwin-arm64": 1.0.1 - dev: false - optional: true - /@img/sharp-darwin-x64@0.33.2: + "@img/sharp-libvips-darwin-x64@1.0.4": resolution: { - integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg== - } - engines: - { - glibc: ">=2.26", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== } cpu: [x64] os: [darwin] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-darwin-x64": 1.0.1 - dev: false - optional: true - /@img/sharp-libvips-darwin-arm64@1.0.1: + "@img/sharp-libvips-linux-arm64@1.0.4": resolution: { - integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw== - } - engines: { macos: ">=11", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-darwin-x64@1.0.1: - resolution: - { - integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog== - } - engines: { macos: ">=10.13", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-arm64@1.0.1: - resolution: - { - integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA== - } - engines: { glibc: ">=2.26", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-arm@1.0.1: - resolution: - { - integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ== - } - engines: { glibc: ">=2.28", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-s390x@1.0.1: - resolution: - { - integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ== - } - engines: { glibc: ">=2.28", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-x64@1.0.1: - resolution: - { - integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw== - } - engines: { glibc: ">=2.26", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linuxmusl-arm64@1.0.1: - resolution: - { - integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg== - } - engines: { musl: ">=1.2.2", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linuxmusl-x64@1.0.1: - resolution: - { - integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw== - } - engines: { musl: ">=1.2.2", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-linux-arm64@0.33.2: - resolution: - { - integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew== - } - engines: - { - glibc: ">=2.26", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== } cpu: [arm64] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linux-arm64": 1.0.1 - dev: false - optional: true - /@img/sharp-linux-arm@0.33.2: + "@img/sharp-libvips-linux-arm@1.0.5": resolution: { - integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA== - } - engines: - { - glibc: ">=2.28", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== } cpu: [arm] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linux-arm": 1.0.1 - dev: false - optional: true - /@img/sharp-linux-s390x@0.33.2: + "@img/sharp-libvips-linux-s390x@1.0.4": resolution: { - integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA== - } - engines: - { - glibc: ">=2.28", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== } cpu: [s390x] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linux-s390x": 1.0.1 - dev: false - optional: true - /@img/sharp-linux-x64@0.33.2: + "@img/sharp-libvips-linux-x64@1.0.4": resolution: { - integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A== - } - engines: - { - glibc: ">=2.26", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== } cpu: [x64] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linux-x64": 1.0.1 - dev: false - optional: true - /@img/sharp-linuxmusl-arm64@0.33.2: + "@img/sharp-libvips-linuxmusl-arm64@1.0.4": resolution: { - integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA== - } - engines: - { - musl: ">=1.2.2", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== } cpu: [arm64] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64": 1.0.1 - dev: false - optional: true - /@img/sharp-linuxmusl-x64@0.33.2: + "@img/sharp-libvips-linuxmusl-x64@1.0.4": resolution: { - integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A== - } - engines: - { - musl: ">=1.2.2", - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== } cpu: [x64] os: [linux] - requiresBuild: true - optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64": 1.0.1 - dev: false - optional: true - /@img/sharp-wasm32@0.33.2: + "@img/sharp-linux-arm64@0.33.5": resolution: { - integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ== + integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== } - engines: + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + + "@img/sharp-linux-arm@0.33.5": + resolution: { - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm] + os: [linux] + + "@img/sharp-linux-s390x@0.33.5": + resolution: + { + integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [s390x] + os: [linux] + + "@img/sharp-linux-x64@0.33.5": + resolution: + { + integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + + "@img/sharp-linuxmusl-arm64@0.33.5": + resolution: + { + integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + + "@img/sharp-linuxmusl-x64@0.33.5": + resolution: + { + integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + + "@img/sharp-wasm32@0.33.5": + resolution: + { + integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [wasm32] - requiresBuild: true - dependencies: - "@emnapi/runtime": 0.45.0 - dev: false - optional: true - /@img/sharp-win32-ia32@0.33.2: + "@img/sharp-win32-ia32@0.33.5": resolution: { - integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g== - } - engines: - { - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [ia32] os: [win32] - requiresBuild: true - dev: false - optional: true - /@img/sharp-win32-x64@0.33.2: + "@img/sharp-win32-x64@0.33.5": resolution: { - integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg== - } - engines: - { - node: ^18.17.0 || ^20.3.0 || >=21.0.0, - npm: ">=9.6.5", - pnpm: ">=7.1.0", - yarn: ">=3.2.0" + integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@jridgewell/gen-mapping@0.3.3: + "@jridgewell/sourcemap-codec@1.5.0": resolution: { - integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== } - engines: { node: ">=6.0.0" } - dependencies: - "@jridgewell/set-array": 1.1.2 - "@jridgewell/sourcemap-codec": 1.4.15 - "@jridgewell/trace-mapping": 0.3.22 - dev: false - /@jridgewell/resolve-uri@3.1.2: - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - } - engines: { node: ">=6.0.0" } - dev: false - - /@jridgewell/set-array@1.1.2: - resolution: - { - integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - } - engines: { node: ">=6.0.0" } - dev: false - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: - { - integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - } - dev: false - - /@jridgewell/trace-mapping@0.3.22: - resolution: - { - integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== - } - dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.4.15 - dev: false - - /@jsdevtools/ono@7.1.3: + "@jsdevtools/ono@7.1.3": resolution: { integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== } - dev: true - /@mdx-js/mdx@3.0.1: + "@mdx-js/mdx@3.1.0": resolution: { - integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA== + integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw== } - dependencies: - "@types/estree": 1.0.5 - "@types/estree-jsx": 1.0.4 - "@types/hast": 3.0.4 - "@types/mdx": 2.0.11 - collapse-white-space: 2.1.0 - devlop: 1.1.0 - estree-util-build-jsx: 3.0.1 - estree-util-is-identifier-name: 3.0.0 - estree-util-to-js: 2.0.0 - estree-walker: 3.0.3 - hast-util-to-estree: 3.1.0 - hast-util-to-jsx-runtime: 2.3.0 - markdown-extensions: 2.0.0 - periscopic: 3.1.0 - remark-mdx: 3.0.1 - remark-parse: 11.0.0 - remark-rehype: 11.1.0 - source-map: 0.7.4 - unified: 11.0.4 - unist-util-position-from-estree: 2.0.0 - unist-util-stringify-position: 4.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: false - /@medv/finder@3.1.0: - resolution: - { - integrity: sha512-ojkXjR3K0Zz3jnCR80tqPL+0yvbZk/lEodb6RIVjLz7W8RVA2wrw8ym/CzCpXO9SYVUIKHFUpc7jvf8UKfIM3w== - } - dev: false - - /@nodelib/fs.scandir@2.1.5: + "@nodelib/fs.scandir@2.1.5": resolution: { integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== } engines: { node: ">= 8" } - dependencies: - "@nodelib/fs.stat": 2.0.5 - run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: + "@nodelib/fs.stat@2.0.5": resolution: { integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== } engines: { node: ">= 8" } - /@nodelib/fs.walk@1.2.8: + "@nodelib/fs.walk@1.2.8": resolution: { integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== } engines: { node: ">= 8" } - dependencies: - "@nodelib/fs.scandir": 2.1.5 - fastq: 1.17.1 - /@pagefind/darwin-arm64@1.0.4: + "@oslojs/encoding@1.1.0": resolution: { - integrity: sha512-2OcthvceX2xhm5XbgOmW+lT45oLuHqCmvFeFtxh1gsuP5cO8vcD8ZH8Laj4pXQFCcK6eAdSShx+Ztx/LsQWZFQ== + integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ== + } + + "@pagefind/darwin-arm64@1.3.0": + resolution: + { + integrity: sha512-365BEGl6ChOsauRjyVpBjXybflXAOvoMROw3TucAROHIcdBvXk9/2AmEvGFU0r75+vdQI4LJdJdpH4Y6Yqaj4A== } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@pagefind/darwin-x64@1.0.4: + "@pagefind/darwin-x64@1.3.0": resolution: { - integrity: sha512-xkdvp0D9Ld/ZKsjo/y1bgfhTEU72ITimd2PMMQtts7jf6JPIOJbsiErCvm37m/qMFuPGEq/8d+fZ4pydOj08HQ== + integrity: sha512-zlGHA23uuXmS8z3XxEGmbHpWDxXfPZ47QS06tGUq0HDcZjXjXHeLG+cboOy828QIV5FXsm9MjfkP5e4ZNbOkow== } cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@pagefind/default-ui@1.0.4: + "@pagefind/default-ui@1.3.0": resolution: { - integrity: sha512-edkcaPSKq67C49Vehjo+LQCpT615v4d7JRhfGzFPccePvdklaL+VXrfghN/uIfsdoG+HoLI1PcYy2iFcB9CTkw== + integrity: sha512-CGKT9ccd3+oRK6STXGgfH+m0DbOKayX6QGlq38TfE1ZfUcPc5+ulTuzDbZUnMo+bubsEOIypm4Pl2iEyzZ1cNg== } - dev: false - /@pagefind/linux-arm64@1.0.4: + "@pagefind/linux-arm64@1.3.0": resolution: { - integrity: sha512-jGBrcCzIrMnNxLKVtogaQyajVfTAXM59KlBEwg6vTn8NW4fQ6nuFbbhlG4dTIsaamjEM5e8ZBEAKZfTB/qd9xw== + integrity: sha512-8lsxNAiBRUk72JvetSBXs4WRpYrQrVJXjlRRnOL6UCdBN9Nlsz0t7hWstRk36+JqHpGWOKYiuHLzGYqYAqoOnQ== } cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@pagefind/linux-x64@1.0.4: + "@pagefind/linux-x64@1.3.0": resolution: { - integrity: sha512-LIn/QcvcEtLEBqKe5vpSbSC2O3fvqbRCWOTIklslqSORisCsvzsWbP6j+LYxE9q0oWIfkdMoWV1vrE/oCKRxHg== + integrity: sha512-hAvqdPJv7A20Ucb6FQGE6jhjqy+vZ6pf+s2tFMNtMBG+fzcdc91uTw7aP/1Vo5plD0dAOHwdxfkyw0ugal4kcQ== } cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@pagefind/windows-x64@1.0.4: + "@pagefind/windows-x64@1.3.0": resolution: { - integrity: sha512-QlBCVeZfj9fc9sbUgdOz76ZDbeK4xZihOBAFqGuRJeChfM8pnVeH9iqSnXgO3+m9oITugTf7PicyRUFAG76xeQ== + integrity: sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ== } cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@pkgr/core@0.1.1: + "@pkgr/core@0.1.1": resolution: { integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== } engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } - dev: true - /@rollup/rollup-android-arm-eabi@4.12.0: + "@rollup/pluginutils@5.1.4": resolution: { - integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w== + integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== + } + engines: { node: ">=14.0.0" } + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + "@rollup/rollup-android-arm-eabi@4.30.0": + resolution: + { + integrity: sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ== } cpu: [arm] os: [android] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-android-arm64@4.12.0: + "@rollup/rollup-android-arm64@4.30.0": resolution: { - integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ== + integrity: sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A== } cpu: [arm64] os: [android] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-darwin-arm64@4.12.0: + "@rollup/rollup-darwin-arm64@4.30.0": resolution: { - integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ== + integrity: sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ== } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-darwin-x64@4.12.0: + "@rollup/rollup-darwin-x64@4.30.0": resolution: { - integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg== + integrity: sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw== } cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.12.0: + "@rollup/rollup-freebsd-arm64@4.30.0": resolution: { - integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA== + integrity: sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA== + } + cpu: [arm64] + os: [freebsd] + + "@rollup/rollup-freebsd-x64@4.30.0": + resolution: + { + integrity: sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg== + } + cpu: [x64] + os: [freebsd] + + "@rollup/rollup-linux-arm-gnueabihf@4.30.0": + resolution: + { + integrity: sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g== } cpu: [arm] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-arm64-gnu@4.12.0: + "@rollup/rollup-linux-arm-musleabihf@4.30.0": resolution: { - integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA== + integrity: sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A== + } + cpu: [arm] + os: [linux] + + "@rollup/rollup-linux-arm64-gnu@4.30.0": + resolution: + { + integrity: sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ== } cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-arm64-musl@4.12.0: + "@rollup/rollup-linux-arm64-musl@4.30.0": resolution: { - integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ== + integrity: sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ== } cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.12.0: + "@rollup/rollup-linux-loongarch64-gnu@4.30.0": resolution: { - integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw== + integrity: sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA== + } + cpu: [loong64] + os: [linux] + + "@rollup/rollup-linux-powerpc64le-gnu@4.30.0": + resolution: + { + integrity: sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw== + } + cpu: [ppc64] + os: [linux] + + "@rollup/rollup-linux-riscv64-gnu@4.30.0": + resolution: + { + integrity: sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g== } cpu: [riscv64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-x64-gnu@4.12.0: + "@rollup/rollup-linux-s390x-gnu@4.30.0": resolution: { - integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA== + integrity: sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw== + } + cpu: [s390x] + os: [linux] + + "@rollup/rollup-linux-x64-gnu@4.30.0": + resolution: + { + integrity: sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg== } cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-linux-x64-musl@4.12.0: + "@rollup/rollup-linux-x64-musl@4.30.0": resolution: { - integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw== + integrity: sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A== } cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-win32-arm64-msvc@4.12.0: + "@rollup/rollup-win32-arm64-msvc@4.30.0": resolution: { - integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw== + integrity: sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw== } cpu: [arm64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-win32-ia32-msvc@4.12.0: + "@rollup/rollup-win32-ia32-msvc@4.30.0": resolution: { - integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA== + integrity: sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g== } cpu: [ia32] os: [win32] - requiresBuild: true - dev: false - optional: true - /@rollup/rollup-win32-x64-msvc@4.12.0: + "@rollup/rollup-win32-x64-msvc@4.30.0": resolution: { - integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg== + integrity: sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg== } cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@types/acorn@4.0.6: + "@shikijs/core@1.26.1": + resolution: + { + integrity: sha512-yeo7sG+WZQblKPclUOKRPwkv1PyoHYkJ4gP9DzhFJbTdueKR7wYTI1vfF/bFi1NTgc545yG/DzvVhZgueVOXMA== + } + + "@shikijs/engine-javascript@1.26.1": + resolution: + { + integrity: sha512-CRhA0b8CaSLxS0E9A4Bzcb3LKBNpykfo9F85ozlNyArxjo2NkijtiwrJZ6eHa+NT5I9Kox2IXVdjUsP4dilsmw== + } + + "@shikijs/engine-oniguruma@1.26.1": + resolution: + { + integrity: sha512-F5XuxN1HljLuvfXv7d+mlTkV7XukC1cawdtOo+7pKgPD83CAB1Sf8uHqP3PK0u7njFH0ZhoXE1r+0JzEgAQ+kg== + } + + "@shikijs/langs@1.26.1": + resolution: + { + integrity: sha512-oz/TQiIqZejEIZbGtn68hbJijAOTtYH4TMMSWkWYozwqdpKR3EXgILneQy26WItmJjp3xVspHdiUxUCws4gtuw== + } + + "@shikijs/themes@1.26.1": + resolution: + { + integrity: sha512-JDxVn+z+wgLCiUhBGx2OQrLCkKZQGzNH3nAxFir4PjUcYiyD8Jdms9izyxIogYmSwmoPTatFTdzyrRKbKlSfPA== + } + + "@shikijs/types@1.26.1": + resolution: + { + integrity: sha512-d4B00TKKAMaHuFYgRf3L0gwtvqpW4hVdVwKcZYbBfAAQXspgkbWqnFfuFl3MDH6gLbsubOcr+prcnsqah3ny7Q== + } + + "@shikijs/vscode-textmate@10.0.1": + resolution: + { + integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg== + } + + "@types/acorn@4.0.6": resolution: { integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== } - dependencies: - "@types/estree": 1.0.5 - dev: false - /@types/babel__core@7.20.5: + "@types/cookie@0.6.0": resolution: { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== } - dependencies: - "@babel/parser": 7.23.9 - "@babel/types": 7.23.9 - "@types/babel__generator": 7.6.8 - "@types/babel__template": 7.4.4 - "@types/babel__traverse": 7.20.5 - dev: false - /@types/babel__generator@7.6.8: - resolution: - { - integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@types/babel__template@7.4.4: - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - } - dependencies: - "@babel/parser": 7.23.9 - "@babel/types": 7.23.9 - dev: false - - /@types/babel__traverse@7.20.5: - resolution: - { - integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== - } - dependencies: - "@babel/types": 7.23.9 - dev: false - - /@types/debug@4.1.12: + "@types/debug@4.1.12": resolution: { integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== } - dependencies: - "@types/ms": 0.7.34 - dev: false - /@types/estree-jsx@1.0.4: + "@types/estree-jsx@1.0.5": resolution: { - integrity: sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ== + integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== } - dependencies: - "@types/estree": 1.0.5 - dev: false - /@types/estree@1.0.5: + "@types/estree@1.0.6": resolution: { - integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== } - dev: false - /@types/hast@2.3.10: - resolution: - { - integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw== - } - dependencies: - "@types/unist": 2.0.10 - dev: false - - /@types/hast@3.0.4: + "@types/hast@3.0.4": resolution: { integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== } - dependencies: - "@types/unist": 3.0.2 - dev: false - /@types/json-schema@7.0.15: + "@types/js-yaml@4.0.9": + resolution: + { + integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== + } + + "@types/json-schema@7.0.15": resolution: { integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } - dev: true - /@types/lodash.clonedeep@4.5.9: + "@types/mdast@4.0.4": resolution: { - integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q== + integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== } - dependencies: - "@types/lodash": 4.14.202 - dev: true - /@types/lodash@4.14.202: + "@types/mdx@2.0.13": resolution: { - integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw== } - dev: true - /@types/mdast@4.0.3: - resolution: - { - integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg== - } - dependencies: - "@types/unist": 3.0.2 - dev: false - - /@types/mdx@2.0.11: - resolution: - { - integrity: sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw== - } - dev: false - - /@types/ms@0.7.34: + "@types/ms@0.7.34": resolution: { integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== } - dev: false - /@types/nlcst@1.0.4: + "@types/nlcst@2.0.3": resolution: { - integrity: sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg== + integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA== } - dependencies: - "@types/unist": 2.0.10 - dev: false - /@types/node@17.0.45: + "@types/node@17.0.45": resolution: { integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== } - dev: false - /@types/parse5@6.0.3: - resolution: - { - integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== - } - dev: false - - /@types/sax@1.2.7: + "@types/sax@1.2.7": resolution: { integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A== } - dependencies: - "@types/node": 17.0.45 - dev: false - /@types/unist@2.0.10: + "@types/unist@2.0.11": resolution: { - integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== } - dev: false - /@types/unist@3.0.2: + "@types/unist@3.0.3": resolution: { - integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== + integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== } - dev: false - /@ungap/structured-clone@1.2.0: + "@ungap/structured-clone@1.2.1": resolution: { - integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA== } - /acorn-jsx@5.3.2(acorn@8.11.3): + acorn-jsx@5.3.2: resolution: { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.11.3 - /acorn@8.11.3: + acorn@8.14.0: resolution: { - integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== } engines: { node: ">=0.4.0" } hasBin: true - /ajv@6.12.6: + ajv@6.12.6: resolution: { integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== } - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - /ansi-align@3.0.1: + ansi-align@3.0.1: resolution: { integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== } - dependencies: - string-width: 4.2.3 - dev: false - /ansi-regex@5.0.1: + ansi-regex@5.0.1: resolution: { integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== } engines: { node: ">=8" } - /ansi-regex@6.0.1: + ansi-regex@6.1.0: resolution: { - integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== } engines: { node: ">=12" } - dev: false - /ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - } - engines: { node: ">=4" } - dependencies: - color-convert: 1.9.3 - dev: false - - /ansi-styles@4.3.0: + ansi-styles@4.3.0: resolution: { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } engines: { node: ">=8" } - dependencies: - color-convert: 2.0.1 - dev: true - /ansi-styles@6.2.1: + ansi-styles@6.2.1: resolution: { integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== } engines: { node: ">=12" } - dev: false - /anymatch@3.1.3: + anymatch@3.1.3: resolution: { integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== } engines: { node: ">= 8" } - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: false - /arg@5.0.2: + arg@5.0.2: resolution: { integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== } - dev: false - /argparse@1.0.10: + argparse@1.0.10: resolution: { integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== } - dependencies: - sprintf-js: 1.0.3 - dev: false - /argparse@2.0.1: + argparse@2.0.1: resolution: { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } - /aria-query@5.3.0: + aria-query@5.3.2: resolution: { - integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== } - dependencies: - dequal: 2.0.3 - dev: false + engines: { node: ">= 0.4" } - /array-iterate@2.0.1: + array-iterate@2.0.1: resolution: { integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg== } - dev: false - /astring@1.8.6: + astring@1.9.0: resolution: { - integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg== + integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== } hasBin: true - dev: false - /astro-expressive-code@0.32.4(astro@4.4.1): + astro-expressive-code@0.38.3: resolution: { - integrity: sha512-/Kq8wLMz0X2gbLWGmPryqEdFV/om/GROsoLtPFqLrLCRD5CpwxXAW185BIGZKf4iYsyJim1vvcpQm5Y9hV5B1g== + integrity: sha512-Tvdc7RV0G92BbtyEOsfJtXU35w41CkM94fOAzxbQP67Wj5jArfserJ321FO4XA7WG9QMV0GIBmQq77NBIRDzpQ== } peerDependencies: - astro: ^3.3.0 || ^4.0.0-beta - dependencies: - astro: 4.4.1 - hast-util-to-html: 8.0.4 - remark-expressive-code: 0.32.4 - dev: false + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 - /astro@4.4.1: + astro@5.1.3: resolution: { - integrity: sha512-nJLgNg8UXKBJYXjWtekgv1TYZES++LAdShgyKL8L5yJMeiqlDSO+/Laq5VfRKoL9hzBdyolJMB0WDE/+bRZytg== + integrity: sha512-Zl/B4hmueJmlI5FJQcwbBRhHbRvbTWaTJzimkbH+jYczR5blt4uSh0DheyAt/+NwcnGfiC11z/XXJYIBR4LPUw== } - engines: { node: ">=18.14.1", npm: ">=6.14.0" } + engines: { node: ^18.17.1 || ^20.3.0 || >=22.0.0, npm: ">=9.6.5", pnpm: ">=7.1.0" } hasBin: true - dependencies: - "@astrojs/compiler": 2.5.3 - "@astrojs/internal-helpers": 0.2.1 - "@astrojs/markdown-remark": 4.2.1 - "@astrojs/telemetry": 3.0.4 - "@babel/core": 7.23.9 - "@babel/generator": 7.23.6 - "@babel/parser": 7.23.9 - "@babel/plugin-transform-react-jsx": 7.23.4(@babel/core@7.23.9) - "@babel/traverse": 7.23.9 - "@babel/types": 7.23.9 - "@medv/finder": 3.1.0 - "@types/babel__core": 7.20.5 - acorn: 8.11.3 - aria-query: 5.3.0 - axobject-query: 4.0.0 - boxen: 7.1.1 - chokidar: 3.6.0 - ci-info: 4.0.0 - clsx: 2.1.0 - common-ancestor-path: 1.0.1 - cookie: 0.6.0 - cssesc: 3.0.0 - debug: 4.3.4 - deterministic-object-hash: 2.0.2 - devalue: 4.3.2 - diff: 5.2.0 - dlv: 1.1.3 - dset: 3.1.3 - es-module-lexer: 1.4.1 - esbuild: 0.19.12 - estree-walker: 3.0.3 - execa: 8.0.1 - fast-glob: 3.3.2 - flattie: 1.1.0 - github-slugger: 2.0.0 - gray-matter: 4.0.3 - html-escaper: 3.0.3 - http-cache-semantics: 4.1.1 - js-yaml: 4.1.0 - kleur: 4.1.5 - magic-string: 0.30.7 - mdast-util-to-hast: 13.0.2 - mime: 3.0.0 - ora: 7.0.1 - p-limit: 5.0.0 - p-queue: 8.0.1 - path-to-regexp: 6.2.1 - preferred-pm: 3.1.2 - prompts: 2.4.2 - rehype: 13.0.1 - resolve: 1.22.8 - semver: 7.6.0 - shikiji: 0.9.19 - shikiji-core: 0.9.19 - string-width: 7.1.0 - strip-ansi: 7.1.0 - tsconfck: 3.0.2 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - vite: 5.1.3 - vitefu: 0.2.5(vite@5.1.3) - which-pm: 2.1.1 - yargs-parser: 21.1.1 - zod: 3.22.4 - optionalDependencies: - sharp: 0.32.6 - transitivePeerDependencies: - - "@types/node" - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - - typescript - dev: false - /axobject-query@4.0.0: + axobject-query@4.1.0: resolution: { - integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw== + integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== } - dependencies: - dequal: 2.0.3 - dev: false + engines: { node: ">= 0.4" } - /b4a@1.6.6: - resolution: - { - integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== - } - requiresBuild: true - dev: false - optional: true - - /bail@2.0.2: + bail@2.0.2: resolution: { integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== } - dev: false - /balanced-match@1.0.2: + balanced-match@1.0.2: resolution: { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } - dev: true - /bare-events@2.2.0: - resolution: - { - integrity: sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg== - } - requiresBuild: true - dev: false - optional: true - - /bare-fs@2.1.5: - resolution: - { - integrity: sha512-5t0nlecX+N2uJqdxe9d18A98cp2u9BETelbjKpiVgQqzzmVNFYWEAjQHqS+2Khgto1vcwhik9cXucaj5ve2WWA== - } - requiresBuild: true - dependencies: - bare-events: 2.2.0 - bare-os: 2.2.0 - bare-path: 2.1.0 - streamx: 2.16.1 - dev: false - optional: true - - /bare-os@2.2.0: - resolution: - { - integrity: sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag== - } - requiresBuild: true - dev: false - optional: true - - /bare-path@2.1.0: - resolution: - { - integrity: sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw== - } - requiresBuild: true - dependencies: - bare-os: 2.2.0 - dev: false - optional: true - - /base-64@1.0.0: + base-64@1.0.0: resolution: { integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== } - dev: false - /base64-js@1.5.1: - resolution: - { - integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - } - dev: false - - /bcp-47-match@2.0.3: + bcp-47-match@2.0.3: resolution: { integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ== } - dev: false - /bcp-47@2.1.0: + bcp-47@2.1.0: resolution: { integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w== } - dependencies: - is-alphabetical: 2.0.1 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - dev: false - /binary-extensions@2.2.0: + binary-extensions@2.3.0: resolution: { - integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== } engines: { node: ">=8" } - dev: false - /bl@4.1.0: - resolution: - { - integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - } - requiresBuild: true - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: false - optional: true - - /bl@5.1.0: - resolution: - { - integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== - } - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: false - - /boolbase@1.0.0: + boolbase@1.0.0: resolution: { integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== } - dev: false - /boxen@7.1.1: + boxen@8.0.1: resolution: { - integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog== + integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw== } - engines: { node: ">=14.16" } - dependencies: - ansi-align: 3.0.1 - camelcase: 7.0.1 - chalk: 5.3.0 - cli-boxes: 3.0.0 - string-width: 5.1.2 - type-fest: 2.19.0 - widest-line: 4.0.1 - wrap-ansi: 8.1.0 - dev: false + engines: { node: ">=18" } - /brace-expansion@1.1.11: + brace-expansion@1.1.11: resolution: { integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== } - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: true - /braces@3.0.2: + braces@3.0.3: resolution: { - integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== } engines: { node: ">=8" } - dependencies: - fill-range: 7.0.1 - dev: false - /browserslist@4.23.0: - resolution: - { - integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } - hasBin: true - dependencies: - caniuse-lite: 1.0.30001588 - electron-to-chromium: 1.4.677 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) - dev: false - - /buffer@5.7.1: - resolution: - { - integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - } - requiresBuild: true - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: false - optional: true - - /buffer@6.0.3: - resolution: - { - integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - } - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: false - - /callsites@3.1.0: + callsites@3.1.0: resolution: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } engines: { node: ">=6" } - dev: true - /camelcase@7.0.1: + camelcase@8.0.0: resolution: { - integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== + integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== } - engines: { node: ">=14.16" } - dev: false + engines: { node: ">=16" } - /caniuse-lite@1.0.30001588: - resolution: - { - integrity: sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ== - } - dev: false - - /ccount@2.0.1: + ccount@2.0.1: resolution: { integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== } - dev: false - /chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - } - engines: { node: ">=4" } - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: false - - /chalk@4.1.2: + chalk@4.1.2: resolution: { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } engines: { node: ">=10" } - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chalk@5.3.0: + chalk@5.4.1: resolution: { - integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== } engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } - dev: false - /character-entities-html4@2.1.0: + character-entities-html4@2.1.0: resolution: { integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== } - dev: false - /character-entities-legacy@3.0.0: + character-entities-legacy@3.0.0: resolution: { integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== } - dev: false - /character-entities@2.0.2: + character-entities@2.0.2: resolution: { integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== } - dev: false - /character-reference-invalid@2.0.1: + character-reference-invalid@2.0.1: resolution: { integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== } - dev: false - /chokidar@3.6.0: + chokidar@3.6.0: resolution: { integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== } engines: { node: ">= 8.10.0" } - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - dev: false - /chownr@1.1.4: + ci-info@4.1.0: resolution: { - integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - } - requiresBuild: true - dev: false - optional: true - - /ci-info@3.9.0: - resolution: - { - integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + integrity: sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== } engines: { node: ">=8" } - dev: false - /ci-info@4.0.0: - resolution: - { - integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== - } - engines: { node: ">=8" } - dev: false - - /cli-boxes@3.0.0: + cli-boxes@3.0.0: resolution: { integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== } engines: { node: ">=10" } - dev: false - /cli-cursor@4.0.0: + clsx@2.1.1: resolution: { - integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - restore-cursor: 4.0.0 - dev: false - - /cli-spinners@2.9.2: - resolution: - { - integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== } engines: { node: ">=6" } - dev: false - /clsx@2.1.0: - resolution: - { - integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== - } - engines: { node: ">=6" } - dev: false - - /collapse-white-space@2.1.0: + collapse-white-space@2.1.0: resolution: { integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw== } - dev: false - /color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - } - dependencies: - color-name: 1.1.3 - dev: false - - /color-convert@2.0.1: + color-convert@2.0.1: resolution: { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } engines: { node: ">=7.0.0" } - dependencies: - color-name: 1.1.4 - /color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - } - dev: false - - /color-name@1.1.4: + color-name@1.1.4: resolution: { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } - /color-string@1.9.1: + color-string@1.9.1: resolution: { integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== } - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - dev: false - /color@4.2.3: + color@4.2.3: resolution: { integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== } engines: { node: ">=12.5.0" } - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - dev: false - /comma-separated-tokens@2.0.3: + comma-separated-tokens@2.0.3: resolution: { integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== } - dev: false - /common-ancestor-path@1.0.1: + common-ancestor-path@1.0.1: resolution: { integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== } - dev: false - /concat-map@0.0.1: + concat-map@0.0.1: resolution: { integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== } - dev: true - /convert-source-map@2.0.0: + consola@3.3.3: resolution: { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + integrity: sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg== } - dev: false + engines: { node: ^14.18.0 || >=16.10.0 } - /cookie@0.6.0: + cookie-es@1.2.2: resolution: { - integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg== + } + + cookie@0.7.2: + resolution: + { + integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== } engines: { node: ">= 0.6" } - dev: false - /cross-spawn@7.0.3: + cross-spawn@7.0.6: resolution: { - integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== } engines: { node: ">= 8" } - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - /css-selector-parser@3.0.4: + crossws@0.3.1: resolution: { - integrity: sha512-pnmS1dbKsz6KA4EW4BznyPL2xxkNDRg62hcD0v8g6DEw2W7hxOln5M953jsp9hmw5Dg57S6o/A8GOn37mbAgcQ== + integrity: sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw== } - dev: false - /cssesc@3.0.0: + css-selector-parser@3.0.5: + resolution: + { + integrity: sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g== + } + + cssesc@3.0.0: resolution: { integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== } engines: { node: ">=4" } hasBin: true - dev: false - /debug@4.3.4: + debug@4.4.0: resolution: { - integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== } engines: { node: ">=6.0" } peerDependencies: @@ -2535,261 +1652,168 @@ packages: peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.1.2 - /decode-named-character-reference@1.0.2: + decode-named-character-reference@1.0.2: resolution: { integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== } - dependencies: - character-entities: 2.0.2 - dev: false - /decompress-response@6.0.0: - resolution: - { - integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - } - engines: { node: ">=10" } - requiresBuild: true - dependencies: - mimic-response: 3.1.0 - dev: false - optional: true - - /deep-extend@0.6.0: - resolution: - { - integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - } - engines: { node: ">=4.0.0" } - requiresBuild: true - dev: false - optional: true - - /deep-is@0.1.4: + deep-is@0.1.4: resolution: { integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== } - dev: true - /dequal@2.0.3: + defu@6.1.4: + resolution: + { + integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + } + + dequal@2.0.3: resolution: { integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== } engines: { node: ">=6" } - dev: false - /detect-libc@2.0.2: + destr@2.0.3: resolution: { - integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ== + } + + detect-libc@2.0.3: + resolution: + { + integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== } engines: { node: ">=8" } - dev: false - /deterministic-object-hash@2.0.2: + deterministic-object-hash@2.0.2: resolution: { integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ== } engines: { node: ">=18" } - dependencies: - base-64: 1.0.0 - dev: false - /devalue@4.3.2: + devalue@5.1.1: resolution: { - integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg== + integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw== } - dev: false - /devlop@1.1.0: + devlop@1.1.0: resolution: { integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== } - dependencies: - dequal: 2.0.3 - dev: false - /diff@5.2.0: + diff@5.2.0: resolution: { integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== } engines: { node: ">=0.3.1" } - dev: false - /direction@2.0.1: + direction@2.0.1: resolution: { integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA== } hasBin: true - dev: false - /dlv@1.1.3: + dlv@1.1.3: resolution: { integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== } - dev: false - /doctrine@3.0.0: + dset@3.1.4: resolution: { - integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - } - engines: { node: ">=6.0.0" } - dependencies: - esutils: 2.0.3 - dev: true - - /dset@3.1.3: - resolution: - { - integrity: sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== + integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== } engines: { node: ">=4" } - dev: false - /eastasianwidth@0.2.0: + emoji-regex-xs@1.0.0: resolution: { - integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== } - dev: false - /electron-to-chromium@1.4.677: + emoji-regex@10.4.0: resolution: { - integrity: sha512-erDa3CaDzwJOpyvfKhOiJjBVNnMM0qxHq47RheVVwsSQrgBA9ZSGV9kdaOfZDPXcHzhG7lBxhj6A7KvfLJBd6Q== + integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== } - dev: false - /emoji-regex@10.3.0: - resolution: - { - integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== - } - dev: false - - /emoji-regex@8.0.0: + emoji-regex@8.0.0: resolution: { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== } - dev: false - /emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - } - dev: false - - /end-of-stream@1.4.4: - resolution: - { - integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - } - requiresBuild: true - dependencies: - once: 1.4.0 - dev: false - optional: true - - /entities@4.5.0: + entities@4.5.0: resolution: { integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== } engines: { node: ">=0.12" } - dev: false - /es-module-lexer@1.4.1: + es-module-lexer@1.6.0: resolution: { - integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== } - dev: false - /esbuild@0.19.12: + esast-util-from-estree@2.0.0: resolution: { - integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== + integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ== + } + + esast-util-from-js@2.0.1: + resolution: + { + integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw== + } + + esbuild@0.21.5: + resolution: + { + integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== } engines: { node: ">=12" } hasBin: true - requiresBuild: true - optionalDependencies: - "@esbuild/aix-ppc64": 0.19.12 - "@esbuild/android-arm": 0.19.12 - "@esbuild/android-arm64": 0.19.12 - "@esbuild/android-x64": 0.19.12 - "@esbuild/darwin-arm64": 0.19.12 - "@esbuild/darwin-x64": 0.19.12 - "@esbuild/freebsd-arm64": 0.19.12 - "@esbuild/freebsd-x64": 0.19.12 - "@esbuild/linux-arm": 0.19.12 - "@esbuild/linux-arm64": 0.19.12 - "@esbuild/linux-ia32": 0.19.12 - "@esbuild/linux-loong64": 0.19.12 - "@esbuild/linux-mips64el": 0.19.12 - "@esbuild/linux-ppc64": 0.19.12 - "@esbuild/linux-riscv64": 0.19.12 - "@esbuild/linux-s390x": 0.19.12 - "@esbuild/linux-x64": 0.19.12 - "@esbuild/netbsd-x64": 0.19.12 - "@esbuild/openbsd-x64": 0.19.12 - "@esbuild/sunos-x64": 0.19.12 - "@esbuild/win32-arm64": 0.19.12 - "@esbuild/win32-ia32": 0.19.12 - "@esbuild/win32-x64": 0.19.12 - dev: false - /escalade@3.1.2: + esbuild@0.24.2: resolution: { - integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== } - engines: { node: ">=6" } - dev: false + engines: { node: ">=18" } + hasBin: true - /escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - } - engines: { node: ">=0.8.0" } - dev: false - - /escape-string-regexp@4.0.0: + escape-string-regexp@4.0.0: resolution: { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } engines: { node: ">=10" } - dev: true - /escape-string-regexp@5.0.0: + escape-string-regexp@5.0.0: resolution: { integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== } engines: { node: ">=12" } - dev: false - /eslint-plugin-prettier@5.1.3(eslint@8.56.0)(prettier@3.2.5): + eslint-plugin-prettier@5.2.1: resolution: { - integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== + integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== } engines: { node: ^14.18.0 || >=16.0.0 } peerDependencies: @@ -2802,3733 +1826,1824 @@ packages: optional: true eslint-config-prettier: optional: true - dependencies: - eslint: 8.56.0 - prettier: 3.2.5 - prettier-linter-helpers: 1.0.0 - synckit: 0.8.8 - dev: true - /eslint-scope@7.2.2: + eslint-scope@8.2.0: resolution: { - integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /eslint-visitor-keys@3.4.3: + eslint-visitor-keys@3.4.3: resolution: { integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== } engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dev: true - /eslint@8.56.0: + eslint-visitor-keys@4.2.0: resolution: { - integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== + integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint@9.17.0: + resolution: + { + integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } hasBin: true - dependencies: - "@eslint-community/eslint-utils": 4.4.0(eslint@8.56.0) - "@eslint-community/regexpp": 4.10.0 - "@eslint/eslintrc": 2.1.4 - "@eslint/js": 8.56.0 - "@humanwhocodes/config-array": 0.11.14 - "@humanwhocodes/module-importer": 1.0.1 - "@nodelib/fs.walk": 1.2.8 - "@ungap/structured-clone": 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true - /espree@9.6.1: + espree@10.3.0: resolution: { - integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - dev: true + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - /esprima@4.0.1: + esprima@4.0.1: resolution: { integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== } engines: { node: ">=4" } hasBin: true - dev: false - /esquery@1.5.0: + esquery@1.6.0: resolution: { - integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== } engines: { node: ">=0.10" } - dependencies: - estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: + esrecurse@4.3.0: resolution: { integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== } engines: { node: ">=4.0" } - dependencies: - estraverse: 5.3.0 - dev: true - /estraverse@5.3.0: + estraverse@5.3.0: resolution: { integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== } engines: { node: ">=4.0" } - dev: true - /estree-util-attach-comments@3.0.0: + estree-util-attach-comments@3.0.0: resolution: { integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw== } - dependencies: - "@types/estree": 1.0.5 - dev: false - /estree-util-build-jsx@3.0.1: + estree-util-build-jsx@3.0.1: resolution: { integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== } - dependencies: - "@types/estree-jsx": 1.0.4 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-walker: 3.0.3 - dev: false - /estree-util-is-identifier-name@3.0.0: + estree-util-is-identifier-name@3.0.0: resolution: { integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== } - dev: false - /estree-util-to-js@2.0.0: + estree-util-scope@1.0.0: + resolution: + { + integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ== + } + + estree-util-to-js@2.0.0: resolution: { integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== } - dependencies: - "@types/estree-jsx": 1.0.4 - astring: 1.8.6 - source-map: 0.7.4 - dev: false - /estree-util-visit@2.0.0: + estree-util-visit@2.0.0: resolution: { integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== } - dependencies: - "@types/estree-jsx": 1.0.4 - "@types/unist": 3.0.2 - dev: false - /estree-walker@3.0.3: + estree-walker@2.0.2: + resolution: + { + integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + } + + estree-walker@3.0.3: resolution: { integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== } - dependencies: - "@types/estree": 1.0.5 - dev: false - /esutils@2.0.3: + esutils@2.0.3: resolution: { integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== } engines: { node: ">=0.10.0" } - dev: true - /eventemitter3@5.0.1: + eventemitter3@5.0.1: resolution: { integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== } - dev: false - /execa@8.0.1: + expressive-code@0.38.3: resolution: { - integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + integrity: sha512-COM04AiUotHCKJgWdn7NtW2lqu8OW8owAidMpkXt1qxrZ9Q2iC7+tok/1qIn2ocGnczvr9paIySgGnEwFeEQ8Q== } - engines: { node: ">=16.17" } - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.2.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - dev: false - /expand-template@2.0.3: - resolution: - { - integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - } - engines: { node: ">=6" } - requiresBuild: true - dev: false - optional: true - - /expressive-code@0.32.4: - resolution: - { - integrity: sha512-r+yUP2JV181tVR2EyYked7lT2W8bvL9o7xpdKU6q60FMU7Wh/DbGtH0jg+WmDxKK1C57iXF9chbBv+BsDPlUEQ== - } - dependencies: - "@expressive-code/core": 0.32.4 - "@expressive-code/plugin-frames": 0.32.4 - "@expressive-code/plugin-shiki": 0.32.4 - "@expressive-code/plugin-text-markers": 0.32.4 - dev: false - - /extend-shallow@2.0.1: - resolution: - { - integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - } - engines: { node: ">=0.10.0" } - dependencies: - is-extendable: 0.1.1 - dev: false - - /extend@3.0.2: + extend@3.0.2: resolution: { integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== } - dev: false - /fast-deep-equal@3.1.3: + fast-deep-equal@3.1.3: resolution: { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } - dev: true - /fast-diff@1.3.0: + fast-diff@1.3.0: resolution: { integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== } - dev: true - /fast-fifo@1.3.2: + fast-glob@3.3.3: resolution: { - integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== - } - requiresBuild: true - dev: false - optional: true - - /fast-glob@3.3.2: - resolution: - { - integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== } engines: { node: ">=8.6.0" } - dependencies: - "@nodelib/fs.stat": 2.0.5 - "@nodelib/fs.walk": 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: false - /fast-json-stable-stringify@2.1.0: + fast-json-stable-stringify@2.1.0: resolution: { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } - dev: true - /fast-levenshtein@2.0.6: + fast-levenshtein@2.0.6: resolution: { integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } - dev: true - /fast-xml-parser@4.3.4: + fast-xml-parser@4.5.1: resolution: { - integrity: sha512-utnwm92SyozgA3hhH2I8qldf2lBqm6qHOICawRNRFu1qMe3+oqr+GcXjGqTmXTMGE5T4eC03kr/rlh5C1IRdZA== + integrity: sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w== } hasBin: true - dependencies: - strnum: 1.0.5 - dev: true - /fastq@1.17.1: + fastq@1.18.0: resolution: { - integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw== } - dependencies: - reusify: 1.0.4 - /file-entry-cache@6.0.1: + file-entry-cache@8.0.0: resolution: { - integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== } - engines: { node: ^10.12.0 || >=12.0.0 } - dependencies: - flat-cache: 3.2.0 - dev: true + engines: { node: ">=16.0.0" } - /fill-range@7.0.1: + fill-range@7.1.1: resolution: { - integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== } engines: { node: ">=8" } - dependencies: - to-regex-range: 5.0.1 - dev: false - /find-up@4.1.0: + find-up-simple@1.0.0: + resolution: + { + integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + } + engines: { node: ">=18" } + + find-up@4.1.0: resolution: { integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== } engines: { node: ">=8" } - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: false - /find-up@5.0.0: + find-up@5.0.0: resolution: { integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== } engines: { node: ">=10" } - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - /find-yarn-workspace-root2@1.2.16: + find-yarn-workspace-root2@1.2.16: resolution: { integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== } - dependencies: - micromatch: 4.0.5 - pkg-dir: 4.2.0 - dev: false - /flat-cache@3.2.0: + flat-cache@4.0.1: resolution: { - integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== } - engines: { node: ^10.12.0 || >=12.0.0 } - dependencies: - flatted: 3.3.0 - keyv: 4.5.4 - rimraf: 3.0.2 - dev: true + engines: { node: ">=16" } - /flatted@3.3.0: + flatted@3.3.2: resolution: { - integrity: sha512-noqGuLw158+DuD9UPRKHpJ2hGxpFyDlYYrfM0mWt4XhT4n0lwzTLh70Tkdyy4kyTmyTT9Bv7bWAJqw7cgkEXDg== + integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== } - dev: true - /flattie@1.1.0: + flattie@1.1.1: resolution: { - integrity: sha512-xU99gDEnciIwJdGcBmNHnzTJ/w5AT+VFJOu6sTB6WM8diOYNA3Sa+K1DiEBQ7XH4QikQq3iFW1U+jRVcotQnBw== + integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ== } engines: { node: ">=8" } - dev: false - /fs-constants@1.0.0: - resolution: - { - integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - } - requiresBuild: true - dev: false - optional: true - - /fs.realpath@1.0.0: - resolution: - { - integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - } - dev: true - - /fsevents@2.3.3: + fsevents@2.3.3: resolution: { integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== } engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] - requiresBuild: true - dev: false - optional: true - /function-bind@1.1.2: + get-east-asian-width@1.3.0: resolution: { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - } - dev: false - - /gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - } - engines: { node: ">=6.9.0" } - dev: false - - /get-east-asian-width@1.2.0: - resolution: - { - integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== } engines: { node: ">=18" } - dev: false - /get-stream@8.0.1: - resolution: - { - integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== - } - engines: { node: ">=16" } - dev: false - - /github-from-package@0.0.0: - resolution: - { - integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== - } - requiresBuild: true - dev: false - optional: true - - /github-slugger@2.0.0: + github-slugger@2.0.0: resolution: { integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== } - dev: false - /glob-parent@5.1.2: + glob-parent@5.1.2: resolution: { integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== } engines: { node: ">= 6" } - dependencies: - is-glob: 4.0.3 - dev: false - /glob-parent@6.0.2: + glob-parent@6.0.2: resolution: { integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== } engines: { node: ">=10.13.0" } - dependencies: - is-glob: 4.0.3 - dev: true - /glob@7.2.3: + globals@14.0.0: resolution: { - integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== } - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true + engines: { node: ">=18" } - /globals@11.12.0: - resolution: - { - integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - } - engines: { node: ">=4" } - dev: false - - /globals@13.24.0: - resolution: - { - integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - } - engines: { node: ">=8" } - dependencies: - type-fest: 0.20.2 - dev: true - - /graceful-fs@4.2.11: + graceful-fs@4.2.11: resolution: { integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== } - dev: false - /graphemer@1.4.0: + h3@1.13.0: resolution: { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + integrity: sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg== } - dev: true - /gray-matter@4.0.3: - resolution: - { - integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== - } - engines: { node: ">=6.0" } - dependencies: - js-yaml: 3.14.1 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - dev: false - - /has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - } - engines: { node: ">=4" } - dev: false - - /has-flag@4.0.0: + has-flag@4.0.0: resolution: { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } engines: { node: ">=8" } - dev: true - /hasown@2.0.1: + hast-util-embedded@3.0.0: resolution: { - integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== + integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA== } - engines: { node: ">= 0.4" } - dependencies: - function-bind: 1.1.2 - dev: false - /hast-util-from-html@2.0.1: + hast-util-format@1.1.0: resolution: { - integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g== + integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA== } - dependencies: - "@types/hast": 3.0.4 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.1 - parse5: 7.1.2 - vfile: 6.0.1 - vfile-message: 4.0.2 - dev: false - /hast-util-from-parse5@7.1.2: + hast-util-from-html@2.0.3: resolution: { - integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw== + integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw== } - dependencies: - "@types/hast": 2.3.10 - "@types/unist": 2.0.10 - hastscript: 7.2.0 - property-information: 6.4.1 - vfile: 5.3.7 - vfile-location: 4.1.0 - web-namespaces: 2.0.1 - dev: false - /hast-util-from-parse5@8.0.1: + hast-util-from-parse5@8.0.2: resolution: { - integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== + integrity: sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A== } - dependencies: - "@types/hast": 3.0.4 - "@types/unist": 3.0.2 - devlop: 1.1.0 - hastscript: 8.0.0 - property-information: 6.4.1 - vfile: 6.0.1 - vfile-location: 5.0.2 - web-namespaces: 2.0.1 - dev: false - /hast-util-has-property@3.0.0: + hast-util-has-property@3.0.0: resolution: { integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA== } - dependencies: - "@types/hast": 3.0.4 - dev: false - /hast-util-is-element@3.0.0: + hast-util-is-body-ok-link@3.0.1: + resolution: + { + integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ== + } + + hast-util-is-element@3.0.0: resolution: { integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== } - dependencies: - "@types/hast": 3.0.4 - dev: false - /hast-util-parse-selector@3.1.1: + hast-util-minify-whitespace@1.0.1: resolution: { - integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA== + integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw== } - dependencies: - "@types/hast": 2.3.10 - dev: false - /hast-util-parse-selector@4.0.0: + hast-util-parse-selector@4.0.0: resolution: { integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== } - dependencies: - "@types/hast": 3.0.4 - dev: false - /hast-util-raw@7.2.3: + hast-util-phrasing@3.0.1: resolution: { - integrity: sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg== + integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ== } - dependencies: - "@types/hast": 2.3.10 - "@types/parse5": 6.0.3 - hast-util-from-parse5: 7.1.2 - hast-util-to-parse5: 7.1.0 - html-void-elements: 2.0.1 - parse5: 6.0.1 - unist-util-position: 4.0.4 - unist-util-visit: 4.1.2 - vfile: 5.3.7 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - /hast-util-raw@9.0.2: + hast-util-raw@9.1.0: resolution: { - integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA== + integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw== } - dependencies: - "@types/hast": 3.0.4 - "@types/unist": 3.0.2 - "@ungap/structured-clone": 1.2.0 - hast-util-from-parse5: 8.0.1 - hast-util-to-parse5: 8.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.1.0 - parse5: 7.1.2 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - /hast-util-select@6.0.2: + hast-util-select@6.0.3: resolution: { - integrity: sha512-hT/SD/d/Meu+iobvgkffo1QecV8WeKWxwsNMzcTJsKw1cKTQKSR/7ArJeURLNJF9HDjp9nVoORyNNJxrvBye8Q== + integrity: sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ== } - dependencies: - "@types/hast": 3.0.4 - "@types/unist": 3.0.2 - bcp-47-match: 2.0.3 - comma-separated-tokens: 2.0.3 - css-selector-parser: 3.0.4 - devlop: 1.1.0 - direction: 2.0.1 - hast-util-has-property: 3.0.0 - hast-util-to-string: 3.0.0 - hast-util-whitespace: 3.0.0 - not: 0.1.0 - nth-check: 2.1.1 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - dev: false - /hast-util-to-estree@3.1.0: + hast-util-to-estree@3.1.1: resolution: { - integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw== + integrity: sha512-IWtwwmPskfSmma9RpzCappDUitC8t5jhAynHhc1m2+5trOgsrp7txscUSavc5Ic8PATyAjfrCK1wgtxh2cICVQ== } - dependencies: - "@types/estree": 1.0.5 - "@types/estree-jsx": 1.0.4 - "@types/hast": 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-attach-comments: 3.0.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - style-to-object: 0.4.4 - unist-util-position: 5.0.0 - zwitch: 2.0.4 - transitivePeerDependencies: - - supports-color - dev: false - /hast-util-to-html@8.0.4: + hast-util-to-html@9.0.4: resolution: { - integrity: sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA== + integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA== } - dependencies: - "@types/hast": 2.3.10 - "@types/unist": 2.0.10 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-raw: 7.2.3 - hast-util-whitespace: 2.0.1 - html-void-elements: 2.0.1 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.3 - zwitch: 2.0.4 - dev: false - /hast-util-to-html@9.0.0: + hast-util-to-jsx-runtime@2.3.2: resolution: { - integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw== + integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg== } - dependencies: - "@types/hast": 3.0.4 - "@types/unist": 3.0.2 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-raw: 9.0.2 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.1.0 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.3 - zwitch: 2.0.4 - dev: false - /hast-util-to-jsx-runtime@2.3.0: - resolution: - { - integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ== - } - dependencies: - "@types/estree": 1.0.5 - "@types/hast": 3.0.4 - "@types/unist": 3.0.2 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - style-to-object: 1.0.5 - unist-util-position: 5.0.0 - vfile-message: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: false - - /hast-util-to-parse5@7.1.0: - resolution: - { - integrity: sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw== - } - dependencies: - "@types/hast": 2.3.10 - comma-separated-tokens: 2.0.3 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - - /hast-util-to-parse5@8.0.0: + hast-util-to-parse5@8.0.0: resolution: { integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== } - dependencies: - "@types/hast": 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: false - /hast-util-to-string@3.0.0: + hast-util-to-string@3.0.1: resolution: { - integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA== + integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A== } - dependencies: - "@types/hast": 3.0.4 - dev: false - /hast-util-whitespace@2.0.1: + hast-util-to-text@4.0.2: resolution: { - integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng== + integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A== } - dev: false - /hast-util-whitespace@3.0.0: + hast-util-whitespace@3.0.0: resolution: { integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== } - dependencies: - "@types/hast": 3.0.4 - dev: false - /hastscript@7.2.0: + hastscript@9.0.0: resolution: { - integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw== + integrity: sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw== } - dependencies: - "@types/hast": 2.3.10 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 3.1.1 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - dev: false - /hastscript@8.0.0: - resolution: - { - integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== - } - dependencies: - "@types/hast": 3.0.4 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 6.4.1 - space-separated-tokens: 2.0.2 - dev: false - - /html-escaper@3.0.3: + html-escaper@3.0.3: resolution: { integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ== } - dev: false - /html-void-elements@2.0.1: - resolution: - { - integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A== - } - dev: false - - /html-void-elements@3.0.0: + html-void-elements@3.0.0: resolution: { integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== } - dev: false - /http-cache-semantics@4.1.1: + html-whitespace-sensitive-tag-names@3.0.1: + resolution: + { + integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA== + } + + http-cache-semantics@4.1.1: resolution: { integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== } - dev: false - /human-signals@5.0.0: + i18next@23.16.8: resolution: { - integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg== } - engines: { node: ">=16.17.0" } - dev: false - /ieee754@1.2.1: + ignore@5.3.2: resolution: { - integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - } - dev: false - - /ignore@5.3.1: - resolution: - { - integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== } engines: { node: ">= 4" } - dev: true - /import-fresh@3.3.0: + import-fresh@3.3.0: resolution: { integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== } engines: { node: ">=6" } - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - /import-meta-resolve@4.0.0: + import-meta-resolve@4.1.0: resolution: { - integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA== + integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== } - dev: false - /imurmurhash@0.1.4: + imurmurhash@0.1.4: resolution: { integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== } engines: { node: ">=0.8.19" } - dev: true - /inflight@1.0.6: + inline-style-parser@0.2.4: resolution: { - integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== } - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true - /inherits@2.0.4: + iron-webcrypto@1.2.1: resolution: { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg== } - /ini@1.3.8: - resolution: - { - integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - } - requiresBuild: true - dev: false - optional: true - - /inline-style-parser@0.1.1: - resolution: - { - integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== - } - dev: false - - /inline-style-parser@0.2.2: - resolution: - { - integrity: sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ== - } - dev: false - - /is-absolute-url@4.0.1: + is-absolute-url@4.0.1: resolution: { integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dev: false - /is-alphabetical@2.0.1: + is-alphabetical@2.0.1: resolution: { integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== } - dev: false - /is-alphanumerical@2.0.1: + is-alphanumerical@2.0.1: resolution: { integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== } - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - dev: false - /is-arrayish@0.3.2: + is-arrayish@0.3.2: resolution: { integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== } - dev: false - /is-binary-path@2.1.0: + is-binary-path@2.1.0: resolution: { integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== } engines: { node: ">=8" } - dependencies: - binary-extensions: 2.2.0 - dev: false - /is-buffer@2.0.5: - resolution: - { - integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - } - engines: { node: ">=4" } - dev: false - - /is-core-module@2.13.1: - resolution: - { - integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - } - dependencies: - hasown: 2.0.1 - dev: false - - /is-decimal@2.0.1: + is-decimal@2.0.1: resolution: { integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== } - dev: false - /is-docker@3.0.0: + is-docker@3.0.0: resolution: { integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } hasBin: true - dev: false - /is-extendable@0.1.1: - resolution: - { - integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - } - engines: { node: ">=0.10.0" } - dev: false - - /is-extglob@2.1.1: + is-extglob@2.1.1: resolution: { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } engines: { node: ">=0.10.0" } - /is-fullwidth-code-point@3.0.0: + is-fullwidth-code-point@3.0.0: resolution: { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } engines: { node: ">=8" } - dev: false - /is-glob@4.0.3: + is-glob@4.0.3: resolution: { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } engines: { node: ">=0.10.0" } - dependencies: - is-extglob: 2.1.1 - /is-hexadecimal@2.0.1: + is-hexadecimal@2.0.1: resolution: { integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== } - dev: false - /is-inside-container@1.0.0: + is-inside-container@1.0.0: resolution: { integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== } engines: { node: ">=14.16" } hasBin: true - dependencies: - is-docker: 3.0.0 - dev: false - /is-interactive@2.0.0: - resolution: - { - integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== - } - engines: { node: ">=12" } - dev: false - - /is-number@7.0.0: + is-number@7.0.0: resolution: { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } engines: { node: ">=0.12.0" } - dev: false - /is-path-inside@3.0.3: - resolution: - { - integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - } - engines: { node: ">=8" } - dev: true - - /is-plain-obj@4.1.0: + is-plain-obj@4.1.0: resolution: { integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== } engines: { node: ">=12" } - dev: false - /is-reference@3.0.2: - resolution: - { - integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg== - } - dependencies: - "@types/estree": 1.0.5 - dev: false - - /is-stream@3.0.0: - resolution: - { - integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dev: false - - /is-unicode-supported@1.3.0: - resolution: - { - integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== - } - engines: { node: ">=12" } - dev: false - - /is-wsl@3.1.0: + is-wsl@3.1.0: resolution: { integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== } engines: { node: ">=16" } - dependencies: - is-inside-container: 1.0.0 - dev: false - /isexe@2.0.0: + isexe@2.0.0: resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } - /js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - } - dev: false - - /js-yaml@3.14.1: + js-yaml@3.14.1: resolution: { integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== } hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: false - /js-yaml@4.1.0: + js-yaml@4.1.0: resolution: { integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== } hasBin: true - dependencies: - argparse: 2.0.1 - /jsesc@2.5.2: - resolution: - { - integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - } - engines: { node: ">=4" } - hasBin: true - dev: false - - /json-buffer@3.0.1: + json-buffer@3.0.1: resolution: { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } - dev: true - /json-schema-traverse@0.4.1: + json-schema-traverse@0.4.1: resolution: { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } - dev: true - /json-stable-stringify-without-jsonify@1.0.1: + json-stable-stringify-without-jsonify@1.0.1: resolution: { integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } - dev: true - /json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - } - engines: { node: ">=6" } - hasBin: true - dev: false - - /keyv@4.5.4: + keyv@4.5.4: resolution: { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } - dependencies: - json-buffer: 3.0.1 - dev: true - /kind-of@6.0.3: - resolution: - { - integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - } - engines: { node: ">=0.10.0" } - dev: false - - /kleur@3.0.3: + kleur@3.0.3: resolution: { integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== } engines: { node: ">=6" } - dev: false - /kleur@4.1.5: + kleur@4.1.5: resolution: { integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== } engines: { node: ">=6" } - dev: false - /levn@0.4.1: + levn@0.4.1: resolution: { integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== } engines: { node: ">= 0.8.0" } - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - /load-yaml-file@0.2.0: + load-yaml-file@0.2.0: resolution: { integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== } engines: { node: ">=6" } - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - dev: false - /locate-path@5.0.0: + locate-path@5.0.0: resolution: { integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== } engines: { node: ">=8" } - dependencies: - p-locate: 4.1.0 - dev: false - /locate-path@6.0.0: + locate-path@6.0.0: resolution: { integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== } engines: { node: ">=10" } - dependencies: - p-locate: 5.0.0 - /lodash.clonedeep@4.5.0: - resolution: - { - integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== - } - dev: true - - /lodash.merge@4.6.2: + lodash.merge@4.6.2: resolution: { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } - dev: true - /log-symbols@5.1.0: - resolution: - { - integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== - } - engines: { node: ">=12" } - dependencies: - chalk: 5.3.0 - is-unicode-supported: 1.3.0 - dev: false - - /longest-streak@3.1.0: + longest-streak@3.1.0: resolution: { integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== } - dev: false - /lru-cache@5.1.1: + lru-cache@10.4.3: resolution: { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== } - dependencies: - yallist: 3.1.1 - dev: false - /lru-cache@6.0.0: + magic-string@0.30.17: resolution: { - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== } - engines: { node: ">=10" } - dependencies: - yallist: 4.0.0 - dev: false - /magic-string@0.30.7: + magicast@0.3.5: resolution: { - integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA== + integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== } - engines: { node: ">=12" } - dependencies: - "@jridgewell/sourcemap-codec": 1.4.15 - dev: false - /markdown-extensions@2.0.0: + markdown-extensions@2.0.0: resolution: { integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== } engines: { node: ">=16" } - dev: false - /markdown-table@3.0.3: + markdown-table@3.0.4: resolution: { - integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== + integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== } - dev: false - /mdast-util-definitions@6.0.0: + mdast-util-definitions@6.0.0: resolution: { integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ== } - dependencies: - "@types/mdast": 4.0.3 - "@types/unist": 3.0.2 - unist-util-visit: 5.0.0 - dev: false - /mdast-util-directive@3.0.0: + mdast-util-directive@3.0.0: resolution: { integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q== } - dependencies: - "@types/mdast": 4.0.3 - "@types/unist": 3.0.2 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - parse-entities: 4.0.1 - stringify-entities: 4.0.3 - unist-util-visit-parents: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-find-and-replace@3.0.1: + mdast-util-find-and-replace@3.0.2: resolution: { - integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA== + integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== } - dependencies: - "@types/mdast": 4.0.3 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - dev: false - /mdast-util-from-markdown@2.0.0: + mdast-util-from-markdown@2.0.2: resolution: { - integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA== + integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== } - dependencies: - "@types/mdast": 4.0.3 - "@types/unist": 3.0.2 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-gfm-autolink-literal@2.0.0: + mdast-util-gfm-autolink-literal@2.0.1: resolution: { - integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg== + integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== } - dependencies: - "@types/mdast": 4.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 - dev: false - /mdast-util-gfm-footnote@2.0.0: + mdast-util-gfm-footnote@2.0.0: resolution: { integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== } - dependencies: - "@types/mdast": 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-gfm-strikethrough@2.0.0: + mdast-util-gfm-strikethrough@2.0.0: resolution: { integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== } - dependencies: - "@types/mdast": 4.0.3 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-gfm-table@2.0.0: + mdast-util-gfm-table@2.0.0: resolution: { integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== } - dependencies: - "@types/mdast": 4.0.3 - devlop: 1.1.0 - markdown-table: 3.0.3 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-gfm-task-list-item@2.0.0: + mdast-util-gfm-task-list-item@2.0.0: resolution: { integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== } - dependencies: - "@types/mdast": 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-gfm@3.0.0: + mdast-util-gfm@3.0.0: resolution: { integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== } - dependencies: - mdast-util-from-markdown: 2.0.0 - mdast-util-gfm-autolink-literal: 2.0.0 - mdast-util-gfm-footnote: 2.0.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-mdx-expression@2.0.0: + mdast-util-mdx-expression@2.0.1: resolution: { - integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw== + integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== } - dependencies: - "@types/estree-jsx": 1.0.4 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-mdx-jsx@3.1.0: + mdast-util-mdx-jsx@3.1.3: resolution: { - integrity: sha512-A8AJHlR7/wPQ3+Jre1+1rq040fX9A4Q1jG8JxmSNp/PLPHg80A6475wxTp3KzHpApFH6yWxFotHrJQA3dXP6/w== + integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ== } - dependencies: - "@types/estree-jsx": 1.0.4 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - "@types/unist": 3.0.2 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - parse-entities: 4.0.1 - stringify-entities: 4.0.3 - unist-util-remove-position: 5.0.0 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-mdx@3.0.0: + mdast-util-mdx@3.0.0: resolution: { integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== } - dependencies: - mdast-util-from-markdown: 2.0.0 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.0 - mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-mdxjs-esm@2.0.1: + mdast-util-mdxjs-esm@2.0.1: resolution: { integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== } - dependencies: - "@types/estree-jsx": 1.0.4 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /mdast-util-phrasing@4.1.0: + mdast-util-phrasing@4.1.0: resolution: { integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== } - dependencies: - "@types/mdast": 4.0.3 - unist-util-is: 6.0.0 - dev: false - /mdast-util-to-hast@13.0.2: + mdast-util-to-hast@13.2.0: resolution: { - integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og== + integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== } - dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - "@ungap/structured-clone": 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - dev: false - /mdast-util-to-hast@13.1.0: + mdast-util-to-markdown@2.1.2: resolution: { - integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== + integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== } - dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - "@ungap/structured-clone": 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - dev: false - /mdast-util-to-markdown@2.1.0: - resolution: - { - integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== - } - dependencies: - "@types/mdast": 4.0.3 - "@types/unist": 3.0.2 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-decode-string: 2.0.0 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - dev: false - - /mdast-util-to-string@4.0.0: + mdast-util-to-string@4.0.0: resolution: { integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== } - dependencies: - "@types/mdast": 4.0.3 - dev: false - /merge-stream@2.0.0: - resolution: - { - integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - } - dev: false - - /merge2@1.4.1: + merge2@1.4.1: resolution: { integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== } engines: { node: ">= 8" } - dev: false - /micromark-core-commonmark@2.0.0: + micromark-core-commonmark@2.0.2: resolution: { - integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA== + integrity: sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w== } - dependencies: - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-directive@3.0.0: + micromark-extension-directive@3.0.2: resolution: { - integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg== + integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA== } - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - parse-entities: 4.0.1 - dev: false - /micromark-extension-gfm-autolink-literal@2.0.0: + micromark-extension-gfm-autolink-literal@2.1.0: resolution: { - integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== + integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== } - dependencies: - micromark-util-character: 2.1.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: resolution: { - integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg== + integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== } - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm-strikethrough@2.0.0: + micromark-extension-gfm-strikethrough@2.1.0: resolution: { - integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw== + integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== } - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm-table@2.0.0: + micromark-extension-gfm-table@2.1.0: resolution: { - integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw== + integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g== } - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm-tagfilter@2.0.0: + micromark-extension-gfm-tagfilter@2.0.0: resolution: { integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== } - dependencies: - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm-task-list-item@2.0.1: + micromark-extension-gfm-task-list-item@2.1.0: resolution: { - integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw== + integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== } - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-gfm@3.0.0: + micromark-extension-gfm@3.0.0: resolution: { integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== } - dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-mdx-expression@3.0.0: + micromark-extension-mdx-expression@3.0.0: resolution: { integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ== } - dependencies: - "@types/estree": 1.0.5 - devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-mdx-jsx@3.0.0: + micromark-extension-mdx-jsx@3.0.1: resolution: { - integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w== + integrity: sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg== } - dependencies: - "@types/acorn": 4.0.6 - "@types/estree": 1.0.5 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - vfile-message: 4.0.2 - dev: false - /micromark-extension-mdx-md@2.0.0: + micromark-extension-mdx-md@2.0.0: resolution: { integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== } - dependencies: - micromark-util-types: 2.0.0 - dev: false - /micromark-extension-mdxjs-esm@3.0.0: + micromark-extension-mdxjs-esm@3.0.0: resolution: { integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== } - dependencies: - "@types/estree": 1.0.5 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.2 - dev: false - /micromark-extension-mdxjs@3.0.0: + micromark-extension-mdxjs@3.0.0: resolution: { integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== } - dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - micromark-extension-mdx-expression: 3.0.0 - micromark-extension-mdx-jsx: 3.0.0 - micromark-extension-mdx-md: 2.0.0 - micromark-extension-mdxjs-esm: 3.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-factory-destination@2.0.0: + micromark-factory-destination@2.0.1: resolution: { - integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== + integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== } - dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-factory-label@2.0.0: + micromark-factory-label@2.0.1: resolution: { - integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== + integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== } - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-factory-mdx-expression@2.0.1: + micromark-factory-mdx-expression@2.0.2: resolution: { - integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg== + integrity: sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw== } - dependencies: - "@types/estree": 1.0.5 - devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.2 - dev: false - /micromark-factory-space@2.0.0: + micromark-factory-space@2.0.1: resolution: { - integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== + integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== } - dependencies: - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-factory-title@2.0.0: + micromark-factory-title@2.0.1: resolution: { - integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== + integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== } - dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-factory-whitespace@2.0.0: + micromark-factory-whitespace@2.0.1: resolution: { - integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== + integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== } - dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-util-character@2.1.0: + micromark-util-character@2.1.1: resolution: { - integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== + integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== } - dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-util-chunked@2.0.0: + micromark-util-chunked@2.0.1: resolution: { - integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== + integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== } - dependencies: - micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-classify-character@2.0.0: + micromark-util-classify-character@2.0.1: resolution: { - integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== + integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== } - dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-util-combine-extensions@2.0.0: + micromark-util-combine-extensions@2.0.1: resolution: { - integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== + integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== } - dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-util-decode-numeric-character-reference@2.0.1: + micromark-util-decode-numeric-character-reference@2.0.2: resolution: { - integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== + integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== } - dependencies: - micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-decode-string@2.0.0: + micromark-util-decode-string@2.0.1: resolution: { - integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== + integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== } - dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-encode@2.0.0: + micromark-util-encode@2.0.1: resolution: { - integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== + integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== } - dev: false - /micromark-util-events-to-acorn@2.0.2: + micromark-util-events-to-acorn@2.0.2: resolution: { integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA== } - dependencies: - "@types/acorn": 4.0.6 - "@types/estree": 1.0.5 - "@types/unist": 3.0.2 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - vfile-message: 4.0.2 - dev: false - /micromark-util-html-tag-name@2.0.0: + micromark-util-html-tag-name@2.0.1: resolution: { - integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== + integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== } - dev: false - /micromark-util-normalize-identifier@2.0.0: + micromark-util-normalize-identifier@2.0.1: resolution: { - integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== + integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== } - dependencies: - micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-resolve-all@2.0.0: + micromark-util-resolve-all@2.0.1: resolution: { - integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== + integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== } - dependencies: - micromark-util-types: 2.0.0 - dev: false - /micromark-util-sanitize-uri@2.0.0: + micromark-util-sanitize-uri@2.0.1: resolution: { - integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== + integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== } - dependencies: - micromark-util-character: 2.1.0 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-subtokenize@2.0.0: + micromark-util-subtokenize@2.0.3: resolution: { - integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg== + integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg== } - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: false - /micromark-util-symbol@2.0.0: + micromark-util-symbol@2.0.1: resolution: { - integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== + integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== } - dev: false - /micromark-util-types@2.0.0: + micromark-util-types@2.0.1: resolution: { - integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== + integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ== } - dev: false - /micromark@4.0.0: + micromark@4.0.1: resolution: { - integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== + integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw== } - dependencies: - "@types/debug": 4.1.12 - debug: 4.3.4 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-subtokenize: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /micromatch@4.0.5: + micromatch@4.0.8: resolution: { - integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== } engines: { node: ">=8.6" } - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - dev: false - /mime@3.0.0: + mime@3.0.0: resolution: { integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== } engines: { node: ">=10.0.0" } hasBin: true - dev: false - /mimic-fn@2.1.0: - resolution: - { - integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - } - engines: { node: ">=6" } - dev: false - - /mimic-fn@4.0.0: - resolution: - { - integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - } - engines: { node: ">=12" } - dev: false - - /mimic-response@3.1.0: - resolution: - { - integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - } - engines: { node: ">=10" } - requiresBuild: true - dev: false - optional: true - - /minimatch@3.1.2: + minimatch@3.1.2: resolution: { integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== } - dependencies: - brace-expansion: 1.1.11 - dev: true - /minimist@1.2.8: + mrmime@2.0.0: resolution: { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== } - requiresBuild: true - dev: false - optional: true + engines: { node: ">=10" } - /mkdirp-classic@0.5.3: + ms@2.1.3: resolution: { - integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - } - requiresBuild: true - dev: false - optional: true - - /ms@2.1.2: - resolution: - { - integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } - /nanoid@3.3.7: + nanoid@3.3.8: resolution: { - integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== } engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true - dev: false - /napi-build-utils@1.0.2: - resolution: - { - integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - } - requiresBuild: true - dev: false - optional: true - - /natural-compare@1.4.0: + natural-compare@1.4.0: resolution: { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } - dev: true - /nlcst-to-string@3.1.1: + neotraverse@0.6.18: resolution: { - integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw== + integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA== } - dependencies: - "@types/nlcst": 1.0.4 - dev: false + engines: { node: ">= 10" } - /node-abi@3.55.0: + nlcst-to-string@4.0.0: resolution: { - integrity: sha512-uPEjtyh2tFEvWYt4Jw7McOD5FPcHkcxm/tHZc5PWaDB3JYq0rGFUbgaAK+CT5pYpQddBfsZVWI08OwoRfdfbcQ== + integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA== } - engines: { node: ">=10" } - requiresBuild: true - dependencies: - semver: 7.6.0 - dev: false - optional: true - /node-addon-api@6.1.0: + node-fetch-native@1.6.4: resolution: { - integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ== } - requiresBuild: true - dev: false - optional: true - /node-releases@2.0.14: - resolution: - { - integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - } - dev: false - - /normalize-path@3.0.0: + normalize-path@3.0.0: resolution: { integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== } engines: { node: ">=0.10.0" } - dev: false - /not@0.1.0: - resolution: - { - integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA== - } - dev: false - - /npm-run-path@5.2.0: - resolution: - { - integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg== - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - path-key: 4.0.0 - dev: false - - /nth-check@2.1.1: + nth-check@2.1.1: resolution: { integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== } - dependencies: - boolbase: 1.0.0 - dev: false - /once@1.4.0: + ofetch@1.4.1: resolution: { - integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw== } - dependencies: - wrappy: 1.0.2 - /onetime@5.1.2: + ohash@1.1.4: resolution: { - integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g== } - engines: { node: ">=6" } - dependencies: - mimic-fn: 2.1.0 - dev: false - /onetime@6.0.0: + oniguruma-to-es@0.10.0: resolution: { - integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + integrity: sha512-zapyOUOCJxt+xhiNRPPMtfJkHGsZ98HHB9qJEkdT8BGytO/+kpe4m1Ngf0MzbzTmhacn11w9yGeDP6tzDhnCdg== } - engines: { node: ">=12" } - dependencies: - mimic-fn: 4.0.0 - dev: false - /optionator@0.9.3: + optionator@0.9.4: resolution: { - integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== } engines: { node: ">= 0.8.0" } - dependencies: - "@aashutoshrathi/word-wrap": 1.2.6 - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - /ora@7.0.1: - resolution: - { - integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== - } - engines: { node: ">=16" } - dependencies: - chalk: 5.3.0 - cli-cursor: 4.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - string-width: 6.1.0 - strip-ansi: 7.1.0 - dev: false - - /p-limit@2.3.0: + p-limit@2.3.0: resolution: { integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== } engines: { node: ">=6" } - dependencies: - p-try: 2.2.0 - dev: false - /p-limit@3.1.0: + p-limit@3.1.0: resolution: { integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== } engines: { node: ">=10" } - dependencies: - yocto-queue: 0.1.0 - /p-limit@5.0.0: + p-limit@6.2.0: resolution: { - integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== + integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA== } engines: { node: ">=18" } - dependencies: - yocto-queue: 1.0.0 - dev: false - /p-locate@4.1.0: + p-locate@4.1.0: resolution: { integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== } engines: { node: ">=8" } - dependencies: - p-limit: 2.3.0 - dev: false - /p-locate@5.0.0: + p-locate@5.0.0: resolution: { integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== } engines: { node: ">=10" } - dependencies: - p-limit: 3.1.0 - /p-queue@8.0.1: + p-queue@8.0.1: resolution: { integrity: sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== } engines: { node: ">=18" } - dependencies: - eventemitter3: 5.0.1 - p-timeout: 6.1.2 - dev: false - /p-timeout@6.1.2: + p-timeout@6.1.4: resolution: { - integrity: sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== + integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg== } engines: { node: ">=14.16" } - dev: false - /p-try@2.2.0: + p-try@2.2.0: resolution: { integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== } engines: { node: ">=6" } - dev: false - /pagefind@1.0.4: + pagefind@1.3.0: resolution: { - integrity: sha512-oRIizYe+zSI2Jw4zcMU0ebDZm27751hRFiSOBLwc1OIYMrsZKk+3m8p9EVaOmc6zZdtqwwdilNUNxXvBeHcP9w== + integrity: sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw== } hasBin: true - optionalDependencies: - "@pagefind/darwin-arm64": 1.0.4 - "@pagefind/darwin-x64": 1.0.4 - "@pagefind/linux-arm64": 1.0.4 - "@pagefind/linux-x64": 1.0.4 - "@pagefind/windows-x64": 1.0.4 - dev: false - /parent-module@1.0.1: + parent-module@1.0.1: resolution: { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } engines: { node: ">=6" } - dependencies: - callsites: 3.1.0 - dev: true - /parse-entities@4.0.1: + parse-entities@4.0.2: resolution: { - integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== + integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== } - dependencies: - "@types/unist": 2.0.10 - character-entities: 2.0.2 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.0.2 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - dev: false - /parse-latin@5.0.1: + parse-latin@7.0.0: resolution: { - integrity: sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg== + integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ== } - dependencies: - nlcst-to-string: 3.1.1 - unist-util-modify-children: 3.1.1 - unist-util-visit-children: 2.0.2 - dev: false - /parse5@6.0.1: + parse5@7.2.1: resolution: { - integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== } - dev: false - /parse5@7.1.2: - resolution: - { - integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== - } - dependencies: - entities: 4.5.0 - dev: false - - /path-exists@4.0.0: + path-exists@4.0.0: resolution: { integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== } engines: { node: ">=8" } - /path-is-absolute@1.0.1: - resolution: - { - integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - } - engines: { node: ">=0.10.0" } - dev: true - - /path-key@3.1.1: + path-key@3.1.1: resolution: { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } engines: { node: ">=8" } - /path-key@4.0.0: + pathe@1.1.2: resolution: { - integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== } - engines: { node: ">=12" } - dev: false - /path-parse@1.0.7: + picocolors@1.1.1: resolution: { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== } - dev: false - /path-to-regexp@6.2.1: - resolution: - { - integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== - } - dev: false - - /periscopic@3.1.0: - resolution: - { - integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw== - } - dependencies: - "@types/estree": 1.0.5 - estree-walker: 3.0.3 - is-reference: 3.0.2 - dev: false - - /picocolors@1.0.0: - resolution: - { - integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - } - dev: false - - /picomatch@2.3.1: + picomatch@2.3.1: resolution: { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } engines: { node: ">=8.6" } - dev: false - /pify@4.0.1: + picomatch@4.0.2: + resolution: + { + integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + } + engines: { node: ">=12" } + + pify@4.0.1: resolution: { integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== } engines: { node: ">=6" } - dev: false - /pkg-dir@4.2.0: + pkg-dir@4.2.0: resolution: { integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== } engines: { node: ">=8" } - dependencies: - find-up: 4.1.0 - dev: false - /postcss-nested@6.0.1(postcss@8.4.35): + postcss-nested@6.2.0: resolution: { - integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== } engines: { node: ">=12.0" } peerDependencies: postcss: ^8.2.14 - dependencies: - postcss: 8.4.35 - postcss-selector-parser: 6.0.15 - dev: false - /postcss-selector-parser@6.0.15: + postcss-selector-parser@6.1.2: resolution: { - integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== + integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== } engines: { node: ">=4" } - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: false - /postcss@8.4.35: + postcss@8.4.49: resolution: { - integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== + integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== } engines: { node: ^10 || ^12 || >=14 } - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: false - /prebuild-install@7.1.1: + preferred-pm@4.0.0: resolution: { - integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + integrity: sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw== } - engines: { node: ">=10" } - hasBin: true - requiresBuild: true - dependencies: - detect-libc: 2.0.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 3.55.0 - pump: 3.0.0 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 - dev: false - optional: true + engines: { node: ">=18.12" } - /preferred-pm@3.1.2: - resolution: - { - integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q== - } - engines: { node: ">=10" } - dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 - dev: false - - /prelude-ls@1.2.1: + prelude-ls@1.2.1: resolution: { integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== } engines: { node: ">= 0.8.0" } - dev: true - /prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.0: resolution: { integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== } engines: { node: ">=6.0.0" } - dependencies: - fast-diff: 1.3.0 - dev: true - /prettier-plugin-astro@0.13.0: + prettier-plugin-astro@0.14.1: resolution: { - integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g== + integrity: sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw== } engines: { node: ^14.15.0 || >=16.0.0 } - dependencies: - "@astrojs/compiler": 1.8.2 - prettier: 3.2.5 - sass-formatter: 0.7.9 - dev: true - /prettier@3.2.5: + prettier@3.4.2: resolution: { - integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== } engines: { node: ">=14" } hasBin: true - dev: true - /prismjs@1.29.0: + prismjs@1.29.0: resolution: { integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== } engines: { node: ">=6" } - dev: false - /prompts@2.4.2: + prompts@2.4.2: resolution: { integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== } engines: { node: ">= 6" } - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: false - /property-information@6.4.1: + property-information@6.5.0: resolution: { - integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w== + integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== } - dev: false - /pump@3.0.0: - resolution: - { - integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - } - requiresBuild: true - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - dev: false - optional: true - - /punycode@2.3.1: + punycode@2.3.1: resolution: { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } engines: { node: ">=6" } - dev: true - /queue-microtask@1.2.3: + queue-microtask@1.2.3: resolution: { integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== } - /queue-tick@1.0.1: + radix3@1.1.2: resolution: { - integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA== } - requiresBuild: true - dev: false - optional: true - /rc@1.2.8: - resolution: - { - integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - } - hasBin: true - requiresBuild: true - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - dev: false - optional: true - - /readable-stream@3.6.2: - resolution: - { - integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - } - engines: { node: ">= 6" } - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: false - - /readdirp@3.6.0: + readdirp@3.6.0: resolution: { integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== } engines: { node: ">=8.10.0" } - dependencies: - picomatch: 2.3.1 - dev: false - /rehype-external-links@3.0.0: + recma-build-jsx@1.0.0: + resolution: + { + integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew== + } + + recma-jsx@1.0.0: + resolution: + { + integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q== + } + + recma-parse@1.0.0: + resolution: + { + integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ== + } + + recma-stringify@1.0.0: + resolution: + { + integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g== + } + + regenerator-runtime@0.14.1: + resolution: + { + integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + } + + regex-recursion@5.1.1: + resolution: + { + integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w== + } + + regex-utilities@2.3.0: + resolution: + { + integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + } + + regex@5.1.1: + resolution: + { + integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw== + } + + rehype-expressive-code@0.38.3: + resolution: + { + integrity: sha512-RYSSDkMBikoTbycZPkcWp6ELneANT4eTpND1DSRJ6nI2eVFUwTBDCvE2vO6jOOTaavwnPiydi4i/87NRyjpdOA== + } + + rehype-external-links@3.0.0: resolution: { integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw== } - dependencies: - "@types/hast": 3.0.4 - "@ungap/structured-clone": 1.2.0 - hast-util-is-element: 3.0.0 - is-absolute-url: 4.0.1 - space-separated-tokens: 2.0.2 - unist-util-visit: 5.0.0 - dev: false - /rehype-parse@9.0.0: + rehype-format@5.0.1: resolution: { - integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw== + integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ== } - dependencies: - "@types/hast": 3.0.4 - hast-util-from-html: 2.0.1 - unified: 11.0.4 - dev: false - /rehype-raw@7.0.0: + rehype-parse@9.0.1: + resolution: + { + integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag== + } + + rehype-raw@7.0.0: resolution: { integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== } - dependencies: - "@types/hast": 3.0.4 - hast-util-raw: 9.0.2 - vfile: 6.0.1 - dev: false - /rehype-stringify@10.0.0: + rehype-recma@1.0.0: resolution: { - integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ== + integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw== } - dependencies: - "@types/hast": 3.0.4 - hast-util-to-html: 9.0.0 - unified: 11.0.4 - dev: false - /rehype@13.0.1: + rehype-stringify@10.0.1: resolution: { - integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg== + integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA== } - dependencies: - "@types/hast": 3.0.4 - rehype-parse: 9.0.0 - rehype-stringify: 10.0.0 - unified: 11.0.4 - dev: false - /remark-directive@3.0.0: + rehype@13.0.2: + resolution: + { + integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A== + } + + remark-directive@3.0.0: resolution: { integrity: sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA== } - dependencies: - "@types/mdast": 4.0.3 - mdast-util-directive: 3.0.0 - micromark-extension-directive: 3.0.0 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: false - /remark-expressive-code@0.32.4: - resolution: - { - integrity: sha512-khV7fVBpVDOyz9EXU+6MFwLj7BtY3DLVlNMMJYQcfp9ksLMxG/i83rIJbMUZCRof9bDBmFFlrF0VDvqJ0/MNeQ== - } - dependencies: - expressive-code: 0.32.4 - hast-util-to-html: 8.0.4 - unist-util-visit: 4.1.2 - dev: false - - /remark-gfm@4.0.0: + remark-gfm@4.0.0: resolution: { integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== } - dependencies: - "@types/mdast": 4.0.3 - mdast-util-gfm: 3.0.0 - micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 - remark-stringify: 11.0.0 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: false - /remark-mdx@3.0.1: + remark-mdx@3.1.0: resolution: { - integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA== + integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA== } - dependencies: - mdast-util-mdx: 3.0.0 - micromark-extension-mdxjs: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /remark-parse@11.0.0: + remark-parse@11.0.0: resolution: { integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== } - dependencies: - "@types/mdast": 4.0.3 - mdast-util-from-markdown: 2.0.0 - micromark-util-types: 2.0.0 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: false - /remark-rehype@11.1.0: + remark-rehype@11.1.1: resolution: { - integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== } - dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.3 - mdast-util-to-hast: 13.0.2 - unified: 11.0.4 - vfile: 6.0.1 - dev: false - /remark-smartypants@2.1.0: + remark-smartypants@3.0.2: resolution: { - integrity: sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw== + integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA== } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - retext: 8.1.0 - retext-smartypants: 5.2.0 - unist-util-visit: 5.0.0 - dev: false + engines: { node: ">=16.0.0" } - /remark-stringify@11.0.0: + remark-stringify@11.0.0: resolution: { integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== } - dependencies: - "@types/mdast": 4.0.3 - mdast-util-to-markdown: 2.1.0 - unified: 11.0.4 - dev: false - /resolve-from@4.0.0: + resolve-from@4.0.0: resolution: { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } engines: { node: ">=4" } - dev: true - /resolve@1.22.8: + retext-latin@4.0.0: resolution: { - integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA== } - hasBin: true - dependencies: - is-core-module: 2.13.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: false - /restore-cursor@4.0.0: + retext-smartypants@6.2.0: resolution: { - integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ== } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: false - /retext-latin@3.1.0: + retext-stringify@4.0.0: resolution: { - integrity: sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ== + integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA== } - dependencies: - "@types/nlcst": 1.0.4 - parse-latin: 5.0.1 - unherit: 3.0.1 - unified: 10.1.2 - dev: false - /retext-smartypants@5.2.0: + retext@9.0.0: resolution: { - integrity: sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw== + integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA== } - dependencies: - "@types/nlcst": 1.0.4 - nlcst-to-string: 3.1.1 - unified: 10.1.2 - unist-util-visit: 4.1.2 - dev: false - /retext-stringify@3.1.0: - resolution: - { - integrity: sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w== - } - dependencies: - "@types/nlcst": 1.0.4 - nlcst-to-string: 3.1.1 - unified: 10.1.2 - dev: false - - /retext@8.1.0: - resolution: - { - integrity: sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q== - } - dependencies: - "@types/nlcst": 1.0.4 - retext-latin: 3.1.0 - retext-stringify: 3.1.0 - unified: 10.1.2 - dev: false - - /reusify@1.0.4: + reusify@1.0.4: resolution: { integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== } engines: { iojs: ">=1.0.0", node: ">=0.10.0" } - /rimraf@3.0.2: + rollup@4.30.0: resolution: { - integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - } - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /rollup@4.12.0: - resolution: - { - integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q== + integrity: sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA== } engines: { node: ">=18.0.0", npm: ">=8.0.0" } hasBin: true - dependencies: - "@types/estree": 1.0.5 - optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.12.0 - "@rollup/rollup-android-arm64": 4.12.0 - "@rollup/rollup-darwin-arm64": 4.12.0 - "@rollup/rollup-darwin-x64": 4.12.0 - "@rollup/rollup-linux-arm-gnueabihf": 4.12.0 - "@rollup/rollup-linux-arm64-gnu": 4.12.0 - "@rollup/rollup-linux-arm64-musl": 4.12.0 - "@rollup/rollup-linux-riscv64-gnu": 4.12.0 - "@rollup/rollup-linux-x64-gnu": 4.12.0 - "@rollup/rollup-linux-x64-musl": 4.12.0 - "@rollup/rollup-win32-arm64-msvc": 4.12.0 - "@rollup/rollup-win32-ia32-msvc": 4.12.0 - "@rollup/rollup-win32-x64-msvc": 4.12.0 - fsevents: 2.3.3 - dev: false - /run-parallel@1.2.0: + run-parallel@1.2.0: resolution: { integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== } - dependencies: - queue-microtask: 1.2.3 - /s.color@0.0.15: + s.color@0.0.15: resolution: { integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA== } - dev: true - /safe-buffer@5.2.1: - resolution: - { - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - } - dev: false - - /sass-formatter@0.7.9: + sass-formatter@0.7.9: resolution: { integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw== } - dependencies: - suf-log: 2.5.3 - dev: true - /sax@1.3.0: + sax@1.4.1: resolution: { - integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== } - /section-matter@1.0.0: + semver@7.6.3: resolution: { - integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== - } - engines: { node: ">=4" } - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - dev: false - - /semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - } - hasBin: true - dev: false - - /semver@7.6.0: - resolution: - { - integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== } engines: { node: ">=10" } hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: false - /sharp@0.32.6: + sharp@0.33.5: resolution: { - integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== + integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== } - engines: { node: ">=14.15.0" } - requiresBuild: true - dependencies: - color: 4.2.3 - detect-libc: 2.0.2 - node-addon-api: 6.1.0 - prebuild-install: 7.1.1 - semver: 7.6.0 - simple-get: 4.0.1 - tar-fs: 3.0.5 - tunnel-agent: 0.6.0 - dev: false - optional: true + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } - /sharp@0.33.2: - resolution: - { - integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ== - } - engines: { libvips: ">=8.15.1", node: ^18.17.0 || ^20.3.0 || >=21.0.0 } - requiresBuild: true - dependencies: - color: 4.2.3 - detect-libc: 2.0.2 - semver: 7.6.0 - optionalDependencies: - "@img/sharp-darwin-arm64": 0.33.2 - "@img/sharp-darwin-x64": 0.33.2 - "@img/sharp-libvips-darwin-arm64": 1.0.1 - "@img/sharp-libvips-darwin-x64": 1.0.1 - "@img/sharp-libvips-linux-arm": 1.0.1 - "@img/sharp-libvips-linux-arm64": 1.0.1 - "@img/sharp-libvips-linux-s390x": 1.0.1 - "@img/sharp-libvips-linux-x64": 1.0.1 - "@img/sharp-libvips-linuxmusl-arm64": 1.0.1 - "@img/sharp-libvips-linuxmusl-x64": 1.0.1 - "@img/sharp-linux-arm": 0.33.2 - "@img/sharp-linux-arm64": 0.33.2 - "@img/sharp-linux-s390x": 0.33.2 - "@img/sharp-linux-x64": 0.33.2 - "@img/sharp-linuxmusl-arm64": 0.33.2 - "@img/sharp-linuxmusl-x64": 0.33.2 - "@img/sharp-wasm32": 0.33.2 - "@img/sharp-win32-ia32": 0.33.2 - "@img/sharp-win32-x64": 0.33.2 - dev: false - - /shebang-command@2.0.0: + shebang-command@2.0.0: resolution: { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } engines: { node: ">=8" } - dependencies: - shebang-regex: 3.0.0 - /shebang-regex@3.0.0: + shebang-regex@3.0.0: resolution: { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } engines: { node: ">=8" } - /shikiji-core@0.9.19: + shiki@1.26.1: resolution: { - integrity: sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw== + integrity: sha512-Gqg6DSTk3wYqaZ5OaYtzjcdxcBvX5kCy24yvRJEgjT5U+WHlmqCThLuBUx0juyxQBi+6ug53IGeuQS07DWwpcw== } - dev: false - /shikiji@0.8.7: - resolution: - { - integrity: sha512-j5usxwI0yHkDTHOuhuSJl9+wT5CNYeYO82dJMSJBlJ/NYT5SIebGcPoL6y9QOyH15wGrJC4LOP2nz5k8mUDGRQ== - } - dependencies: - hast-util-to-html: 9.0.0 - dev: false - - /shikiji@0.9.19: - resolution: - { - integrity: sha512-Kw2NHWktdcdypCj1GkKpXH4o6Vxz8B8TykPlPuLHOGSV8VkhoCLcFOH4k19K4LXAQYRQmxg+0X/eM+m2sLhAkg== - } - dependencies: - shikiji-core: 0.9.19 - dev: false - - /signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - } - dev: false - - /signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - } - engines: { node: ">=14" } - dev: false - - /simple-concat@1.0.1: - resolution: - { - integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - } - requiresBuild: true - dev: false - optional: true - - /simple-get@4.0.1: - resolution: - { - integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== - } - requiresBuild: true - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - dev: false - optional: true - - /simple-swizzle@0.2.2: + simple-swizzle@0.2.2: resolution: { integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== } - dependencies: - is-arrayish: 0.3.2 - dev: false - /sisteransi@1.0.5: + sisteransi@1.0.5: resolution: { integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== } - dev: false - /sitemap@7.1.1: + sitemap@8.0.0: resolution: { - integrity: sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== + integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A== } - engines: { node: ">=12.0.0", npm: ">=5.6.0" } + engines: { node: ">=14.0.0", npm: ">=6.0.0" } hasBin: true - dependencies: - "@types/node": 17.0.45 - "@types/sax": 1.2.7 - arg: 5.0.2 - sax: 1.3.0 - dev: false - /source-map-js@1.0.2: + source-map-js@1.2.1: resolution: { - integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== } engines: { node: ">=0.10.0" } - dev: false - /source-map@0.7.4: + source-map@0.7.4: resolution: { integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== } engines: { node: ">= 8" } - dev: false - /space-separated-tokens@2.0.2: + space-separated-tokens@2.0.2: resolution: { integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== } - dev: false - /sprintf-js@1.0.3: + sprintf-js@1.0.3: resolution: { integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== } - dev: false - /stdin-discarder@0.1.0: + stream-replace-string@2.0.0: resolution: { - integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== + integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w== } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - bl: 5.1.0 - dev: false - /streamx@2.16.1: - resolution: - { - integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== - } - requiresBuild: true - dependencies: - fast-fifo: 1.3.2 - queue-tick: 1.0.1 - optionalDependencies: - bare-events: 2.2.0 - dev: false - optional: true - - /string-width@4.2.3: + string-width@4.2.3: resolution: { integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== } engines: { node: ">=8" } - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: false - /string-width@5.1.2: + string-width@7.2.0: resolution: { - integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - } - engines: { node: ">=12" } - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - dev: false - - /string-width@6.1.0: - resolution: - { - integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== - } - engines: { node: ">=16" } - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 10.3.0 - strip-ansi: 7.1.0 - dev: false - - /string-width@7.1.0: - resolution: - { - integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== } engines: { node: ">=18" } - dependencies: - emoji-regex: 10.3.0 - get-east-asian-width: 1.2.0 - strip-ansi: 7.1.0 - dev: false - /string_decoder@1.3.0: + stringify-entities@4.0.4: resolution: { - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== } - dependencies: - safe-buffer: 5.2.1 - dev: false - /stringify-entities@4.0.3: - resolution: - { - integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== - } - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - dev: false - - /strip-ansi@6.0.1: + strip-ansi@6.0.1: resolution: { integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== } engines: { node: ">=8" } - dependencies: - ansi-regex: 5.0.1 - /strip-ansi@7.1.0: + strip-ansi@7.1.0: resolution: { integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== } engines: { node: ">=12" } - dependencies: - ansi-regex: 6.0.1 - dev: false - /strip-bom-string@1.0.0: - resolution: - { - integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== - } - engines: { node: ">=0.10.0" } - dev: false - - /strip-bom@3.0.0: + strip-bom@3.0.0: resolution: { integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== } engines: { node: ">=4" } - dev: false - /strip-final-newline@3.0.0: - resolution: - { - integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - } - engines: { node: ">=12" } - dev: false - - /strip-json-comments@2.0.1: - resolution: - { - integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - } - engines: { node: ">=0.10.0" } - requiresBuild: true - dev: false - optional: true - - /strip-json-comments@3.1.1: + strip-json-comments@3.1.1: resolution: { integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== } engines: { node: ">=8" } - dev: true - /strnum@1.0.5: + strnum@1.0.5: resolution: { integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== } - dev: true - /style-to-object@0.4.4: + style-to-object@1.0.8: resolution: { - integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg== + integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== } - dependencies: - inline-style-parser: 0.1.1 - dev: false - /style-to-object@1.0.5: - resolution: - { - integrity: sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ== - } - dependencies: - inline-style-parser: 0.2.2 - dev: false - - /suf-log@2.5.3: + suf-log@2.5.3: resolution: { integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow== } - dependencies: - s.color: 0.0.15 - dev: true - /supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - } - engines: { node: ">=4" } - dependencies: - has-flag: 3.0.0 - dev: false - - /supports-color@7.2.0: + supports-color@7.2.0: resolution: { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } engines: { node: ">=8" } - dependencies: - has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: + synckit@0.9.2: resolution: { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - } - engines: { node: ">= 0.4" } - dev: false - - /synckit@0.8.8: - resolution: - { - integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== + integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== } engines: { node: ^14.18.0 || >=16.0.0 } - dependencies: - "@pkgr/core": 0.1.1 - tslib: 2.6.2 - dev: true - /tar-fs@2.1.1: + tinyexec@0.3.2: resolution: { - integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== } - requiresBuild: true - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 2.2.0 - dev: false - optional: true - /tar-fs@3.0.5: - resolution: - { - integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg== - } - requiresBuild: true - dependencies: - pump: 3.0.0 - tar-stream: 3.1.7 - optionalDependencies: - bare-fs: 2.1.5 - bare-path: 2.1.0 - dev: false - optional: true - - /tar-stream@2.2.0: - resolution: - { - integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - } - engines: { node: ">=6" } - requiresBuild: true - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.4 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: false - optional: true - - /tar-stream@3.1.7: - resolution: - { - integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== - } - requiresBuild: true - dependencies: - b4a: 1.6.6 - fast-fifo: 1.3.2 - streamx: 2.16.1 - dev: false - optional: true - - /text-table@0.2.0: - resolution: - { - integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - } - dev: true - - /to-fast-properties@2.0.0: - resolution: - { - integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - } - engines: { node: ">=4" } - dev: false - - /to-regex-range@5.0.1: + to-regex-range@5.0.1: resolution: { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } engines: { node: ">=8.0" } - dependencies: - is-number: 7.0.0 - dev: false - /trim-lines@3.0.1: + trim-lines@3.0.1: resolution: { integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== } - dev: false - /trough@2.2.0: + trough@2.2.0: resolution: { integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== } - dev: false - /tsconfck@3.0.2: + tsconfck@3.1.4: resolution: { - integrity: sha512-6lWtFjwuhS3XI4HsX4Zg0izOI3FU/AI9EGVlPEUMDIhvLPMD4wkiof0WCoDgW7qY+Dy198g4d9miAqUHWHFH6Q== + integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ== } engines: { node: ^18 || >=20 } hasBin: true @@ -6537,510 +3652,3139 @@ packages: peerDependenciesMeta: typescript: optional: true - dev: false - /tslib@2.6.2: + tslib@2.8.1: resolution: { - integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== } - /tunnel-agent@0.6.0: - resolution: - { - integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - } - requiresBuild: true - dependencies: - safe-buffer: 5.2.1 - dev: false - optional: true - - /type-check@0.4.0: + type-check@0.4.0: resolution: { integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== } engines: { node: ">= 0.8.0" } - dependencies: - prelude-ls: 1.2.1 - dev: true - /type-fest@0.20.2: + type-fest@4.31.0: resolution: { - integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + integrity: sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ== } - engines: { node: ">=10" } - dev: true + engines: { node: ">=16" } - /type-fest@2.19.0: + typescript@5.6.3: resolution: { - integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== } - engines: { node: ">=12.20" } - dev: false + engines: { node: ">=14.17" } + hasBin: true - /unherit@3.0.1: + ufo@1.5.4: resolution: { - integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg== + integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== } - dev: false - /unified@10.1.2: + ultrahtml@1.5.3: resolution: { - integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== + integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg== } - dependencies: - "@types/unist": 2.0.10 - bail: 2.0.2 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 5.3.7 - dev: false - /unified@11.0.4: + uncrypto@0.1.3: resolution: { - integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== + integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q== } - dependencies: - "@types/unist": 3.0.2 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.1 - dev: false - /unist-util-is@5.2.1: + unenv@1.10.0: resolution: { - integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== + integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ== } - dependencies: - "@types/unist": 2.0.10 - dev: false - /unist-util-is@6.0.0: + unified@11.0.5: + resolution: + { + integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + } + + unist-util-find-after@5.0.0: + resolution: + { + integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ== + } + + unist-util-is@6.0.0: resolution: { integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== } - dependencies: - "@types/unist": 3.0.2 - dev: false - /unist-util-modify-children@3.1.1: + unist-util-modify-children@4.0.0: resolution: { - integrity: sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA== + integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw== } - dependencies: - "@types/unist": 2.0.10 - array-iterate: 2.0.1 - dev: false - /unist-util-position-from-estree@2.0.0: + unist-util-position-from-estree@2.0.0: resolution: { integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== } - dependencies: - "@types/unist": 3.0.2 - dev: false - /unist-util-position@4.0.4: - resolution: - { - integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg== - } - dependencies: - "@types/unist": 2.0.10 - dev: false - - /unist-util-position@5.0.0: + unist-util-position@5.0.0: resolution: { integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== } - dependencies: - "@types/unist": 3.0.2 - dev: false - /unist-util-remove-position@5.0.0: + unist-util-remove-position@5.0.0: resolution: { integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q== } - dependencies: - "@types/unist": 3.0.2 - unist-util-visit: 5.0.0 - dev: false - /unist-util-remove@4.0.0: - resolution: - { - integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg== - } - dependencies: - "@types/unist": 3.0.2 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - dev: false - - /unist-util-stringify-position@3.0.3: - resolution: - { - integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== - } - dependencies: - "@types/unist": 2.0.10 - dev: false - - /unist-util-stringify-position@4.0.0: + unist-util-stringify-position@4.0.0: resolution: { integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== } - dependencies: - "@types/unist": 3.0.2 - dev: false - /unist-util-visit-children@2.0.2: + unist-util-visit-children@3.0.0: resolution: { - integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q== + integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA== } - dependencies: - "@types/unist": 2.0.10 - dev: false - /unist-util-visit-parents@5.1.3: - resolution: - { - integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== - } - dependencies: - "@types/unist": 2.0.10 - unist-util-is: 5.2.1 - dev: false - - /unist-util-visit-parents@6.0.1: + unist-util-visit-parents@6.0.1: resolution: { integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== } - dependencies: - "@types/unist": 3.0.2 - unist-util-is: 6.0.0 - dev: false - /unist-util-visit@4.1.2: - resolution: - { - integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== - } - dependencies: - "@types/unist": 2.0.10 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - dev: false - - /unist-util-visit@5.0.0: + unist-util-visit@5.0.0: resolution: { integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== } - dependencies: - "@types/unist": 3.0.2 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - dev: false - /update-browserslist-db@1.0.13(browserslist@4.23.0): + unstorage@1.14.4: resolution: { - integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + integrity: sha512-1SYeamwuYeQJtJ/USE1x4l17LkmQBzg7deBJ+U9qOBoHo15d1cDxG4jM31zKRgF7pG0kirZy4wVMX6WL6Zoscg== } - hasBin: true peerDependencies: - browserslist: ">= 4.21.0" - dependencies: - browserslist: 4.23.0 - escalade: 3.1.2 - picocolors: 1.0.0 - dev: false + "@azure/app-configuration": ^1.8.0 + "@azure/cosmos": ^4.2.0 + "@azure/data-tables": ^13.3.0 + "@azure/identity": ^4.5.0 + "@azure/keyvault-secrets": ^4.9.0 + "@azure/storage-blob": ^12.26.0 + "@capacitor/preferences": ^6.0.3 + "@deno/kv": ">=0.8.4" + "@netlify/blobs": ^6.5.0 || ^7.0.0 || ^8.1.0 + "@planetscale/database": ^1.19.0 + "@upstash/redis": ^1.34.3 + "@vercel/blob": ">=0.27.0" + "@vercel/kv": ^1.0.1 + aws4fetch: ^1.0.20 + db0: ">=0.2.1" + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.1 + peerDependenciesMeta: + "@azure/app-configuration": + optional: true + "@azure/cosmos": + optional: true + "@azure/data-tables": + optional: true + "@azure/identity": + optional: true + "@azure/keyvault-secrets": + optional: true + "@azure/storage-blob": + optional: true + "@capacitor/preferences": + optional: true + "@deno/kv": + optional: true + "@netlify/blobs": + optional: true + "@planetscale/database": + optional: true + "@upstash/redis": + optional: true + "@vercel/blob": + optional: true + "@vercel/kv": + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true - /uri-js@4.4.1: + uri-js@4.4.1: resolution: { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } - dependencies: - punycode: 2.3.1 - dev: true - /util-deprecate@1.0.2: + util-deprecate@1.0.2: resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } - dev: false - /vfile-location@4.1.0: + vfile-location@5.0.3: resolution: { - integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw== + integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== } - dependencies: - "@types/unist": 2.0.10 - vfile: 5.3.7 - dev: false - /vfile-location@5.0.2: - resolution: - { - integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg== - } - dependencies: - "@types/unist": 3.0.2 - vfile: 6.0.1 - dev: false - - /vfile-message@3.1.4: - resolution: - { - integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== - } - dependencies: - "@types/unist": 2.0.10 - unist-util-stringify-position: 3.0.3 - dev: false - - /vfile-message@4.0.2: + vfile-message@4.0.2: resolution: { integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== } - dependencies: - "@types/unist": 3.0.2 - unist-util-stringify-position: 4.0.0 - dev: false - /vfile@5.3.7: + vfile@6.0.3: resolution: { - integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== + integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== } - dependencies: - "@types/unist": 2.0.10 - is-buffer: 2.0.5 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - dev: false - /vfile@6.0.1: + vite@6.0.7: resolution: { - integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== + integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ== } - dependencies: - "@types/unist": 3.0.2 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 - dev: false - - /vite@5.1.3: - resolution: - { - integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew== - } - engines: { node: ^18.0.0 || >=20.0.0 } + engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } hasBin: true peerDependencies: - "@types/node": ^18.0.0 || >=20.0.0 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: ">=1.21.0" less: "*" lightningcss: ^1.21.0 sass: "*" + sass-embedded: "*" stylus: "*" sugarss: "*" - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: "@types/node": optional: true + jiti: + optional: true less: optional: true lightningcss: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: optional: true terser: optional: true - dependencies: - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.12.0 - optionalDependencies: - fsevents: 2.3.3 - dev: false + tsx: + optional: true + yaml: + optional: true - /vitefu@0.2.5(vite@5.1.3): + vitefu@1.0.5: resolution: { - integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== + integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA== } peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 peerDependenciesMeta: vite: optional: true - dependencies: - vite: 5.1.3 - dev: false - /web-namespaces@2.0.1: + web-namespaces@2.0.1: resolution: { integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== } - dev: false - /which-pm-runs@1.1.0: + which-pm-runs@1.1.0: resolution: { integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== } engines: { node: ">=4" } - dev: false - /which-pm@2.0.0: + which-pm@3.0.0: resolution: { - integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== + integrity: sha512-ysVYmw6+ZBhx3+ZkcPwRuJi38ZOTLJJ33PSHaitLxSKUMsh0LkKd0nC69zZCwt5D+AYUcMK2hhw4yWny20vSGg== } - engines: { node: ">=8.15" } - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - dev: false + engines: { node: ">=18.12" } - /which-pm@2.1.1: - resolution: - { - integrity: sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ== - } - engines: { node: ">=8.15" } - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - dev: false - - /which@2.0.2: + which@2.0.2: resolution: { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } engines: { node: ">= 8" } hasBin: true - dependencies: - isexe: 2.0.0 - /widest-line@4.0.1: + widest-line@5.0.0: resolution: { - integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA== } - engines: { node: ">=12" } - dependencies: - string-width: 5.1.2 - dev: false + engines: { node: ">=18" } - /wrap-ansi@8.1.0: + word-wrap@1.2.5: resolution: { - integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== } - engines: { node: ">=12" } - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - dev: false + engines: { node: ">=0.10.0" } - /wrappy@1.0.2: + wrap-ansi@9.0.0: resolution: { - integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== } + engines: { node: ">=18" } - /xml-js@1.6.11: + xml-js@1.6.11: resolution: { integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== } hasBin: true - dependencies: - sax: 1.3.0 - dev: true - /yallist@3.1.1: + xxhash-wasm@1.1.0: resolution: { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA== } - dev: false - /yallist@4.0.0: - resolution: - { - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - } - dev: false - - /yargs-parser@21.1.1: + yargs-parser@21.1.1: resolution: { integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== } engines: { node: ">=12" } - dev: false - /yocto-queue@0.1.0: + yocto-queue@0.1.0: resolution: { integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== } engines: { node: ">=10" } - /yocto-queue@1.0.0: + yocto-queue@1.1.1: resolution: { - integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== } engines: { node: ">=12.20" } - dev: false - /zod@3.22.4: + yocto-spinner@0.1.2: resolution: { - integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== + integrity: sha512-VfmLIh/ZSZOJnVRQZc/dvpPP90lWL4G0bmxQMP0+U/2vKBA8GSpcBuWv17y7F+CZItRuO97HN1wdbb4p10uhOg== } - dev: false + engines: { node: ">=18.19" } - /zwitch@2.0.4: + yoctocolors@2.1.1: + resolution: + { + integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ== + } + engines: { node: ">=18" } + + zod-to-json-schema@3.24.1: + resolution: + { + integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w== + } + peerDependencies: + zod: ^3.24.1 + + zod-to-ts@1.2.0: + resolution: + { + integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA== + } + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod@3.24.1: + resolution: + { + integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A== + } + + zwitch@2.0.4: resolution: { integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== } - dev: false + +snapshots: + "@apidevtools/json-schema-ref-parser@11.7.3": + dependencies: + "@jsdevtools/ono": 7.1.3 + "@types/json-schema": 7.0.15 + js-yaml: 4.1.0 + + "@astrojs/compiler@2.10.3": {} + + "@astrojs/internal-helpers@0.4.2": {} + + "@astrojs/markdown-remark@6.0.1": + dependencies: + "@astrojs/prism": 3.2.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + remark-smartypants: 3.0.2 + shiki: 1.26.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + "@astrojs/mdx@4.0.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3))": + dependencies: + "@astrojs/markdown-remark": 6.0.1 + "@mdx-js/mdx": 3.1.0(acorn@8.14.0) + acorn: 8.14.0 + astro: 5.1.3(rollup@4.30.0)(typescript@5.6.3) + es-module-lexer: 1.6.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.4 + kleur: 4.1.5 + rehype-raw: 7.0.0 + remark-gfm: 4.0.0 + remark-smartypants: 3.0.2 + source-map: 0.7.4 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + "@astrojs/prism@3.2.0": + dependencies: + prismjs: 1.29.0 + + "@astrojs/sitemap@3.2.1": + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.24.1 + + "@astrojs/starlight@0.30.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3))": + dependencies: + "@astrojs/mdx": 4.0.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3)) + "@astrojs/sitemap": 3.2.1 + "@pagefind/default-ui": 1.3.0 + "@types/hast": 3.0.4 + "@types/js-yaml": 4.0.9 + "@types/mdast": 4.0.4 + astro: 5.1.3(rollup@4.30.0)(typescript@5.6.3) + astro-expressive-code: 0.38.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3)) + bcp-47: 2.1.0 + hast-util-from-html: 2.0.3 + hast-util-select: 6.0.3 + hast-util-to-string: 3.0.1 + hastscript: 9.0.0 + i18next: 23.16.8 + js-yaml: 4.1.0 + mdast-util-directive: 3.0.0 + mdast-util-to-markdown: 2.1.2 + mdast-util-to-string: 4.0.0 + pagefind: 1.3.0 + rehype: 13.0.2 + rehype-format: 5.0.1 + remark-directive: 3.0.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + "@astrojs/telemetry@3.2.0": + dependencies: + ci-info: 4.1.0 + debug: 4.4.0 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + "@babel/helper-string-parser@7.25.9": {} + + "@babel/helper-validator-identifier@7.25.9": {} + + "@babel/parser@7.26.3": + dependencies: + "@babel/types": 7.26.3 + + "@babel/runtime@7.26.0": + dependencies: + regenerator-runtime: 0.14.1 + + "@babel/types@7.26.3": + dependencies: + "@babel/helper-string-parser": 7.25.9 + "@babel/helper-validator-identifier": 7.25.9 + + "@ctrl/tinycolor@4.1.0": {} + + "@emnapi/runtime@1.3.1": + dependencies: + tslib: 2.8.1 + optional: true + + "@esbuild/aix-ppc64@0.21.5": + optional: true + + "@esbuild/aix-ppc64@0.24.2": + optional: true + + "@esbuild/android-arm64@0.21.5": + optional: true + + "@esbuild/android-arm64@0.24.2": + optional: true + + "@esbuild/android-arm@0.21.5": + optional: true + + "@esbuild/android-arm@0.24.2": + optional: true + + "@esbuild/android-x64@0.21.5": + optional: true + + "@esbuild/android-x64@0.24.2": + optional: true + + "@esbuild/darwin-arm64@0.21.5": + optional: true + + "@esbuild/darwin-arm64@0.24.2": + optional: true + + "@esbuild/darwin-x64@0.21.5": + optional: true + + "@esbuild/darwin-x64@0.24.2": + optional: true + + "@esbuild/freebsd-arm64@0.21.5": + optional: true + + "@esbuild/freebsd-arm64@0.24.2": + optional: true + + "@esbuild/freebsd-x64@0.21.5": + optional: true + + "@esbuild/freebsd-x64@0.24.2": + optional: true + + "@esbuild/linux-arm64@0.21.5": + optional: true + + "@esbuild/linux-arm64@0.24.2": + optional: true + + "@esbuild/linux-arm@0.21.5": + optional: true + + "@esbuild/linux-arm@0.24.2": + optional: true + + "@esbuild/linux-ia32@0.21.5": + optional: true + + "@esbuild/linux-ia32@0.24.2": + optional: true + + "@esbuild/linux-loong64@0.21.5": + optional: true + + "@esbuild/linux-loong64@0.24.2": + optional: true + + "@esbuild/linux-mips64el@0.21.5": + optional: true + + "@esbuild/linux-mips64el@0.24.2": + optional: true + + "@esbuild/linux-ppc64@0.21.5": + optional: true + + "@esbuild/linux-ppc64@0.24.2": + optional: true + + "@esbuild/linux-riscv64@0.21.5": + optional: true + + "@esbuild/linux-riscv64@0.24.2": + optional: true + + "@esbuild/linux-s390x@0.21.5": + optional: true + + "@esbuild/linux-s390x@0.24.2": + optional: true + + "@esbuild/linux-x64@0.21.5": + optional: true + + "@esbuild/linux-x64@0.24.2": + optional: true + + "@esbuild/netbsd-arm64@0.24.2": + optional: true + + "@esbuild/netbsd-x64@0.21.5": + optional: true + + "@esbuild/netbsd-x64@0.24.2": + optional: true + + "@esbuild/openbsd-arm64@0.24.2": + optional: true + + "@esbuild/openbsd-x64@0.21.5": + optional: true + + "@esbuild/openbsd-x64@0.24.2": + optional: true + + "@esbuild/sunos-x64@0.21.5": + optional: true + + "@esbuild/sunos-x64@0.24.2": + optional: true + + "@esbuild/win32-arm64@0.21.5": + optional: true + + "@esbuild/win32-arm64@0.24.2": + optional: true + + "@esbuild/win32-ia32@0.21.5": + optional: true + + "@esbuild/win32-ia32@0.24.2": + optional: true + + "@esbuild/win32-x64@0.21.5": + optional: true + + "@esbuild/win32-x64@0.24.2": + optional: true + + "@eslint-community/eslint-utils@4.4.1(eslint@9.17.0)": + dependencies: + eslint: 9.17.0 + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.1": {} + + "@eslint/config-array@0.19.1": + dependencies: + "@eslint/object-schema": 2.1.5 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + "@eslint/core@0.9.1": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/eslintrc@3.2.0": + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + "@eslint/js@9.17.0": {} + + "@eslint/object-schema@2.1.5": {} + + "@eslint/plugin-kit@0.2.4": + dependencies: + levn: 0.4.1 + + "@expressive-code/core@0.38.3": + dependencies: + "@ctrl/tinycolor": 4.1.0 + hast-util-select: 6.0.3 + hast-util-to-html: 9.0.4 + hast-util-to-text: 4.0.2 + hastscript: 9.0.0 + postcss: 8.4.49 + postcss-nested: 6.2.0(postcss@8.4.49) + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + + "@expressive-code/plugin-frames@0.38.3": + dependencies: + "@expressive-code/core": 0.38.3 + + "@expressive-code/plugin-shiki@0.38.3": + dependencies: + "@expressive-code/core": 0.38.3 + shiki: 1.26.1 + + "@expressive-code/plugin-text-markers@0.38.3": + dependencies: + "@expressive-code/core": 0.38.3 + + "@humanfs/core@0.19.1": {} + + "@humanfs/node@0.16.6": + dependencies: + "@humanfs/core": 0.19.1 + "@humanwhocodes/retry": 0.3.1 + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/retry@0.3.1": {} + + "@humanwhocodes/retry@0.4.1": {} + + "@img/sharp-darwin-arm64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-darwin-arm64": 1.0.4 + optional: true + + "@img/sharp-darwin-x64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-darwin-x64": 1.0.4 + optional: true + + "@img/sharp-libvips-darwin-arm64@1.0.4": + optional: true + + "@img/sharp-libvips-darwin-x64@1.0.4": + optional: true + + "@img/sharp-libvips-linux-arm64@1.0.4": + optional: true + + "@img/sharp-libvips-linux-arm@1.0.5": + optional: true + + "@img/sharp-libvips-linux-s390x@1.0.4": + optional: true + + "@img/sharp-libvips-linux-x64@1.0.4": + optional: true + + "@img/sharp-libvips-linuxmusl-arm64@1.0.4": + optional: true + + "@img/sharp-libvips-linuxmusl-x64@1.0.4": + optional: true + + "@img/sharp-linux-arm64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linux-arm64": 1.0.4 + optional: true + + "@img/sharp-linux-arm@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linux-arm": 1.0.5 + optional: true + + "@img/sharp-linux-s390x@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linux-s390x": 1.0.4 + optional: true + + "@img/sharp-linux-x64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linux-x64": 1.0.4 + optional: true + + "@img/sharp-linuxmusl-arm64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64": 1.0.4 + optional: true + + "@img/sharp-linuxmusl-x64@0.33.5": + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64": 1.0.4 + optional: true + + "@img/sharp-wasm32@0.33.5": + dependencies: + "@emnapi/runtime": 1.3.1 + optional: true + + "@img/sharp-win32-ia32@0.33.5": + optional: true + + "@img/sharp-win32-x64@0.33.5": + optional: true + + "@jridgewell/sourcemap-codec@1.5.0": {} + + "@jsdevtools/ono@7.1.3": {} + + "@mdx-js/mdx@3.1.0(acorn@8.14.0)": + dependencies: + "@types/estree": 1.0.6 + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdx": 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.2 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.14.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + + "@nodelib/fs.scandir@2.1.5": + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: 1.2.0 + + "@nodelib/fs.stat@2.0.5": {} + + "@nodelib/fs.walk@1.2.8": + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: 1.18.0 + + "@oslojs/encoding@1.1.0": {} + + "@pagefind/darwin-arm64@1.3.0": + optional: true + + "@pagefind/darwin-x64@1.3.0": + optional: true + + "@pagefind/default-ui@1.3.0": {} + + "@pagefind/linux-arm64@1.3.0": + optional: true + + "@pagefind/linux-x64@1.3.0": + optional: true + + "@pagefind/windows-x64@1.3.0": + optional: true + + "@pkgr/core@0.1.1": {} + + "@rollup/pluginutils@5.1.4(rollup@4.30.0)": + dependencies: + "@types/estree": 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.30.0 + + "@rollup/rollup-android-arm-eabi@4.30.0": + optional: true + + "@rollup/rollup-android-arm64@4.30.0": + optional: true + + "@rollup/rollup-darwin-arm64@4.30.0": + optional: true + + "@rollup/rollup-darwin-x64@4.30.0": + optional: true + + "@rollup/rollup-freebsd-arm64@4.30.0": + optional: true + + "@rollup/rollup-freebsd-x64@4.30.0": + optional: true + + "@rollup/rollup-linux-arm-gnueabihf@4.30.0": + optional: true + + "@rollup/rollup-linux-arm-musleabihf@4.30.0": + optional: true + + "@rollup/rollup-linux-arm64-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-arm64-musl@4.30.0": + optional: true + + "@rollup/rollup-linux-loongarch64-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-powerpc64le-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-riscv64-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-s390x-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-x64-gnu@4.30.0": + optional: true + + "@rollup/rollup-linux-x64-musl@4.30.0": + optional: true + + "@rollup/rollup-win32-arm64-msvc@4.30.0": + optional: true + + "@rollup/rollup-win32-ia32-msvc@4.30.0": + optional: true + + "@rollup/rollup-win32-x64-msvc@4.30.0": + optional: true + + "@shikijs/core@1.26.1": + dependencies: + "@shikijs/engine-javascript": 1.26.1 + "@shikijs/engine-oniguruma": 1.26.1 + "@shikijs/types": 1.26.1 + "@shikijs/vscode-textmate": 10.0.1 + "@types/hast": 3.0.4 + hast-util-to-html: 9.0.4 + + "@shikijs/engine-javascript@1.26.1": + dependencies: + "@shikijs/types": 1.26.1 + "@shikijs/vscode-textmate": 10.0.1 + oniguruma-to-es: 0.10.0 + + "@shikijs/engine-oniguruma@1.26.1": + dependencies: + "@shikijs/types": 1.26.1 + "@shikijs/vscode-textmate": 10.0.1 + + "@shikijs/langs@1.26.1": + dependencies: + "@shikijs/types": 1.26.1 + + "@shikijs/themes@1.26.1": + dependencies: + "@shikijs/types": 1.26.1 + + "@shikijs/types@1.26.1": + dependencies: + "@shikijs/vscode-textmate": 10.0.1 + "@types/hast": 3.0.4 + + "@shikijs/vscode-textmate@10.0.1": {} + + "@types/acorn@4.0.6": + dependencies: + "@types/estree": 1.0.6 + + "@types/cookie@0.6.0": {} + + "@types/debug@4.1.12": + dependencies: + "@types/ms": 0.7.34 + + "@types/estree-jsx@1.0.5": + dependencies: + "@types/estree": 1.0.6 + + "@types/estree@1.0.6": {} + + "@types/hast@3.0.4": + dependencies: + "@types/unist": 3.0.3 + + "@types/js-yaml@4.0.9": {} + + "@types/json-schema@7.0.15": {} + + "@types/mdast@4.0.4": + dependencies: + "@types/unist": 3.0.3 + + "@types/mdx@2.0.13": {} + + "@types/ms@0.7.34": {} + + "@types/nlcst@2.0.3": + dependencies: + "@types/unist": 3.0.3 + + "@types/node@17.0.45": {} + + "@types/sax@1.2.7": + dependencies: + "@types/node": 17.0.45 + + "@types/unist@2.0.11": {} + + "@types/unist@3.0.3": {} + + "@ungap/structured-clone@1.2.1": {} + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + astring@1.9.0: {} + + astro-expressive-code@0.38.3(astro@5.1.3(rollup@4.30.0)(typescript@5.6.3)): + dependencies: + astro: 5.1.3(rollup@4.30.0)(typescript@5.6.3) + rehype-expressive-code: 0.38.3 + + astro@5.1.3(rollup@4.30.0)(typescript@5.6.3): + dependencies: + "@astrojs/compiler": 2.10.3 + "@astrojs/internal-helpers": 0.4.2 + "@astrojs/markdown-remark": 6.0.1 + "@astrojs/telemetry": 3.2.0 + "@oslojs/encoding": 1.1.0 + "@rollup/pluginutils": 5.1.4(rollup@4.30.0) + "@types/cookie": 0.6.0 + acorn: 8.14.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.1.0 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 0.7.2 + cssesc: 3.0.0 + debug: 4.4.0 + deterministic-object-hash: 2.0.2 + devalue: 5.1.1 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.6.0 + esbuild: 0.21.5 + estree-walker: 3.0.3 + fast-glob: 3.3.3 + flattie: 1.1.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.1.1 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + magicast: 0.3.5 + micromatch: 4.0.8 + mrmime: 2.0.0 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.0.1 + preferred-pm: 4.0.0 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.6.3 + shiki: 1.26.1 + tinyexec: 0.3.2 + tsconfck: 3.1.4(typescript@5.6.3) + ultrahtml: 1.5.3 + unist-util-visit: 5.0.0 + unstorage: 1.14.4 + vfile: 6.0.3 + vite: 6.0.7 + vitefu: 1.0.5(vite@6.0.7) + which-pm: 3.0.0 + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.1.2 + zod: 3.24.1 + zod-to-json-schema: 3.24.1(zod@3.24.1) + zod-to-ts: 1.2.0(typescript@5.6.3)(zod@3.24.1) + optionalDependencies: + sharp: 0.33.5 + transitivePeerDependencies: + - "@azure/app-configuration" + - "@azure/cosmos" + - "@azure/data-tables" + - "@azure/identity" + - "@azure/keyvault-secrets" + - "@azure/storage-blob" + - "@capacitor/preferences" + - "@deno/kv" + - "@netlify/blobs" + - "@planetscale/database" + - "@types/node" + - "@upstash/redis" + - "@vercel/blob" + - "@vercel/kv" + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + base-64@1.0.0: {} + + bcp-47-match@2.0.3: {} + + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + + binary-extensions@2.3.0: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.4.1 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.31.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + camelcase@8.0.0: {} + + ccount@2.0.1: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + ci-info@4.1.0: {} + + cli-boxes@3.0.0: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + comma-separated-tokens@2.0.3: {} + + common-ancestor-path@1.0.1: {} + + concat-map@0.0.1: {} + + consola@3.3.3: {} + + cookie-es@1.2.2: {} + + cookie@0.7.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crossws@0.3.1: + dependencies: + uncrypto: 0.1.3 + + css-selector-parser@3.0.5: {} + + cssesc@3.0.0: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + + deep-is@0.1.4: {} + + defu@6.1.4: {} + + dequal@2.0.3: {} + + destr@2.0.3: {} + + detect-libc@2.0.3: {} + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.1.1: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@5.2.0: {} + + direction@2.0.1: {} + + dlv@1.1.3: {} + + dset@3.1.4: {} + + emoji-regex-xs@1.0.0: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + entities@4.5.0: {} + + es-module-lexer@1.6.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + "@types/estree-jsx": 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + acorn: 8.14.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 + + esbuild@0.21.5: + optionalDependencies: + "@esbuild/aix-ppc64": 0.21.5 + "@esbuild/android-arm": 0.21.5 + "@esbuild/android-arm64": 0.21.5 + "@esbuild/android-x64": 0.21.5 + "@esbuild/darwin-arm64": 0.21.5 + "@esbuild/darwin-x64": 0.21.5 + "@esbuild/freebsd-arm64": 0.21.5 + "@esbuild/freebsd-x64": 0.21.5 + "@esbuild/linux-arm": 0.21.5 + "@esbuild/linux-arm64": 0.21.5 + "@esbuild/linux-ia32": 0.21.5 + "@esbuild/linux-loong64": 0.21.5 + "@esbuild/linux-mips64el": 0.21.5 + "@esbuild/linux-ppc64": 0.21.5 + "@esbuild/linux-riscv64": 0.21.5 + "@esbuild/linux-s390x": 0.21.5 + "@esbuild/linux-x64": 0.21.5 + "@esbuild/netbsd-x64": 0.21.5 + "@esbuild/openbsd-x64": 0.21.5 + "@esbuild/sunos-x64": 0.21.5 + "@esbuild/win32-arm64": 0.21.5 + "@esbuild/win32-ia32": 0.21.5 + "@esbuild/win32-x64": 0.21.5 + + esbuild@0.24.2: + optionalDependencies: + "@esbuild/aix-ppc64": 0.24.2 + "@esbuild/android-arm": 0.24.2 + "@esbuild/android-arm64": 0.24.2 + "@esbuild/android-x64": 0.24.2 + "@esbuild/darwin-arm64": 0.24.2 + "@esbuild/darwin-x64": 0.24.2 + "@esbuild/freebsd-arm64": 0.24.2 + "@esbuild/freebsd-x64": 0.24.2 + "@esbuild/linux-arm": 0.24.2 + "@esbuild/linux-arm64": 0.24.2 + "@esbuild/linux-ia32": 0.24.2 + "@esbuild/linux-loong64": 0.24.2 + "@esbuild/linux-mips64el": 0.24.2 + "@esbuild/linux-ppc64": 0.24.2 + "@esbuild/linux-riscv64": 0.24.2 + "@esbuild/linux-s390x": 0.24.2 + "@esbuild/linux-x64": 0.24.2 + "@esbuild/netbsd-arm64": 0.24.2 + "@esbuild/netbsd-x64": 0.24.2 + "@esbuild/openbsd-arm64": 0.24.2 + "@esbuild/openbsd-x64": 0.24.2 + "@esbuild/sunos-x64": 0.24.2 + "@esbuild/win32-arm64": 0.24.2 + "@esbuild/win32-ia32": 0.24.2 + "@esbuild/win32-x64": 0.24.2 + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-plugin-prettier@5.2.1(eslint@9.17.0)(prettier@3.4.2): + dependencies: + eslint: 9.17.0 + prettier: 3.4.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.2 + + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.17.0: + dependencies: + "@eslint-community/eslint-utils": 4.4.1(eslint@9.17.0) + "@eslint-community/regexpp": 4.12.1 + "@eslint/config-array": 0.19.1 + "@eslint/core": 0.9.1 + "@eslint/eslintrc": 3.2.0 + "@eslint/js": 9.17.0 + "@eslint/plugin-kit": 0.2.4 + "@humanfs/node": 0.16.6 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.1 + "@types/estree": 1.0.6 + "@types/json-schema": 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + "@types/estree": 1.0.6 + + estree-util-build-jsx@3.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + "@types/estree": 1.0.6 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + "@types/estree-jsx": 1.0.5 + astring: 1.9.0 + source-map: 0.7.4 + + estree-util-visit@2.0.0: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/unist": 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + "@types/estree": 1.0.6 + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + expressive-code@0.38.3: + dependencies: + "@expressive-code/core": 0.38.3 + "@expressive-code/plugin-frames": 0.38.3 + "@expressive-code/plugin-shiki": 0.38.3 + "@expressive-code/plugin-text-markers": 0.38.3 + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-xml-parser@4.5.1: + dependencies: + strnum: 1.0.5 + + fastq@1.18.0: + dependencies: + reusify: 1.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up-simple@1.0.0: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-yarn-workspace-root2@1.2.16: + dependencies: + micromatch: 4.0.8 + pkg-dir: 4.2.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + + flatted@3.3.2: {} + + flattie@1.1.1: {} + + fsevents@2.3.3: + optional: true + + get-east-asian-width@1.3.0: {} + + github-slugger@2.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + graceful-fs@4.2.11: {} + + h3@1.13.0: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.1 + defu: 6.1.4 + destr: 2.0.3 + iron-webcrypto: 1.2.1 + ohash: 1.1.4 + radix3: 1.1.2 + ufo: 1.5.4 + uncrypto: 0.1.3 + unenv: 1.10.0 + + has-flag@4.0.0: {} + + hast-util-embedded@3.0.0: + dependencies: + "@types/hast": 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + "@types/hast": 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.1 + + hast-util-from-html@2.0.3: + dependencies: + "@types/hast": 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.2 + parse5: 7.2.1 + vfile: 6.0.3 + vfile-message: 4.0.2 + + hast-util-from-parse5@8.0.2: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.0 + property-information: 6.5.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-has-property@3.0.0: + dependencies: + "@types/hast": 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + "@types/hast": 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + "@types/hast": 3.0.4 + + hast-util-minify-whitespace@1.0.1: + dependencies: + "@types/hast": 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.0 + + hast-util-parse-selector@4.0.0: + dependencies: + "@types/hast": 3.0.4 + + hast-util-phrasing@3.0.1: + dependencies: + "@types/hast": 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + + hast-util-raw@9.1.0: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + "@ungap/structured-clone": 1.2.1 + hast-util-from-parse5: 8.0.2 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.2.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-select@6.0.3: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + bcp-47-match: 2.0.3 + comma-separated-tokens: 2.0.3 + css-selector-parser: 3.0.5 + devlop: 1.1.0 + direction: 2.0.1 + hast-util-has-property: 3.0.0 + hast-util-to-string: 3.0.1 + hast-util-whitespace: 3.0.0 + nth-check: 2.1.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.1: + dependencies: + "@types/estree": 1.0.6 + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.8 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.4: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.2: + dependencies: + "@types/estree": 1.0.6 + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.8 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + "@types/hast": 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@3.0.1: + dependencies: + "@types/hast": 3.0.4 + + hast-util-to-text@4.0.2: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + "@types/hast": 3.0.4 + + hastscript@9.0.0: + dependencies: + "@types/hast": 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + html-whitespace-sensitive-tag-names@3.0.1: {} + + http-cache-semantics@4.1.1: {} + + i18next@23.16.8: + dependencies: + "@babel/runtime": 7.26.0 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.1.0: {} + + imurmurhash@0.1.4: {} + + inline-style-parser@0.2.4: {} + + iron-webcrypto@1.2.1: {} + + is-absolute-url@4.0.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-decimal@2.0.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + load-yaml-file@0.2.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + longest-streak@3.1.0: {} + + lru-cache@10.4.3: {} + + magic-string@0.30.17: + dependencies: + "@jridgewell/sourcemap-codec": 1.5.0 + + magicast@0.3.5: + dependencies: + "@babel/parser": 7.26.3 + "@babel/types": 7.26.3 + source-map-js: 1.2.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-definitions@6.0.0: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + unist-util-visit: 5.0.0 + + mdast-util-directive@3.0.0: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-find-and-replace@3.0.2: + dependencies: + "@types/mdast": 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + "@types/mdast": 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.1.3: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + "@types/mdast": 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@ungap/structured-clone": 1.2.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + "@types/mdast": 4.0.4 + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.2: + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-directive@3.0.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-table@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.1 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-mdx-expression@3.0.0: + dependencies: + "@types/estree": 1.0.6 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-mdx-jsx@3.0.1: + dependencies: + "@types/acorn": 4.0.6 + "@types/estree": 1.0.6 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.1 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + "@types/estree": 1.0.6 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + micromark-extension-mdx-expression: 3.0.0 + micromark-extension-mdx-jsx: 3.0.1 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-mdx-expression@2.0.2: + dependencies: + "@types/estree": 1.0.6 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.1 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.2: + dependencies: + "@types/acorn": 4.0.6 + "@types/estree": 1.0.6 + "@types/unist": 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + vfile-message: 4.0.2 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.1 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.0.3: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.1: {} + + micromark@4.0.1: + dependencies: + "@types/debug": 4.1.12 + debug: 4.4.0 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime@3.0.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + mrmime@2.0.0: {} + + ms@2.1.3: {} + + nanoid@3.3.8: {} + + natural-compare@1.4.0: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + "@types/nlcst": 2.0.3 + + node-fetch-native@1.6.4: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + ofetch@1.4.1: + dependencies: + destr: 2.0.3 + node-fetch-native: 1.6.4 + ufo: 1.5.4 + + ohash@1.1.4: {} + + oniguruma-to-es@0.10.0: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 5.1.1 + regex-recursion: 5.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@6.2.0: + dependencies: + yocto-queue: 1.1.1 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-queue@8.0.1: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + + p-timeout@6.1.4: {} + + p-try@2.2.0: {} + + pagefind@1.3.0: + optionalDependencies: + "@pagefind/darwin-arm64": 1.3.0 + "@pagefind/darwin-x64": 1.3.0 + "@pagefind/linux-arm64": 1.3.0 + "@pagefind/linux-x64": 1.3.0 + "@pagefind/windows-x64": 1.3.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + "@types/unist": 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-latin@7.0.0: + dependencies: + "@types/nlcst": 2.0.3 + "@types/unist": 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.2.1: + dependencies: + entities: 4.5.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pathe@1.1.2: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pify@4.0.1: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + postcss-nested@6.2.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.4.49: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preferred-pm@4.0.0: + dependencies: + find-up-simple: 1.0.0 + find-yarn-workspace-root2: 1.2.16 + which-pm: 3.0.0 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-astro@0.14.1: + dependencies: + "@astrojs/compiler": 2.10.3 + prettier: 3.4.2 + sass-formatter: 0.7.9 + + prettier@3.4.2: {} + + prismjs@1.29.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@6.5.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + radix3@1.1.2: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + recma-build-jsx@1.0.0: + dependencies: + "@types/estree": 1.0.6 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.14.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.14.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + "@types/estree": 1.0.6 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + "@types/estree": 1.0.6 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regenerator-runtime@0.14.1: {} + + regex-recursion@5.1.1: + dependencies: + regex: 5.1.1 + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@5.1.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-expressive-code@0.38.3: + dependencies: + expressive-code: 0.38.3 + + rehype-external-links@3.0.0: + dependencies: + "@types/hast": 3.0.4 + "@ungap/structured-clone": 1.2.1 + hast-util-is-element: 3.0.0 + is-absolute-url: 4.0.1 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + + rehype-format@5.0.1: + dependencies: + "@types/hast": 3.0.4 + hast-util-format: 1.1.0 + + rehype-parse@9.0.1: + dependencies: + "@types/hast": 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + "@types/hast": 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + "@types/estree": 1.0.6 + "@types/hast": 3.0.4 + hast-util-to-estree: 3.1.1 + transitivePeerDependencies: + - supports-color + + rehype-stringify@10.0.1: + dependencies: + "@types/hast": 3.0.4 + hast-util-to-html: 9.0.4 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + "@types/hast": 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-directive@3.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-directive: 3.0.0 + micromark-extension-directive: 3.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.0: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.1 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.1: + dependencies: + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + + remark-stringify@11.0.0: + dependencies: + "@types/mdast": 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + resolve-from@4.0.0: {} + + retext-latin@4.0.0: + dependencies: + "@types/nlcst": 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + "@types/nlcst": 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + "@types/nlcst": 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + "@types/nlcst": 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + reusify@1.0.4: {} + + rollup@4.30.0: + dependencies: + "@types/estree": 1.0.6 + optionalDependencies: + "@rollup/rollup-android-arm-eabi": 4.30.0 + "@rollup/rollup-android-arm64": 4.30.0 + "@rollup/rollup-darwin-arm64": 4.30.0 + "@rollup/rollup-darwin-x64": 4.30.0 + "@rollup/rollup-freebsd-arm64": 4.30.0 + "@rollup/rollup-freebsd-x64": 4.30.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.30.0 + "@rollup/rollup-linux-arm-musleabihf": 4.30.0 + "@rollup/rollup-linux-arm64-gnu": 4.30.0 + "@rollup/rollup-linux-arm64-musl": 4.30.0 + "@rollup/rollup-linux-loongarch64-gnu": 4.30.0 + "@rollup/rollup-linux-powerpc64le-gnu": 4.30.0 + "@rollup/rollup-linux-riscv64-gnu": 4.30.0 + "@rollup/rollup-linux-s390x-gnu": 4.30.0 + "@rollup/rollup-linux-x64-gnu": 4.30.0 + "@rollup/rollup-linux-x64-musl": 4.30.0 + "@rollup/rollup-win32-arm64-msvc": 4.30.0 + "@rollup/rollup-win32-ia32-msvc": 4.30.0 + "@rollup/rollup-win32-x64-msvc": 4.30.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + s.color@0.0.15: {} + + sass-formatter@0.7.9: + dependencies: + suf-log: 2.5.3 + + sax@1.4.1: {} + + semver@7.6.3: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.3 + optionalDependencies: + "@img/sharp-darwin-arm64": 0.33.5 + "@img/sharp-darwin-x64": 0.33.5 + "@img/sharp-libvips-darwin-arm64": 1.0.4 + "@img/sharp-libvips-darwin-x64": 1.0.4 + "@img/sharp-libvips-linux-arm": 1.0.5 + "@img/sharp-libvips-linux-arm64": 1.0.4 + "@img/sharp-libvips-linux-s390x": 1.0.4 + "@img/sharp-libvips-linux-x64": 1.0.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.0.4 + "@img/sharp-libvips-linuxmusl-x64": 1.0.4 + "@img/sharp-linux-arm": 0.33.5 + "@img/sharp-linux-arm64": 0.33.5 + "@img/sharp-linux-s390x": 0.33.5 + "@img/sharp-linux-x64": 0.33.5 + "@img/sharp-linuxmusl-arm64": 0.33.5 + "@img/sharp-linuxmusl-x64": 0.33.5 + "@img/sharp-wasm32": 0.33.5 + "@img/sharp-win32-ia32": 0.33.5 + "@img/sharp-win32-x64": 0.33.5 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@1.26.1: + dependencies: + "@shikijs/core": 1.26.1 + "@shikijs/engine-javascript": 1.26.1 + "@shikijs/engine-oniguruma": 1.26.1 + "@shikijs/langs": 1.26.1 + "@shikijs/themes": 1.26.1 + "@shikijs/types": 1.26.1 + "@shikijs/vscode-textmate": 10.0.1 + "@types/hast": 3.0.4 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sisteransi@1.0.5: {} + + sitemap@8.0.0: + dependencies: + "@types/node": 17.0.45 + "@types/sax": 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + + source-map-js@1.2.1: {} + + source-map@0.7.4: {} + + space-separated-tokens@2.0.2: {} + + sprintf-js@1.0.3: {} + + stream-replace-string@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strnum@1.0.5: {} + + style-to-object@1.0.8: + dependencies: + inline-style-parser: 0.2.4 + + suf-log@2.5.3: + dependencies: + s.color: 0.0.15 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + synckit@0.9.2: + dependencies: + "@pkgr/core": 0.1.1 + tslib: 2.8.1 + + tinyexec@0.3.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tsconfck@3.1.4(typescript@5.6.3): + optionalDependencies: + typescript: 5.6.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@4.31.0: {} + + typescript@5.6.3: {} + + ufo@1.5.4: {} + + ultrahtml@1.5.3: {} + + uncrypto@0.1.3: {} + + unenv@1.10.0: + dependencies: + consola: 3.3.3 + defu: 6.1.4 + mime: 3.0.0 + node-fetch-native: 1.6.4 + pathe: 1.1.2 + + unified@11.0.5: + dependencies: + "@types/unist": 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + "@types/unist": 3.0.3 + array-iterate: 2.0.1 + + unist-util-position-from-estree@2.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-position@5.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + "@types/unist": 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + unstorage@1.14.4: + dependencies: + anymatch: 3.1.3 + chokidar: 3.6.0 + destr: 2.0.3 + h3: 1.13.0 + lru-cache: 10.4.3 + node-fetch-native: 1.6.4 + ofetch: 1.4.1 + ufo: 1.5.4 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vfile-location@5.0.3: + dependencies: + "@types/unist": 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.2: + dependencies: + "@types/unist": 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + "@types/unist": 3.0.3 + vfile-message: 4.0.2 + + vite@6.0.7: + dependencies: + esbuild: 0.24.2 + postcss: 8.4.49 + rollup: 4.30.0 + optionalDependencies: + fsevents: 2.3.3 + + vitefu@1.0.5(vite@6.0.7): + optionalDependencies: + vite: 6.0.7 + + web-namespaces@2.0.1: {} + + which-pm-runs@1.1.0: {} + + which-pm@3.0.0: + dependencies: + load-yaml-file: 0.2.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + word-wrap@1.2.5: {} + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + xml-js@1.6.11: + dependencies: + sax: 1.4.1 + + xxhash-wasm@1.1.0: {} + + yargs-parser@21.1.1: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.1.1: {} + + yocto-spinner@0.1.2: + dependencies: + yoctocolors: 2.1.1 + + yoctocolors@2.1.1: {} + + zod-to-json-schema@3.24.1(zod@3.24.1): + dependencies: + zod: 3.24.1 + + zod-to-ts@1.2.0(typescript@5.6.3)(zod@3.24.1): + dependencies: + typescript: 5.6.3 + zod: 3.24.1 + + zod@3.24.1: {} + + zwitch@2.0.4: {} diff --git a/docs/src/assets/docs-images/details/asset_bundle.webp b/docs/src/assets/docs-images/details/asset_bundle.webp deleted file mode 100644 index 9484111f..00000000 Binary files a/docs/src/assets/docs-images/details/asset_bundle.webp and /dev/null differ diff --git a/docs/src/assets/docs-images/eye_of_the_universe/animController.webp b/docs/src/assets/docs-images/eye_of_the_universe/animController.webp new file mode 100644 index 00000000..5e1e2f8c Binary files /dev/null and b/docs/src/assets/docs-images/eye_of_the_universe/animController.webp differ diff --git a/docs/src/content/docs/guides/details.md b/docs/src/content/docs/guides/details.md index 35549a0f..f62e9eea 100644 --- a/docs/src/content/docs/guides/details.md +++ b/docs/src/content/docs/guides/details.md @@ -5,77 +5,27 @@ description: A guide to adding details to planets in New Horizons For physical objects there are currently two ways of setting them up: specify an asset bundle and path to load a custom asset you created, or specify the path to the item you want to copy from the game in the scene hierarchy. Use the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) mod to find an object you want to copy onto your new body. Some objects work better than others for this. Good luck. Some pointers: -- Use "Object Explorer" to search -- Generally you can find planets by writing their name with no spaces/punctuation followed by "\_Body". -- There's also [this community-maintained list of props](https://docs.google.com/spreadsheets/d/1VJaglB1kRL0VqaXhvXepIeymo93zqhWex-j7_QDm6NE/edit?usp=sharing) which you can use to find interesting props and check to see if they have collision. +- Use "Object Explorer" to search +- Generally you can find planets by writing their name with no spaces/punctuation followed by "\_Body". +- There's also [this community-maintained list of props](https://docs.google.com/spreadsheets/d/1VJaglB1kRL0VqaXhvXepIeymo93zqhWex-j7_QDm6NE/edit?usp=sharing) which you can use to find interesting props and check to see if they have collision. -## Using the Prop Placer +## Debug Raycast -The Prop Placer is a convenience tool that lets you manually place details from inside the game. Once enabled, press "G" and your currently selected prop will be placed wherever your crosshair is pointing. +If you turn on debug mode (the mod option), you can press P to shoot a ray where you're looking. This will print location info to the console that you can paste into your configs, as well as paths that you can explore further in Unity Explorer. +Of note: the rotation of the raycast will have the up direction facing away from the ground/wall/ceiling and the forward direction facing you. -### Enabling +## [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer/) -1. Pause the game. You will see an extra menu option titled "Toggle Prop Placer Menu". Click it -2. The prop placer menu should now be open. At the bottom of the menu, you will see a list of mods. Click yours. - 1. This menu scrolls. If you do not see your mod, it may be further down the list. -3. The Prop Placer is now active! Unpause the game, and you can now place Nomai vases using "G" - -### How to Select Props - -1. Pause the game again. The prop placer menu should still be visible. -2. At the top of the menu, you'll see a text box containing the path for the vase. Replace this with the path for the prop you want to place. For example: `DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var` -3. Tip: use the Unity Explorer mod to find the path for the object you want to place. You only have to do this once. -4. Unpause the game and press "G". Say hello to your new tree! -5. Pause the game again. You will now see the prop you just placed on the list of recently placed props just below the "path" text box. -6. Click on the button titled "Prefab_NOM_VaseThin". You can now place vases again. - -### Extra features - -1. Made a mistake? **Press the "-" key to undo.** Press the "+" key to redo. -2. If you have the Unity Explorer mod enabled, you can use this to tweak the position, rotation, and scale of your props. Your changes will be saved. -3. Want to save some recently placed props between game launches? On the recently placed props list, click the star next to the prop's name to favorite it. -4. Found a bug that ruined your configs? Check `AppData\Roaming\OuterWildsModManager\OWML\Mods\xen.NewHorizons\configBackups` for backup saves of your work. Folders are titled "\[date\]T\[time\]". -5. Want to add props to Ember Twin but don't feel like making a config file for it? We got you! Place that prop and the config file will be created automatically on your next save. -6. This even works for planets that were created by other mods! +You can use this to tweak the position, rotation, and scale of your props. These docs will not elaborate too much on this tool. There are other tutorials out there. ## Asset Bundles -Here is a template project: [Outer Wilds Unity Template](https://github.com/xen-42/outer-wilds-unity-template) +There is an [old unity template](https://github.com/xen-42/outer-wilds-unity-template) and a [new one](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki#outer-wilds-unity-assets) -The template project contains ripped versions of all the game scripts, meaning you can put things like DirectionalForceVolumes in your Unity project to have artificial gravity volumes loaded right into the game. +The project contains ripped versions of all the game scripts, meaning you can put things like DirectionalForceVolumes in your Unity project to have artificial gravity volumes loaded right into the game. +Either one works, but the tool one has more tools and more feature-full versions of the scripts (in exchange for being invite-only) -If for whatever reason you want to set up a Unity project manually instead of using the template, follow these instructions: - -1. Start up a Unity 2019.4.39f1 project -2. In the "Assets" folder in Unity, create a new folder called "Editor". In it create a file called "CreateAssetBundle.cs" with the following code in it: - -```cs title="Editor/CreateAssetBundle.cs" -using UnityEditor; -using UnityEngine; -using System.IO; - -public class CreateAssetBundles -{ - [MenuItem("Assets/Build AssetBundles")] - static void BuildAllAssetBundles() - { - string assetBundleDirectory = "Assets/StreamingAssets"; - if (!Directory.Exists(Application.streamingAssetsPath)) - { - Directory.CreateDirectory(assetBundleDirectory); - } - BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); - } -} -``` - -3. Create your object in the Unity scene and save it as a prefab. -4. Add all files used (models, prefabs, textures, materials, etc.) to an asset bundle by selecting them and using the dropdown in the bottom right. Here I am adding a rover model to my "rss" asset bundle for the Real Solar System add-on. - -![setting asset bundle](@/assets/docs-images/details/asset_bundle.webp) - -1. In the top left click the "Assets" drop-down and select "Build AssetBundles". This should create your asset bundle in a folder in the root directory called "StreamingAssets". -2. Copy the asset bundle and asset bundle .manifest files from StreamingAssets into your mod's "planets" folder. If you did everything properly they should work in game. To double-check everything is included, open the .manifest file in a text editor to see the files included and their paths. +Read [this guide](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki/Tutorials-%E2%80%90-Using-asset-bundles) on how to work with asset bundles in editor. ## Importing a planet's surface from Unity @@ -88,7 +38,7 @@ To add a Mars rover to the red planet in [RSS](https://github.com/xen-42/outer-w ```json {5-6} { "Props": { - "Details": [ + "details": [ { "assetBundle": "planets/assetbundle/rss", "path": "Assets/RSS/Prefabs/Rover.prefab", @@ -97,7 +47,7 @@ To add a Mars rover to the red planet in [RSS](https://github.com/xen-42/outer-w "y": -10.83688, "z": -36.02736 }, - "alignToNormal": true + "alignRadial": true } ] } @@ -109,7 +59,7 @@ To scatter 12 trees from the Dream World around Wetrock in [NH Examples](https:/ ```json { "Props": { - "Scatter": [ + "scatter": [ { "path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", "count": 12 @@ -124,7 +74,7 @@ You can swap these around too. The following would scatter 12 Mars rovers across ```json { "Props": { - "Details": [ + "details": [ { "path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", "position": { @@ -132,10 +82,10 @@ You can swap these around too. The following would scatter 12 Mars rovers across "y": -10.83688, "z": -36.02736 }, - "alignToNormal": true + "alignRadial": true } ], - "Scatter": [ + "scatter": [ { "assetBundle": "planets/assetbundle/rss", "path": "Assets/RSS/Prefabs/Rover.prefab", @@ -146,6 +96,157 @@ You can swap these around too. The following would scatter 12 Mars rovers across } ``` +## Custom Items and Item Sockets + +### Items + +You can convert details into custom items for the player to pick up and hold by adding the `item` properties to your detail: + +```json +{ + "Props": { + "details": [ + { + "path": "Moon_Body/Sector_THM/Interactables_THM/Prefab_HEA_Recorder/Props_HEA_Recorder_Geo", + "position": {"x": -35.30206, "y": -79.12967, "z": 182.912}, + "rotation": {"x": 300.8207, "y": 32.93826, "z": 141.4214}, + "item": { + "name": "Tape Recorder", + "itemType": "Decoration" + } + } + ] + } +} +``` + +The `name` and `itemType` can be anything you want. The `name` will be what's displayed to the player when they mouse over it ("Pick up X"), and `itemType` determines the kinds of item sockets that the item can be placed in. + +> Note: Outer Wilds is unfortunately very picky about the types of objects you can use as items. There must be a collider on the object itself which the game will use to check if the cursor is over it or not to allow you to pick it up. Colliders on child objects will have collision as normal but won't allow you to pick up the item when you mouse over them. By default, New Horizons will add a spherical collider to your detail to act as this mouse-over collider. If your object already has a working collider on it, you can disable the New Horizons spherical collider by setting `"colliderRadius": 0`. + +Here's a more complex example, with every property filled out: + +```json +{ + "Props": { + "details": [ + { + "path": "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_BlackHoleForge/BlackHoleForgePivot/Props_BlackHoleForge/Prefab_NOM_VaseThick", + "position": {"x": -33.30206, "y": -79.12967, "z": 182.912}, + "rotation": {"x": 300.8207, "y": 32.93826, "z": 141.4214}, + "item": { + "name": "Nomai Vase", + "itemType": "Decoration", + "interactRange": 3, + "colliderRadius": 0, + + "droppable": true, + "dropOffset": {"x": 0, "y": 0, "z": 0}, + "dropNormal": {"x":0, "y": 1, "z": 0}, + + "holdOffset": {"x": 0, "y": -0.3, "z": 0}, + "holdRotation": {"x": 0, "y": 45, "z": 0}, + + "socketOffset": {"x": 0, "y": 0.2, "z": 0}, + "socketRotation": {"x": 0, "y": 45, "z": 0}, + + "pickupAudio": "ToolProbeRetrieve", + "dropAudio": "ToolProbeLaunch", + "socketAudio": "PlayerSuitLockOn", + "unsocketAudio": "PlayerSuitLockOff", + + "pickupCondition": "VASE_PICKED_UP", + "clearPickupConditionOnDrop": true, + "pickupFact": "EXAMPLES_VASE", + + "pathToInitialSocket": "Sector_TH/Strucutre_NOM_Shelf" + } + } + ] + } +} +``` + +To see the full list of item properties and descriptions of what each property does, check [the ItemInfo schema](/schemas/body-schema/defs/iteminfo/). + +### Item Sockets + +You can also designate a detail as an "item socket," which will allow certain items to be placed into it. For example, the Nomai whiteboards have a socket for scrolls, and the Vessel's warp core slot is also a socket. You do this by adding the `itemSocket` properties to your detail: + +```json +{ + "Props": { + "details": [ + { + "path": "QuantumIsland_Body/Sector_QuantumIsland/Sector_QuantumTowerInterior/Sector_QuantumTowerFinalRoom/Interactables_FinalRoom/FinalRoom_QProps/FinalRoom_Shelf/Q_Shelf/Prefab_NOM_Shelf/Strucutre_NOM_Shelf", + "position": {"x": -29.89541, "y": -79.87562, "z": 183.1785}, + "rotation": {"x": 314.8642, "y": 286.1038, "z": 235.4039}, + "itemSocket": { + "itemType": "Decoration", + "interactRange": 3, + "colliderRadius": 0.5, + "useGiveTakePrompts": true, + + "insertCondition": "VASE_INSERTED", + "clearInsertConditionOnRemoval": true, + "insertFact": "EXAMPLES_VASE_INSERTED", + + "removalCondition": "VASE_REMOVED", + "clearRemovalConditionOnInsert": true, + "removalFact": "EXAMPLES_VASE_REMOVED", + + "position": {"x": 0, "y": 0, "z": 0.5}, + "rotation": {"x": 0, "y": 0, "z": 0}, + "isRelativeToParent": true + } + } + ] + } +} +``` + +Item sockets will allow any item with a matching `itemType` to be placed into them; all other item types will show the "item does not fit" message. + +> Item sockets are also picky about colliders in the same way items are. The detail object must have a collider on it; colliders on child objects will not work for placing and removing items when the cursor is over them. New Horizons will add a spherical collider for you if you set `colliderRadius` to a non-zero value. + +The `position`, `rotation`, and `isRelativeToParent` properties on the `itemSocket` don't describe the location of the detail itself, but rather the point on the detail where items will be inserted. This will likely not be at the exact center of the detail, so you should use this properties to customize the location of the socket. If there is already an child object you want to use as a pivot point, you can put a relative path from the detail to the child object in the `socketPath` property to use instead of the generated socket point. + +To see the full list of item socket properties and descriptions of what each property does, check [the ItemSocketInfo schema](/schemas/body-schema/defs/itemsocketinfo/). + +### Making Puzzles with Items and Sockets + +You can use items and sockets to create simple puzzles purely with New Horizons configs! Consider this cut-down example. It describes a "Rusty Key" object, and a "Locked Door" object. The locked door will disappear when the key is inserted into it, allowing the player to move through the doorway. + +```json +{ + "Props": { + "details": [ + { + "path": "BlahBlahBlah", + "item": { + "name": "Rusty Key", + "itemType": "Key" + } + }, + { + "path": "BlahBlahBlah", + "rename": "Locked Door", + "deactivationCondition": "DOOR_UNLOCKED", + "itemSocket": { + "itemType": "Key", + "insertCondition": "DOOR_UNLOCKED" + } + } + ] + } +} +``` + +When the "Rusty Key" item (or any other item with `"itemType": "Key"`, for that matter) is inserted into the "Locked Door" item socket, a condition we've named `DOOR_UNLOCKED` is set to true. These conditions are mainly used for dialogue in Outer Wilds, but New Horizons also allows us to control objects with them. The locked door object has the `DOOR_UNLOCKED` condition as its `deactivationCondition`, which will cause it to disappear ("deactivate") when the condition is set. + +There are a handful of other properties that can be used like this: `activationCondition` and `deactivationCondition` on all details, `insertCondition` and `removalCondition` on item sockets, and `pickupCondition` on items. There are other properties that can further customize these behaviors. + + ## Use the schema To view additional options for detailing, check [the schema](/schemas/body-schema/defs/propmodule#details) diff --git a/docs/src/content/docs/guides/dialogue.md b/docs/src/content/docs/guides/dialogue.md index 77d8631d..0375e773 100644 --- a/docs/src/content/docs/guides/dialogue.md +++ b/docs/src/content/docs/guides/dialogue.md @@ -15,13 +15,9 @@ A dialogue tree is an entire conversation, it's made up of dialogue nodes. A node is a set of pages shown to the player followed by options the player can choose from to change the flow of the conversation. -### Condition +### Conditions -A condition is a yes/no value stored **for this loop and this loop only**. It can be used to show new dialogue options, stop someone from talking to you (looking at you Slate), and more. - -### Persistent Condition - -A persistent condition is similar to a condition, except it _persists_ through loops, and is saved on the players save file. +In dialogue, the available conversation topics can be limited by what the player knows, defined using dialogue conditions, persistent conditions, and ship log facts. Dialogue can also set conditions to true or false, and reveal ship log facts to the player. This is covered in detail later on this page. ### Remote Trigger @@ -156,11 +152,39 @@ In addition to ``, there are other ways to control the flow of Defining `` in the `` tag instead of a `` will make the conversation go directly to that target after the character is done talking. -### DialogueTargetShipLogCondition +### EntryCondition -Used in tandem with `DialogueTarget`, makes it so you must have a [ship log fact](/guides/ship-log#explore-facts) to go to the next node. +The first dialogue node that opens when a player starts talking to a character is chosen using this property. To mark a DialogueNode as beginning the dialogue by default, use the condition DEFAULT (a DialogueTree should always have a node with the DEFAULT entry condition to ensure there is a way to start dialogue). -### Adding to existing dialogue +The entry condition can be either a condition or a persistent condition. + +### Condition + +A condition is a yes/no value stored **for this loop and this loop only**. It can be used to show new dialogue options, stop someone from talking to you (looking at you Slate), and more. + +Conditions can be set in dialogue using `CONDITION_NAME`. This can go in a DialogueNode in which case it will set the condition to true when that node is read. There is a similar version of this for DialogueOptions called `CONDITION_NAME` which will set it to true when that option is selected. Conditions can be disabled using `CONDITION_NAME` in a DialogueOption, but cannot be disabled just by entering a DialogueNode. + +You can lock a DialogueOption behind a condition using `CONDITION_NAME`, or remove a DialogueOption after the condition is set to true using `CONDITION_NAME`. + +Dialogue conditions can also be set in code with `DialogueConditionManager.SharedInstance.SetConditionState("CONDITION_NAME", true/false)` or read with `DialogueConditionManager.SharedInstance.GetConditionState("CONDITION_NAME")`. + +Note that `CONDITION_NAME` is a placeholder that you would replace with whatever you want to call your condition. Consider appending conditions with the name of your mod to make for better compatibility between mods, for example a condition name like `SPOKEN_TO` is very generic and might conflict with other mods whereas `NH_EXAMPLES_SPOKEN_TO_ERNESTO` is much less likely to conflict with another mod. + +### Persistent Condition + +A persistent condition is similar to a condition, except it _persists_ through loops, and is saved on the players save file. + +Persistent conditions shared many similar traits with regular dialogue conditions. You can use ``, ``. On dialogue options you can use ``, `` + +Persistent conditions can also be set in code with `PlayerData.SetPersistentCondition("PERSISTENT_CONDITION_NAME", true/false)` and read using `PlayerData.GetPersistentCondition("PERSISTENT_CONDITION_NAME")`. + +### Ship Logs + +Dialogue can interact with ship logs, either granting them to the player (`` on a DialogueNode) or locking dialogue behind ship log completion (`` on a DialogueOption). + +You can also use `` in tandem with `DialogueTarget` to make it so you must have a [ship log fact](/guides/ship-log#explore-facts) to go to the next node. + +## Adding to existing dialogue Here's an example of how to add new dialogue to Slate, without overwriting their existing dialogue. This will also allow multiple mods to all add new dialogue to the same character. @@ -200,3 +224,34 @@ To use this additional dialogue you need to reference it in a planet config file } ] ``` + +### ReuseDialogueOptionsListFrom + +This is a custom XML node introduced by New Horizons. Use it when adding new dialogue to existing characters, to repeat the dialogue options list from another node. + +For example, Slate's first dialogue with options is named `Scientist5`. To make a custom DialogueNode using these dialogue options (meaning new dialogue said by Slate, but reusing the possible player responses) you can write: + +```xml + + ... + + NEW DIALOGUE FOR SLATE HERE. + + + Scientist5 + + +``` + +Note: If you're loading dialogue in code, 2 frames must pass before entering the conversation in order for ReuseDialogueOptionsListFrom to take effect. + + +## Dialogue FAQ + +### How do I easily position my dialogue relative to a speaking character + +Use `pathToAnimController` to specify the path to the speaking character (if they are a Nomai or Hearthian make sure this goes directly to whatever script controls their animations), then set `isRelativeToParent` to true (this is setting available on all NH props for easier positioning). Now when you set their `position`, it will be relative to the speaker. Since this position is normally where the character is standing, set the `y` position to match how tall the character is. Instead of `pathToAnimController` you can also use `parentPath`. + +### How do I have the dialogue prompt say "Read" or "Play recording" + +`` sets the name of the character, which will then show in the prompt to start dialogue. You can alternatively use `SIGN` to have the prompt say "Read", and `RECORDING` to have it say "Play recording". \ No newline at end of file diff --git a/docs/src/content/docs/guides/extending-configs.md b/docs/src/content/docs/guides/extending-configs.md index 0d3ee903..63b39fc5 100644 --- a/docs/src/content/docs/guides/extending-configs.md +++ b/docs/src/content/docs/guides/extending-configs.md @@ -49,7 +49,7 @@ Then, use the `QueryBody` method: var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); api.GetBodyLoadedEvent().AddListener((name) => { ModHelper.Console.WriteLine($"Body: {name} Loaded!"); - var data = api.QueryBody("$.extras.myCoolExtensionData", name); + var data = api.QueryBody(name, "$.extras.myCoolExtensionData"); // Makes sure the module is not null if (data != null) { ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); diff --git a/docs/src/content/docs/guides/eye-of-the-universe.mdx b/docs/src/content/docs/guides/eye-of-the-universe.mdx new file mode 100644 index 00000000..0a02d614 --- /dev/null +++ b/docs/src/content/docs/guides/eye-of-the-universe.mdx @@ -0,0 +1,342 @@ +--- +title: Eye of the Universe +description: A guide to adding additional content to the Eye of the Universe with New Horizons +--- + +import { Aside } from "@astrojs/starlight/components"; + +This guide covers some 'gotchas' and features unique to the Eye of the Universe. + +## Extending the Eye of the Universe + +### Star System + +To define a Star System config for the Eye of the Universe, name your star system config file `EyeOfTheUniverse.json` or specify the `"name"` as `"EyeOfTheUniverse"`. Note that many of the star system features have no effect at the Eye compared to a regular custom star system. + +```json title="systems/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/star_system_schema.json", + "name": "EyeOfTheUniverse", + // etc. +} +``` + +### Existing Planet + +The existing areas of the Eye of the Universe, such as the "sixth planet" and funnel, the museum/observatory, the forest of galaxies, and the campfire, are all contained within one static "planet" (despite visually being distinct locations). To add to these areas, you'll need to specify a planet config file with a `"name"` of `"EyeOfTheUniverse"` and *also* a `"starSystem"` of `"EyeOfTheUniverse"`, as the star system and "planet" share the same name. + +```json title="planets/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "EyeOfTheUniverse", + "starSystem": "EyeOfTheUniverse", + "Props": { + "details": [ + // etc. + ] + }, + // etc. +} +``` + +### The Vessel + +You can also add props to the Vessel at the Eye: + +```json title="planets/Vessel.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "Vessel", + "starSystem": "EyeOfTheUniverse", + "Props": { + "details": [ + // etc. + ] + }, + // etc. +} +``` + +## Eye Travelers + +Eye Travelers are a special kind of detail prop (see [Detailing](/guides/details/)) that get added in as additional characters around the campfire at the end of the Eye sequence, similar to Solanum and the Prisoner in the vanilla game. + +At minimum, you will need a character object to act as the traveler, an audio file for the looping part of the new instrument track, an audio file to layer over the finale, a dialogue XML file with certain dialogue conditions set up, and a [quantum instrument](#quantum-instruments). + +The traveler will only appear once their quantum instrument is gathered. After that, they will appear in the circle around the campfire, and they can be interacted with through dialogue to start playing their instrument. The instrument audio is handled via a signal on the traveler that only becomes audible after talking to them. + +Custom travelers will automatically be included in the inflation animation that pushes everyone away from the campfire at the end of the sequence. + +[Eye Travelers](#eye-travelers), [Quantum Instruments](#quantum-instruments), and [Instrument Zones](#instrument-zones) are all linked by their `"id"` properties. Ensure that your ID matches between those details and is unique enough to not conflict with other mods. + +Here's an example config: +```json title="planets/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "EyeOfTheUniverse", + "starSystem": "EyeOfTheUniverse", + "EyeOfTheUniverse": { + "eyeTravelers": [ + { + "id": "Slate", + "signal": { + "name": "Slate", + "audio": "planets/MetronomeLoop.wav", + "detectionRadius": 0, + "identificationRadius": 10, + "onlyAudibleToScope": false, + "position": {"x": 0, "y": 0.75, "z": 0}, + "isRelativeToParent": true + }, + "finaleAudio": "planets/MetronomeFinale.wav", + "startPlayingCondition": "EyeSlatePlaying", + "participatingCondition": "EyeSlateParticipating", + "dialogue": { + "xmlFile": "planets/EyeSlate.xml", + "position": {"x": 0.0, "y": 1.5, "z": 0.0}, + "isRelativeToParent": true + }, + "path": "TimberHearth_Body/Sector_TH/Sector_Village/Sector_StartingCamp/Characters_StartingCamp/Villager_HEA_Slate" + } + ] + } +} +``` + +To see the full list of eye traveler properties and descriptions of what each property does, check [the EyeTravelerInfo schema](/schemas/body-schema/defs/eyetravelerinfo/). + + + +### Audio + +The looping audio clip is used for the signals emitted by the traveler and their quantum instrument, and is the audio that gets played when they play their instrument. It should be a WAV file with 16 measures of music at 92 BPM (exactly 2,003,478 samples at 48,000 Hz, or approximately 42 seconds long). It is highly recommended that you use Audacity or another audio editor to trim your audio clip to exactly the same length as one of the vanilla traveler audio clips, or else it will fall gradually out of sync with the other instrument loops. + +The finale audio clip is only played once, after all travelers have started playing. It should be 8 measures of the main loop at 92 BPM followed by 2 measures of fade-out (approximately 26 seconds long in total). Unlike the looping audio clip, it does not need to precisely match the length of the vanilla finale clip; it can end early or continue playing after the other ends. + +The game plays all of the looping audio clips (including your custom one) simultaneously once you tell the first traveler to start playing, and then fades them in one by one as you talk to the others. After all travelers are playing, the game selects a finale audio clip that contains all Hearthian and Nomai/Owlk instruments mixed into one file, and then your custom finale audio clip will be layered over whichever vanilla clip plays. Only include your own instrument in the clip, and ensure it sounds okay alongside Solanum, the Prisoner, and both/neither. + +### Dialogue + +The dialogue XML for your traveler works like other dialogue (see the [Dialogue](/guides/dialogue/) guide) but there are specially named conditions you will need to use for the functionality to work as expected: +- Use `AnyTravelersGathered` to check if any traveler has been gathered yet. This includes Riebeck and Esker, so it should always be true, unless you forcibly enable your traveler to be enabled early. +- Use `AllTravelersGathered` to check if all of the travelers have been gathered and are ready to start playing. +- Use `JamSessionIsOver` to check if the travelers have stopped playing the song and the sphere of possibilities has appeared. +- Use a `` with the condition defined in your eye traveler config's `"startPlayingCondition"` on the node or dialogue option that should make your traveler start playing their instrument. This condition name must be unique and not conflict with other mods. +- If you want your traveler to be present but have an option to not participate in the campfire song (like the Prisoner), use a `` with the condition defined in your eye traveler config's `"participatingCondition"` on the node or dialogue option where your traveler agrees to join in. This condition name must be unique and not conflict with other mods. + +```xml title="planets/dialogue/SlateEyeTraveler.xml" + + Slate, Probably + + WAITING_FOR_OTHERS + DEFAULT + + It's me, definitely Slate and not a dreamstalker in disguise. + + + + ANY_GATHERED + AnyTravelersGathered + + You still have other travelers to gather. + + + + You're going to join in, right? + PARTICIPATING + + + Okay then... + + + + + PARTICIPATING + + Sure. + + EyeSlateParticipating + + + READY_TO_PLAY + AllTravelersGathered + + We're all here. Time to start the music. + + + + Ready to go. + START_PLAYING + + + Not yet. + NOT_YET + + + + + START_PLAYING + + Let's begin. + + EyeSlatePlaying + + + NOT_YET + + Whenever you're ready. + + + + FAREWELL + JamSessionIsOver + + It's rewind time. + + + +``` + +### Custom Animation + +To add custom animations to your Eye Traveler, there is some setup work that has to be done in Unity. You will need to set up your character in Unity and load them via asset bundle, like you would any other detail: + +```json title="planets/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "EyeOfTheUniverse", + "starSystem": "EyeOfTheUniverse", + "EyeOfTheUniverse": { + "eyeTravelers": [ + { + "id": "MyCoolEyeGuy", + "assetBundle": "planets/bundles/eyeoftheuniverse", + "path": "Assets/EyeOfTheUniverse/Traveler/MyCoolEyeGuy.prefab" + } + ] + } +} +``` + +Next, create an Animator Controller asset in Unity with at least two states, named "Idle" and "PlayingInstrument". You can assign whatever animation clip you like to these states, but the names of the states must match exactly. The default Idle state will play when the traveler is first spawned in, and will transition to the PlayingInstrument state when the right conditions are met to start playing the instrument. Ensure that both animation clips are set to loop in their import settings. + +Add a boolean Parameter in the left panel named "Playing". This will be set to true when the traveler starts playing their instrument. + +Add a transition in both directions between the Idle and PlayingInstrument states. Uncheck "Has Exit Time" for both transitions and adjust the other timing settings as desired. + +Add a Condition on the `Idle -> PlayingInstrument` transition to check for `Playing` = `true`, and the inverse for `PlayingInstrument -> Idle`. + +![animController](@/assets/docs-images/eye_of_the_universe/animController.webp) + +In your character object, find the `Animator` component and set its `Controller` property to the Animator Controller asset you created. If you have a `TravelerEyeController` component in your object, set its `_animator` property to your Animator component. + +If everything was set up correctly, your character should play their animations in-game. + +## Quantum Instruments + +Quantum instruments are the interactible instruments, typically hidden by a short 'puzzle', that cause their corresponding traveler to appear around the campfire. They are just like any other detail prop (see [Detailing](/guides/details/)) but they have additional handling to only activate after gathering and speaking to Riebeck, like the other instrument 'puzzles' in the Eye sequence. + +If not specified, the quantum instrument will inherit some of the properties for its `"signal"` from the corresponding eye traveler config. + +If you want other objects besides the traveler to appear or disappear in response to gathering the instrument, specify a custom dialogue condition name for `"gatherCondition"` and use that same condition as the `"activationCondition"` or `"deactivationCondition"` for the details you want to toggle. + +Quantum instruments will automatically be included in the inflation animation that pushes everyone away from the campfire at the end of the sequence. + +[Eye Travelers](#eye-travelers), [Quantum Instruments](#quantum-instruments), and [Instrument Zones](#instrument-zones) are all linked by their `"id"` properties. Ensure that your ID matches between those details and is unique enough to not conflict with other mods. + +```json title="planets/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "EyeOfTheUniverse", + "starSystem": "EyeOfTheUniverse", + "EyeOfTheUniverse": { + "eyeTravelers": [ + { + "id": "Slate", + "signal": { + "name": "Slate", + "audio": "planets/MetronomeLoop.wav" + // etc. + }, + // etc. + } + ], + "quantumInstruments": [ + { + "id": "Slate", + "gatherWithScope": false, + "gatherCondition": "EyeSlateGather", + "path": "TimberHearth_Body/Sector_TH/Sector_Village/Sector_StartingCamp/Props_StartingCamp/OtherComponentsGroup/Props_HEA_CampsiteLogAssets/Props_HEA_MarshmallowCanOpened", + "position": {"x": -43.94369, "y": 0, "z": 7506.436}, + "signal": { + "detectionRadius": 0, + "identificationRadius": 10, + "position": {"x": 0, "y": 0.1, "z": 0}, + "isRelativeToParent": true + } + } + ] + } +} +``` + +To see the full list of quantum instrument properties and descriptions of what each property does, check [the QuantumInstrumentInfo schema](/schemas/body-schema/defs/quantuminstrumentinfo/). + +## Instrument Zones + +Instrument zones are just like any other detail prop (see [Detailing](/guides/details/)) but they have additional handling to only activate after gathering and speaking to Riebeck, like the other instrument 'puzzles' in the Eye sequence. + +Custom instrument zones will automatically be included in the inflation animation that pushes everyone away from the campfire at the end of the sequence. + +[Eye Travelers](#eye-travelers), [Quantum Instruments](#quantum-instruments), and [Instrument Zones](#instrument-zones) are all linked by their `"id"` properties. Ensure that your ID matches between those details and is unique enough to not conflict with other mods. + +```json title="planets/EyeOfTheUniverse.json" +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", + "name": "EyeOfTheUniverse", + "starSystem": "EyeOfTheUniverse", + "EyeOfTheUniverse": { + "eyeTravelers": [ + { + "id": "Slate", + // etc. + } + ], + "instrumentZones": [ + { + "id": "Slate", + "deactivationCondition": "EyeSlateGather", + "path": "TimberHearth_Body/Sector_TH/Sector_Village/Sector_StartingCamp/Props_StartingCamp/OtherComponentsGroup/Props_HEA_CampsiteLogAssets", + "removeChildren": [ + "Props_HEA_MarshmallowCanOpened" + ], + "position": {"x": -43.30302, "y": 0, "z": 7507.822} + } + ] + } +} +``` + +To see the full list of instrument zone properties and descriptions of what each property does, check [the InstrumentZoneInfo schema](/schemas/body-schema/defs/instrumentzoneinfo/). + +## Eye-Specific Considerations + +#### Cross-System Details + +Specifying details with a `"path"` pointing to an object in the regular solar system normally wouldn't work, as the Eye of the Universe lives in a completely separate scene from the rest of the game and those objects don't exist at the Eye. New Horizons works around this by force-loading the regular solar system, grabbing any objects referenced in Eye of the Universe config files, and then attempting to preserve these objects when loading the Eye of the Universe scene. This can cause issues with many different kinds of props, especially interactive ones that depend on some other part of the solar system existing. + +Because the objects are not available outside of this workaround, objects from the regular solar system cannot be spawned in via the New Horizons API. + +#### Custom Planets + +While you *can* define completely custom planets the same as you would in a regular custom solar system, they may exhibit weird orbital behaviors or pass through the existing static Eye of the Universe objects. Prefer adding onto the existing bodies or setting a `"staticPosition"` on your planet configs to lock them in place. + +#### The Player Ship and Ship Logs + +The player's ship does not exist in the Eye of the Universe scene. In addition to the obvious issues this causes (no access to the ship's warp functionality, ship spawn points being non-functional, etc.), the ship log computer not existing causes some methods of checking and learning ship log facts to not function at all while at the Eye. If you need to track whether the player has met certain conditions elsewhere in the game (for example, if they've previously met a character that you now want to appear at the campfire), consider using a Persistent Dialogue Condition, which does not have these issues. + +#### Mod Compatibility + +Other existing and future story mods will want to add additional content to the Eye of the Universe, and unlike entirely custom planets and star systems, there is a high probability that objects placed at the Eye may overlap with those placed by other mods. When testing, try installing as many of these other mods as possible and seeing if the objects they add overlap with yours. If so, consider moving your objects to a different position. When possible, use New Horizons features that preserve compatibility between mods. \ No newline at end of file diff --git a/docs/src/content/docs/guides/nomai-text.md b/docs/src/content/docs/guides/nomai-text.md new file mode 100644 index 00000000..959aeeb4 --- /dev/null +++ b/docs/src/content/docs/guides/nomai-text.md @@ -0,0 +1,26 @@ +--- +title: Nomai Text +description: Guide to making Nomai Text in New Horizons +--- + +This page goes over how to use Nomai text in New Horizons. + +## Understanding Nomai Text + +Nomai text is the backbone of many story mods. There are two parts to setting up Nomai text: The XML file and the planet config. + +### XML + +In your XML, you define the actual raw text which will be displayed, the ship logs it unlocks, and the way it branches. See [the Nomai text XML schema](/schemas/text-schema/) for more info. + +Nomai text contains a root `` node, followed by `` nodes and optionally a `` node. + +Nomai text is made up of `TextBlock`s. Each text block has an `ID` which must be unique (you can just number them for simplicity). After the first defined text block, each must have a `ParentID`. For scrolls and regular wall text, the text block only gets revealed after its parent block. Multiple text blocks can have the same parent, allowing for branching paths. In recorders and computers, each text block must procede in order (the second parented to the first, the third to the second, etc). In cairns, there is only one text block. + +To unlock ship logs after reading each text block, add a `` node. This can contains multiple `` nodes, each one defining a ``, ``. The ship log conditions node can either have `` or ``, which means the logs will unlock only if you are at that location. The `` lists the TextBlock ids which must be read to reveal the fact as a comma delimited list (e.g., `1,2,4`).. + +### Json + +In your planet config, you must define where the Nomai text is positioned. See [the translator text json schema](/schemas/body-schema/defs/translatortextinfo/) for more info. + +You can input a `seed` for a wall of text which will randomly generate the position of each arc. To test out different combinations, just keep incrementing the number and then hit "Reload Configs" from the pause menu with debug mode on. This seed ensures the same positioning each time the mod is played. Alternatively, you can use `arcInfo` to set the position and rotation of all text arcs, as well as determining their types (adult, teenager, child, or Stranger). The various age stages make the text look messier, while Stranger allows you to make a translatable version of the DLC text. \ No newline at end of file diff --git a/docs/src/content/docs/guides/planet-generation.md b/docs/src/content/docs/guides/planet-generation.md index 7baad807..241138e8 100644 --- a/docs/src/content/docs/guides/planet-generation.md +++ b/docs/src/content/docs/guides/planet-generation.md @@ -35,12 +35,12 @@ There are also tools to help generate these images for you such as [Textures For The following modules support variable sizing, meaning they can change scale over the course of the loop. -- Water -- Lava -- Star -- Sand -- Funnel -- Ring +- Water +- Lava +- Star +- Sand +- Funnel +- Ring To do this, simply specify a `curve` property on the module @@ -90,6 +90,9 @@ This makes the second planet a quantum state of the first, anything you specify } ``` +Keep in mind that if you redefine `Orbit` on all configs (even with the same parameters each time), **the planet will change its position within its orbit when changing states.** +*If you want your Quantum Planet's position to* ***NOT*** *change,* ***only define `Orbit` on the main state***. + ## Barycenters (Focal Points) To create a binary system of planets (like ash twin and ember twin), first create a config with `FocalPoint` set diff --git a/docs/src/content/docs/guides/publishing.md b/docs/src/content/docs/guides/publishing.md index 65ad5f70..c1ab4204 100644 --- a/docs/src/content/docs/guides/publishing.md +++ b/docs/src/content/docs/guides/publishing.md @@ -10,10 +10,11 @@ This guide assumes you've created your addon by following [the addon creation gu Before you release anything, you'll want to make sure: -- Your mod has a descriptive `README.md`. (This will be shown on the website) -- Your repo has the description field (click the cog in the right column on the "Code" tab) set. (this will be shown in the manager) -- There's no `config.json` in your addon. (Not super important, but good practice) -- Your manifest has a valid name, author, and unique name. +- Your mod has a descriptive `README.md`. (This will be shown on the website) +- Your repo has the description field (click the cog in the right column on the "Code" tab) set. (this will be shown in the manager) +- There's no `config.json` in your addon. (Not super important, but good practice) +- Your manifest has a valid name, author, and unique name. +- You have included any caches New Horizons has made (i.e., slide reel caches). Since these are made in the install location of the mod you will have to manually copy them into the mod repo and ensure they stay up to date. While these files are not required, they ensure that your players will have faster loading times and reduced memory usage on their first loop (after which the caches will generate for them locally). ## Releasing diff --git a/docs/src/content/docs/guides/ship-log.md b/docs/src/content/docs/guides/ship-log.md index 15dafd5c..55399d2b 100644 --- a/docs/src/content/docs/guides/ship-log.md +++ b/docs/src/content/docs/guides/ship-log.md @@ -404,31 +404,28 @@ Of course, having a custom ship log is neat and all, but what use is it if the p ### Initial Reveal -You can set facts to reveal as soon as the player enters the system by adding the `initialReveal` property +You can set facts to reveal as soon as the player enters the system by adding the `initialReveal` property to your **star system config** ```json {4} { - "ShipLog": { - "xmlFile": "planets/example.xml", - "initialReveal": ["EXAMPLE_RUMOR_FACT"] - } + "initialReveal": ["EXAMPLE_RUMOR_FACT"] } ``` ### Signal Discovery -You can set a fact to reveal as soon as a signal is identified by editing the signal's `Reveals` attribute +You can set a fact to reveal as soon as a signal is identified by editing the signal's `reveals` attribute ```json { - "Signal": { - "Signals": [ + "Props": { + "signals": [ { - "Frequency": "Quantum", - "Name": "Quantum Planet", - "AudioClip": "OW_QuantumSignal", - "SourceRadius": 1000, - "Reveals": "EXAMPLE_EXPLORE_FACT" + "frequency": "Quantum", + "name": "Quantum Planet", + "audio": "OW_QuantumSignal", + "sourceRadius": 1000, + "reveals": "EXAMPLE_EXPLORE_FACT" } ] } @@ -459,7 +456,7 @@ You can set a fact to reveal in dialogue with the `` tag ### Reveal Volumes Reveal volumes are triggers/colliders in the world that can unlock facts from a variety of actions. -Reveal volumes are specified in the `Props` module, its key is `reveal`. +Reveal volumes are specified in the `Volumes` module, its key is `revealVolumes`. #### Position @@ -503,8 +500,8 @@ trigger the reveal ```json { - "Props": { - "reveal": [ + "Volumes": { + "revealVolumes": [ { "position": { "x": -55.65454, @@ -545,3 +542,28 @@ Adding an entry location is similar to adding a Reveal Volume: ``` ![entryLocationExample](@/assets/docs-images/ship_log/entry_position.webp) + +## Extending Base Game Entries + +You can add new facts and sub-entries to existing ship log entries by adding a dummy entry to your ship log XML file with the same ID as an existing entry. Any facts and sub-entries will be applied to the existing entry. For example, to extend the "Village" entry on Timber Hearth (which has the internal ID "TH_VILLAGE"): + +```xml title="ExampleShipLog.xml" + + TIMBER_HEARTH + + TH_VILLAGE + + EXAMPLES_VILLAGE + A lot of unfamiliar characters are hanging around the village now. + + + EXAMPLES_DREAM + Wetrock Dream + + EXAMPLES_DREAM_EXPLORED + I dreamed about Wetrock using a green campfire. + + + + +``` diff --git a/docs/src/content/docs/guides/star-systems.md b/docs/src/content/docs/guides/star-systems.md index 69782ff9..a7d8f671 100644 --- a/docs/src/content/docs/guides/star-systems.md +++ b/docs/src/content/docs/guides/star-systems.md @@ -23,10 +23,12 @@ A star system config file will look something like this: "y": [0, 5, 4], "z": [5, 4, 0, 3, 1] }, - "vesselPosition": { - "x": 0, - "y": 0, - "z": 8000 + "vesselSpawn": { + "position": { + "x": 0, + "y": 0, + "z": 8000 + } } } } diff --git a/docs/src/content/docs/guides/translation.md b/docs/src/content/docs/guides/translation.md index 56a3dacb..cd910f05 100644 --- a/docs/src/content/docs/guides/translation.md +++ b/docs/src/content/docs/guides/translation.md @@ -35,8 +35,8 @@ Are you tired of manually translating JSON? Do you want an automatic translator? This tool has the following features: -- Extract text from XML files and create english.json as the translation source. -- Translate english.json to create a json file for another language. +- Extract text from XML files and create english.json as the translation source. +- Translate english.json to create a json file for another language. This section outlines how to install and use the nh-translation-helper. @@ -44,10 +44,10 @@ This section outlines how to install and use the nh-translation-helper. To get started, head over to the [repo for the tool](https://github.com/96-38/nh-translation-helper) and prepare the requirements: -- Install [Node.js](https://nodejs.org/) >= 12.0.0 - - Install the LTS version. -- Get [DeepL API](https://www.deepl.com/docs-api) Key (Free or Pro) - - Sign up [here](https://www.deepl.com/pro#developer) +- Install [Node.js](https://nodejs.org/) >= 12.0.0 + - Install the LTS version. +- Get [DeepL API](https://www.deepl.com/docs-api) Key (Free or Pro) + - Sign up [here](https://www.deepl.com/pro#developer) When you are ready, execute the following command in a terminal or command prompt: @@ -81,20 +81,20 @@ Please enter the DeepL API key for the first time only. The API key will be save ### Note -- Not supported extracting UIDictionary and AchievementTranslations +- Not supported extracting UIDictionary and AchievementTranslations - - It is difficult to parse these automatically, and the number of words is small that it would be better to add them by MOD developers manually for better results. - - Translating UIDictionary and AchievementTranslations is supported. + - It is difficult to parse these automatically, and the number of words is small that it would be better to add them by MOD developers manually for better results. + - Translating UIDictionary and AchievementTranslations is supported. -- Not supported translation into Korean +- Not supported translation into Korean - - Translation is provided by the DeepL API, so it is not possible to translate into languages that are not supported by DeepL. + - Translation is provided by the DeepL API, so it is not possible to translate into languages that are not supported by DeepL. -- The generated translations are "**not**" perfect +- The generated translations are "**not**" perfect - - It is a machine translation though DeepL. The translations on DeepL are known to be too casual or to abbreviate some sentences. - - It will need to be manually corrected to make it a good translation. However, this tool allows you to prototype and is more efficient than starting from scratch. Also, the CDATA tag has been removed from the translated text and must be added manually. + - It is a machine translation though DeepL. The translations on DeepL are known to be too casual or to abbreviate some sentences. + - It will need to be manually corrected to make it a good translation. However, this tool allows you to prototype and is more efficient than starting from scratch. Also, the CDATA tag has been removed from the translated text and must be added manually. -- Parsing errors may occur when trying to translate manually created JSON files - - In many cases, this is due to a specific comment in the JSON. Please remove the comments and try again. - - Most comments are processed normally, but errors may occur if the comment contains special symbols or if the comment is located at the end of a JSON object. +- Parsing errors may occur when trying to translate manually created JSON files + - In many cases, this is due to a specific comment in the JSON. Please remove the comments and try again. + - Most comments are processed normally, but errors may occur if the comment contains special symbols or if the comment is located at the end of a JSON object. diff --git a/docs/src/content/docs/guides/troubleshooting.md b/docs/src/content/docs/guides/troubleshooting.md new file mode 100644 index 00000000..d4650d40 --- /dev/null +++ b/docs/src/content/docs/guides/troubleshooting.md @@ -0,0 +1,23 @@ +--- +title: Troubleshooting +description: A guide to troubleshooting common issues with mods +--- + +## My slide reels aren't updating when I change them + +Certain images (such as slide reels) get modified by New Horizons before usage, to save on resources NH will cache +the modified version of these images on the file system to be recalled later for easier access. If you are changing +an image you'll need to clear the cache located in the `SlideReelsCache` folder of your mod's directory to see changes. To do this simply delete the folder and restart the game. + +## My planet is flying away at light speed and also I have anglerfish + +Be sure to disable `hasFluidDetector` (previous had to enable `invulnerableToSun`). The anglerfish have fluid volumes in their mouths for killing you +which interact poorly with the fluid detector and can mess up the movement of the planet. + +## My Nomai text isn't updating + +Either clear the .nhcache files or enable Debug mode to always regenerate the text cache. + +## Prop placer is gone! +This is not a bug, actually. We removed prop placer because it was inconsistent and buggy, and no one in years cared enough to fix it. +Use the debug raycast button and Unity Explorer to place your props, or otherwise work in unity editor. diff --git a/docs/src/content/docs/guides/updating-planets.md b/docs/src/content/docs/guides/updating-planets.md index ad9784d4..55dc08d5 100644 --- a/docs/src/content/docs/guides/updating-planets.md +++ b/docs/src/content/docs/guides/updating-planets.md @@ -3,31 +3,31 @@ title: Update Existing Planets description: A guide for updating base-game planets in New Horizons --- -Similar to above, make a config where "Name" is the name of the planet. The name should be able to just match their in-game english names, however if you encounter any issues with that here are the in-code names for planets that are guaranteed to work: +Similar to above, make a config where "Name" is the name of the planet. The name should be able to just match their in-game english names, however if you encounter any issues with that here are the in-code names for planets that are guaranteed to work (case sensitive!): -- `SUN` -- `CAVE_TWIN` (Ember Twin) -- `TOWER_TWIN` (Ash Twin) -- `TIMBER_HEARTH` -- `BRITTLE_HOLLOW` -- `GIANTS_DEEP` -- `DARK_BRAMBLE` -- `COMET` (Interloper) -- `WHITE_HOLE` -- `WHITE_HOLE_TARGET` (The Whitehole Station) -- `QUANTUM_MOON` -- `ORBITAL_PROBE_CANNON` -- `TIMBER_MOON` (Attlerock) -- `VOLCANIC_MOON` (Hollow's Lantern) -- `DREAMWORLD` -- `MapSatellite` -- `RINGWORLD` (The Stranger) +- `SUN` +- `CAVE_TWIN` (Ember Twin) +- `TOWER_TWIN` (Ash Twin) +- `TIMBER_HEARTH` +- `BRITTLE_HOLLOW` +- `GIANTS_DEEP` +- `DARK_BRAMBLE` +- `COMET` (Interloper) +- `WHITE_HOLE` +- `WhiteholeStation` +- `QUANTUM_MOON` +- `ORBITAL_PROBE_CANNON` +- `TIMBER_MOON` (Attlerock) +- `VOLCANIC_MOON` (Hollow's Lantern) +- `DREAMWORLD` +- `MapSatellite` +- `RINGWORLD` (The Stranger) Some features will not work if you try to add them to a base planet config. These include: -- FocalPoints (just makes no sense really, a focal point is meant to be a intangible point between two binary bodies). -- Gravity (including the strength, fall-off, and the size of the gravitational sphere of influence) -- Reference frames (the volume used for targetting a planet with your ships navigation systems) +- FocalPoints (just makes no sense really, a focal point is meant to be a intangible point between two binary bodies). +- Gravity (including the strength, fall-off, and the size of the gravitational sphere of influence) +- Reference frames (the volume used for targetting a planet with your ships navigation systems) You can also delete parts of an existing planet. Here's part of an example config which would delete the rising sand from Ember Twin: diff --git a/docs/src/content/docs/reference/audio-enum.md b/docs/src/content/docs/reference/audio-enum.md index 6cbd7919..199825b7 100644 --- a/docs/src/content/docs/reference/audio-enum.md +++ b/docs/src/content/docs/reference/audio-enum.md @@ -769,1164 +769,1164 @@ Ignore the numbers, just take the name. This is a list of AudioClips that will also work, there's a lot of overlap with the AudioType list. Many old addons with signals use values from this list. -- Hotel Oneshot - Heavy Thud 4 -- Tool_Put_Away_01 -- OW Quantum Lightning 091118 AP 07 -- OW_GD_ElectricBarrier_Idle_Loop -- Nomai_Stone_Door_End_V2_11 -- General Destruction 1 -- Dream Rule 02 backdrop loop 072321_2 AP -- amb_observatory -- Dream World Water Ambience - Creek 3 -- Pickup_Rock_01 -- gasp_traumatic4_lessmale -- Ship_Impact_Light_Damage_V3_06 -- BlackHole_02 -- OW_GD_WavesAgainstRock -- OW_TH_AmbienceInCanyons -- OW_PR_FootstepsBushRustle_03 -- OW_PR_ActivateProbeMode -- Ghost Walk Footstep Wood_v2 6 -- rockingchair4 -- Water Spray Impact 4 -- OW Secret Library 040821_2 AP -- Real World Dam Break Alex Composite 2 -- Ghost Run Footstep Wood_v2 4 -- UI_Enter_Dialog_V6-002_highpass_2 -- Spark_10 -- Mournful Prisoner 3 -- OW_PR_FootstepsLeaves_06 -- FootstepsWoodCreak_02 -- OW Dreamworld Ruins 072021 AP 02c -- OW_PR_HitWallUnderwater1 -- OW_TH_RiverWaterFlow_loop -- OW ReelBeat 01c 021021 AP -- FootstepsWoodCreak_07 -- Ignite_Marshmallow_03 -- Marshmallow_Replace_02 -- Ship_FuelLeak -- Raft Heavy Impact 5 -- elevatorloop -- Real World Alarm Bell Oneshot 3 -- BigBang_EndFlash -- OW_FinalEndTimes_DB_loop -- fogsphere_pulse4 -- Ringworld Ambience 3 -- Destruction Impact 4 -- Artifact Unconceal -- OW_NM_DoorStart_06 -- OW_PR_SignalscopeZoomOut -- Nomai_Stone_Door_End_Big_V2_03 -- Player Gravel Footstep 3 -- General Destruction 5 -- OW Dream Fire Room 121820_4 AP LP -- OW_PR_DeactivateProbeMode -- OW_PR_LandInWater4 -- OW_PR_FootstepsJumpNomai_04 -- OW_PR_FootstepsJumpMetal_03 -- WarpCore_Remove_V3_01 -- Probe_SnapShot_03 -- Ghost Idle Search 4 -- Solanum_Foley_IconExit -- Dream Fire Room Ambience Test 1 -- Lantern Extinguish 4 -- OW_PR_OxygenLeakingFromSuit_loop_louder -- Wood Door Open Stop -- OW_PR_FootstepsJumpGrass_01 -- OW_SP_ThrustAfterburn -- OW_PR_FootstepsGrass_01 -- Real World Alarm Bell Oneshot 4 -- OW_PR_FootstepsNomai_04 -- Hotel Oneshot - Heavy Thud 2 -- Light Sensor Fade In 4 -- AnglerFish_OpenMouth_v2_01 -- Footstep_Run4 -- Ghost Walk Footstep Forest 6 -- OW_PR_FootstepsGrass_06 -- OW_GD_AmbienceRain -- RotationalThruster04 -- OW New Raft Music 082321_4 AP theme -- Spark_01 -- OW Muted End Times 040821 AP -- Nomai_WhiteHoleStationActivation -- Tool_Take_Out_02 -- OW_PR_FootstepsJumpDirt_05 -- mallowpuff2 -- Dreamworld Tower Fall Part 2 -- Jump_Into_Fogsphere_04 -- FootstepsWoodCreak_05 -- OW_SP_MetalCreak_14 -- OW_NM_DoorStart_03 -- drowning_firsthalf2 -- Forest Oneshot - Tree Creak 4 -- shiplog_misc1 -- FootstepsWoodCreak_04 -- Real World Dam Crack -- OW_NM_DoorStart_09 -- OW_SP_LandingPadHard4 -- OW_PR_FootstepsSand_06 -- BH_Ambience_Surface -- Ghost Grunt 3 -- glass_crack_02 -- House Destruction 3 -- OW_QuantumMoon -- Orb_Roll_Energy_Loop_v2_01 -- OW Eye Of The Universe 082018_2 AP -- Footstep3 -- OW_NM_DoorStart_Big_01 -- OW Dreamworld Ruins Story Beats 071621 AP 1d -- Dream World Tidal Wave Loop -- Nomai_Stone_Door_End_Big_V2_15 -- OW_SP_ActivateComputer 1 -- OW_PR_FootstepsIce_07 -- Raft Light Impact V2 3 -- UI_Pause_v2_08 -- signalscope_static -- OW_SP_LandingCamActivated 1 -- Footstep4 -- OW_PR_FootstepsSand_03 -- Prisoner Pick Up Vision Torch -- OW_SP_MetalCreak_16 -- OW_SP_Touchdown_04 -- Platform_Break_V2_03 -- OW Nomai Time Loop Device 081818 AP -- RockPile_Fall_02 -- Ship_Impact_No_Damage_V3_02 -- Destruction Impact 7 -- Ghost Grab Player 2 -- Forest Oneshot - Tree Creak 5 -- OW_PR_FootstepsMetal_01 -- OW NM Flashback 082818_3 AP base -- Projector Totem Light 2 -- Slot_Linking_Stone_Loop_02 -- breathing_suit3 -- OW_PR_FootstepsWood_07 -- OW_SP_HeadlightsOff_v2 -- Nomai_Stone_Door_End_V2_06 -- Fix_Puncture_03 -- OW Dream Rule LP 032421 AP glitch -- Real World Tidal Wave Loop Louder -- OW Dreamworld Ruins 072021 AP 02loop -- gasp_normal11_lessmale -- OW ReelBeat 01a 021021 AP -- OW_TH_ModelRocketThrustRotational_01 -- OW_PR_FootstepsBushRustle_05 -- OW NomaiRuinsRegular 081918 AP motif3c v2 -- OW Final End Times 022519_2 AP LOOP1 -- OW_PR_FootstepsSand_01 -- Anglerfish_Awake4 -- OW_PR_FootstepsJumpIce_04 -- Fix_Puncture_01 -- Raft Light Impact V2 1 -- Warp_Loop_01_v2 -- Ghost Start Hunt Grunt 1 -- Destruction Debris 4 -- Solanum_IconAppear_V3 -- OW_SP_ActivateComputer_OneShot -- Destruction Impact 8 -- OW_SP_ShipGroan1_v2 -- OW ReelBackdrop 02a 021021 AP -- OW_NM_ComputerRing1 -- OW_PR_FootstepsJumpGlass_04 -- Forest Oneshot - Tree Creak 6 -- OW_GD_HeatLightning_01 -- Raft Heavy Impacts V2 3 -- Solanum_Foley_RockFormStart -- Raft Light Impact V2 5 -- Ghost Grunt 4 -- OW_PR_FootstepsJumpDirt_06 -- OW_PR_FootstepsJumpGrass_03 -- OW_TravelerTheme_whistling -- Raft Heavy Impact 2 -- Nature Oneshot - Distant Deep Creature 1 -- OW_SP_CloseHatch_v2 -- linkingstone_in -- OW_PR_FootstepsSnow_03 -- OW_NM_OrbDeSelect_Energy_02 -- Impact_Light_02 -- Nomai_Stone_Door_End_V2_10 -- Fix_Puncture_07 -- OW_NM_HoleEnterExit -- OW NM Nomai Ruins 081718 AP -- OW_PR_OxygenRefill -- GhostMatter_Splash_v4_05 -- glass_crack_01 -- Ignite_Marshmallow_02 -- Ship_Impact_Medium_Damage_V3_03 -- Forest Oneshot - Animal 4 -- OW_PR_FootstepsIce_02 -- OW_GD_RainOnHelmet -- Spaceship_RattleLoop -- BigBang_WhooshLeadToExplo -- OW_TH_Campfire_loop_01 -- bigbang_cosmicinflation_v2 -- Hotel Oneshot - Heavy Creak 4 -- OW_PR_FootstepsJumpGrass_02 -- Tronworld Ambience 1 -- AnglerFish_Target_v2_07 -- Eye_of_Universe_Ambience_v2_01 -- OW_PR_FootstepsJumpLeaves_04 -- OW_PR_FootstepsJumpSnow_03 -- OW_PR_FootstepsJumpGlass_01 -- OW Traveler Theme 091118 AP FINAL TIME NO PIANO EDIT -- OW ReelBeat 02f 082521 AP -- Metal Door Close Stop -- medkit -- OW_NM_BlackHole_Lp -- Ghost Blow Out Lantern -- OW_PR_MarshmallowEatBurnt_shorter -- Solanum_Foley_IconEnter -- OW_PR_SignalscopeSwitchFrequencies -- Prisoner Pick Up Artifact -- OW_PR_BanjoStrum_3b -- Water Spray Impact 5 -- UI_Advance_Dialog_V6-002_highpass -- Destruction Debris 8 -- OW_PR_FootstepsDirt_06 -- Vision Torch - Step In -- OW_NM_DoorStart_Big_04 -- OW_PR_FootstepsBushRustle_06 -- Hotel Oneshot - Creak 5 -- OW_PR_FootstepsJumpLeaves_01 -- OW_SP_ShipGroan4_v2 -- Jump_Into_Fogsphere_03 -- Dreamworld Forest Ambience 2 -- OW_PR_FootstepsJumpRock_02 -- CrushedByElevator -- Player Gravel Footstep 1 -- House Destruction 4 -- Ghost Begin Stalk Grunt 1 -- flashlightOff -- OW ReelBeat 04b_2 040921 AP -- OW_PR_FootstepsRock_02 -- AshTwinCore_Open_01 -- OW_SP_HeadlightsOn_v2 -- Tower Fall Part 2 -- OW_NM_DoorStart_Big_02 -- Meteor_Impact_01_b -- OW_SP_LandingCamStatic -- OW_PR_FootstepsWood_01 -- gasp_light5 -- OW_PR_FootstepsGrass_03 -- Spark_03 -- Loading Tunnel - Loop -- FootstepsJumpWoodCreak_02 -- OW_Main_Menu -- Lantern Put Down -- OW Secret Library Whispers LP 040821 AP REF MIX -- OW_DB_Ambience -- Vision Torch Light Rays - On -- Hotel Oneshot - Creak 1 -- Dreamworld Nature Ambience 4 -- OW_PR_ThrustRotationalUnderwater_04 -- OW_PR_FootstepsJumpGrass_06 -- Nomai_Stone_Door_End_Big_V2_09 -- Ship_Impact_No_Damage_V3_01 -- Raft Movement Stop 3 -- Destruction Impact 9 -- Recorder_Start_Button -- AnglerFish_Target_v2_14 -- Ghost Begin Chase Grunt 2 -- Hotel Oneshot - Creak 6 -- Fragment_Break -- Tronworld Exit 2 -- Ghost Walk Footstep Forest 2 -- Incinerate_v3_01 -- Light Sensor Fade Out 3 -- OW_TravelerTheme_flute -- OW_PR_FootstepsLeaves_02 -- OW_PR_FootstepsLeaves_01 -- Fix_Puncture_05 -- Ghost Walk Footstep Wood_v2 3 -- OW_PR_FootstepsJumpSand_01 -- Sarcophagus Strain 2 -- OW_PR_HitWallUnderwater4 -- OW_PR_FootstepsRock_03 -- Loading Tunnel - Unload -- OW_PR_FootstepsJumpNomai_03 -- OW_PR_FootstepsDirt_07 -- OW_PR_FootstepsSnow_04 -- Wood Door Close Stop -- OW_PR_FootstepsJumpIce_02 -- OW ReelBeat 04a 031521 AP -- Raft Light Impact V2 2 -- Meteor_Impact_02_b -- JellyFish_Shock_02 -- Metal Door Open Start -- OW_GD_UnderwaterCurrent -- OW_GD_HeatLightning_06 -- nomai_textbranchout_noenergy2 -- asphyxiation_nosuit_secondhalf_version3 -- Artifact Focus -- Ice_Cave_Amb_loop_v3_01 -- Forest Oneshot - Tree Creak 3 -- OW NomaiRuinsRegular 081918 AP motif4c -- Dreamworld Lights Out Ambience 4 -- Projector Next Slide 2 -- BeaconIdea4 -- Destruction Impact 1 -- OW_PR_FootstepsNomai_06 -- OW_NM_FlickeryGravityCrystalAmbience -- OW Demonic Vocal Sting 082321 AP -- OW Quantum Lightning 091118 AP 08 -- OW_NM_SadTheme_older -- Nature Oneshot - Distant Creature 2 - less reverb -- FootstepsJumpWoodCreak_03 -- OW_PR_FootstepsJumpSand_02 -- Player Gravel Footstep 8 -- OW_PR_FootstepsSnow_06 -- gasp_traumatic3_lessmale -- Real World Dam Break Water Oneshot -- Repair_Loop -- OW_TH_FlagFlapping_loop.\_01 -- Ship_Impact_Light_Damage_V3_02 -- Raft Socket -- Jump_Into_TinyGalaxy_v2_01 -- OW NomaiRuinsRegular 081918 AP motif7c -- gasp_normal13_lessmale -- Sandfall_Inside_Loop_01 -- Solanum_RocksForm -- Fix_Puncture_06 -- OW Dreamworld Ruins Story Beats 071621 AP 1h -- Sarcophagus Open 2 -- fogsphere_pulse2 -- Elevator Rattle Loop 3 -- IllusoryWall_Alex -- OW_GD_HeatLightning_04 -- Nomai_Stone_Door_End_Big_V2_14 -- OW_PR_FootstepsJumpMetal_01 -- Vine_Crash_V3_03_LowPassDelay -- OW_PR_BanjoStrum_2b -- OW_PR_FootstepsGlass_05 -- OW_PR_FootstepsLeaves_07 -- OW_NM_DoorStart_01 -- OW_PR_FootstepsGrass_05 -- OW_PR_FootstepsJumpIce_01 -- OW_PR_ThrustRotationalUnderwater_01 -- Anglerfish_Chase_Breathing -- OW_PR_FootstepsRock_04 -- Metal Door Close Start -- Gear Rotate 1 -- Airlock Loop -- Solanum_Foley_RockFormEnd -- mallowpuff3 -- OW_PR_FootstepsSand_05 -- OW_PR_FootstepsJumpRock_01 -- Artifact Unfocus -- OW Eye Of The Universe 082818_2 AP -- OW_PR_FootstepsJumpRock_03 -- galaxy_zoomout2 -- OW_GD_IslandFalling_v2_loop -- Projector Previous Slide 2 -- Ignite_CampFire_04 -- SpaceshipAlarm2_3Iterations -- Ghost Individual Death 3 -- OW_PR_FootstepsJumpNomai_01 -- OW_PR_FootstepsJumpSnow_01 -- Forest Oneshot - Animal 2 -- OW_PR_FootstepsJumpDirt_01 -- Dreamworld Tower Fall Part 1 -- Ship_Impact_Medium_Damage_V3_05 -- OW_NM_ComputerRing3 -- OW_PR_SuitOn -- Airlock Pressurize -- OW_PR_FootstepsBushRustle_01 -- OW_PR_FootstepsNomai_02 -- Ship_Impact_Medium_Damage_V3_04 -- OW_NM_VesselDiscovery -- PlayerSubmerge -- Player Gravel Footstep 5 -- OW_EndTimes -- HGT_SandColumn_Ship -- OW ReelBeat 02c 021021 AP -- Spark_09 -- OW_PR_FootstepsSand_02 -- Damage_Light_05 -- OW_NM_ComputerRingFall2 -- gasp_traumatic7_lessmale -- Ghost Investigation Grunt -- Dream World Alarm Bell Oneshot 2 -- rockingchair2 -- OW_PR_FootstepsJumpDirt_04 -- Projector Next Slide -- OW_PR_FootstepsSand_08 -- OW_NM_WHAmbience2_v2 -- drowning_secondhalf2 -- Ghost Walk Footstep Forest 1 -- Prisoner Cloth Foley 3 -- OW_PR_FootstepsGlass_03 -- Vision Torch Scanning - Loop -- Raft Medium Impact V2 2 -- Hotel Oneshot - Heavy Thud 3 -- Destruction Impact - Large 4 -- OW_PR_FootstepsJumpLeaves_03 -- OW_PR_FootstepsJumpSand_03 -- OW Dreamworld Ruins SILENCE 02 -- OW_PR_FootstepsWood_06 -- ModelRocket_LightImpact -- shiplog_scanningloop -- OW Ghost Ambiences v2 011221 AP low LP -- OW NomaiRuinsRegular 081918 AP motif2c -- glass_crack_03 -- Fix_Puncture_08 -- OW_PR_FootstepsSnow_01 -- OW_PR_FootstepsDirt_01 -- asphyxiation_nosuit_firsthalf1 -- OW_NM_DoorSlide_Big_LP_01 -- OW_SUN_SupernovaWall -- OW_NM_DoorAirLockAirPourOut_03 -- OW_PR_ThrustUnderwater -- Solar Sail Stop -- OW_TH_Waterwheel_loop -- Forest Oneshot - Tree Creak 1 -- OW ReelBackdrop 03a 050321 LOOP -- Platform_Break_V2_04 -- FireBall_01 -- OW ReelBackdrop 01a 022521 AP -- Dreamworld Lights Out Ambience 1 -- Nomai_Stone_Door_End_Big_V2_11 -- Spark_07 -- OW_SP_ThrustTranslationalUnderwater -- Dream World Water Ambience - Creek 1 -- Ghost Begin Stalk Grunt 2 -- Comet_Purr -- JellyFish_Shock_04 -- OW_PR_FootstepsWood_08 -- Raft Reeling Loop -- shiplog_switchmode_forward -- Station Light Flicker - Dreamworld -- Lantern Wake Up Light 3 -- flashlightOn -- Dreamworld Base Ambience 1 -- Ship_Impact_Heavy_Damage_V3_09 -- Crushed_To_Death_V2_01 -- ow_kazoo_theme -- JetPack_NotificationBeep -- Ghost Run Footstep Wood_v2 1 -- OW_PR_FootstepsJumpNomai_05 -- OW_NM_DataStream_v2 -- UI_Tab_v2_02 -- OW_PR_ProbeTakePicture -- OW_NM_GravityCannonAmbience -- OW NM Flashback 082818 AP loop -- OW_SP_LandingPadSoft4 -- OW_PR_FootstepsGlass_07 -- WarpCore_Insert_V3_01 -- OW_NM_TractorBeamLP -- ShipRepair_Finish -- Ghost Begin Chase Grunt 1 -- Destruction Impact 2 -- OW_PR_FootstepsLeaves_08 -- Recorder_Stop_Button -- OW_PR_FootstepsLeaves_04 -- OW_PR_Jump1 -- Jump_Into_TinyGalaxy_v2_04 -- OW_GD_Tornado_v2_04 -- OW_PR_FootstepsJumpDirt_02 -- OW_SP_ConsoleReadoutStart -- OW_NM_EscapePodHatch -- OW_PR_FootstepsJumpNomai_02 -- Light Sensor Fade In 1 -- Light Sensor Fade In 2 -- Hotel Oneshot - Creak 9 -- CampfireTune_All_Reverb -- Recording_Loop_03 -- PartyHouseWhistle_Confident -- mallowpuff4 -- OW_NM_WhiteHoleAmbienceL -- Station Shudder -- drowning_firsthalf1 -- UI_Exit_Dialog_V6-002_highpass -- Dream World Dam Break -- Dreamfire Extinguish 1 -- MediumSplash -- Vision Torch - First Slide Appears -- Footstep1 -- OW_TravelerTheme_drums -- Artifact Conceal -- HGT_Ambience_Cave_Small -- HGT_Ambience_Cave_Big -- OW_TH_Waterfall_loop_01 -- OW Discovery 083021_2 AP darker shorter -- Secret Passageway Open Loop 2 -- Hotel Oneshot - Creak 4 -- supernova_explosion_deepnuclear2 -- Ghost Shout 7 -- OW NM Flashback 081718 AP slam -- Airlock Close -- OW_TH_FlagFlapping_loop.\_02 -- OW_SP_RefuelJetpack_v2_short -- Ship_Impact_No_Damage_V3_03 -- Raft Medium Impact V2 4 -- Anglerfish_Awake3 -- Wood Door Open Start -- OW_TH_FlagFlapping_loop -- Slide Reel Pickup 2 -- Tronworld Ambience 2 Alex Hack -- Ghost Run Footstep Wood_v2 5 -- Ignite_CampFire_03 -- Single Ghost Scream 14 -- Raft Start Reeling -- OW_PR_FootstepsGrass_08 -- Tronworld Enter 2 -- OW_PR_ProbeUnderwaterLaunch -- OW_GD_IslandCrashingInWater_v2 -- Grapple Totem Zoom In -- OW ReelBeat 02a 021021 AP -- Nomai_Stone_Door_End_V2_08 -- OW_PR_FootstepsJumpGlass_02 -- OW_NM_DoorStart_02 -- Destruction Impact - Large 3 -- OW_SP_SignalscopeSlideV2 -- OW NomaiRuinsRegular 081918 AP motif6c -- Wood Door Close Start 3 Alex -- Destruction Impact - Large 2 -- Nomai_Stone_Door_End_Big_V2_01 -- OW_NM_ComputerAmbienceLP -- Anglerfish_Awake2 -- OW_TH_Insects_loop_03 -- OW_PR_SignalscopeActivate -- OW_NM_ComputerRingFall1 -- OW_PR_FootstepsDirt_02 -- OW_NM_ShuttleLight -- Single Ghost Scream 1 -- Ghost Identify Fail -- OW_SP_ShipGroan5_v2 -- Cloaking Field Exit -- OW_PR_FootstepsNomai_01 -- OW Dreamworld Ruins 072021 AP 02e -- Forest Oneshot - Tree Creak 2 -- fogsphere_pulse1 -- OW_PR_PullOutStick -- OW_PR_LockOn -- Nomai_Stone_Door_End_Big_V2_07 -- OW_PR_FootstepsJumpSand_06 -- InstantDeath2_Long_Ringing -- Light Sensor Door Stop 2 -- Light Sensor Door Loop 2 -- OW_TH_GeyserEnd_02 -- pickaxe_01 -- OW_PR_FootstepsWaterWade_03 -- RockPile_Fall_03 -- OW_PR_ThrustAfterburn_v2_01 -- Light Sensor Fade Out 2 -- Light Sensor Fade In 3 -- OW Finally Set Free 072021_2 AP -- Ghost Blow Out Lantern Charge -- Wood Door Loop -- BigBang_Explo -- TH_Geyser_Loop_v3 -- Water Spray Impact 8 -- OW_TH_FlagFlapping_loop.\_03 -- OW_PR_FootstepsJumpLeaves_02 -- OW_PR_FootstepsIceSlide_Lp_01 -- Dreamworld Lights Out Ambience 3 -- knife_scrape_01 -- OW OBSERVATORY 011317 AP -- Footstep_Run5 -- Light Sensor Door Open 2 -- Jetpack_O2_loop_01 -- OW_PR_FootstepsJumpWood_04 -- OW_SP_BuckleUp -- Probe_SnapShot_02 -- Pickup_Ceramic_01 -- OW_PR_FootstepsWood_03 -- Destruction Debris 3 -- OW_PR_SignalscopeDeactivate -- OW_PR_OxygenLeakingFromSuit_loop -- Probe_SnapShot_01 -- Dreamworld Ghost Hotel Ambience 2 -- Slide Reel Put Down 2 -- Nomai_Stone_Door_End_V2_07 -- OW_SP_ProbeLauncherRotation_v2 -- OW_PR_MarshmallowEatUnburnt_noMmm_v3 -- SystemBackOnline -- Ghost Idle Search 1 -- Ship_Impact_Light_Damage_V3_03 -- OW Dreamworld Ruins Story Beats 071621 AP 1i -- OW_PR_FootstepsGrass_02 -- OW_NM_DoorSlide_LP_02 -- Ringworld Cave Ambience -- Fix_Puncture_02 -- OW_PR_FootstepsWaterWade_08 -- OW_TH_GeyserStart_01 -- OW_TH_Insects_loop_01 -- OW ReelBeat 04d_2 040921 AP -- OW_TH_ProjectorStop -- Prisoner Cloth Foley 6 -- Dream World Alarm Bell Oneshot 3 -- shiplog_selectplanet2 -- Nature Oneshot - Distant Creature 1 - less reverb -- OW_PR_FootstepsDirt_08 -- Spark_04 -- OW_GD_HeatLightning_02 -- OW Secret Loop 090121 AP muted -- Creature Voice Test 1 Short -- General Destruction 4 -- House Destruction 1 -- OW_PR_FootstepsSnow_02 -- Huge Splash 2 -- Player Gravel Footstep 4 -- Ghost Grab Player 1 -- shiplog_switchmode_back -- OW_PR_FootstepsJumpWood_03 -- OW_PR_FootstepsJumpMetal_05 -- OW_SP_AutopilotEngaged 1 -- OW_NM_DoorAirlockOpen_03 -- OW Dreamworld Ruins 072021 AP 02b -- Ghost Investiagation Grunt 2 -- Wood Door Close Start 2 -- Real World Alarm Bell Oneshot 2 -- OW Fabric SFX 102119 AP screen shatter SHORT -- OW_NM_Ruins_ambience_scary_loop -- Raft Heavy Impact 1 -- OW Whispers 041321_2 AP LP -- Ghost Run Footstep Forest 4 -- Vine_Crash_V3_01_LowPassDelay -- Solanum_Foley_HandLower -- OW_PR_FootstepsSand_07 -- Grapple Totem Zoom Out Louder -- Ship_Impact_Heavy_Damage_V3_07 -- OW_PR_FootstepsIce_01 -- OW Ghost Ambiences v2 011221 AP pad LP -- Raft Movement Stop -- OW_TH_Museum -- gasp_normal12_lessmale -- OW_PR_FootstepsJumpRock_05 -- OW_SP_ThrustRotationalUnderwater_02 -- Pool_Exit_v3 -- Flashlight_Malfunction_02 -- OW NomaiRuinsRegular 081918 AP motif1c -- Nomai_Stone_Door_End_V2_05 -- OW_PR_FootstepsLeaves_05 -- Nomai_Stone_Door_End_V2_09 -- asphyxiation_suit_secondhalf1 -- Spark_08 -- OW Aquatic Exploration 050318 AP LOOP -- OW_TH_GeyserEnd_01 -- UI_Navigate_03 -- OW ReelBeat 02b 021021 AP -- Player_Impact_Damage_Light_02 -- OW_PR_FootstepsWood_02 -- Jump_Into_TinyGalaxy_v2_02 -- OW_PR_ThrustRotationalUnderwater_05 -- Atmosphere_High_Suit -- OW_SP_MetalCreak_04 -- OW Prisoner Reveal 063021 AP -- Ghost Grunt 1 -- OW_PR_FootstepsJumpDirt_03 -- OW Reelbeat 03c 061721 AP -- Nomai_Stone_Door_End_Big_V2_10 -- Damage_Light_01 -- PutDown_Rock_01 -- OW_TH_ModelRocketCrashing -- OW ReelBeat 03b 032521 AP -- OW END OF GAME 021818 AP -- Raft Movement Start 3 -- Impact_Light_03 -- OW_NM_Tech_Advanced -- Ghost Walk Footstep Wood_v2 4 -- OW_PR_FootstepsJumpWood_05 -- OW_PR_FootstepsMetal_02 -- FootstepsJumpWoodCreak_01 -- OW Timber Hearth 032719 AP v2 -- Hotel Oneshot - Heavy Creak 3 -- shiplog_highlight -- OW_GD_Tornado_v2_03 -- RotationalThruster02 -- OW Quantum Lightning 091118 AP 06 -- JetPack_NotificationBeep_Fast -- SmallSplash -- Ghost Run Footstep Forest 3 -- Nomai_Stone_Door_End_Big_V2_04 -- OW Prisoner Elevator 061121_5 AP loop -- Tower Fall Part 1 -- OW_PR_FootstepsMetal_03 -- OW_PR_FootstepsRock_01 -- Ignite_Marshmallow_01 -- Ignite_CampFire_02 -- Ghost Walk Footstep Wood_v2 5 -- OW_GD_AmbienceCave -- Ship_Impact_Heavy_Damage_V3_11 -- Raft Release -- elevatorstop -- Hotel Oneshot - Creak 2 -- Station Light Flicker -- Forest Oneshot - Tree Creak 7 -- OW_GD_HeatLightning_03 -- Nomai_Stone_Door_End_Big_V2_08 -- OW_TH_AmbienceHighAltitude -- Water Spray Impact 3 -- Ghost Grunt 2_SmoothFade -- linkingstone_out -- Ghost Shout 6 -- Ghost Individual Death 4 -- shiplog_highlight2 -- OW_PR_FootstepsMetal_04 -- OW Morning Cello 101718_2 -- Spark_11 -- fogsphere_jump2_delayed -- Nomai_Stone_Door_End_V2_04 -- OW_SP_ElectricalDamageLP -- OW_PR_FootstepsGlass_02 -- Ghost Run Footstep Forest 6 -- OW_NM_CenterClampsRemoving -- OW_SP_ThrustRotationalUnderwater_03 -- OW_NM_InsertScroll -- GhostMatter_Splash_v4_02 -- Airlock Open -- OW_PR_TranslatorTranslateNew -- Ship_Impact_Heavy_Damage_V3_10 -- House Destruction 2 -- shiplog_newentry3_softer -- OW_TravelerTheme_piano -- Stilts Destruction 3 -- OW_PR_LockOff -- OW_TH_ModelRocketThrustTranslational_01 -- Hotel Oneshot - Creak 8 -- Dreamworld Candle Lighting Test Variation 6 -- Stilts Destruction 4 -- OW_SUN_BurnPlanet -- Real World Dam Crack Water Loop -- OW Final End Times 022519_2 AP LOOP2 -- OxygenRefill_Short -- OW ReelBeat 03a 031521 AP -- OW NomaiRuinsRegular 081918 AP motif5c v2 -- StoryReel5Short 063021_3 AP -- OW_GD_AmbienceOcean -- OW_TH_BridgeCreaking_loop -- OW Traveler Theme 021821 AP FINAL TIME NO PIANO ADD PRISONER -- OW_SP_ShipAmbiance_01 -- Prisoner Grunt 2 -- OW_NM_ComputerRingFall3 -- OW_NM_DoorStart_04 -- Ghost Neck Snap -- Nomai_Warp_01 -- General Destruction 2 -- pickaxe_02 -- OW_PR_FootstepsSnow_07 -- Lava_Splash_02 -- OW_PR_FootstepsGlass_06 -- Anglerfish_Awake -- OW_PR_ProbeRetrieval -- OW_PR_FootstepsJumpSnow_04 -- Lantern Wake Up Light 2 -- Secret Passageway Open Stop 2 -- Lantern Shorting Out 2 -- OW_PR_BanjoStrum_1b -- OW_PR_FootstepsSnow_08 -- Air Rushing Out Into Space -- OW_PR_FootstepsJumpGlass_03 -- OW_PR_ProbeInAirSound -- shiplog_movebetweenplanets -- OW_PR_BanjoStrum_4b -- Raft Stop Reeling -- OW Traveler Theme 091118 AP FINAL TIME WITH PIANO EDIT -- OW_PR_FootstepsMetal_08 -- OW ReelBeat 04a_2 040921 AP -- OW_SP_ShipExploding -- OW_PR_FootstepsJumpMetal_02 -- OW_TH_ModelRocketThrustRotational_04 -- JellyFish_Shock_03 -- OW No-Eye Ruins 121120_2 AP LP -- OW_GD_HeatLightning_05 -- OW_NM_GravityCrystalAmbience_Louder -- OW_GD_HeatLightning_07 -- Solar Sail Loop 2 -- OW_PR_FootstepsBushRustle_02 -- CityLights_Off_01 -- RotationalThruster01 -- OW_NM_BHEnterItem_v2 -- OW_PR_FootstepsBushRustle_09 -- Vision Torch - Vision Dissolves -- OW_PR_FootstepsJumpMetal_04 -- Lantern Remove 2 -- Player_Impact_Damage_Light_01 -- StoryReel5Full 062821_4 AP -- OW_PR_FootstepsWaterWade_07 -- OW_NM_SkypeLP -- OW_SP_ShipGroan2_v2 -- Volcano_Ambience_Loop_V2 -- AnglerFish_Chomp_Loop_v2 -- Destruction Debris 2 -- OW_PR_LandInWater2 -- OW_PR_FootstepsIce_05 -- Footstep_Run1 -- Anglerfish_ChompBite_01 -- Destruction Debris 1 -- Ghost Run Footstep Forest 1 -- AnglerFish_Target_v2_02 -- Signalscope_Zoom_Loop -- BH_Ambience_Below_Crust -- fogsphere_pulse3 -- OW_GD_HeatLightning_08 -- AshTwinCore_Close_01 -- Airlock Depressurize -- OW_TH_ModelRocketThrustRotational_03 -- OW_NM_ComputerRing2 -- RockPile_Fall_05 -- Vision Torch Fire - Loop -- OW Farewell 061721 AP -- Hologram_Enter_v2_01 -- OW_PR_ThrustRotationalUnderwater_03 -- Forest Oneshot - Tree Creak 8 -- OW_PR_LandInWater3 -- Hotel Oneshot - Creak 3 -- Hotel Oneshot - Heavy Thud 1 -- Distant Ghost Cacophony 1 -- Footstep_Run2 -- OW_TH_ProjectorRun_loop -- OW_PR_FootstepsJumpGrass_04 -- Destruction Impact - Large 5 -- FootstepsWoodCreak_03 -- OW_PR_FootstepsIce_03 -- Solanum_Foley_HandRaise -- OW_NM_DoorStart_Big_03 -- Candle Extinguishing Test Variation 6 -- Ignite_CampFire_01 -- Nature Oneshot - Distant Creature 3 - less reverb -- Real World Water Ambience - Slow River -- Ghost Grunt 5 -- IceMelt_v2_LowPass -- OW_NM_GravityCannonDeactivated -- OW Ghost Sequence 011121 AP LOW SUSPENSE LP -- Anglerfish_Sleeping -- Real World Water Ambience - Slow River 2 -- OW_PR_FootstepsWaterWade_02 -- Ghost Walk Footstep Forest 3 -- Spark_05 -- OW No-Eye Ruins 082121 AP stinger -- Nature Oneshot - Distant Deep Creature 3 -- OW_NM_SunStation -- Projector Totem Extinguish 1 -- Dreamfire Crackling Loop 3 -- OW Ghost Ambiences 012921_2 AP slam -- Dreamfire Explosion -- Dream World Water Ambience - Creek 8 -- OW_PR_FootstepsSnow_05 -- OW_PR_FootstepsIce_04 -- Dream World Water Ambience - Creek 5 -- Ghost Run Footstep Forest 2 -- OW_NM_DoorStart_08 -- Outer Wilds Party House v8 050321_2 AP traveler drone -- Hotel Oneshot - Heavy Creak 1 -- Secret Passageway Open Start 2 -- FootstepsWoodCreak_01 -- Ghost Grunt 8 -- OW_PR_FootstepsMetal_06 -- Probe_Attach_v3_02 -- OW_PR_LandInWater1 -- FootstepsWoodCreak_06 -- OW_PR_FootstepsBushRustle_04 -- Projector Totem Blow -- OW_PR_FootstepsWaterWade_01 -- Raft Movement Loop -- OW_SP_ThrustRotationalUnderwater_01 -- OW_PR_FallingIntoLavaBeep_loop -- Ship_Impact_Light_Damage_V3_01 -- Ship_Impact_Medium_Damage_V3_02 -- Ghost Walk Footstep Wood_v2 1 -- Nomai_Stone_Door_End_Big_V2_02 -- OW ReelBeat 04c_2 040921 AP -- Raft Push -- OW_PR_FootstepsJumpIce_03 -- Nomai_Stone_Door_End_Big_V2_05 -- Destruction Impact 5 -- HGT_SandFallSmall_Inside -- OW Quantum Lightning 091118 AP 01 -- OW_PR_OxygenLeakingFromSuit_in -- OW_PR_HelmetOn -- OW_SP_AutopilotDisengaged 1 -- PutDown_Ceramic_01 -- OW_PR_FootstepsMetal_07 -- Water Spray Impact 7 -- Dream World Alarm Bell Oneshot 4 -- CityLights_On_01 -- rockingchair3 -- fogsphere_pulse5 -- Nomai_ShipPowerOn_V2_01 -- OW_GD_IslandSuckedInTornado -- OW_NM_DoorStart_05 -- Probe_Attach_v3_01 -- OW_PR_ThrustRotationalUnderwater_02 -- Outer Wilds Party House v8 050321_3 AP traveler -- Artifact Put Down -- OW ReelBeat 01b 021021 AP -- stoppedasphyxiating_suit2 -- FootstepsJumpWoodCreak_04 -- Destruction Debris 6 -- Nomai_Warp_03_Shorter -- mallowpuff1 -- OW ReelBeat 02d 021021 AP -- OW_TravelerTheme_harmonica -- shiplog_misc4 -- Projector Totem Extinguish 3 -- OW ReelBackdrop 02b 021021 AP -- OW_PR_FootstepsDirt_03 -- Lava_Splash_01 -- Engine_Start_V2 -- Solar Sail Start -- OW_PR_ThrustTranslational_v2_01 -- Light Sensor Fade Out 1 -- Prisoner Cloth Foley 4 -- OW Quantum Lightning 091118 AP 02 -- OW_NM_RemoveScroll -- asphyxiation_suit_firsthalf2 -- OW_TH_AmbienceNightInCanyons -- Footstep2 -- OW ReelBackdrop 03b LOOP 031521 AP -- OW_SP_ThrustRotationalUnderwater_04 -- OW_PR_FootstepsNomai_08 -- OW_PR_FootstepsNomai_03 -- OW ReelBeat 03d 061121 AP -- OW_PR_FootstepsJumpSand_04 -- RotationalThruster03 -- Stilts Destruction 5 -- Raft Run Aground -- OW Space Station 081420_2 AP -- OW_PR_FootstepsWaterWade_06 -- Spark_02 -- Cloaking Field Entry -- OW_NM_DataWormhole -- OW_QuantumSignal -- General Destruction 3 -- OW New Raft Music 082321_4 AP quiet -- OW_SP_Unbuckle -- Real World Water Ambience - Calm 2 -- Prisoner Grunt 3 -- OW_NM_OrbMoveGlass_lp_01 -- Prisoner Cloth Foley 7 -- OW_PR_FootstepsRock_05 -- OW Traveler Theme 021821 AP FINAL TIME WITH PIANO ADD PRISONER -- OW Quantum Lightning 091118 AP 03 -- OW_TH_ModelRocketThrustRotational_02 -- OW_PR_HitWallUnderwater3 -- Flashlight_Malfunction_03 -- OW NM Flashback 082818 loop overlay AP -- supernova_corecollapse_10sec_fadeOut -- OW_PR_FootstepsWood_05 -- rockingchair1 -- Power_Failure_v2_02 -- OW_PR_FootstepsMetal_05 -- Stilts Destruction 2 -- Dreamworld Tower Tilt -- OW New Texture 082921 AP loop -- Lantern Insert 3 -- OW_PR_FootstepsNomai_07 -- OW_PR_FootstepsJumpWood_06 -- Pool_Enter_v3_Fade -- Raft Movement Start -- OW_PR_FootstepsNomai_05 -- OW_PR_HardSplash -- Destruction Debris 5 -- Dreamworld Candle Lighting Test Variation 2 -- OW NM Flashback 082818_2 AP stinger delayed -- Ghost Shout 4 -- Nomai_Stone_Door_End_Big_V2_06 -- Tower Tilt -- OW_NM_DoorStart_Big_06 -- OW_PR_FootstepsLeaves_03 -- Ghost Run Footstep Wood_v2 3 -- OW_NM_BHExitItem_v2 -- Ghost Idle Search 5 -- Forest Oneshot - Animal 1 -- OW Dreamworld Ruins 072021 AP 02d -- OW_PR_FootstepsJumpSnow_02 -- Platform_Break_V2_01 -- OW Party House 092820 AP short loop -- OW_PR_FootstepsRock_06 -- OW_SP_ThrustTranslational -- Footstep6 -- OW_PR_FootstepsWaterWade_05 -- Ghost Start Hunt Grunt 2 -- OW Dreamworld Ruins 072021 AP 02f -- Nomai_Stone_Door_End_Big_V2_12 -- Jump_Into_TinyGalaxy_v2_03 -- Footstep_Run3 -- Negative1 -- OW_TravelerTheme_banjo -- SandColumnEnd_v2 -- OW_PR_FootstepsJumpSand_05 -- Dreamworld Lights Out Ambience 2 -- OW Blair Witch Project 082921_3 AP darker mix loop -- Fig_Backer_Sat_Audio_V3 -- OW_PR_FootstepsGlass_01 -- Light Sensor Fade Out 4 -- Projector Totem Light 1 -- Player Gravel Footstep 6 -- OW_TH_GeyserStart_03 -- Slide Reel Remove 3 -- Ship_Impact_Medium_Damage_V3_06 -- OW Quantum Lightning 091118 AP 05 -- OW_GD_Tornado_v2_01 -- OW_SP_ActivateComputerLP -- Footstep_Run6 -- Spark_13 -- Vine_Crash_V3_02_LowPassDelay -- OW Slideshow BURNT LOOP small 031521_2 AP -- Raft Heavy Impact 4 -- Light Sensor Door Loop - Door Sensor Sliding -- Sarcophagus Strain 3 -- Loading Tunnel - Load -- elevatorstart -- Nomai_Stone_Door_End_V2_02 -- OW_GD_Tornado_v2_02 -- OW_NM_DoorAirLockAirPourOut_01 -- OW_TravelerTheme_newtraveler 061021 AP -- OW ReelBackdrop 04a_3 041321 AP LP -- OW_PR_FootstepsJumpRock_04 -- Damage_Heavy_04 -- Raft Movement Stop 2 -- OW_SP_AnalogClick2_v2 -- Nomai_Stone_Door_End_V2_01 -- OW_PR_FootstepsRock_07 -- Raft Light Impact V2 4 -- Marshmallow_Replace_01 -- Footstep5 -- Destruction Impact 3 -- Helmet_Glass_Crack_08 -- Ghost Run Footstep Wood_v2 6 -- OW End Credits 022019_3 AP -- Raft Heavy Impacts V2 2 -- Nomai_Stone_Door_End_Big_V2_13 -- OW_NM_GravityCannonActivated -- Outer Wilds Party House v8 050321_2 AP Bass -- Ringworld Muffled Indoor Ambience -- Ghost Run Footstep Wood_v2 2 -- OW_GD_AmbienceUndewater -- Forge_Loop_V3_03 -- OW_PR_CampfireAmbience -- OW_PR_FootstepsRock_08 -- Platform_Break_V2_02 -- OW_PR_FootstepsJumpWood_02 -- OW Eye Temple 121820_2 AP LP -- OW_GD_WavesAgainstBeach -- OW_PR_ThrowProbe -- OW_NM_DoorStart_07 -- OW_PR_FootstepsSand_04 -- Lantern Pickup -- Real World Water Ambience - Rapids 2 -- OW_PR_FootstepsWood_04 -- OW_NM_TractorBeamPowerUp -- OW New Stinger 082921 AP -- OW_PR_FootstepsGrass_07 -- Hotel Oneshot - Creak 7 -- Player_On_Fire_Loop -- OW_TH_UnderwaterRushing -- GhostMatter_Splash_v4_01 -- OW_SP_ThrustRotationalUnderwater_06 -- OW Fabric SFX 102119 AP rip FADE OUT -- OW Dreamworld Ruins 072021 AP 02a -- Spark_06 -- OW_SP_SignalscopeChunkV2 -- Player Gravel Footstep 2 -- OW_PR_FootstepsBushRustle_07 -- Hologram_Exit_v2_01 -- Metal Door Open Stop -- OW_PR_FootstepsJumpWood_01 -- OW_PR_FootstepsWaterWade_04 -- Nature Oneshot - Distant Deep Creature 2 -- Stilts Destruction 1 -- OW NM Flashback 082818 AP overlay1 -- drowning_secondhalf1 -- Gear Rotate Locked_Short -- Prisoner Cloth Foley 5 -- shiplog_deselectplanet -- Orb_End_v3_01 -- Atmosphere_High_Ship -- Solar Sail Start 2 -- knife_scrape_02 -- OW_TH_GeyserEnd_03 -- Meteor_Impact_03_b -- Destruction Impact 6 -- JellyFish_Shock_01 -- Light Sensor Loop -- Candle Extinguishing Test Variation 4 -- Vision Torch Light Rays - Off -- OW_NM_DoorSlide_Big_LP_05 -- OW_PR_FootstepsDirt_05 -- Sand_Column_Start_v2_03 -- Water Spray Impact 6 -- OW_NM_DoorStart_Big_07 -- OW_SP_ShipGroan3_v2 -- Heat_Damage_Loop_01 -- OW_SP_LandingCamDeactivated 1 -- breathing_lowO2_6 -- OW_PR_OxygenLeakingFromSuit_out -- LogUpdated_001 -- Vine_Crash_V3_04_LowPassDelay -- Destruction Debris 7 -- Hotel Oneshot - Heavy Creak 2 -- OW_PR_HelmetOff -- OW_PR_FootstepsGrass_04 -- Forest Oneshot - Animal 3 -- OW Discovery 083021_2 AP shorter -- Slide Reel Insert 3 -- OW_NM_BHEnterExitPlayer_v2 -- Ship_Impact_Light_Damage_V3_05 -- OW_SP_OpenHatch_NoBeep -- OW_SP_ThrustRotationalUnderwater_05 -- ShipSubmerge -- Artifact Fire Loop -- OW_DarkBramble_loop -- OW_PR_FootstepsBushRustle_08 -- OW NM Nomai City 081718 AP LOOP -- Solar Sail Stop 2 -- Destruction Impact - Large 1 -- OW Dream Rule LP 032421 AP normal -- Solar Sail Loop -- OW_TH_GeyserStart_02 -- Ship_Impact_No_Damage_V3_05 -- OW_PR_FootstepsIce_06 -- Outer Wilds Party House v8 050321_2 AP vocals -- asphyxiation_suit_secondhalf2 -- Affirmative1 -- Spark_12 -- OW_SP_ConsoleReadoutLP -- OW ReelBackdrop 03c 042621 AP LP -- gasp_light9 -- Nomai_Stone_Door_End_V2_03 -- OW_PR_FootstepsJumpNomai_06 -- OW_PR_FootstepsGlass_04 -- OW_PR_FootstepsDirt_04 -- Destruction Debris 9 -- OW_PR_FootstepsIce_08 -- OW_PR_SuitOff -- Artifact Pickup -- OW_SUN_Ambience_v4 -- JellyFish_Shock_05 -- OW_TH_Underwater -- Station Shudder - Dreamworld -- Ghost Walk Footstep Forest 5 -- OW_TH_ProjectorActivate -- asphyxiation_suit_firsthalf1 -- OW_PR_SignalscopeZoomIn -- HGT_Ambience_Surface -- FootstepsWoodCreak_08 -- GhostMatter_Splash_v4_03 -- UI_Tab_v2_05 -- Raft Medium Impact V2 3 -- OW_NM_TractorBeamPowerDown -- OW_Travel_Theme_Remaster -- Big_Galaxy_Burn_v2_01 -- OW_TH_Insects_loop_02 -- Raft Heavy Impact 3 -- Raft Movement Start 2 -- OW Quantum Lightning 091118 AP 04 -- OW_TH_FlagFlapping_loop.\_04 -- OW_TH_ModelRocketThrustTranslational_02 -- knife_scrape_03 -- Incinerate_v3_02 -- Light Sensor Rotate -- OW_NM_EscapePodDistressSignal -- MapZoomOut_Tone -- OW_NM_DoorStart_Big_05 -- Player Gravel Footstep 7 -- Ghost Walk Footstep Wood_v2 2 -- Ghost Run Footstep Forest 5 -- Ghost Walk Footstep Forest 4 -- OW_PR_HitWallUnderwater2 -- OW NM Flashback 082818_2 AP stinger -- OW_PR_FootstepsJumpGrass_05 -- Fix_Puncture_04 -- Ghost Idle Search 3 +- Hotel Oneshot - Heavy Thud 4 +- Tool_Put_Away_01 +- OW Quantum Lightning 091118 AP 07 +- OW_GD_ElectricBarrier_Idle_Loop +- Nomai_Stone_Door_End_V2_11 +- General Destruction 1 +- Dream Rule 02 backdrop loop 072321_2 AP +- amb_observatory +- Dream World Water Ambience - Creek 3 +- Pickup_Rock_01 +- gasp_traumatic4_lessmale +- Ship_Impact_Light_Damage_V3_06 +- BlackHole_02 +- OW_GD_WavesAgainstRock +- OW_TH_AmbienceInCanyons +- OW_PR_FootstepsBushRustle_03 +- OW_PR_ActivateProbeMode +- Ghost Walk Footstep Wood_v2 6 +- rockingchair4 +- Water Spray Impact 4 +- OW Secret Library 040821_2 AP +- Real World Dam Break Alex Composite 2 +- Ghost Run Footstep Wood_v2 4 +- UI_Enter_Dialog_V6-002_highpass_2 +- Spark_10 +- Mournful Prisoner 3 +- OW_PR_FootstepsLeaves_06 +- FootstepsWoodCreak_02 +- OW Dreamworld Ruins 072021 AP 02c +- OW_PR_HitWallUnderwater1 +- OW_TH_RiverWaterFlow_loop +- OW ReelBeat 01c 021021 AP +- FootstepsWoodCreak_07 +- Ignite_Marshmallow_03 +- Marshmallow_Replace_02 +- Ship_FuelLeak +- Raft Heavy Impact 5 +- elevatorloop +- Real World Alarm Bell Oneshot 3 +- BigBang_EndFlash +- OW_FinalEndTimes_DB_loop +- fogsphere_pulse4 +- Ringworld Ambience 3 +- Destruction Impact 4 +- Artifact Unconceal +- OW_NM_DoorStart_06 +- OW_PR_SignalscopeZoomOut +- Nomai_Stone_Door_End_Big_V2_03 +- Player Gravel Footstep 3 +- General Destruction 5 +- OW Dream Fire Room 121820_4 AP LP +- OW_PR_DeactivateProbeMode +- OW_PR_LandInWater4 +- OW_PR_FootstepsJumpNomai_04 +- OW_PR_FootstepsJumpMetal_03 +- WarpCore_Remove_V3_01 +- Probe_SnapShot_03 +- Ghost Idle Search 4 +- Solanum_Foley_IconExit +- Dream Fire Room Ambience Test 1 +- Lantern Extinguish 4 +- OW_PR_OxygenLeakingFromSuit_loop_louder +- Wood Door Open Stop +- OW_PR_FootstepsJumpGrass_01 +- OW_SP_ThrustAfterburn +- OW_PR_FootstepsGrass_01 +- Real World Alarm Bell Oneshot 4 +- OW_PR_FootstepsNomai_04 +- Hotel Oneshot - Heavy Thud 2 +- Light Sensor Fade In 4 +- AnglerFish_OpenMouth_v2_01 +- Footstep_Run4 +- Ghost Walk Footstep Forest 6 +- OW_PR_FootstepsGrass_06 +- OW_GD_AmbienceRain +- RotationalThruster04 +- OW New Raft Music 082321_4 AP theme +- Spark_01 +- OW Muted End Times 040821 AP +- Nomai_WhiteHoleStationActivation +- Tool_Take_Out_02 +- OW_PR_FootstepsJumpDirt_05 +- mallowpuff2 +- Dreamworld Tower Fall Part 2 +- Jump_Into_Fogsphere_04 +- FootstepsWoodCreak_05 +- OW_SP_MetalCreak_14 +- OW_NM_DoorStart_03 +- drowning_firsthalf2 +- Forest Oneshot - Tree Creak 4 +- shiplog_misc1 +- FootstepsWoodCreak_04 +- Real World Dam Crack +- OW_NM_DoorStart_09 +- OW_SP_LandingPadHard4 +- OW_PR_FootstepsSand_06 +- BH_Ambience_Surface +- Ghost Grunt 3 +- glass_crack_02 +- House Destruction 3 +- OW_QuantumMoon +- Orb_Roll_Energy_Loop_v2_01 +- OW Eye Of The Universe 082018_2 AP +- Footstep3 +- OW_NM_DoorStart_Big_01 +- OW Dreamworld Ruins Story Beats 071621 AP 1d +- Dream World Tidal Wave Loop +- Nomai_Stone_Door_End_Big_V2_15 +- OW_SP_ActivateComputer 1 +- OW_PR_FootstepsIce_07 +- Raft Light Impact V2 3 +- UI_Pause_v2_08 +- signalscope_static +- OW_SP_LandingCamActivated 1 +- Footstep4 +- OW_PR_FootstepsSand_03 +- Prisoner Pick Up Vision Torch +- OW_SP_MetalCreak_16 +- OW_SP_Touchdown_04 +- Platform_Break_V2_03 +- OW Nomai Time Loop Device 081818 AP +- RockPile_Fall_02 +- Ship_Impact_No_Damage_V3_02 +- Destruction Impact 7 +- Ghost Grab Player 2 +- Forest Oneshot - Tree Creak 5 +- OW_PR_FootstepsMetal_01 +- OW NM Flashback 082818_3 AP base +- Projector Totem Light 2 +- Slot_Linking_Stone_Loop_02 +- breathing_suit3 +- OW_PR_FootstepsWood_07 +- OW_SP_HeadlightsOff_v2 +- Nomai_Stone_Door_End_V2_06 +- Fix_Puncture_03 +- OW Dream Rule LP 032421 AP glitch +- Real World Tidal Wave Loop Louder +- OW Dreamworld Ruins 072021 AP 02loop +- gasp_normal11_lessmale +- OW ReelBeat 01a 021021 AP +- OW_TH_ModelRocketThrustRotational_01 +- OW_PR_FootstepsBushRustle_05 +- OW NomaiRuinsRegular 081918 AP motif3c v2 +- OW Final End Times 022519_2 AP LOOP1 +- OW_PR_FootstepsSand_01 +- Anglerfish_Awake4 +- OW_PR_FootstepsJumpIce_04 +- Fix_Puncture_01 +- Raft Light Impact V2 1 +- Warp_Loop_01_v2 +- Ghost Start Hunt Grunt 1 +- Destruction Debris 4 +- Solanum_IconAppear_V3 +- OW_SP_ActivateComputer_OneShot +- Destruction Impact 8 +- OW_SP_ShipGroan1_v2 +- OW ReelBackdrop 02a 021021 AP +- OW_NM_ComputerRing1 +- OW_PR_FootstepsJumpGlass_04 +- Forest Oneshot - Tree Creak 6 +- OW_GD_HeatLightning_01 +- Raft Heavy Impacts V2 3 +- Solanum_Foley_RockFormStart +- Raft Light Impact V2 5 +- Ghost Grunt 4 +- OW_PR_FootstepsJumpDirt_06 +- OW_PR_FootstepsJumpGrass_03 +- OW_TravelerTheme_whistling +- Raft Heavy Impact 2 +- Nature Oneshot - Distant Deep Creature 1 +- OW_SP_CloseHatch_v2 +- linkingstone_in +- OW_PR_FootstepsSnow_03 +- OW_NM_OrbDeSelect_Energy_02 +- Impact_Light_02 +- Nomai_Stone_Door_End_V2_10 +- Fix_Puncture_07 +- OW_NM_HoleEnterExit +- OW NM Nomai Ruins 081718 AP +- OW_PR_OxygenRefill +- GhostMatter_Splash_v4_05 +- glass_crack_01 +- Ignite_Marshmallow_02 +- Ship_Impact_Medium_Damage_V3_03 +- Forest Oneshot - Animal 4 +- OW_PR_FootstepsIce_02 +- OW_GD_RainOnHelmet +- Spaceship_RattleLoop +- BigBang_WhooshLeadToExplo +- OW_TH_Campfire_loop_01 +- bigbang_cosmicinflation_v2 +- Hotel Oneshot - Heavy Creak 4 +- OW_PR_FootstepsJumpGrass_02 +- Tronworld Ambience 1 +- AnglerFish_Target_v2_07 +- Eye_of_Universe_Ambience_v2_01 +- OW_PR_FootstepsJumpLeaves_04 +- OW_PR_FootstepsJumpSnow_03 +- OW_PR_FootstepsJumpGlass_01 +- OW Traveler Theme 091118 AP FINAL TIME NO PIANO EDIT +- OW ReelBeat 02f 082521 AP +- Metal Door Close Stop +- medkit +- OW_NM_BlackHole_Lp +- Ghost Blow Out Lantern +- OW_PR_MarshmallowEatBurnt_shorter +- Solanum_Foley_IconEnter +- OW_PR_SignalscopeSwitchFrequencies +- Prisoner Pick Up Artifact +- OW_PR_BanjoStrum_3b +- Water Spray Impact 5 +- UI_Advance_Dialog_V6-002_highpass +- Destruction Debris 8 +- OW_PR_FootstepsDirt_06 +- Vision Torch - Step In +- OW_NM_DoorStart_Big_04 +- OW_PR_FootstepsBushRustle_06 +- Hotel Oneshot - Creak 5 +- OW_PR_FootstepsJumpLeaves_01 +- OW_SP_ShipGroan4_v2 +- Jump_Into_Fogsphere_03 +- Dreamworld Forest Ambience 2 +- OW_PR_FootstepsJumpRock_02 +- CrushedByElevator +- Player Gravel Footstep 1 +- House Destruction 4 +- Ghost Begin Stalk Grunt 1 +- flashlightOff +- OW ReelBeat 04b_2 040921 AP +- OW_PR_FootstepsRock_02 +- AshTwinCore_Open_01 +- OW_SP_HeadlightsOn_v2 +- Tower Fall Part 2 +- OW_NM_DoorStart_Big_02 +- Meteor_Impact_01_b +- OW_SP_LandingCamStatic +- OW_PR_FootstepsWood_01 +- gasp_light5 +- OW_PR_FootstepsGrass_03 +- Spark_03 +- Loading Tunnel - Loop +- FootstepsJumpWoodCreak_02 +- OW_Main_Menu +- Lantern Put Down +- OW Secret Library Whispers LP 040821 AP REF MIX +- OW_DB_Ambience +- Vision Torch Light Rays - On +- Hotel Oneshot - Creak 1 +- Dreamworld Nature Ambience 4 +- OW_PR_ThrustRotationalUnderwater_04 +- OW_PR_FootstepsJumpGrass_06 +- Nomai_Stone_Door_End_Big_V2_09 +- Ship_Impact_No_Damage_V3_01 +- Raft Movement Stop 3 +- Destruction Impact 9 +- Recorder_Start_Button +- AnglerFish_Target_v2_14 +- Ghost Begin Chase Grunt 2 +- Hotel Oneshot - Creak 6 +- Fragment_Break +- Tronworld Exit 2 +- Ghost Walk Footstep Forest 2 +- Incinerate_v3_01 +- Light Sensor Fade Out 3 +- OW_TravelerTheme_flute +- OW_PR_FootstepsLeaves_02 +- OW_PR_FootstepsLeaves_01 +- Fix_Puncture_05 +- Ghost Walk Footstep Wood_v2 3 +- OW_PR_FootstepsJumpSand_01 +- Sarcophagus Strain 2 +- OW_PR_HitWallUnderwater4 +- OW_PR_FootstepsRock_03 +- Loading Tunnel - Unload +- OW_PR_FootstepsJumpNomai_03 +- OW_PR_FootstepsDirt_07 +- OW_PR_FootstepsSnow_04 +- Wood Door Close Stop +- OW_PR_FootstepsJumpIce_02 +- OW ReelBeat 04a 031521 AP +- Raft Light Impact V2 2 +- Meteor_Impact_02_b +- JellyFish_Shock_02 +- Metal Door Open Start +- OW_GD_UnderwaterCurrent +- OW_GD_HeatLightning_06 +- nomai_textbranchout_noenergy2 +- asphyxiation_nosuit_secondhalf_version3 +- Artifact Focus +- Ice_Cave_Amb_loop_v3_01 +- Forest Oneshot - Tree Creak 3 +- OW NomaiRuinsRegular 081918 AP motif4c +- Dreamworld Lights Out Ambience 4 +- Projector Next Slide 2 +- BeaconIdea4 +- Destruction Impact 1 +- OW_PR_FootstepsNomai_06 +- OW_NM_FlickeryGravityCrystalAmbience +- OW Demonic Vocal Sting 082321 AP +- OW Quantum Lightning 091118 AP 08 +- OW_NM_SadTheme_older +- Nature Oneshot - Distant Creature 2 - less reverb +- FootstepsJumpWoodCreak_03 +- OW_PR_FootstepsJumpSand_02 +- Player Gravel Footstep 8 +- OW_PR_FootstepsSnow_06 +- gasp_traumatic3_lessmale +- Real World Dam Break Water Oneshot +- Repair_Loop +- OW_TH_FlagFlapping_loop.\_01 +- Ship_Impact_Light_Damage_V3_02 +- Raft Socket +- Jump_Into_TinyGalaxy_v2_01 +- OW NomaiRuinsRegular 081918 AP motif7c +- gasp_normal13_lessmale +- Sandfall_Inside_Loop_01 +- Solanum_RocksForm +- Fix_Puncture_06 +- OW Dreamworld Ruins Story Beats 071621 AP 1h +- Sarcophagus Open 2 +- fogsphere_pulse2 +- Elevator Rattle Loop 3 +- IllusoryWall_Alex +- OW_GD_HeatLightning_04 +- Nomai_Stone_Door_End_Big_V2_14 +- OW_PR_FootstepsJumpMetal_01 +- Vine_Crash_V3_03_LowPassDelay +- OW_PR_BanjoStrum_2b +- OW_PR_FootstepsGlass_05 +- OW_PR_FootstepsLeaves_07 +- OW_NM_DoorStart_01 +- OW_PR_FootstepsGrass_05 +- OW_PR_FootstepsJumpIce_01 +- OW_PR_ThrustRotationalUnderwater_01 +- Anglerfish_Chase_Breathing +- OW_PR_FootstepsRock_04 +- Metal Door Close Start +- Gear Rotate 1 +- Airlock Loop +- Solanum_Foley_RockFormEnd +- mallowpuff3 +- OW_PR_FootstepsSand_05 +- OW_PR_FootstepsJumpRock_01 +- Artifact Unfocus +- OW Eye Of The Universe 082818_2 AP +- OW_PR_FootstepsJumpRock_03 +- galaxy_zoomout2 +- OW_GD_IslandFalling_v2_loop +- Projector Previous Slide 2 +- Ignite_CampFire_04 +- SpaceshipAlarm2_3Iterations +- Ghost Individual Death 3 +- OW_PR_FootstepsJumpNomai_01 +- OW_PR_FootstepsJumpSnow_01 +- Forest Oneshot - Animal 2 +- OW_PR_FootstepsJumpDirt_01 +- Dreamworld Tower Fall Part 1 +- Ship_Impact_Medium_Damage_V3_05 +- OW_NM_ComputerRing3 +- OW_PR_SuitOn +- Airlock Pressurize +- OW_PR_FootstepsBushRustle_01 +- OW_PR_FootstepsNomai_02 +- Ship_Impact_Medium_Damage_V3_04 +- OW_NM_VesselDiscovery +- PlayerSubmerge +- Player Gravel Footstep 5 +- OW_EndTimes +- HGT_SandColumn_Ship +- OW ReelBeat 02c 021021 AP +- Spark_09 +- OW_PR_FootstepsSand_02 +- Damage_Light_05 +- OW_NM_ComputerRingFall2 +- gasp_traumatic7_lessmale +- Ghost Investigation Grunt +- Dream World Alarm Bell Oneshot 2 +- rockingchair2 +- OW_PR_FootstepsJumpDirt_04 +- Projector Next Slide +- OW_PR_FootstepsSand_08 +- OW_NM_WHAmbience2_v2 +- drowning_secondhalf2 +- Ghost Walk Footstep Forest 1 +- Prisoner Cloth Foley 3 +- OW_PR_FootstepsGlass_03 +- Vision Torch Scanning - Loop +- Raft Medium Impact V2 2 +- Hotel Oneshot - Heavy Thud 3 +- Destruction Impact - Large 4 +- OW_PR_FootstepsJumpLeaves_03 +- OW_PR_FootstepsJumpSand_03 +- OW Dreamworld Ruins SILENCE 02 +- OW_PR_FootstepsWood_06 +- ModelRocket_LightImpact +- shiplog_scanningloop +- OW Ghost Ambiences v2 011221 AP low LP +- OW NomaiRuinsRegular 081918 AP motif2c +- glass_crack_03 +- Fix_Puncture_08 +- OW_PR_FootstepsSnow_01 +- OW_PR_FootstepsDirt_01 +- asphyxiation_nosuit_firsthalf1 +- OW_NM_DoorSlide_Big_LP_01 +- OW_SUN_SupernovaWall +- OW_NM_DoorAirLockAirPourOut_03 +- OW_PR_ThrustUnderwater +- Solar Sail Stop +- OW_TH_Waterwheel_loop +- Forest Oneshot - Tree Creak 1 +- OW ReelBackdrop 03a 050321 LOOP +- Platform_Break_V2_04 +- FireBall_01 +- OW ReelBackdrop 01a 022521 AP +- Dreamworld Lights Out Ambience 1 +- Nomai_Stone_Door_End_Big_V2_11 +- Spark_07 +- OW_SP_ThrustTranslationalUnderwater +- Dream World Water Ambience - Creek 1 +- Ghost Begin Stalk Grunt 2 +- Comet_Purr +- JellyFish_Shock_04 +- OW_PR_FootstepsWood_08 +- Raft Reeling Loop +- shiplog_switchmode_forward +- Station Light Flicker - Dreamworld +- Lantern Wake Up Light 3 +- flashlightOn +- Dreamworld Base Ambience 1 +- Ship_Impact_Heavy_Damage_V3_09 +- Crushed_To_Death_V2_01 +- ow_kazoo_theme +- JetPack_NotificationBeep +- Ghost Run Footstep Wood_v2 1 +- OW_PR_FootstepsJumpNomai_05 +- OW_NM_DataStream_v2 +- UI_Tab_v2_02 +- OW_PR_ProbeTakePicture +- OW_NM_GravityCannonAmbience +- OW NM Flashback 082818 AP loop +- OW_SP_LandingPadSoft4 +- OW_PR_FootstepsGlass_07 +- WarpCore_Insert_V3_01 +- OW_NM_TractorBeamLP +- ShipRepair_Finish +- Ghost Begin Chase Grunt 1 +- Destruction Impact 2 +- OW_PR_FootstepsLeaves_08 +- Recorder_Stop_Button +- OW_PR_FootstepsLeaves_04 +- OW_PR_Jump1 +- Jump_Into_TinyGalaxy_v2_04 +- OW_GD_Tornado_v2_04 +- OW_PR_FootstepsJumpDirt_02 +- OW_SP_ConsoleReadoutStart +- OW_NM_EscapePodHatch +- OW_PR_FootstepsJumpNomai_02 +- Light Sensor Fade In 1 +- Light Sensor Fade In 2 +- Hotel Oneshot - Creak 9 +- CampfireTune_All_Reverb +- Recording_Loop_03 +- PartyHouseWhistle_Confident +- mallowpuff4 +- OW_NM_WhiteHoleAmbienceL +- Station Shudder +- drowning_firsthalf1 +- UI_Exit_Dialog_V6-002_highpass +- Dream World Dam Break +- Dreamfire Extinguish 1 +- MediumSplash +- Vision Torch - First Slide Appears +- Footstep1 +- OW_TravelerTheme_drums +- Artifact Conceal +- HGT_Ambience_Cave_Small +- HGT_Ambience_Cave_Big +- OW_TH_Waterfall_loop_01 +- OW Discovery 083021_2 AP darker shorter +- Secret Passageway Open Loop 2 +- Hotel Oneshot - Creak 4 +- supernova_explosion_deepnuclear2 +- Ghost Shout 7 +- OW NM Flashback 081718 AP slam +- Airlock Close +- OW_TH_FlagFlapping_loop.\_02 +- OW_SP_RefuelJetpack_v2_short +- Ship_Impact_No_Damage_V3_03 +- Raft Medium Impact V2 4 +- Anglerfish_Awake3 +- Wood Door Open Start +- OW_TH_FlagFlapping_loop +- Slide Reel Pickup 2 +- Tronworld Ambience 2 Alex Hack +- Ghost Run Footstep Wood_v2 5 +- Ignite_CampFire_03 +- Single Ghost Scream 14 +- Raft Start Reeling +- OW_PR_FootstepsGrass_08 +- Tronworld Enter 2 +- OW_PR_ProbeUnderwaterLaunch +- OW_GD_IslandCrashingInWater_v2 +- Grapple Totem Zoom In +- OW ReelBeat 02a 021021 AP +- Nomai_Stone_Door_End_V2_08 +- OW_PR_FootstepsJumpGlass_02 +- OW_NM_DoorStart_02 +- Destruction Impact - Large 3 +- OW_SP_SignalscopeSlideV2 +- OW NomaiRuinsRegular 081918 AP motif6c +- Wood Door Close Start 3 Alex +- Destruction Impact - Large 2 +- Nomai_Stone_Door_End_Big_V2_01 +- OW_NM_ComputerAmbienceLP +- Anglerfish_Awake2 +- OW_TH_Insects_loop_03 +- OW_PR_SignalscopeActivate +- OW_NM_ComputerRingFall1 +- OW_PR_FootstepsDirt_02 +- OW_NM_ShuttleLight +- Single Ghost Scream 1 +- Ghost Identify Fail +- OW_SP_ShipGroan5_v2 +- Cloaking Field Exit +- OW_PR_FootstepsNomai_01 +- OW Dreamworld Ruins 072021 AP 02e +- Forest Oneshot - Tree Creak 2 +- fogsphere_pulse1 +- OW_PR_PullOutStick +- OW_PR_LockOn +- Nomai_Stone_Door_End_Big_V2_07 +- OW_PR_FootstepsJumpSand_06 +- InstantDeath2_Long_Ringing +- Light Sensor Door Stop 2 +- Light Sensor Door Loop 2 +- OW_TH_GeyserEnd_02 +- pickaxe_01 +- OW_PR_FootstepsWaterWade_03 +- RockPile_Fall_03 +- OW_PR_ThrustAfterburn_v2_01 +- Light Sensor Fade Out 2 +- Light Sensor Fade In 3 +- OW Finally Set Free 072021_2 AP +- Ghost Blow Out Lantern Charge +- Wood Door Loop +- BigBang_Explo +- TH_Geyser_Loop_v3 +- Water Spray Impact 8 +- OW_TH_FlagFlapping_loop.\_03 +- OW_PR_FootstepsJumpLeaves_02 +- OW_PR_FootstepsIceSlide_Lp_01 +- Dreamworld Lights Out Ambience 3 +- knife_scrape_01 +- OW OBSERVATORY 011317 AP +- Footstep_Run5 +- Light Sensor Door Open 2 +- Jetpack_O2_loop_01 +- OW_PR_FootstepsJumpWood_04 +- OW_SP_BuckleUp +- Probe_SnapShot_02 +- Pickup_Ceramic_01 +- OW_PR_FootstepsWood_03 +- Destruction Debris 3 +- OW_PR_SignalscopeDeactivate +- OW_PR_OxygenLeakingFromSuit_loop +- Probe_SnapShot_01 +- Dreamworld Ghost Hotel Ambience 2 +- Slide Reel Put Down 2 +- Nomai_Stone_Door_End_V2_07 +- OW_SP_ProbeLauncherRotation_v2 +- OW_PR_MarshmallowEatUnburnt_noMmm_v3 +- SystemBackOnline +- Ghost Idle Search 1 +- Ship_Impact_Light_Damage_V3_03 +- OW Dreamworld Ruins Story Beats 071621 AP 1i +- OW_PR_FootstepsGrass_02 +- OW_NM_DoorSlide_LP_02 +- Ringworld Cave Ambience +- Fix_Puncture_02 +- OW_PR_FootstepsWaterWade_08 +- OW_TH_GeyserStart_01 +- OW_TH_Insects_loop_01 +- OW ReelBeat 04d_2 040921 AP +- OW_TH_ProjectorStop +- Prisoner Cloth Foley 6 +- Dream World Alarm Bell Oneshot 3 +- shiplog_selectplanet2 +- Nature Oneshot - Distant Creature 1 - less reverb +- OW_PR_FootstepsDirt_08 +- Spark_04 +- OW_GD_HeatLightning_02 +- OW Secret Loop 090121 AP muted +- Creature Voice Test 1 Short +- General Destruction 4 +- House Destruction 1 +- OW_PR_FootstepsSnow_02 +- Huge Splash 2 +- Player Gravel Footstep 4 +- Ghost Grab Player 1 +- shiplog_switchmode_back +- OW_PR_FootstepsJumpWood_03 +- OW_PR_FootstepsJumpMetal_05 +- OW_SP_AutopilotEngaged 1 +- OW_NM_DoorAirlockOpen_03 +- OW Dreamworld Ruins 072021 AP 02b +- Ghost Investiagation Grunt 2 +- Wood Door Close Start 2 +- Real World Alarm Bell Oneshot 2 +- OW Fabric SFX 102119 AP screen shatter SHORT +- OW_NM_Ruins_ambience_scary_loop +- Raft Heavy Impact 1 +- OW Whispers 041321_2 AP LP +- Ghost Run Footstep Forest 4 +- Vine_Crash_V3_01_LowPassDelay +- Solanum_Foley_HandLower +- OW_PR_FootstepsSand_07 +- Grapple Totem Zoom Out Louder +- Ship_Impact_Heavy_Damage_V3_07 +- OW_PR_FootstepsIce_01 +- OW Ghost Ambiences v2 011221 AP pad LP +- Raft Movement Stop +- OW_TH_Museum +- gasp_normal12_lessmale +- OW_PR_FootstepsJumpRock_05 +- OW_SP_ThrustRotationalUnderwater_02 +- Pool_Exit_v3 +- Flashlight_Malfunction_02 +- OW NomaiRuinsRegular 081918 AP motif1c +- Nomai_Stone_Door_End_V2_05 +- OW_PR_FootstepsLeaves_05 +- Nomai_Stone_Door_End_V2_09 +- asphyxiation_suit_secondhalf1 +- Spark_08 +- OW Aquatic Exploration 050318 AP LOOP +- OW_TH_GeyserEnd_01 +- UI_Navigate_03 +- OW ReelBeat 02b 021021 AP +- Player_Impact_Damage_Light_02 +- OW_PR_FootstepsWood_02 +- Jump_Into_TinyGalaxy_v2_02 +- OW_PR_ThrustRotationalUnderwater_05 +- Atmosphere_High_Suit +- OW_SP_MetalCreak_04 +- OW Prisoner Reveal 063021 AP +- Ghost Grunt 1 +- OW_PR_FootstepsJumpDirt_03 +- OW Reelbeat 03c 061721 AP +- Nomai_Stone_Door_End_Big_V2_10 +- Damage_Light_01 +- PutDown_Rock_01 +- OW_TH_ModelRocketCrashing +- OW ReelBeat 03b 032521 AP +- OW END OF GAME 021818 AP +- Raft Movement Start 3 +- Impact_Light_03 +- OW_NM_Tech_Advanced +- Ghost Walk Footstep Wood_v2 4 +- OW_PR_FootstepsJumpWood_05 +- OW_PR_FootstepsMetal_02 +- FootstepsJumpWoodCreak_01 +- OW Timber Hearth 032719 AP v2 +- Hotel Oneshot - Heavy Creak 3 +- shiplog_highlight +- OW_GD_Tornado_v2_03 +- RotationalThruster02 +- OW Quantum Lightning 091118 AP 06 +- JetPack_NotificationBeep_Fast +- SmallSplash +- Ghost Run Footstep Forest 3 +- Nomai_Stone_Door_End_Big_V2_04 +- OW Prisoner Elevator 061121_5 AP loop +- Tower Fall Part 1 +- OW_PR_FootstepsMetal_03 +- OW_PR_FootstepsRock_01 +- Ignite_Marshmallow_01 +- Ignite_CampFire_02 +- Ghost Walk Footstep Wood_v2 5 +- OW_GD_AmbienceCave +- Ship_Impact_Heavy_Damage_V3_11 +- Raft Release +- elevatorstop +- Hotel Oneshot - Creak 2 +- Station Light Flicker +- Forest Oneshot - Tree Creak 7 +- OW_GD_HeatLightning_03 +- Nomai_Stone_Door_End_Big_V2_08 +- OW_TH_AmbienceHighAltitude +- Water Spray Impact 3 +- Ghost Grunt 2_SmoothFade +- linkingstone_out +- Ghost Shout 6 +- Ghost Individual Death 4 +- shiplog_highlight2 +- OW_PR_FootstepsMetal_04 +- OW Morning Cello 101718_2 +- Spark_11 +- fogsphere_jump2_delayed +- Nomai_Stone_Door_End_V2_04 +- OW_SP_ElectricalDamageLP +- OW_PR_FootstepsGlass_02 +- Ghost Run Footstep Forest 6 +- OW_NM_CenterClampsRemoving +- OW_SP_ThrustRotationalUnderwater_03 +- OW_NM_InsertScroll +- GhostMatter_Splash_v4_02 +- Airlock Open +- OW_PR_TranslatorTranslateNew +- Ship_Impact_Heavy_Damage_V3_10 +- House Destruction 2 +- shiplog_newentry3_softer +- OW_TravelerTheme_piano +- Stilts Destruction 3 +- OW_PR_LockOff +- OW_TH_ModelRocketThrustTranslational_01 +- Hotel Oneshot - Creak 8 +- Dreamworld Candle Lighting Test Variation 6 +- Stilts Destruction 4 +- OW_SUN_BurnPlanet +- Real World Dam Crack Water Loop +- OW Final End Times 022519_2 AP LOOP2 +- OxygenRefill_Short +- OW ReelBeat 03a 031521 AP +- OW NomaiRuinsRegular 081918 AP motif5c v2 +- StoryReel5Short 063021_3 AP +- OW_GD_AmbienceOcean +- OW_TH_BridgeCreaking_loop +- OW Traveler Theme 021821 AP FINAL TIME NO PIANO ADD PRISONER +- OW_SP_ShipAmbiance_01 +- Prisoner Grunt 2 +- OW_NM_ComputerRingFall3 +- OW_NM_DoorStart_04 +- Ghost Neck Snap +- Nomai_Warp_01 +- General Destruction 2 +- pickaxe_02 +- OW_PR_FootstepsSnow_07 +- Lava_Splash_02 +- OW_PR_FootstepsGlass_06 +- Anglerfish_Awake +- OW_PR_ProbeRetrieval +- OW_PR_FootstepsJumpSnow_04 +- Lantern Wake Up Light 2 +- Secret Passageway Open Stop 2 +- Lantern Shorting Out 2 +- OW_PR_BanjoStrum_1b +- OW_PR_FootstepsSnow_08 +- Air Rushing Out Into Space +- OW_PR_FootstepsJumpGlass_03 +- OW_PR_ProbeInAirSound +- shiplog_movebetweenplanets +- OW_PR_BanjoStrum_4b +- Raft Stop Reeling +- OW Traveler Theme 091118 AP FINAL TIME WITH PIANO EDIT +- OW_PR_FootstepsMetal_08 +- OW ReelBeat 04a_2 040921 AP +- OW_SP_ShipExploding +- OW_PR_FootstepsJumpMetal_02 +- OW_TH_ModelRocketThrustRotational_04 +- JellyFish_Shock_03 +- OW No-Eye Ruins 121120_2 AP LP +- OW_GD_HeatLightning_05 +- OW_NM_GravityCrystalAmbience_Louder +- OW_GD_HeatLightning_07 +- Solar Sail Loop 2 +- OW_PR_FootstepsBushRustle_02 +- CityLights_Off_01 +- RotationalThruster01 +- OW_NM_BHEnterItem_v2 +- OW_PR_FootstepsBushRustle_09 +- Vision Torch - Vision Dissolves +- OW_PR_FootstepsJumpMetal_04 +- Lantern Remove 2 +- Player_Impact_Damage_Light_01 +- StoryReel5Full 062821_4 AP +- OW_PR_FootstepsWaterWade_07 +- OW_NM_SkypeLP +- OW_SP_ShipGroan2_v2 +- Volcano_Ambience_Loop_V2 +- AnglerFish_Chomp_Loop_v2 +- Destruction Debris 2 +- OW_PR_LandInWater2 +- OW_PR_FootstepsIce_05 +- Footstep_Run1 +- Anglerfish_ChompBite_01 +- Destruction Debris 1 +- Ghost Run Footstep Forest 1 +- AnglerFish_Target_v2_02 +- Signalscope_Zoom_Loop +- BH_Ambience_Below_Crust +- fogsphere_pulse3 +- OW_GD_HeatLightning_08 +- AshTwinCore_Close_01 +- Airlock Depressurize +- OW_TH_ModelRocketThrustRotational_03 +- OW_NM_ComputerRing2 +- RockPile_Fall_05 +- Vision Torch Fire - Loop +- OW Farewell 061721 AP +- Hologram_Enter_v2_01 +- OW_PR_ThrustRotationalUnderwater_03 +- Forest Oneshot - Tree Creak 8 +- OW_PR_LandInWater3 +- Hotel Oneshot - Creak 3 +- Hotel Oneshot - Heavy Thud 1 +- Distant Ghost Cacophony 1 +- Footstep_Run2 +- OW_TH_ProjectorRun_loop +- OW_PR_FootstepsJumpGrass_04 +- Destruction Impact - Large 5 +- FootstepsWoodCreak_03 +- OW_PR_FootstepsIce_03 +- Solanum_Foley_HandRaise +- OW_NM_DoorStart_Big_03 +- Candle Extinguishing Test Variation 6 +- Ignite_CampFire_01 +- Nature Oneshot - Distant Creature 3 - less reverb +- Real World Water Ambience - Slow River +- Ghost Grunt 5 +- IceMelt_v2_LowPass +- OW_NM_GravityCannonDeactivated +- OW Ghost Sequence 011121 AP LOW SUSPENSE LP +- Anglerfish_Sleeping +- Real World Water Ambience - Slow River 2 +- OW_PR_FootstepsWaterWade_02 +- Ghost Walk Footstep Forest 3 +- Spark_05 +- OW No-Eye Ruins 082121 AP stinger +- Nature Oneshot - Distant Deep Creature 3 +- OW_NM_SunStation +- Projector Totem Extinguish 1 +- Dreamfire Crackling Loop 3 +- OW Ghost Ambiences 012921_2 AP slam +- Dreamfire Explosion +- Dream World Water Ambience - Creek 8 +- OW_PR_FootstepsSnow_05 +- OW_PR_FootstepsIce_04 +- Dream World Water Ambience - Creek 5 +- Ghost Run Footstep Forest 2 +- OW_NM_DoorStart_08 +- Outer Wilds Party House v8 050321_2 AP traveler drone +- Hotel Oneshot - Heavy Creak 1 +- Secret Passageway Open Start 2 +- FootstepsWoodCreak_01 +- Ghost Grunt 8 +- OW_PR_FootstepsMetal_06 +- Probe_Attach_v3_02 +- OW_PR_LandInWater1 +- FootstepsWoodCreak_06 +- OW_PR_FootstepsBushRustle_04 +- Projector Totem Blow +- OW_PR_FootstepsWaterWade_01 +- Raft Movement Loop +- OW_SP_ThrustRotationalUnderwater_01 +- OW_PR_FallingIntoLavaBeep_loop +- Ship_Impact_Light_Damage_V3_01 +- Ship_Impact_Medium_Damage_V3_02 +- Ghost Walk Footstep Wood_v2 1 +- Nomai_Stone_Door_End_Big_V2_02 +- OW ReelBeat 04c_2 040921 AP +- Raft Push +- OW_PR_FootstepsJumpIce_03 +- Nomai_Stone_Door_End_Big_V2_05 +- Destruction Impact 5 +- HGT_SandFallSmall_Inside +- OW Quantum Lightning 091118 AP 01 +- OW_PR_OxygenLeakingFromSuit_in +- OW_PR_HelmetOn +- OW_SP_AutopilotDisengaged 1 +- PutDown_Ceramic_01 +- OW_PR_FootstepsMetal_07 +- Water Spray Impact 7 +- Dream World Alarm Bell Oneshot 4 +- CityLights_On_01 +- rockingchair3 +- fogsphere_pulse5 +- Nomai_ShipPowerOn_V2_01 +- OW_GD_IslandSuckedInTornado +- OW_NM_DoorStart_05 +- Probe_Attach_v3_01 +- OW_PR_ThrustRotationalUnderwater_02 +- Outer Wilds Party House v8 050321_3 AP traveler +- Artifact Put Down +- OW ReelBeat 01b 021021 AP +- stoppedasphyxiating_suit2 +- FootstepsJumpWoodCreak_04 +- Destruction Debris 6 +- Nomai_Warp_03_Shorter +- mallowpuff1 +- OW ReelBeat 02d 021021 AP +- OW_TravelerTheme_harmonica +- shiplog_misc4 +- Projector Totem Extinguish 3 +- OW ReelBackdrop 02b 021021 AP +- OW_PR_FootstepsDirt_03 +- Lava_Splash_01 +- Engine_Start_V2 +- Solar Sail Start +- OW_PR_ThrustTranslational_v2_01 +- Light Sensor Fade Out 1 +- Prisoner Cloth Foley 4 +- OW Quantum Lightning 091118 AP 02 +- OW_NM_RemoveScroll +- asphyxiation_suit_firsthalf2 +- OW_TH_AmbienceNightInCanyons +- Footstep2 +- OW ReelBackdrop 03b LOOP 031521 AP +- OW_SP_ThrustRotationalUnderwater_04 +- OW_PR_FootstepsNomai_08 +- OW_PR_FootstepsNomai_03 +- OW ReelBeat 03d 061121 AP +- OW_PR_FootstepsJumpSand_04 +- RotationalThruster03 +- Stilts Destruction 5 +- Raft Run Aground +- OW Space Station 081420_2 AP +- OW_PR_FootstepsWaterWade_06 +- Spark_02 +- Cloaking Field Entry +- OW_NM_DataWormhole +- OW_QuantumSignal +- General Destruction 3 +- OW New Raft Music 082321_4 AP quiet +- OW_SP_Unbuckle +- Real World Water Ambience - Calm 2 +- Prisoner Grunt 3 +- OW_NM_OrbMoveGlass_lp_01 +- Prisoner Cloth Foley 7 +- OW_PR_FootstepsRock_05 +- OW Traveler Theme 021821 AP FINAL TIME WITH PIANO ADD PRISONER +- OW Quantum Lightning 091118 AP 03 +- OW_TH_ModelRocketThrustRotational_02 +- OW_PR_HitWallUnderwater3 +- Flashlight_Malfunction_03 +- OW NM Flashback 082818 loop overlay AP +- supernova_corecollapse_10sec_fadeOut +- OW_PR_FootstepsWood_05 +- rockingchair1 +- Power_Failure_v2_02 +- OW_PR_FootstepsMetal_05 +- Stilts Destruction 2 +- Dreamworld Tower Tilt +- OW New Texture 082921 AP loop +- Lantern Insert 3 +- OW_PR_FootstepsNomai_07 +- OW_PR_FootstepsJumpWood_06 +- Pool_Enter_v3_Fade +- Raft Movement Start +- OW_PR_FootstepsNomai_05 +- OW_PR_HardSplash +- Destruction Debris 5 +- Dreamworld Candle Lighting Test Variation 2 +- OW NM Flashback 082818_2 AP stinger delayed +- Ghost Shout 4 +- Nomai_Stone_Door_End_Big_V2_06 +- Tower Tilt +- OW_NM_DoorStart_Big_06 +- OW_PR_FootstepsLeaves_03 +- Ghost Run Footstep Wood_v2 3 +- OW_NM_BHExitItem_v2 +- Ghost Idle Search 5 +- Forest Oneshot - Animal 1 +- OW Dreamworld Ruins 072021 AP 02d +- OW_PR_FootstepsJumpSnow_02 +- Platform_Break_V2_01 +- OW Party House 092820 AP short loop +- OW_PR_FootstepsRock_06 +- OW_SP_ThrustTranslational +- Footstep6 +- OW_PR_FootstepsWaterWade_05 +- Ghost Start Hunt Grunt 2 +- OW Dreamworld Ruins 072021 AP 02f +- Nomai_Stone_Door_End_Big_V2_12 +- Jump_Into_TinyGalaxy_v2_03 +- Footstep_Run3 +- Negative1 +- OW_TravelerTheme_banjo +- SandColumnEnd_v2 +- OW_PR_FootstepsJumpSand_05 +- Dreamworld Lights Out Ambience 2 +- OW Blair Witch Project 082921_3 AP darker mix loop +- Fig_Backer_Sat_Audio_V3 +- OW_PR_FootstepsGlass_01 +- Light Sensor Fade Out 4 +- Projector Totem Light 1 +- Player Gravel Footstep 6 +- OW_TH_GeyserStart_03 +- Slide Reel Remove 3 +- Ship_Impact_Medium_Damage_V3_06 +- OW Quantum Lightning 091118 AP 05 +- OW_GD_Tornado_v2_01 +- OW_SP_ActivateComputerLP +- Footstep_Run6 +- Spark_13 +- Vine_Crash_V3_02_LowPassDelay +- OW Slideshow BURNT LOOP small 031521_2 AP +- Raft Heavy Impact 4 +- Light Sensor Door Loop - Door Sensor Sliding +- Sarcophagus Strain 3 +- Loading Tunnel - Load +- elevatorstart +- Nomai_Stone_Door_End_V2_02 +- OW_GD_Tornado_v2_02 +- OW_NM_DoorAirLockAirPourOut_01 +- OW_TravelerTheme_newtraveler 061021 AP +- OW ReelBackdrop 04a_3 041321 AP LP +- OW_PR_FootstepsJumpRock_04 +- Damage_Heavy_04 +- Raft Movement Stop 2 +- OW_SP_AnalogClick2_v2 +- Nomai_Stone_Door_End_V2_01 +- OW_PR_FootstepsRock_07 +- Raft Light Impact V2 4 +- Marshmallow_Replace_01 +- Footstep5 +- Destruction Impact 3 +- Helmet_Glass_Crack_08 +- Ghost Run Footstep Wood_v2 6 +- OW End Credits 022019_3 AP +- Raft Heavy Impacts V2 2 +- Nomai_Stone_Door_End_Big_V2_13 +- OW_NM_GravityCannonActivated +- Outer Wilds Party House v8 050321_2 AP Bass +- Ringworld Muffled Indoor Ambience +- Ghost Run Footstep Wood_v2 2 +- OW_GD_AmbienceUndewater +- Forge_Loop_V3_03 +- OW_PR_CampfireAmbience +- OW_PR_FootstepsRock_08 +- Platform_Break_V2_02 +- OW_PR_FootstepsJumpWood_02 +- OW Eye Temple 121820_2 AP LP +- OW_GD_WavesAgainstBeach +- OW_PR_ThrowProbe +- OW_NM_DoorStart_07 +- OW_PR_FootstepsSand_04 +- Lantern Pickup +- Real World Water Ambience - Rapids 2 +- OW_PR_FootstepsWood_04 +- OW_NM_TractorBeamPowerUp +- OW New Stinger 082921 AP +- OW_PR_FootstepsGrass_07 +- Hotel Oneshot - Creak 7 +- Player_On_Fire_Loop +- OW_TH_UnderwaterRushing +- GhostMatter_Splash_v4_01 +- OW_SP_ThrustRotationalUnderwater_06 +- OW Fabric SFX 102119 AP rip FADE OUT +- OW Dreamworld Ruins 072021 AP 02a +- Spark_06 +- OW_SP_SignalscopeChunkV2 +- Player Gravel Footstep 2 +- OW_PR_FootstepsBushRustle_07 +- Hologram_Exit_v2_01 +- Metal Door Open Stop +- OW_PR_FootstepsJumpWood_01 +- OW_PR_FootstepsWaterWade_04 +- Nature Oneshot - Distant Deep Creature 2 +- Stilts Destruction 1 +- OW NM Flashback 082818 AP overlay1 +- drowning_secondhalf1 +- Gear Rotate Locked_Short +- Prisoner Cloth Foley 5 +- shiplog_deselectplanet +- Orb_End_v3_01 +- Atmosphere_High_Ship +- Solar Sail Start 2 +- knife_scrape_02 +- OW_TH_GeyserEnd_03 +- Meteor_Impact_03_b +- Destruction Impact 6 +- JellyFish_Shock_01 +- Light Sensor Loop +- Candle Extinguishing Test Variation 4 +- Vision Torch Light Rays - Off +- OW_NM_DoorSlide_Big_LP_05 +- OW_PR_FootstepsDirt_05 +- Sand_Column_Start_v2_03 +- Water Spray Impact 6 +- OW_NM_DoorStart_Big_07 +- OW_SP_ShipGroan3_v2 +- Heat_Damage_Loop_01 +- OW_SP_LandingCamDeactivated 1 +- breathing_lowO2_6 +- OW_PR_OxygenLeakingFromSuit_out +- LogUpdated_001 +- Vine_Crash_V3_04_LowPassDelay +- Destruction Debris 7 +- Hotel Oneshot - Heavy Creak 2 +- OW_PR_HelmetOff +- OW_PR_FootstepsGrass_04 +- Forest Oneshot - Animal 3 +- OW Discovery 083021_2 AP shorter +- Slide Reel Insert 3 +- OW_NM_BHEnterExitPlayer_v2 +- Ship_Impact_Light_Damage_V3_05 +- OW_SP_OpenHatch_NoBeep +- OW_SP_ThrustRotationalUnderwater_05 +- ShipSubmerge +- Artifact Fire Loop +- OW_DarkBramble_loop +- OW_PR_FootstepsBushRustle_08 +- OW NM Nomai City 081718 AP LOOP +- Solar Sail Stop 2 +- Destruction Impact - Large 1 +- OW Dream Rule LP 032421 AP normal +- Solar Sail Loop +- OW_TH_GeyserStart_02 +- Ship_Impact_No_Damage_V3_05 +- OW_PR_FootstepsIce_06 +- Outer Wilds Party House v8 050321_2 AP vocals +- asphyxiation_suit_secondhalf2 +- Affirmative1 +- Spark_12 +- OW_SP_ConsoleReadoutLP +- OW ReelBackdrop 03c 042621 AP LP +- gasp_light9 +- Nomai_Stone_Door_End_V2_03 +- OW_PR_FootstepsJumpNomai_06 +- OW_PR_FootstepsGlass_04 +- OW_PR_FootstepsDirt_04 +- Destruction Debris 9 +- OW_PR_FootstepsIce_08 +- OW_PR_SuitOff +- Artifact Pickup +- OW_SUN_Ambience_v4 +- JellyFish_Shock_05 +- OW_TH_Underwater +- Station Shudder - Dreamworld +- Ghost Walk Footstep Forest 5 +- OW_TH_ProjectorActivate +- asphyxiation_suit_firsthalf1 +- OW_PR_SignalscopeZoomIn +- HGT_Ambience_Surface +- FootstepsWoodCreak_08 +- GhostMatter_Splash_v4_03 +- UI_Tab_v2_05 +- Raft Medium Impact V2 3 +- OW_NM_TractorBeamPowerDown +- OW_Travel_Theme_Remaster +- Big_Galaxy_Burn_v2_01 +- OW_TH_Insects_loop_02 +- Raft Heavy Impact 3 +- Raft Movement Start 2 +- OW Quantum Lightning 091118 AP 04 +- OW_TH_FlagFlapping_loop.\_04 +- OW_TH_ModelRocketThrustTranslational_02 +- knife_scrape_03 +- Incinerate_v3_02 +- Light Sensor Rotate +- OW_NM_EscapePodDistressSignal +- MapZoomOut_Tone +- OW_NM_DoorStart_Big_05 +- Player Gravel Footstep 7 +- Ghost Walk Footstep Wood_v2 2 +- Ghost Run Footstep Forest 5 +- Ghost Walk Footstep Forest 4 +- OW_PR_HitWallUnderwater2 +- OW NM Flashback 082818_2 AP stinger +- OW_PR_FootstepsJumpGrass_05 +- Fix_Puncture_04 +- Ghost Idle Search 3 diff --git a/docs/src/content/docs/schemas/body-schema/index.mdx b/docs/src/content/docs/schemas/body-schema/index.mdx index f64c364c..852b892c 100644 --- a/docs/src/content/docs/schemas/body-schema/index.mdx +++ b/docs/src/content/docs/schemas/body-schema/index.mdx @@ -1,8 +1,8 @@ --- -title: Celestial Body Schema -description: Describes a celestial body to generate +title: "Celestial Body Schema" +description: "Describes a celestial body to generate" editUrl: false -schemaFile: body_schema.json +schemaFile: "body_schema.json" --- import Schema from "/src/components/Schemas/Schema.astro"; diff --git a/docs/src/content/docs/start-here/getting-started.md b/docs/src/content/docs/start-here/getting-started.md index 8f4b709d..1b94c8be 100644 --- a/docs/src/content/docs/start-here/getting-started.md +++ b/docs/src/content/docs/start-here/getting-started.md @@ -30,13 +30,12 @@ Once in VSCode, paste this code into the file: ```json title="wetrock.json" { "name": "Wetrock", - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/body_schema.json", + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json", "starSystem": "SolarSystem", "Base": { "groundSize": 100, "surfaceSize": 101, - "surfaceGravity": 12, - "hasMapMarker": true + "surfaceGravity": 12 }, "Orbit": { "semiMajorAxis": 1300, @@ -55,6 +54,9 @@ Once in VSCode, paste this code into the file: "fogSize": 150, "fogDensity": 0.2, "hasRain": true + }, + "MapMarker": { + "enabled": true } } ``` @@ -64,34 +66,35 @@ It has an object called Base, which has a groundSize of 100, and a surfaceSize o Alright so now that we understand how the file is structures, let's look into what each value actually does: -- `name` simply sets the name of the planet -- `$schema` we'll get to in a second -- `starSystem` specifies what star system this planet is located in, in this case we're using the base game star system, so we put "SolarSystem" -- Then it has an object called `Base` - - Base has a `groundSize` of 100, this generates a perfect sphere that is 100 units in radius as the ground of our planet - - It also has a `surfaceSize` of 101, surface size is used in many calculations, it's generally good to set it to a bit bigger than ground size. - - `surfaceGravity` describes the strength of gravity on this planet, in this case it's 12 which is the same as Timber Hearth - - `hasMapMarker` tells new horizons that we want this planet to have a marker on the map screen -- Next it has another object called `Orbit` - - `semiMajorAxis` specifies the radius of the orbit (how far away the body is from its parent) - - `primaryBody` is set to `TIMBER_HEARTH``, this makes our planet orbit timber hearth - - `isMoon` simply tells the game how close you have to be to the planet in map mode before its name appears - - `isTidallyLocked` makes sure that one side of our planet is always facing timber hearth (the primary body) -- Finally, we have `Atmosphere` - - Its `size` is 150, this simply sets how far away from the planet our atmosphere stretches - - Its `fogTint` is set to a color which is an object with r, g, b, and a properties (properties is another word for keys) - - `fogSize` determines how far away the fog stretches from the planet - - `fogDensity` is simply how dense the fog is - - `hasRain` makes rainfall on the planet +- `name` simply sets the name of the planet +- `$schema` we'll get to in a second +- `starSystem` specifies what star system this planet is located in, in this case we're using the base game star system, so we put "SolarSystem" +- Then it has an object called `Base` + - Base has a `groundSize` of 100, this generates a perfect sphere that is 100 units in radius as the ground of our planet + - It also has a `surfaceSize` of 101, surface size is used in many calculations, it's generally good to set it to a bit bigger than ground size. + - `surfaceGravity` describes the strength of gravity on this planet, in this case it's 12 which is the same as Timber Hearth +- Next it has another object called `Orbit` + - `semiMajorAxis` specifies the radius of the orbit (how far away the body is from its parent) + - `primaryBody` is set to `TIMBER_HEARTH``, this makes our planet orbit timber hearth + - `isMoon` simply tells the game how close you have to be to the planet in map mode before its name appears + - `isTidallyLocked` makes sure that one side of our planet is always facing timber hearth (the primary body) +- Next, we have `Atmosphere` + - Its `size` is 150, this simply sets how far away from the planet our atmosphere stretches + - Its `fogTint` is set to a color which is an object with r, g, b, and a properties (properties is another word for keys) + - `fogSize` determines how far away the fog stretches from the planet + - `fogDensity` is simply how dense the fog is + - `hasRain` makes rainfall on the planet +- Finally, we have `MapMarker` + - `enabled` tells New Horizons that we want this planet to have a marker on the map screen #### What's a Schema? That `$schema` property is a bit special, it instructs VSCode to use a pre-made schema to provide a better editing experience. With the schema you get: -- Automatic descriptions for properties when hovering over keys -- Automatic error detection for incorrect data types or values -- Autocomplete, also called IntelliSense +- Automatic descriptions for properties when hovering over keys +- Automatic error detection for incorrect data types or values +- Autocomplete, also called IntelliSense The schema we're using here is the [Celestial Body Schema](/schemas/body-schema), but there are many others available in the Schemas section of the left sidebar. @@ -101,18 +104,18 @@ With the new planet created (_and saved!_), launch the game through the mod mana If you run into issues please make sure: -- You placed the JSON file in a folder called `planets` in the New Horizons mod folder -- There are no red or yellow squiggly lines in your file +- You placed the JSON file in a folder called `planets` in the New Horizons mod folder +- There are no red or yellow squiggly lines in your file ## Creating An Addon ### Making a GitHub Repository -To get started, you'll need to fork the [the New Horizons addon template](https://github.com/xen-42/ow-new-horizons-config-template) +To get started, you'll need to click the green "Use This Template" button on [the New Horizons addon template](https://github.com/xen-42/ow-new-horizons-config-template) GitHub repository. -- Set the Name to your username followed by a dot (`.`), followed by your mod's name in PascalCase (no spaces, new words have capital letters). So for example if my username was "Test" and my mod's name was "Really Cool Addon", I would name the repo `Test.ReallyCoolAddon`. -- The description is what will appear in the mod manager under the mod's name, you can always edit this later -- You can set the visibility to what you want; But when you go to publish your mod, it will need to be public +- Set the Name to your username followed by a dot (`.`), followed by your mod's name in PascalCase (no spaces, new words have capital letters). So for example if my username was "Test" and my mod's name was "Really Cool Addon", I would name the repo `Test.ReallyCoolAddon`. +- The description is what will appear in the mod manager under the mod's name, you can always edit this later +- You can set the visibility to what you want; But when you go to publish your mod, it will need to be public ### Open The Project @@ -120,16 +123,16 @@ Now clone the repository to your local computer and open it in your favorite edi ### Project Layout -- .github: This folder contains special files for use on GitHub, they aren't useful right now but will be when we go to publish the mod -- planets: This folder contains a single example config file that destroys the Quantum Moon, we'll keep it for now so we can test our addon later. -- .gitattributes: This is another file that will be useful when publishing -- default-config.json: This file is used in C#-based mods to allow a custom options menu, New Horizons doesn't support a custom options menu, but we still need the file here in order for the addon to work. -- manifest.json: This is the first file we're going to edit, we need to fill it out with information about our mod - - First you're going to set `author` to your author name, this should be the same name that you used when creating the GitHub repo. - - Next, set `name` to the name you want to appear in the mod manager and website. - - Now set `uniqueName` to the name of your GitHub Repo. - - You can leave `version`, `owmlVersion`, and `dependencies` alone -- NewHorizonsConfig.dll: This is the heart of your addon, make sure to never move or rename it. +- .github: This folder contains special files for use on GitHub, they aren't useful right now but will be when we go to publish the mod +- planets: This folder contains a single example config file that destroys the Quantum Moon, we'll keep it for now so we can test our addon later. +- .gitattributes: This is another file that will be useful when publishing +- default-config.json: This file is used in C#-based mods to allow a custom options menu, New Horizons doesn't support a custom options menu, but we still need the file here in order for the addon to work. +- manifest.json: This is the first file we're going to edit, we need to fill it out with information about our mod + - First you're going to set `author` to your author name, this should be the same name that you used when creating the GitHub repo. + - Next, set `name` to the name you want to appear in the mod manager and website. + - Now set `uniqueName` to the name of your GitHub Repo. + - You can leave `version`, `owmlVersion`, and `dependencies` alone +- NewHorizonsConfig.dll: This is the heart of your addon, make sure to never move or rename it. ### Testing The Addon diff --git a/docs/src/content/docs/start-here/helpful-resources.md b/docs/src/content/docs/start-here/helpful-resources.md index a2642049..0ca13791 100644 --- a/docs/src/content/docs/start-here/helpful-resources.md +++ b/docs/src/content/docs/start-here/helpful-resources.md @@ -51,18 +51,18 @@ rectangular-to-polar coordinate transformation, useful for fixing abnormalities These mods are useful when developing your addon -- [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) - Used to find the paths of game objects for copying and can be used to manually position props, ship log entries, and more. -- [Collider Visualizer](https://outerwildsmods.com/mods/collidervisualizer) - Useful when creating dialogue triggers or reveal volumes. -- [Save Editor](https://outerwildsmods.com/mods/saveeditor) - Useful when creating a custom [ship log](/ship-log), can be used to reveal all custom facts so you can see them in the ship's computer. -- [Time Saver](https://outerwildsmods.com/mods/timesaver/) - Lets you skip some repeated cutscenes and get into the game faster. -- [The Examples Mod](https://github.com/Outer-Wilds-New-Horizons/nh-examples) - A mod that contains examples of how to use New Horizons features. +- [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) - Used to find the paths of game objects for copying and can be used to manually position props, ship log entries, and more. +- [Collider Visualizer](https://outerwildsmods.com/mods/collidervisualizer) - Useful when creating dialogue triggers or reveal volumes. +- [Save Editor](https://outerwildsmods.com/mods/saveeditor) - Useful when creating a custom [ship log](/ship-log), can be used to reveal all custom facts so you can see them in the ship's computer. +- [Time Saver](https://outerwildsmods.com/mods/timesaver/) - Lets you skip some repeated cutscenes and get into the game faster. +- [The Examples Mod](https://github.com/Outer-Wilds-New-Horizons/nh-examples) - A mod that contains examples of how to use New Horizons features. ## Helpful Tools These tools/references are highly recommended -- [VSCode](https://code.visualstudio.com/) -- [VSCode XML Addon](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml) -- [XML Basics Tutorial](https://www.w3schools.com/xml/xml_whatis.asp) -- [JSON Basics Tutorial](https://www.tutorialspoint.com/json/index.htm) -- [OWML Docs](https://owml.outerwildsmods.com/) +- [VSCode](https://code.visualstudio.com/) +- [VSCode XML Addon](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml) +- [XML Basics Tutorial](https://www.w3schools.com/xml/xml_whatis.asp) +- [JSON Basics Tutorial](https://www.tutorialspoint.com/json/index.htm) +- [OWML Docs](https://owml.outerwildsmods.com/) diff --git a/docs/src/util/schema_generator.ts b/docs/src/util/schema_generator.ts index 18a2290f..38b76157 100644 --- a/docs/src/util/schema_generator.ts +++ b/docs/src/util/schema_generator.ts @@ -5,7 +5,9 @@ const addFrontmatter = ( content: string, frontmatter: Record ) => { - const entries = Object.entries(frontmatter).map(([key, value]) => `${key}: ${value}`); + const entries = Object.entries(frontmatter).map( + ([key, value]) => `${key}: ${JSON.stringify(value)}` + ); if (entries.length === 0) { return content;