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/DefaultMapModeStar.png b/NewHorizons/Assets/DefaultMapModeStar.png index 7bc0ac1b..bdcedc5b 100644 Binary files a/NewHorizons/Assets/DefaultMapModeStar.png and b/NewHorizons/Assets/DefaultMapModeStar.png differ 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/newhorizons_private b/NewHorizons/Assets/newhorizons_private index f15bd4c7..1351ea40 100644 Binary files a/NewHorizons/Assets/newhorizons_private and b/NewHorizons/Assets/newhorizons_private differ diff --git a/NewHorizons/Assets/newhorizons_private.manifest b/NewHorizons/Assets/newhorizons_private.manifest index 71d62e91..3248cae9 100644 --- a/NewHorizons/Assets/newhorizons_private.manifest +++ b/NewHorizons/Assets/newhorizons_private.manifest @@ -1,12 +1,12 @@ ManifestFileVersion: 0 -CRC: 3537427957 +CRC: 2245901288 Hashes: AssetFileHash: serializedVersion: 2 - Hash: c4d8f41970054074bb375ac5cbe82855 + Hash: e765e5fc418c1ed69586a3826e0cdea3 TypeTreeHash: serializedVersion: 2 - Hash: de71b9c55befb829b1640ea21774b932 + Hash: 65942a71d50cdc9f2387a8fa9383a3f8 HashAppended: 0 ClassTypes: - Class: 1 @@ -17,6 +17,8 @@ ClassTypes: Script: {instanceID: 0} - Class: 23 Script: {instanceID: 0} +- Class: 28 + Script: {instanceID: 0} - Class: 33 Script: {instanceID: 0} - Class: 43 @@ -39,6 +41,8 @@ ClassTypes: Script: {instanceID: 0} - Class: 114 Script: {fileID: 11500000, guid: 70edf1000ebf31e4eb3ab4e289a345c0, type: 3} +- Class: 114 + Script: {fileID: 11500000, guid: 86d5ae109bbc920409997135e88f1755, type: 3} - Class: 114 Script: {fileID: 11500000, guid: 77b727c07614b4041a5fe1fba0cfacff, type: 3} - Class: 114 @@ -75,6 +79,8 @@ ClassTypes: Script: {fileID: 11500000, guid: 040dd594681f07a4a975890a61d44be5, type: 3} - Class: 114 Script: {fileID: 11500000, guid: 327eb94566c9e284dae5d7b1cfe11ccd, type: 3} +- Class: 114 + Script: {fileID: 11500000, guid: 8d56b3759dd12424c8425ed62fc02796, type: 3} - Class: 114 Script: {fileID: 11500000, guid: c317f6a5634f15f4c80f89e306616924, type: 3} - Class: 114 @@ -97,10 +103,12 @@ ClassTypes: Script: {fileID: 11500000, guid: b4b79e57677045045a95bfe4fe447ce5, type: 3} - Class: 114 Script: {fileID: 11500000, guid: 64247dd7b0c5ac640a6d9ae5360a0f5a, type: 3} +- Class: 114 + Script: {fileID: 11500000, guid: f645b92850d716a4488617b651223700, type: 3} - Class: 114 Script: {fileID: 11500000, guid: 8ef66a28deb09ab4aaba30bb60b9f19a, type: 3} - Class: 114 - Script: {fileID: 11500000, guid: 0863077874402f14dba0ca4ae81752dd, type: 3} + Script: {fileID: 11500000, guid: bf998978a8a701b4eb09fcd94048f916, type: 3} - Class: 114 Script: {fileID: 11500000, guid: a9da74c8b134add4ba1d884336a5e075, type: 3} - Class: 114 @@ -171,7 +179,24 @@ ClassTypes: Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] Assets: +- Assets/SlideReels/Prefab_DW_Reel_Whole.prefab +- Assets/SlideReels/Prefab_IP_Reel_Rusted_7.prefab +- Assets/SlideReels/Prefab_IP_Reel_Destroyed_6.prefab +- Assets/SlideReels/Effects_IP_SIM_SlideReel.prefab +- Assets/SlideReels/Prefab_DW_Reel_7.prefab +- Assets/SlideReels/Prefab_IP_Reel_Rusted_6.prefab +- Assets/SlideReels/Prefab_IP_Reel_Rusted_8.prefab +- Assets/SlideReels/Prefab_DW_Reel_6.prefab +- Assets/SlideReels/Prefab_IP_Reel_8.prefab +- Assets/SlideReels/Prefab_IP_Reel_6.prefab +- Assets/SlideReels/Prefab_IP_Reel_Destroyed_7.prefab +- Assets/SlideReels/Prefab_IP_Reel_Destroyed_Whole.prefab +- Assets/SlideReels/Prefab_IP_Reel_Destroyed_8.prefab +- Assets/SlideReels/Prefab_IP_Reel_Whole.prefab +- Assets/SlideReels/Prefab_IP_Reel_Rusted_Whole.prefab - Assets/BrambleCollision.prefab +- Assets/SlideReels/Prefab_DW_Reel_8.prefab - Assets/Vessel_Body.prefab - Assets/AmbientLight_QM.png +- Assets/SlideReels/Prefab_IP_Reel_7.prefab Dependencies: [] 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/Builder/Atmosphere/FogBuilder.cs b/NewHorizons/Builder/Atmosphere/FogBuilder.cs index 9f1d7da8..f47e3e29 100644 --- a/NewHorizons/Builder/Atmosphere/FogBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/FogBuilder.cs @@ -1,10 +1,10 @@ using NewHorizons.External.Modules; -using NewHorizons.External.Modules.Props; using NewHorizons.Utility; using NewHorizons.Utility.Files; using OWML.Common; using System; using UnityEngine; + namespace NewHorizons.Builder.Atmosphere { public static class FogBuilder @@ -26,7 +26,9 @@ namespace NewHorizons.Builder.Atmosphere internal static void InitPrefabs() { - if (_ramp == null) _ramp = ImageUtilities.GetTexture(Main.Instance, "Assets/textures/FogColorRamp.png"); + // Checking null here it was getting destroyed and wouldnt reload and never worked outside of the first loop + // GetTexture caches itself anyway so it doesn't matter that this gets called multiple times + _ramp = ImageUtilities.GetTexture(Main.Instance, "Assets/textures/FogColorRamp.png"); if (_isInit) return; @@ -73,6 +75,7 @@ namespace NewHorizons.Builder.Atmosphere atmo.fogRampPath != null ? ImageUtilities.GetTexture(mod, atmo.fogRampPath) : atmo.fogTint != null ? ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()) : _ramp; + PFC.fogColorRampTexture = colorRampTexture; PFC.fogColorRampIntensity = 1f; if (atmo.fogTint != null) diff --git a/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs b/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs index df696f4d..9ee0e365 100644 --- a/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs @@ -1,6 +1,7 @@ using NewHorizons.External.Modules; using NewHorizons.External.Modules.VariableSize; using UnityEngine; + namespace NewHorizons.Builder.Atmosphere { public static class SunOverrideBuilder diff --git a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs index 8568f61b..01fd7f18 100644 --- a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs +++ b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs @@ -36,7 +36,6 @@ namespace NewHorizons.Builder.Body config.Base = new BaseModule() { - hasMapMarker = false, surfaceGravity = 1, surfaceSize = size, gravityFallOff = GravityFallOff.InverseSquared @@ -58,6 +57,11 @@ namespace NewHorizons.Builder.Body enabled = false }; + config.MapMarker = new MapMarkerModule() + { + enabled = false + }; + config.ProcGen = belt.procGen; if (config.ProcGen == null) { diff --git a/NewHorizons/Builder/Body/SingularityBuilder.cs b/NewHorizons/Builder/Body/SingularityBuilder.cs index 9db42133..a961c9d5 100644 --- a/NewHorizons/Builder/Body/SingularityBuilder.cs +++ b/NewHorizons/Builder/Body/SingularityBuilder.cs @@ -11,6 +11,8 @@ using NewHorizons.Builder.Props; using NewHorizons.Utility.OWML; using NewHorizons.Utility.OuterWilds; using NewHorizons.External.SerializableData; +using NewHorizons.Builder.Volumes; +using System; namespace NewHorizons.Builder.Body { @@ -109,31 +111,52 @@ namespace NewHorizons.Builder.Body { foreach (var pair in _pairsToLink) { - var (blackHoleID, whiteHoleID) = pair; - if (!_singularitiesByID.TryGetValue(blackHoleID, out GameObject blackHole)) + try { - NHLogger.LogWarning($"Black hole [{blackHoleID}] is missing."); - continue; + var (blackHoleID, whiteHoleID) = pair; + if (!_singularitiesByID.TryGetValue(blackHoleID, out GameObject blackHole)) + { + NHLogger.LogWarning($"Black hole [{blackHoleID}] is missing."); + continue; + } + if (!_singularitiesByID.TryGetValue(whiteHoleID, out GameObject whiteHole)) + { + NHLogger.LogWarning($"White hole [{whiteHoleID}] is missing."); + continue; + } + var whiteHoleVolume = whiteHole.GetComponentInChildren(); + var blackHoleVolume = blackHole.GetComponentInChildren(); + if (whiteHoleVolume == null || blackHoleVolume == null) + { + NHLogger.LogWarning($"Singularities [{blackHoleID}] and [{whiteHoleID}] do not have compatible polarities."); + continue; + } + if (blackHoleVolume._whiteHole != null && blackHoleVolume._whiteHole != whiteHoleVolume) + { + NHLogger.LogWarning($"Black hole [{blackHoleID}] has already been linked!"); + continue; + } + NHLogger.LogVerbose($"Pairing singularities [{blackHoleID}], [{whiteHoleID}]"); + blackHoleVolume._whiteHole = whiteHoleVolume; + + // If warping to a vanilla planet, we add a streaming volume to pre-load it + var streamingGroup = whiteHoleVolume.GetAttachedOWRigidbody().GetComponentInChildren(); + if (streamingGroup != null) + { + var sphereCollider = blackHoleVolume.GetComponent(); + // Shouldn't ever be null but doesn't hurt ig + var loadRadius = sphereCollider == null ? 100f : sphereCollider.radius + 50f; + var streamingVolume = VolumeBuilder.Make(blackHoleVolume.GetAttachedOWRigidbody().gameObject, blackHoleVolume.GetComponentInParent(), + new External.Modules.Volumes.VolumeInfos.VolumeInfo() { radius = loadRadius }); + streamingVolume.streamingGroup = streamingGroup; + streamingVolume.transform.parent = blackHoleVolume.transform; + streamingVolume.transform.localPosition = Vector3.zero; + } } - if (!_singularitiesByID.TryGetValue(whiteHoleID, out GameObject whiteHole)) + catch (Exception e) { - NHLogger.LogWarning($"White hole [{whiteHoleID}] is missing."); - continue; + NHLogger.LogError($"Failed to pair singularities {e}"); } - var whiteHoleVolume = whiteHole.GetComponentInChildren(); - var blackHoleVolume = blackHole.GetComponentInChildren(); - if (whiteHoleVolume == null || blackHoleVolume == null) - { - NHLogger.LogWarning($"Singularities [{blackHoleID}] and [{whiteHoleID}] do not have compatible polarities."); - continue; - } - if (blackHoleVolume._whiteHole != null && blackHoleVolume._whiteHole != whiteHoleVolume) - { - NHLogger.LogWarning($"Black hole [{blackHoleID}] has already been linked!"); - continue; - } - NHLogger.LogVerbose($"Pairing singularities [{blackHoleID}], [{whiteHoleID}]"); - blackHoleVolume._whiteHole = whiteHoleVolume; } } @@ -167,7 +190,7 @@ namespace NewHorizons.Builder.Body OWAudioSource oneShotOWAudioSource = null; - var singularityAmbience = Object.Instantiate(_blackHoleAmbience, singularity.transform); + var singularityAmbience = GameObject.Instantiate(_blackHoleAmbience, singularity.transform); singularityAmbience.name = polarity ? "BlackHoleAmbience" : "WhiteHoleAmbience"; singularityAmbience.SetActive(true); singularityAmbience.GetComponent().SetSector(sector); @@ -214,7 +237,7 @@ namespace NewHorizons.Builder.Body } else { - var blackHoleOneShot = Object.Instantiate(_blackHoleEmissionOneShot, singularity.transform); + var blackHoleOneShot = GameObject.Instantiate(_blackHoleEmissionOneShot, singularity.transform); blackHoleOneShot.name = "BlackHoleEmissionOneShot"; blackHoleOneShot.SetActive(true); oneShotOWAudioSource = blackHoleOneShot.GetComponent(); @@ -223,7 +246,7 @@ namespace NewHorizons.Builder.Body oneShotAudioSource.minDistance = horizon; if (sizeController != null) sizeController.oneShotAudioSource = oneShotAudioSource; - var blackHoleVolume = Object.Instantiate(_blackHoleVolume, singularity.transform); + var blackHoleVolume = GameObject.Instantiate(_blackHoleVolume, singularity.transform); blackHoleVolume.name = "BlackHoleVolume"; // Scale vanish effect to black hole size @@ -249,7 +272,7 @@ namespace NewHorizons.Builder.Body { foreach (var renderer in blackHoleVolume.GetComponentsInChildren(true)) { - Object.Destroy(renderer); + GameObject.Destroy(renderer); } }); } @@ -257,7 +280,7 @@ namespace NewHorizons.Builder.Body } else { - GameObject whiteHoleVolumeGO = Object.Instantiate(_whiteHoleVolume); + GameObject whiteHoleVolumeGO = GameObject.Instantiate(_whiteHoleVolume); whiteHoleVolumeGO.transform.parent = singularity.transform; whiteHoleVolumeGO.transform.localPosition = Vector3.zero; whiteHoleVolumeGO.transform.localScale = Vector3.one; diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index cdb68c7e..32839346 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -408,10 +408,15 @@ namespace NewHorizons.Builder.Body if (starModule.endTint != null) { var endColour = starModule.endTint.ToColor(); - darkenedColor = new Color(endColour.r * modifier, endColour.g * modifier, endColour.b * modifier); + var adjustedEndColour = new Color(endColour.r * modifier, endColour.g * modifier, endColour.b * modifier); + Color.RGBToHSV(adjustedEndColour, out var hEnd, out var sEnd, out var vEnd); + var darkenedEndColor = Color.HSVToRGB(hEnd, sEnd * 1.2f, vEnd * 0.1f); + surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImageAlongX(_colorOverTime, adjustedColour, darkenedColor, adjustedEndColour, darkenedEndColor)); + } + else + { + surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImage(_colorOverTime, adjustedColour, darkenedColor)); } - - surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImage(_colorOverTime, adjustedColour, darkenedColor)); } if (!string.IsNullOrEmpty(starModule.starRampTexture)) diff --git a/NewHorizons/Builder/Body/WaterBuilder.cs b/NewHorizons/Builder/Body/WaterBuilder.cs index 4a636c08..8eba73c0 100644 --- a/NewHorizons/Builder/Body/WaterBuilder.cs +++ b/NewHorizons/Builder/Body/WaterBuilder.cs @@ -135,7 +135,8 @@ namespace NewHorizons.Builder.Body var fogGO = Object.Instantiate(_oceanFog, waterGO.transform); fogGO.name = "OceanFog"; fogGO.transform.localPosition = Vector3.zero; - fogGO.transform.localScale = Vector3.one; + // In base game GD ocean fog is 550 while the water volume is 500 + fogGO.transform.localScale = Vector3.one * 550f / 500f; fogGO.SetActive(true); if (module.tint != null) diff --git a/NewHorizons/Builder/General/AstroObjectBuilder.cs b/NewHorizons/Builder/General/AstroObjectBuilder.cs index 45966762..21be3536 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,11 +8,15 @@ namespace NewHorizons.Builder.General { public static class AstroObjectBuilder { - public static NHAstroObject Make(GameObject body, AstroObject primaryBody, PlanetConfig config, bool isVanilla) + public static NHAstroObject Make(GameObject body, AstroObject primaryBody, NewHorizonsBody nhBody, bool isVanilla) { NHAstroObject astroObject = body.AddComponent(); + astroObject.modUniqueName = nhBody.Mod.ModHelper.Manifest.UniqueName; + + var config = nhBody.Config; + astroObject.isVanilla = isVanilla; - astroObject.HideDisplayName = !config.Base.hasMapMarker; + astroObject.HideDisplayName = !config.MapMarker.enabled; astroObject.invulnerableToSun = config.Base.invulnerableToSun; if (config.Orbit != null) astroObject.SetOrbitalParametersFromConfig(config.Orbit); 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/Props/Audio/SignalBuilder.cs b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs index 66a86061..9fd8617c 100644 --- a/NewHorizons/Builder/Props/Audio/SignalBuilder.cs +++ b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs @@ -1,9 +1,11 @@ using HarmonyLib; +using NewHorizons.External; using NewHorizons.External.Modules.Props.Audio; using NewHorizons.Utility; using NewHorizons.Utility.OWML; using OWML.Common; using OWML.Utils; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -36,27 +38,16 @@ namespace NewHorizons.Builder.Props.Audio }; NumberOfFrequencies = EnumUtils.GetValues().Length; - _qmSignals = new (){ SearchUtilities.Find("QuantumMoon_Body/Signal_Quantum").GetComponent() }; + _qmSignals = new () { SearchUtilities.Find("QuantumMoon_Body/Signal_Quantum").GetComponent() }; _cloakedSignals = new(); Initialized = true; SceneManager.sceneUnloaded -= OnSceneUnloaded; SceneManager.sceneUnloaded += OnSceneUnloaded; - Main.Instance.OnStarSystemLoaded.RemoveListener(OnStarSystemLoaded); - Main.Instance.OnStarSystemLoaded.AddListener(OnStarSystemLoaded); - } - private static HashSet _frequenciesInUse = new(); - - private static void OnSceneUnloaded(Scene _) - { - _frequenciesInUse.Clear(); - } - - private static void OnStarSystemLoaded(string starSystem) - { // If its the base game solar system or eye we get all the main frequencies + var starSystem = Main.Instance.CurrentStarSystem; if (starSystem == "SolarSystem" || starSystem == "EyeOfTheUniverse") { _frequenciesInUse.Add(SignalFrequency.Quantum); @@ -69,19 +60,43 @@ namespace NewHorizons.Builder.Props.Audio // We don't want a scenario where the player knows no frequencies _frequenciesInUse.Add(SignalFrequency.Traveler); + // Make sure the NH save file has all the right frequencies + // Skip "default" + for (int i = 1; i < PlayerData._currentGameSave.knownFrequencies.Length; i++) + { + if (PlayerData._currentGameSave.knownFrequencies[i]) + { + NewHorizonsData.LearnFrequency(AudioSignal.IndexToFrequency(i).ToString()); + } + } + NHLogger.LogVerbose($"Frequencies in use in {starSystem}: {_frequenciesInUse.Join(x => x.ToString())}"); } + private static HashSet _frequenciesInUse = new(); + + private static void OnSceneUnloaded(Scene _) + { + _frequenciesInUse.Clear(); + } + public static bool IsFrequencyInUse(SignalFrequency freq) => _frequenciesInUse.Contains(freq); + public static bool IsFrequencyInUse(string freqString) + { + if (Enum.TryParse(freqString, out var freq)) + { + return IsFrequencyInUse(freq); + } + return false; + } + public static bool IsCloaked(this AudioSignal signal) => _cloakedSignals.Contains(signal); public static bool IsOnQuantumMoon(this AudioSignal signal) => _qmSignals.Contains(signal); public static SignalFrequency AddFrequency(string str) { - if (_customFrequencyNames == null) Init(); - var freq = CollectionUtilities.KeyByValue(_customFrequencyNames, str); if (freq != default) return freq; @@ -99,23 +114,20 @@ namespace NewHorizons.Builder.Props.Audio NumberOfFrequencies = EnumUtils.GetValues().Length; // This stuff happens after the signalscope is Awake so we have to change the number of frequencies now - Object.FindObjectOfType()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1]; + GameObject.FindObjectOfType()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1]; return freq; } public static string GetCustomFrequencyName(SignalFrequency frequencyName) { - if (_customFrequencyNames == null) Init(); - + if (_customFrequencyNames == null) return string.Empty; _customFrequencyNames.TryGetValue(frequencyName, out string name); return name; } public static SignalName AddSignalName(string str) { - if (_customSignalNames == null) Init(); - var name = CollectionUtilities.KeyByValue(_customSignalNames, str); if (name != default) return name; @@ -129,8 +141,7 @@ namespace NewHorizons.Builder.Props.Audio public static string GetCustomSignalName(SignalName signalName) { - if (_customSignalNames == null) Init(); - + if (_customSignalNames == null) return string.Empty; _customSignalNames.TryGetValue(signalName, out string name); return name; } diff --git a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs index 3e5437e7..810b43d8 100644 --- a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs +++ b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs @@ -6,6 +6,7 @@ using NewHorizons.Handlers; using NewHorizons.Utility; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OWML; +using Newtonsoft.Json; using OWML.Common; using System.Collections.Generic; using System.Linq; @@ -33,12 +34,17 @@ namespace NewHorizons.Builder.Props private static GameObject _brambleSeedPrefab; private static GameObject _brambleNodePrefab; + private static HashSet _nhFogWarpVolumes = new(); + + public static bool IsNHFogWarpVolume(FogWarpVolume volume) => _nhFogWarpVolumes.Contains(volume); + public static void Init(PlanetConfig[] dimensionConfigs) { _unpairedNodes.Clear(); _propagatedSignals.Clear(); namedNodes.Clear(); builtBrambleNodes.Clear(); + _nhFogWarpVolumes.Clear(); PropagateSignals(dimensionConfigs); } @@ -107,6 +113,10 @@ namespace NewHorizons.Builder.Props if (dimension.Bramble.nodes == null) continue; foreach (var node in dimension.Bramble.nodes) { + if (!dimensionNameToIndex.ContainsKey(node.linksTo)) + { + NHLogger.LogError($"There is no bramble dimension named {node.linksTo}"); + } var destinationDimensionIndex = dimensionNameToIndex[node.linksTo]; access[dimensionIndex, destinationDimensionIndex] = true; } @@ -190,6 +200,12 @@ namespace NewHorizons.Builder.Props collider.enabled = true; } + // We track all the fog warp volumes that NH created so we can only effect those in patches, this way we leave base game stuff alone. + foreach (var fogWarpVolume in brambleNode.GetComponentsInChildren(true).Append(brambleNode.GetComponent())) + { + _nhFogWarpVolumes.Add(fogWarpVolume); + } + var innerFogWarpVolume = brambleNode.GetComponent(); var outerFogWarpVolume = GetOuterFogWarpVolumeFromAstroObject(go); var fogLight = brambleNode.GetComponent(); @@ -239,6 +255,12 @@ namespace NewHorizons.Builder.Props foreach(Transform child in brambleNode.transform) { child.localScale = Vector3.one * config.scale; + + // The fog on bramble seeds has a specific scale we need to copy over + if (child.name == "VolumetricFogSphere (2)") + { + child.localScale *= 6.3809f; + } } innerFogWarpVolume._warpRadius *= config.scale; innerFogWarpVolume._exitRadius *= config.scale; @@ -397,7 +419,13 @@ namespace NewHorizons.Builder.Props { foreach (var signalConfig in connectedSignals) { - var signalGO = SignalBuilder.Make(go, sector, signalConfig, mod); + // Have to ensure that this new signal doesn't use parent path, else it looks for a parent that only exists on the original body + // Have to make a copy of it as well to avoid modifying the old body's info + var signalConfigCopy = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(signalConfig)); + signalConfigCopy.parentPath = null; + signalConfigCopy.isRelativeToParent = false; + + var signalGO = SignalBuilder.Make(go, sector, signalConfigCopy, mod); signalGO.GetComponent()._identificationDistance = 0; signalGO.GetComponent()._sourceRadius = 1; signalGO.transform.position = brambleNode.transform.position; diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 054f53d3..89d0887b 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -101,6 +101,7 @@ namespace NewHorizons.Builder.Props GameObject prop; bool isItem; bool invalidComponentFound = false; + bool isFromAssetBundle = !string.IsNullOrEmpty(detail.assetBundle); // We save copies with all their components fixed, good if the user is placing the same detail more than once if (detail?.path != null && _fixedPrefabCache.TryGetValue((sector, detail.path), out var storedPrefab)) @@ -139,7 +140,29 @@ namespace NewHorizons.Builder.Props } else { - FixSectoredComponent(component, sector, existingSectors, detail.keepLoaded); + // Fix cull groups only when not from an asset bundle (because then they're there on purpose!) + // keepLoaded should remove existing groups + // renderers/colliders get enabled later so we dont have to do that here + if (detail.keepLoaded && !isFromAssetBundle && component is SectorCullGroup or SectorCollisionGroup or SectorLightsCullGroup) + { + UnityEngine.Object.DestroyImmediate(component); + continue; + } + + FixSectoredComponent(component, sector, existingSectors); + } + + // Asset bundle is a real string -> Object loaded from unity + // If they're adding dialogue we have to manually register the xml text + if (isFromAssetBundle && component is CharacterDialogueTree dialogue) + { + DialogueBuilder.HandleUnityCreatedDialogue(dialogue); + } + + // copied details need their lanterns fixed + if (!isFromAssetBundle && component is DreamLanternController lantern) + { + lantern.gameObject.AddComponent(); } FixComponent(component, go, detail.ignoreSun); @@ -171,6 +194,11 @@ namespace NewHorizons.Builder.Props if (detail.item != null) { ItemBuilder.MakeItem(prop, go, sector, detail.item, mod); + isItem = true; + if (detail.hasPhysics) + { + NHLogger.LogWarning($"An item with the path {detail.path} has both '{nameof(DetailInfo.hasPhysics)}' and '{nameof(DetailInfo.item)}' set. This will usually result in undesirable behavior."); + } } if (detail.itemSocket != null) @@ -266,16 +294,8 @@ namespace NewHorizons.Builder.Props /// /// Fix components that have sectors. Has a specific fix if there is a VisionTorchItem on the object. /// - private static void FixSectoredComponent(Component component, Sector sector, HashSet existingSectors, bool keepLoaded) + private static void FixSectoredComponent(Component component, Sector sector, HashSet existingSectors) { - // keepLoaded should remove existing groups - // renderers/colliders get enabled later so we dont have to do that here - if (keepLoaded && component is SectorCullGroup or SectorCollisionGroup or SectorLightsCullGroup) - { - UnityEngine.Object.DestroyImmediate(component); - return; - } - // fix Sector stuff, eg SectorCullGroup (without this, props that have a SectorCullGroup component will become invisible inappropriately) if (component is ISectorGroup sectorGroup && !existingSectors.Contains(sectorGroup.GetSector())) { @@ -283,26 +303,8 @@ namespace NewHorizons.Builder.Props } // Not doing else if here because idk if any of the classes below implement ISectorGroup - - // Null check else shuttles controls break - // parent sector is always null before Awake so this code actually never runs lol - if (component is Sector s && s.GetParentSector() != null && !existingSectors.Contains(s.GetParentSector())) - { - s.SetParentSector(sector); - } - else if (component is SectorCullGroup sectorCullGroup) - { - sectorCullGroup._controllingProxy = null; - - // fixes sector cull group deactivating renderers on map view enter and fast foward - // TODO: does this actually work? what? how? - sectorCullGroup._inMapView = false; - sectorCullGroup._isFastForwarding = false; - sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); - } - - else if(component is SectoredMonoBehaviour behaviour && !existingSectors.Contains(behaviour._sector)) + if(component is SectoredMonoBehaviour behaviour && !existingSectors.Contains(behaviour._sector)) { // not using SetSector here because it registers the events twice // perhaps this happens with ISectorGroup.SetSector or Sector.SetParentSector too? idk and nothing seems to break because of it yet @@ -421,6 +423,11 @@ 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(); + } } /// @@ -437,7 +444,7 @@ namespace NewHorizons.Builder.Props NHLogger.LogVerbose("Fixing anglerfish animation"); - // Remove any event reference to its angler + // Remove any event reference to its angler so that they dont change its state if (angler._anglerfishController) { angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; @@ -445,7 +452,8 @@ namespace NewHorizons.Builder.Props angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; } - angler.enabled = true; + // Disable the angler anim controller because we don't want Update or LateUpdate to run, just need it to set the initial Animator state + angler.enabled = false; angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); Destroy(this); @@ -499,5 +507,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 c2068b1c..1459230f 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 { @@ -25,6 +30,18 @@ namespace NewHorizons.Builder.Props // Create dialogue directly from xml string instead of loading it from a file public static (CharacterDialogueTree, RemoteDialogueTrigger) Make(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName) { + if (string.IsNullOrEmpty(info.pathToExistingDialogue)) + { + return MakeNewDialogue(go, sector, info, xml, dialogueName); + } + else + { + return (AddToExistingDialogue(go, sector, info, xml, dialogueName), null); + } + } + + private static (CharacterDialogueTree, RemoteDialogueTrigger) MakeNewDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName) + { NHLogger.LogVerbose($"[DIALOGUE] Created a new character dialogue [{info.rename}] on [{info.parentPath}]"); // In stock I think they disable dialogue stuff with conditions @@ -36,6 +53,8 @@ namespace NewHorizons.Builder.Props } var dialogue = MakeConversationZone(go, sector, info, xml, dialogueName); + + MakeAttentionPoints(go, sector, dialogue, info); RemoteDialogueTrigger remoteTrigger = null; if (info.remoteTrigger != null) @@ -54,6 +73,177 @@ namespace NewHorizons.Builder.Props return (dialogue, remoteTrigger); } + private static CharacterDialogueTree AddToExistingDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName) + { + var dialogueObject = go.FindChild(info.pathToExistingDialogue); + if (dialogueObject == null) dialogueObject = SearchUtilities.Find(info.pathToExistingDialogue); + var existingDialogue = dialogueObject != null ? dialogueObject.GetComponent() : null; + + if (existingDialogue == null) + { + NHLogger.LogError($"Couldn't find dialogue at {info.pathToExistingDialogue}!"); + return null; + } + + var existingAsset = existingDialogue._xmlCharacterDialogueAsset; + if (existingAsset == null) + { + var dialogueDoc = new XmlDocument(); + dialogueDoc.LoadXml(xml); + var xmlNode = dialogueDoc.SelectSingleNode("DialogueTree"); + AddTranslation(xmlNode); + + xml = xmlNode.OuterXml; + + var text = new TextAsset(xml) + { + // Text assets need a name to be used with VoiceMod + name = dialogueName + }; + existingDialogue.SetTextXml(text); + + FixDialogueNextFrame(existingDialogue); + + return existingDialogue; + } + + var existingText = existingAsset.text; + + var existingDialogueDoc = new XmlDocument(); + existingDialogueDoc.LoadXml(existingText); + var existingDialogueTree = existingDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree"); + + var existingDialogueNodesByName = new Dictionary(); + foreach (XmlNode existingDialogueNode in existingDialogueTree.GetChildNodes("DialogueNode")) + { + var name = existingDialogueNode.GetChildNode("Name").InnerText; + existingDialogueNodesByName[name] = existingDialogueNode; + } + + var additionalDialogueDoc = new XmlDocument(); + additionalDialogueDoc.LoadXml(xml); + var newDialogueNodes = additionalDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree").GetChildNodes("DialogueNode"); + + foreach (XmlNode newDialogueNode in newDialogueNodes) + { + var name = newDialogueNode.GetChildNode("Name").InnerText; + + if (existingDialogueNodesByName.TryGetValue(name, out var existingNode)) + { + // We just have to merge the dialogue options + var dialogueOptions = newDialogueNode.GetChildNode("DialogueOptionsList").GetChildNodes("DialogueOption"); + var existingDialogueOptionsList = existingNode.GetChildNode("DialogueOptionsList"); + if (existingDialogueOptionsList == null) + { + existingDialogueOptionsList = existingDialogueDoc.CreateElement("DialogueOptionsList"); + existingNode.AppendChild(existingDialogueOptionsList); + } + foreach (XmlNode node in dialogueOptions) + { + var importedNode = existingDialogueOptionsList.OwnerDocument.ImportNode(node, true); + // We add them to the start because normally the last option is to return to menu or exit + existingDialogueOptionsList.PrependChild(importedNode); + } + } + else + { + // We add the new dialogue node to the existing dialogue + var importedNode = existingDialogueTree.OwnerDocument.ImportNode(newDialogueNode, true); + existingDialogueTree.AppendChild(importedNode); + } + } + + // Character name is required for adding translations, something to do with how OW prefixes its dialogue + var characterName = existingDialogueTree.SelectSingleNode("NameField").InnerText; + AddTranslation(additionalDialogueDoc.GetChildNode("DialogueTree"), characterName); + + var newTextAsset = new TextAsset(existingDialogueDoc.OuterXml) + { + name = existingDialogue._xmlCharacterDialogueAsset.name + }; + + existingDialogue.SetTextXml(newTextAsset); + + FixDialogueNextFrame(existingDialogue); + + MakeAttentionPoints(go, sector, existingDialogue, info); + + return existingDialogue; + } + + private static void FixDialogueNextFrame(CharacterDialogueTree characterDialogueTree) + { + Delay.FireOnNextUpdate(() => + { + var rawText = characterDialogueTree._xmlCharacterDialogueAsset.text; + + var doc = new XmlDocument(); + doc.LoadXml(rawText); + var dialogueTree = doc.DocumentElement.SelectSingleNode("//DialogueTree"); + + DoDialogueOptionsListReplacement(dialogueTree); + + var newTextAsset = new TextAsset(doc.OuterXml) + { + name = characterDialogueTree._xmlCharacterDialogueAsset.name + }; + + characterDialogueTree.SetTextXml(newTextAsset); + }); + } + + /// + /// 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); @@ -109,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) { @@ -136,9 +332,37 @@ namespace NewHorizons.Builder.Props conversationZone.SetActive(true); + FixDialogueNextFrame(dialogueTree); + return dialogueTree; } + private static void MakeAttentionPoints(GameObject go, Sector sector, CharacterDialogueTree dialogue, DialogueInfo info) + { + if (info.attentionPoint != null) + { + var ptGo = GeneralPropBuilder.MakeNew("AttentionPoint", go, sector, info.attentionPoint, defaultParent: dialogue.transform); + dialogue._attentionPoint = ptGo.transform; + dialogue._attentionPointOffset = info.attentionPoint.offset; + ptGo.SetActive(true); + } + if (info.swappedAttentionPoints != null && info.swappedAttentionPoints.Length > 0) + { + foreach (var pointInfo in info.swappedAttentionPoints) + { + var ptGo = GeneralPropBuilder.MakeNew($"AttentionPoint_{pointInfo.dialogueNode}_{pointInfo.dialoguePage}", go, sector, pointInfo, defaultParent: dialogue.transform); + var swapper = ptGo.AddComponent(); + swapper._dialogueTree = dialogue; + swapper._attentionPoint = ptGo.transform; + swapper._attentionPointOffset = pointInfo.offset; + swapper._nodeName = pointInfo.dialogueNode; + swapper._dialoguePage = pointInfo.dialoguePage; + swapper._lookEasing = pointInfo.lookEasing; + ptGo.SetActive(true); + } + } + } + private static void MakePlayerTrackingZone(GameObject go, CharacterDialogueTree dialogue, DialogueInfo info) { var character = go.transform.Find(info.pathToAnimController); @@ -300,14 +524,26 @@ namespace NewHorizons.Builder.Props } } - private static void AddTranslation(string xml) + [Obsolete("Pass in the DialogueTree XmlNode instead, this is still here because Pikpik was using it in EOTP")] + public static void AddTranslation(string xml, string characterName = null) { var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xml); var xmlNode = xmlDocument.SelectSingleNode("DialogueTree"); + AddTranslation(xmlNode, characterName); + } + + public static void AddTranslation(XmlNode xmlNode, string characterName = null) + { var xmlNodeList = xmlNode.SelectNodes("DialogueNode"); - string characterName = xmlNode.SelectSingleNode("NameField").InnerText; - TranslationHandler.AddDialogue(characterName); + + // When adding dialogue to existing stuff, we have to pass in the character name + // Otherwise we translate it if its from a new dialogue object + if (characterName == null) + { + characterName = xmlNode.SelectSingleNode("NameField").InnerText; + TranslationHandler.AddDialogue(characterName); + } foreach (object obj in xmlNodeList) { @@ -333,5 +569,23 @@ namespace NewHorizons.Builder.Props } } } + + public static void HandleUnityCreatedDialogue(CharacterDialogueTree dialogue) + { + var asset = dialogue._xmlCharacterDialogueAsset; + if (asset == null) return; + var text = asset.text; + var dialogueDoc = new XmlDocument(); + dialogueDoc.LoadXml(text); + var xmlNode = dialogueDoc.SelectSingleNode("DialogueTree"); + AddTranslation(xmlNode, null); + var newTextAsset = new TextAsset(dialogueDoc.OuterXml) + { + name = asset.name + }; + dialogue.SetTextXml(newTextAsset); + + FixDialogueNextFrame(dialogue); + } } } diff --git a/NewHorizons/Builder/Props/ItemBuilder.cs b/NewHorizons/Builder/Props/ItemBuilder.cs index 825eafce..2a678d58 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); @@ -79,6 +83,12 @@ namespace NewHorizons.Builder.Props item.ClearPickupConditionOnDrop = info.clearPickupConditionOnDrop; item.PickupFact = info.pickupFact; + if (info.colliderRadius > 0f) + { + go.AddComponent().radius = info.colliderRadius; + go.GetAddComponent(); + } + Delay.FireOnNextUpdate(() => { if (item != null && !string.IsNullOrEmpty(info.pathToInitialSocket)) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 7795f81c..7a55d891 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -1,3 +1,4 @@ +using HarmonyLib; using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props.EchoesOfTheEye; using NewHorizons.Handlers; @@ -8,14 +9,35 @@ using OWML.Common; using System; using System.Collections.Generic; using System.IO; -using System.Threading; +using System.Linq; using UnityEngine; +using UnityEngine.InputSystem; +using static NewHorizons.Main; namespace NewHorizons.Builder.Props { public static class ProjectionBuilder { - private static GameObject _slideReelPrefab; + public const string INVERTED_SLIDE_CACHE_FOLDER = "SlideReelCache/Inverted"; + public const string ATLAS_SLIDE_CACHE_FOLDER = "SlideReelCache/Atlas"; + + public static GameObject SlideReelWholePrefab { get; private set; } + public static GameObject SlideReelWholePristinePrefab { get; private set; } + public static GameObject SlideReelWholeRustedPrefab { get; private set; } + public static GameObject SlideReelWholeDestroyedPrefab { get; private set; } + public static GameObject SlideReel8Prefab { get; private set; } + public static GameObject SlideReel8PristinePrefab { get; private set; } + public static GameObject SlideReel8RustedPrefab { get; private set; } + public static GameObject SlideReel8DestroyedPrefab { get; private set; } + public static GameObject SlideReel7Prefab { get; private set; } + public static GameObject SlideReel7PristinePrefab { get; private set; } + public static GameObject SlideReel7RustedPrefab { get; private set; } + public static GameObject SlideReel7DestroyedPrefab { get; private set; } + public static GameObject SlideReel6Prefab { get; private set; } + public static GameObject SlideReel6PristinePrefab { get; private set; } + public static GameObject SlideReel6RustedPrefab { get; private set; } + public static GameObject SlideReel6DestroyedPrefab { get; private set; } + private static GameObject _autoPrefab; private static GameObject _visionTorchDetectorPrefab; private static GameObject _standingVisionTorchPrefab; @@ -23,20 +45,30 @@ namespace NewHorizons.Builder.Props private static bool _isInit; + public static bool CacheExists(IModBehaviour mod) => Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER)); + internal static void InitPrefabs() { if (_isInit) return; _isInit = true; - if (_slideReelPrefab == null) - { - _slideReelPrefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Sector_SlideBurningRoom_Zone1/Interactables_SlideBurningRoom_Zone1/Prefab_IP_SecretAlcove/RotationPivot/SlideReelSocket/Prefab_IP_Reel_1_LibraryPath")?.gameObject?.InstantiateInactive()?.Rename("Prefab_IP_Reel")?.DontDestroyOnLoad(); - if (_slideReelPrefab == null) - NHLogger.LogWarning($"Tried to make slide reel prefab but couldn't. Do you have the DLC installed?"); - else - _slideReelPrefab.AddComponent()._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,13 +120,17 @@ namespace NewHorizons.Builder.Props } } + public static string GetUniqueSlideReelID(IModBehaviour mod, SlideInfo[] slides) => $"{mod.ModHelper.Manifest.UniqueName}{slides.Join(x => x.imagePath)}".GetHashCode().ToString(); + private static GameObject MakeSlideReel(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod) { InitPrefabs(); - if (_slideReelPrefab == null) return null; + GameObject prefab = GetSlideReelPrefab(info.reelModel, info.reelCondition); - var slideReelObj = GeneralPropBuilder.MakeFromPrefab(_slideReelPrefab, $"Prefab_IP_Reel_{mod.ModHelper.Manifest.Name}", planetGO, sector, info); + if (prefab == null) return null; + + var slideReelObj = GeneralPropBuilder.MakeFromPrefab(prefab, $"Prefab_IP_Reel_{GetSlideReelName(info.reelModel, info.reelCondition)}_{mod.ModHelper.Manifest.Name}", planetGO, sector, info); var slideReel = slideReelObj.GetComponent(); slideReel.SetSector(sector); @@ -112,41 +148,82 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - // The base game ones only have 15 slides max - var textures = new Texture2D[slidesCount >= 15 ? 15 : slidesCount]; + // We can fit 16 slides max into an atlas + var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount]; - var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection); + var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true); - // this variable just lets us track how many of the first 15 slides have been loaded. - // this way as soon as the last one is loaded (due to async loading, this may be - // slide 7, or slide 3, or whatever), we can build the slide reel texture. This allows us - // to avoid doing a "is every element in the array `textures` not null" check every time a texture finishes loading - int displaySlidesLoaded = 0; - imageLoader.imageLoadedEvent.AddListener( - (Texture2D tex, int index) => - { - slideCollection.slides[index]._image = ImageUtilities.Invert(tex); + // If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory + imageLoader.deleteTexturesWhenDone = !CacheExists(mod); - // Track the first 15 to put on the slide reel object - if (index < textures.Length) + var key = GetUniqueSlideReelID(mod, info.slides); + + if (invImageLoader != null && atlasImageLoader != null) + { + // Loading directly from cache + invImageLoader.imageLoadedEvent.AddListener( + (Texture2D tex, int index, string originalPath) => + { + slideCollection.slides[index]._image = tex; + } + ); + atlasImageLoader.imageLoadedEvent.AddListener( + (Texture2D tex, int _, string originalPath) => + { + // all textures required to build the reel's textures have been loaded + var slidesBack = slideReelObj.GetComponentInChildren(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; + + 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); + var reelTexture = ImageUtilities.MakeReelTexture(mod, textures, key); slidesBack.material.mainTexture = reelTexture; slidesBack.material.SetTexture(EmissionMap, reelTexture); + slidesBack.material.name = reelTexture.name; slidesFront.material.mainTexture = reelTexture; slidesFront.material.SetTexture(EmissionMap, reelTexture); + slidesFront.material.name = reelTexture.name; } } - } - ); + + NHLogger.LogVerbose($"Slide reel make reel texture {(DateTime.Now - time).TotalMilliseconds}ms"); + }); + } // Else when you put them down you can't pick them back up slideReelObj.GetComponent()._physicsRemoved = false; @@ -162,6 +239,97 @@ namespace NewHorizons.Builder.Props return slideReelObj; } + private static GameObject GetSlideReelPrefab(ProjectionInfo.SlideReelType model, ProjectionInfo.SlideReelCondition condition) + { + switch (model) + { + case ProjectionInfo.SlideReelType.SixSlides: + { + switch (condition) + { + case ProjectionInfo.SlideReelCondition.Antique: + default: + return SlideReel6Prefab; + case ProjectionInfo.SlideReelCondition.Pristine: + return SlideReel6PristinePrefab; + case ProjectionInfo.SlideReelCondition.Rusted: + return SlideReel6RustedPrefab; + } + } + case ProjectionInfo.SlideReelType.SevenSlides: + default: + { + switch (condition) + { + case ProjectionInfo.SlideReelCondition.Antique: + default: + return SlideReel7Prefab; + case ProjectionInfo.SlideReelCondition.Pristine: + return SlideReel7PristinePrefab; + case ProjectionInfo.SlideReelCondition.Rusted: + return SlideReel7RustedPrefab; + } + } + case ProjectionInfo.SlideReelType.EightSlides: + { + switch (condition) + { + case ProjectionInfo.SlideReelCondition.Antique: + default: + return SlideReel8Prefab; + case ProjectionInfo.SlideReelCondition.Pristine: + return SlideReel8PristinePrefab; + case ProjectionInfo.SlideReelCondition.Rusted: + return SlideReel8RustedPrefab; + } + } + case ProjectionInfo.SlideReelType.Whole: + { + switch (condition) + { + case ProjectionInfo.SlideReelCondition.Antique: + default: + return SlideReelWholePrefab; + case ProjectionInfo.SlideReelCondition.Pristine: + return SlideReelWholePristinePrefab; + case ProjectionInfo.SlideReelCondition.Rusted: + return SlideReelWholeRustedPrefab; + } + } + } + } + + public static string GetSlideReelName(ProjectionInfo.SlideReelType model, ProjectionInfo.SlideReelCondition condition) + { + switch (model) + { + case ProjectionInfo.SlideReelType.SixSlides: + return $"6_{condition}"; + case ProjectionInfo.SlideReelType.SevenSlides: + return $"7_{condition}"; + case ProjectionInfo.SlideReelType.EightSlides: + return $"8_{condition}"; + case ProjectionInfo.SlideReelType.Whole: + default: + return $"{model}_{condition}"; + } + } + + public static int GetSlideCount(ProjectionInfo.SlideReelType model) + { + switch (model) + { + case ProjectionInfo.SlideReelType.SixSlides: + return 6; + case ProjectionInfo.SlideReelType.SevenSlides: + case ProjectionInfo.SlideReelType.Whole: + return 7; + case ProjectionInfo.SlideReelType.EightSlides: + default: + return 8; + } + } + public static GameObject MakeAutoProjector(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod) { InitPrefabs(); @@ -180,8 +348,29 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); 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); + + // 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; @@ -219,8 +408,13 @@ 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); + 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(); @@ -254,20 +448,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); // 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,15 +469,18 @@ namespace NewHorizons.Builder.Props // to avoid doing a "is every element in the array `slideCollection.slides` not null" check every time a texture finishes loading int displaySlidesLoaded = 0; imageLoader.imageLoadedEvent.AddListener( - (Texture2D tex, int index) => - { + (Texture2D tex, int index, string originalPath) => + { + var time = DateTime.Now; slideCollection.slides[index]._image = tex; - if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length) + displaySlidesLoaded++; + if (displaySlidesLoaded == slides.Length) { mindSlideProjector.enabled = true; visionBeamEffect.SetActive(true); } + NHLogger.LogVerbose($"Slide reel another set time {(DateTime.Now - time).TotalMilliseconds}ms"); } ); @@ -307,21 +504,56 @@ namespace NewHorizons.Builder.Props return standingTorch; } - private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) + private static (SlideReelAsyncImageLoader inverted, SlideReelAsyncImageLoader atlas, SlideReelAsyncImageLoader slides) + StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache) { - var imageLoader = gameObject.AddComponent(); + var invertedImageLoader = new SlideReelAsyncImageLoader(); + var atlasImageLoader = new SlideReelAsyncImageLoader(); + var imageLoader = new SlideReelAsyncImageLoader(); + + var atlasKey = GetUniqueSlideReelID(mod, slides); + + var cacheExists = CacheExists(mod); + + NHLogger.Log($"Does cache exist for slide reels? {cacheExists}"); + + if (useAtlasCache && cacheExists) + { + NHLogger.LogVerbose($"The atlas cache for slide reel containing [{slides.FirstOrDefault(x => !string.IsNullOrEmpty(x.imagePath))?.imagePath}] is {atlasKey}"); + // Load the atlas texture used to draw onto the physical slide reel object + atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"))); + } + for (int i = 0; i < slides.Length; i++) { var slide = new Slide(); var slideInfo = slides[i]; + slide._streamingImageID = i; // for SlideRotationModule if (string.IsNullOrEmpty(slideInfo.imagePath)) { - imageLoader.imageLoadedEvent?.Invoke(Texture2D.blackTexture, i); + if (useInvertedCache && cacheExists) + { + // Load the inverted images used when displaying slide reels to a screen + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"))); + } + else + { + // Used to then make cached stuff + imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"))); + } } else { - imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); + if (useInvertedCache && cacheExists) + { + // Load the inverted images used when displaying slide reels to a screen + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); + } + else + { + imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); + } } AddModules(slideInfo, ref slide, mod); @@ -329,7 +561,36 @@ namespace NewHorizons.Builder.Props slideCollection.slides[i] = slide; } - return imageLoader; + if (cacheExists) + { + NHLogger.Log("Loading slide reels from cache"); + + if (useAtlasCache) + { + atlasImageLoader.Start(false, false); + } + // When using the inverted cache we never need the regular images + if (useInvertedCache) + { + invertedImageLoader.Start(true, false); + } + else + { + imageLoader.Start(true, false); + } + + return (invertedImageLoader, atlasImageLoader, imageLoader); + } + else + { + NHLogger.Log("Generating slide reel cache"); + + // Will be slow and create the cache if needed + // Will run sequentially to ensure we don't run out of memory + imageLoader.Start(true, true); + + return (null, null, imageLoader); + } } private static void AddModules(SlideInfo slideInfo, ref Slide slide, IModBehaviour mod) @@ -388,10 +649,14 @@ namespace NewHorizons.Builder.Props }; modules.Add(shipLogEntry); } + if (slideInfo.rotate) + { + modules.Add(new SlideRotationModule()); + } Slide.WriteModules(modules, ref slide._modulesList, ref slide._modulesData, ref slide.lengths); } - + private static void LinkShipLogFacts(ProjectionInfo info, SlideCollectionContainer slideCollectionContainer) { // Idk why but it wants reveals to be comma delimited not a list diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 86dcf826..433b7bc4 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -4,49 +4,141 @@ using NewHorizons.Builder.Props.TranslatorText; using NewHorizons.Builder.ShipLog; using NewHorizons.External; using NewHorizons.External.Configs; +using NewHorizons.External.Modules; +using NewHorizons.Utility; using NewHorizons.Utility.OWML; using OWML.Common; using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace NewHorizons.Builder.Props { public static class PropBuildManager { + public static string InfoToName() 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) MakeGeneralProps(go, config.Props.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody)); + MakeGeneralProps(go, config.Props.tornados, (tornado) => TornadoBuilder.Make(go, sector, tornado, config.Atmosphere?.clouds != null)); + MakeGeneralProps(go, config.Props.volcanoes, (volcano) => VolcanoBuilder.Make(go, sector, volcano)); + MakeGeneralProps(go, config.Props.dialogue, (dialogueInfo) => { - foreach (var gravityCannonInfo in config.Props.gravityCannons) + var (dialogue, trigger) = DialogueBuilder.Make(go, sector, dialogueInfo, mod); + if (dialogue == null) { - try - { - GravityCannonBuilder.Make(go, sector, gravityCannonInfo, mod); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make gravity cannon [{gravityCannonInfo.shuttleID}] for [{go.name}]:\n{ex}"); - } + NHLogger.LogVerbose($"[DIALOGUE] Failed to create dialogue [{dialogueInfo.xmlFile}]"); } - } - if (config.Props.shuttles != null) - { - foreach (var shuttleInfo in config.Props.shuttles) - { - try - { - ShuttleBuilder.Make(go, sector, nhBody.Mod, shuttleInfo); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make shuttle [{shuttleInfo.id}] for [{go.name}]:\n{ex}"); - } - } - } + }, (dialogueInfo) => dialogueInfo.xmlFile); + MakeGeneralProps(go, config.Props.entryLocation, (entryLocationInfo) => EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod), (entryLocationInfo) => entryLocationInfo.id); + // Backwards compatibility +#pragma warning disable 612, 618 + MakeGeneralProps(go, config.Props.nomaiText, (nomaiTextInfo) => NomaiTextBuilder.Make(go, sector, nomaiTextInfo, mod), (nomaiTextInfo) => nomaiTextInfo.xmlFile); +#pragma warning restore 612, 618 + MakeGeneralProps(go, config.Props.translatorText, (nomaiTextInfo) => TranslatorTextBuilder.Make(go, sector, nomaiTextInfo, nhBody), (nomaiTextInfo) => nomaiTextInfo.xmlFile); + if (Main.HasDLC) MakeGeneralProps(go, config.Props.slideShows, (slideReelInfo) => ProjectionBuilder.Make(go, sector, slideReelInfo, mod), (slideReelInfo) => slideReelInfo.type.ToString().ToCamelCase()); + MakeGeneralProps(go, config.Props.singularities, (singularity) => SingularityBuilder.Make(go, sector, go.GetComponent(), config, singularity), (singularity) => (string.IsNullOrEmpty(singularity.uniqueID) ? config.name : singularity.uniqueID)); + MakeGeneralProps(go, config.Props.signals, (signal) => SignalBuilder.Make(go, sector, signal, mod), (signal) => signal.name); + MakeGeneralProps(go, config.Props.warpReceivers, (warpReceiver) => WarpPadBuilder.Make(go, sector, mod, warpReceiver), (warpReceiver) => warpReceiver.frequency); + MakeGeneralProps(go, config.Props.warpTransmitters, (warpTransmitter) => WarpPadBuilder.Make(go, sector, mod, warpTransmitter), (warpTransmitter) => warpTransmitter.frequency); + MakeGeneralProps(go, config.Props.audioSources, (audioSource) => AudioSourceBuilder.Make(go, sector, audioSource, mod), (audioSource) => audioSource.audio); + RemoteBuilder.MakeGeneralProps(go, sector, config.Props.remotes, nhBody); + + RunMultiPass(); + + /* + * + * Builders below don't inherit the same base class so if they have complicated parentPaths they might just break + * If a prop above sets one of these as its parent path it will break (but that was always the case) + * + */ + if (config.Props.scatter != null) { try @@ -58,156 +150,7 @@ namespace NewHorizons.Builder.Props NHLogger.LogError($"Couldn't make planet scatter for [{go.name}]:\n{ex}"); } } - if (config.Props.details != null) - { - foreach (var detail in config.Props.details) - { - try - { - var detailGO = DetailBuilder.Make(go, sector, mod, detail); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make planet detail [{detail.path}] for [{go.name}]:\n{ex}"); - } - } - } - if (config.Props.geysers != null) - { - foreach (var geyserInfo in config.Props.geysers) - { - try - { - GeyserBuilder.Make(go, sector, geyserInfo); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make geyser for [{go.name}]:\n{ex}"); - } - } - } - if (Main.HasDLC && config.Props.rafts != null) - { - foreach (var raftInfo in config.Props.rafts) - { - try - { - RaftBuilder.Make(go, sector, raftInfo, planetBody); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make raft for [{go.name}]:\n{ex}"); - } - } - } - if (config.Props.tornados != null) - { - foreach (var tornadoInfo in config.Props.tornados) - { - try - { - TornadoBuilder.Make(go, sector, tornadoInfo, config.Atmosphere?.clouds != null); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make tornado for [{go.name}]:\n{ex}"); - } - } - } - if (config.Props.volcanoes != null) - { - foreach (var volcanoInfo in config.Props.volcanoes) - { - try - { - VolcanoBuilder.Make(go, sector, volcanoInfo); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make volcano for [{go.name}]:\n{ex}"); - } - } - } - // Reminder that dialogue has to be built after props if they're going to be using CharacterAnimController stuff - if (config.Props.dialogue != null) - { - foreach (var dialogueInfo in config.Props.dialogue) - { - try - { - var (dialogue, trigger) = DialogueBuilder.Make(go, sector, dialogueInfo, mod); - if (dialogue == null) - { - NHLogger.LogVerbose($"[DIALOGUE] Failed to create dialogue [{dialogueInfo.xmlFile}]"); - } - } - catch (Exception ex) - { - NHLogger.LogError($"[DIALOGUE] Couldn't make dialogue [{dialogueInfo.xmlFile}] for [{go.name}]:\n{ex}"); - } - } - } - if (config.Props.entryLocation != null) - { - foreach (var entryLocationInfo in config.Props.entryLocation) - { - try - { - EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make entry location [{entryLocationInfo.id}] for [{go.name}]:\n{ex}"); - } - } - } - // Backwards compatibility -#pragma warning disable 612, 618 - if (config.Props.nomaiText != null) - { - foreach (var nomaiTextInfo in config.Props.nomaiText) - { - try - { - NomaiTextBuilder.Make(go, sector, nomaiTextInfo, nhBody.Mod); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make text [{nomaiTextInfo.xmlFile}] for [{go.name}]:\n{ex}"); - } - } - } -#pragma warning restore 612, 618 - if (config.Props.translatorText != null) - { - foreach (var nomaiTextInfo in config.Props.translatorText) - { - try - { - TranslatorTextBuilder.Make(go, sector, nomaiTextInfo, nhBody); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make text [{nomaiTextInfo.xmlFile}] for [{go.name}]:\n{ex}"); - } - - } - } - if (Main.HasDLC && config.Props.slideShows != null) - { - foreach (var slideReelInfo in config.Props.slideShows) - { - try - { - ProjectionBuilder.Make(go, sector, slideReelInfo, mod); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make slide reel for [{go.name}]:\n{ex}"); - } - } - } if (config.Props.quantumGroups != null) { Dictionary> propsByGroup = new Dictionary>(); @@ -235,89 +178,23 @@ namespace NewHorizons.Builder.Props } } } - if (config.Props.singularities != null) + } + + private static bool _ignoreParent; + + private static bool DoesParentExist(GameObject go, BasePropInfo prop) + { + if (_ignoreParent) { - foreach (var singularity in config.Props.singularities) - { - try - { - SingularityBuilder.Make(go, sector, go.GetComponent(), 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/RaftBuilder.cs b/NewHorizons/Builder/Props/RaftBuilder.cs index 14ccf8d4..98196885 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; @@ -73,6 +74,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/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index 9ad3ba5c..9c1a4eba 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(); 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 d1a174b0..c51ece61 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -7,19 +7,47 @@ using NewHorizons.Utility; using NewHorizons.Utility.Files; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OWML; +using OWML.ModHelper; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; +using static NewHorizons.External.Modules.ShipLogModule; namespace NewHorizons.Builder.ShipLog { public static class MapModeBuilder { + // Takes the game object because sometimes we change the AO to an NHAO and it breaks + private static Dictionary _astroObjectToShipLog = new(); + #region General public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer) { + _astroObjectToShipLog = new(); + + // Add stock planets + foreach (var shipLogAstroObject in currentNav.SelectMany(x => x)) + { + var astroObject = Locator.GetAstroObject(AstroObject.StringIDToAstroObjectName(shipLogAstroObject._id)); + if (astroObject == null) + { + // Outsider compat + if (shipLogAstroObject._id == "POWER_STATION") + { + astroObject = GameObject.FindObjectsOfType().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 @@ -30,7 +58,7 @@ namespace NewHorizons.Builder.ShipLog { if (body.Config.ShipLog == null) continue; - if (body.Config.ShipLog?.mapMode?.manualPosition == null) + if (body.Config.ShipLog.mapMode?.manualPosition == null) { flagAutoPositionUsed = true; } @@ -45,18 +73,46 @@ namespace NewHorizons.Builder.ShipLog } } - if (flagManualPositionUsed) + // If they're both false, just default to auto (this means that no planets even have ship log info) + if (!flagManualPositionUsed && !flagAutoPositionUsed) { - if (flagAutoPositionUsed && flagManualPositionUsed) - NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to manual"); - return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer); - } - else if (flagAutoPositionUsed) - { - return ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer); + flagAutoPositionUsed = true; } - return null; + var isBaseSolarSystem = systemName == "SolarSystem"; + + // Default to MANUAL in Base Solar System (we can't automatically fix them so it might just break, but AUTO breaks even more!) + var useManual = (flagManualPositionUsed && !flagAutoPositionUsed) || (flagAutoPositionUsed && flagManualPositionUsed && isBaseSolarSystem); + + // Default to AUTO in other solar systems (since we can actually fix them) + var useAuto = (flagAutoPositionUsed && !flagManualPositionUsed) || (flagAutoPositionUsed && flagManualPositionUsed && !isBaseSolarSystem); + + if (flagAutoPositionUsed && flagManualPositionUsed) + { + if (useAuto) + { + NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to AUTOMATIC"); + } + else + { + NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to MANUAL"); + } + } + + ShipLogAstroObject[][] newNavMatrix = null; + + if (useAuto) + { + newNavMatrix = ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer); + } + else if (useManual) + { + newNavMatrix = ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer); + } + + ReplaceExistingMapModeIcons(); + + return newNavMatrix; } public static string GetAstroBodyShipLogName(string id) @@ -109,6 +165,7 @@ namespace NewHorizons.Builder.ShipLog ShipLogAstroObject astroObject = gameObject.AddComponent(); astroObject._id = ShipLogHandler.GetAstroObjectId(body); + _astroObjectToShipLog[body.Object] = astroObject; Texture2D image = null; Texture2D outline = null; @@ -124,6 +181,7 @@ namespace NewHorizons.Builder.ShipLog astroObject._imageObj = CreateImage(gameObject, image, body.Config.name + " Revealed", layer); astroObject._outlineObj = CreateImage(gameObject, outline, body.Config.name + " Outline", layer); + if (ShipLogHandler.BodyHasEntries(body)) { Image revealedImage = astroObject._imageObj.GetComponent(); @@ -138,6 +196,12 @@ namespace NewHorizons.Builder.ShipLog Rect imageRect = astroObject._imageObj.GetComponent().rect; astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); + + // Set all icons inactive, they will be conditionally activated when the map mode is opened for the first time + astroObject._unviewedObj.SetActive(false); + astroObject._imageObj.SetActive(false); + astroObject._outlineObj.SetActive(false); + return astroObject; } #endregion @@ -475,7 +539,12 @@ namespace NewHorizons.Builder.ShipLog private static void MakeNode(ref MapModeObject node, GameObject parent, Material greyScaleMaterial, int layer) { - const float padding = 100f; + // Space between this node and the previous node + // Take whatever scale will prevent overlap + var lastSiblingScale = node.lastSibling?.mainBody?.Config?.ShipLog?.mapMode?.scale ?? 1f; + var scale = node.mainBody?.Config?.ShipLog?.mapMode?.scale ?? 1f; + float padding = 100f * (scale + lastSiblingScale) / 2f; + Vector2 position = Vector2.zero; if (node.lastSibling != null) { @@ -568,5 +637,68 @@ namespace NewHorizons.Builder.ShipLog return Color.white; } + + #region Replacement + private static List<(NewHorizonsBody, ModBehaviour, MapModeInfo)> _mapModIconsToUpdate = new(); + public static void TryReplaceExistingMapModeIcon(NewHorizonsBody body, ModBehaviour mod, MapModeInfo info) + { + if (!string.IsNullOrEmpty(info.revealedSprite) || !string.IsNullOrEmpty(info.outlineSprite)) + { + _mapModIconsToUpdate.Add((body, mod, info)); + } + } + + private static void ReplaceExistingMapModeIcons() + { + foreach (var (body, mod, info) in _mapModIconsToUpdate) + { + ReplaceExistingMapModeIcon(body, mod, info); + } + _mapModIconsToUpdate.Clear(); + } + + private static void ReplaceExistingMapModeIcon(NewHorizonsBody body, ModBehaviour mod, MapModeInfo info) + { + var astroObject = _astroObjectToShipLog[body.Object]; + var gameObject = astroObject.gameObject; + var layer = gameObject.layer; + + if (!string.IsNullOrEmpty(info.revealedSprite)) + { + var revealedTexture = ImageUtilities.GetTexture(body.Mod, info.revealedSprite); + if (revealedTexture == null) + { + NHLogger.LogError($"Couldn't load replacement revealed texture {info.revealedSprite}"); + } + else + { + GameObject.Destroy(astroObject._imageObj); + if (ShipLogHandler.IsVanillaBody(body) || ShipLogHandler.BodyHasEntries(body)) + { + Image revealedImage = astroObject._imageObj.GetComponent(); + 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/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/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/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/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 new file mode 100644 index 00000000..1eb440d9 --- /dev/null +++ b/NewHorizons/Components/Fixers/PlayerShipAtmosphereDetectorFix.cs @@ -0,0 +1,37 @@ +using NewHorizons.Utility.OWML; +using UnityEngine; + +namespace NewHorizons.Components.Fixers; + +/// +/// Fixes a bug where spawning into the ship would not trigger the hatch entryway, so the player could drown if the ship flew into water +/// +internal class PlayerShipAtmosphereDetectorFix : MonoBehaviour +{ + private PlayerCameraFluidDetector _fluidDetector; + private SimpleFluidVolume _shipAtmosphereVolume; + + public void Start() + { + _fluidDetector = Locator.GetPlayerCameraDetector().GetComponent(); + _shipAtmosphereVolume = Locator.GetShipBody()?.transform?.Find("Volumes/ShipAtmosphereVolume")?.GetComponent(); + if (_shipAtmosphereVolume == null) + { + Destroy(this); + } + } + + public void Update() + { + if (PlayerState.IsInsideShip()) + { + if (!_fluidDetector._activeVolumes.Contains(_shipAtmosphereVolume)) + { + NHLogger.LogVerbose($"{nameof(PlayerShipAtmosphereDetectorFix)} had to add the ship atmosphere volume [{_shipAtmosphereVolume}] to the fluid detector"); + _fluidDetector.AddVolume(_shipAtmosphereVolume); + } + NHLogger.LogVerbose($"{nameof(PlayerShipAtmosphereDetectorFix)} applied its fix"); + Component.Destroy(this); + } + } +} 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 2fd0cde1..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; @@ -19,6 +21,7 @@ namespace NewHorizons.Components.Props { var conditionalObjectActivationGO = new GameObject($"{go.name}_{condition}"); var component = conditionalObjectActivationGO.AddComponent(); + component.transform.parent = go.transform.parent; component.GameObject = go; component.DialogueCondition = condition; component.CloseEyes = closeEyes; @@ -46,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() @@ -53,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() @@ -87,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/NHItem.cs b/NewHorizons/Components/Props/NHItem.cs index b21db7ea..bdf3e388 100644 --- a/NewHorizons/Components/Props/NHItem.cs +++ b/NewHorizons/Components/Props/NHItem.cs @@ -19,6 +19,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 +46,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 +62,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/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/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..86d6cfa8 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 avaialble 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/TimeLoopController.cs b/NewHorizons/Components/TimeLoopController.cs index 7110e6db..fd38ab3c 100644 --- a/NewHorizons/Components/TimeLoopController.cs +++ b/NewHorizons/Components/TimeLoopController.cs @@ -13,6 +13,9 @@ namespace NewHorizons.Components public void Update() { + // So that mods can turn the time loop on/off using the TimLoop.SetTimeLoopEnabled method + if (!TimeLoop._timeLoopEnabled) return; + // Stock gives like 33 seconds after the sun collapses if (_supernovaHappened && Time.time > _supernovaTime + 50f) { 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/BaseVolume.cs b/NewHorizons/Components/Volumes/BaseVolume.cs index f313310c..970c3ad9 100644 --- a/NewHorizons/Components/Volumes/BaseVolume.cs +++ b/NewHorizons/Components/Volumes/BaseVolume.cs @@ -3,22 +3,27 @@ using UnityEngine; namespace NewHorizons.Components.Volumes { [RequireComponent(typeof(OWTriggerVolume))] - public abstract class BaseVolume : MonoBehaviour + public abstract class BaseVolume : SectoredMonoBehaviour { private OWTriggerVolume _triggerVolume; - public virtual void Awake() + public override void Awake() { + base.Awake(); _triggerVolume = this.GetRequiredComponent(); _triggerVolume.OnEntry += OnTriggerVolumeEntry; _triggerVolume.OnExit += OnTriggerVolumeExit; } - public virtual void OnDestroy() + public override void OnDestroy() { - if (_triggerVolume == null) return; - _triggerVolume.OnEntry -= OnTriggerVolumeEntry; - _triggerVolume.OnExit -= OnTriggerVolumeExit; + base.OnDestroy(); + + if (_triggerVolume != null) + { + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } } public abstract void OnTriggerVolumeEntry(GameObject hitObj); diff --git a/NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs b/NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs deleted file mode 100644 index 70d74979..00000000 --- a/NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace NewHorizons.Components.Volumes -{ - public class NHInnerFogWarpVolume : InnerFogWarpVolume - { - public override bool IsProbeOnly() => _exitRadius <= 6; - public override float GetFogThickness() => _exitRadius; - } -} diff --git a/NewHorizons/Components/Volumes/StreamingWarpVolume.cs b/NewHorizons/Components/Volumes/StreamingWarpVolume.cs new file mode 100644 index 00000000..163a9e78 --- /dev/null +++ b/NewHorizons/Components/Volumes/StreamingWarpVolume.cs @@ -0,0 +1,128 @@ +using NewHorizons.Utility.OWML; +using UnityEngine; + +namespace NewHorizons.Components.Volumes +{ + /// + /// Currently only relevant for Vanilla planets, which actually have streaming groups + /// + internal class StreamingWarpVolume : BaseVolume + { + public StreamingGroup streamingGroup; + private bool _probeInVolume; + private bool _playerInVolume; + private bool _preloadingRequiredAssets; + private bool _preloadingGeneralAssets; + + private SurveyorProbe _probe; + + public void Start() + { + _probe = Locator.GetProbe(); + base.enabled = false; + } + + public void FixedUpdate() + { + // Bug report on Astral Codec mod page - Huge lag inside streaming warp volume, possible NRE? + if (_probe == null) + { + _probe = Locator.GetProbe(); + if (_probe == null) + { + NHLogger.LogError($"How is your scout probe null? Destroying {nameof(StreamingWarpVolume)}"); + GameObject.DestroyImmediate(gameObject); + } + } + + if (streamingGroup == null) + { + NHLogger.LogError($"{nameof(StreamingWarpVolume)} has no streaming group. Destroying {nameof(StreamingWarpVolume)}"); + GameObject.DestroyImmediate(gameObject); + } + + bool probeActive = _probe.IsLaunched() && !_probe.IsAnchored(); + + bool shouldBeLoadingRequiredAssets = _playerInVolume || (_probeInVolume && probeActive); + bool shouldBeLoadingGeneralAssets = _playerInVolume; + + UpdatePreloadingState(shouldBeLoadingRequiredAssets, shouldBeLoadingGeneralAssets); + } + + private void UpdatePreloadingState(bool shouldBeLoadingRequiredAssets, bool shouldBeLoadingGeneralAssets) + { + if (!this._preloadingRequiredAssets && shouldBeLoadingRequiredAssets) + { + this.streamingGroup.RequestRequiredAssets(0); + this._preloadingRequiredAssets = true; + } + else if (this._preloadingRequiredAssets && !shouldBeLoadingRequiredAssets) + { + this.streamingGroup.ReleaseRequiredAssets(); + this._preloadingRequiredAssets = false; + } + if (!this._preloadingGeneralAssets && shouldBeLoadingGeneralAssets) + { + this.streamingGroup.RequestGeneralAssets(0); + this._preloadingGeneralAssets = true; + return; + } + if (this._preloadingGeneralAssets && !shouldBeLoadingGeneralAssets) + { + this.streamingGroup.ReleaseGeneralAssets(); + this._preloadingGeneralAssets = false; + } + } + + public override void OnSectorOccupantsUpdated() + { + if (this._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe)) + { + if (StreamingManager.isStreamingEnabled && this.streamingGroup != null) + { + base.enabled = true; + return; + } + } + else + { + this.UpdatePreloadingState(false, false); + base.enabled = false; + } + } + + public override void OnTriggerVolumeEntry(GameObject hitObj) + { + OWRigidbody attachedOWRigidbody = hitObj.GetAttachedOWRigidbody(false); + if (attachedOWRigidbody != null) + { + if (attachedOWRigidbody.CompareTag("Player")) + { + this._playerInVolume = true; + return; + } + if (attachedOWRigidbody.CompareTag("Probe")) + { + this._probeInVolume = true; + } + } + } + + public override void OnTriggerVolumeExit(GameObject hitObj) + { + OWRigidbody attachedOWRigidbody = hitObj.GetAttachedOWRigidbody(false); + if (attachedOWRigidbody != null) + { + if (attachedOWRigidbody.CompareTag("Player")) + { + this._playerInVolume = false; + return; + } + if (attachedOWRigidbody.CompareTag("Probe")) + { + this._probeInVolume = false; + } + } + } + } +} diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index 54d8cf0e..cfcc731b 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -31,5 +31,17 @@ namespace NewHorizons.External.Configs /// If popupMessage is set, should it repeat every time the game starts or only once /// public bool repeatPopup; + + /// + /// These asset bundles will be loaded on the title screen and stay loaded. Will improve initial load time at the cost of increased memory use. + /// The path is the relative directory of the asset bundle in the mod folder. + /// + public string[] preloadAssetBundles; + + /// + /// The path to the addons subtitle for the main menu. + /// Defaults to "subtitle.png". + /// + public string subtitlePath = "subtitle.png"; } } diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index 40a9f7f5..28f00318 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -113,6 +113,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) /// @@ -213,8 +218,8 @@ namespace NewHorizons.External.Configs // Always have to have a base module if (Base == null) Base = new BaseModule(); if (Orbit == null) Orbit = new OrbitModule(); - if (ShipLog == null) ShipLog = new ShipLogModule(); if (ReferenceFrame == null) ReferenceFrame = new ReferenceFrameModule(); + if (MapMarker == null) MapMarker = new MapMarkerModule(); } public void Validate() @@ -308,6 +313,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) diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index 902c99bc..72613588 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; @@ -31,10 +32,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 +65,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 +96,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 /// @@ -117,6 +134,11 @@ namespace NewHorizons.External.Configs /// public string[] initialReveal; + /// + /// The planet to focus on when entering the ship log for the first time in a loop. If not set this will be the planet at navtigation position (1, 0) + /// + public string shipLogStartingPlanetID; + /// /// List colors of curiosity entries /// @@ -187,6 +209,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 { @@ -278,7 +339,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; @@ -297,6 +357,21 @@ namespace NewHorizons.External.Configs Vessel ??= otherConfig.Vessel; } + if (GlobalMusic != null && otherConfig.GlobalMusic != null) + { + GlobalMusic.travelAudio = string.IsNullOrEmpty(GlobalMusic.travelAudio) ? otherConfig.GlobalMusic.travelAudio : GlobalMusic.travelAudio; + GlobalMusic.endTimesAudio = string.IsNullOrEmpty(GlobalMusic.endTimesAudio) ? otherConfig.GlobalMusic.endTimesAudio : GlobalMusic.endTimesAudio; + GlobalMusic.endTimesDreamAudio = string.IsNullOrEmpty(GlobalMusic.endTimesDreamAudio) ? otherConfig.GlobalMusic.endTimesDreamAudio : GlobalMusic.endTimesDreamAudio; + GlobalMusic.brambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.brambleDimensionAudio) ? otherConfig.GlobalMusic.brambleDimensionAudio : GlobalMusic.brambleDimensionAudio; + GlobalMusic.finalEndTimesIntroAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesIntroAudio) ? otherConfig.GlobalMusic.finalEndTimesIntroAudio : GlobalMusic.finalEndTimesIntroAudio; + GlobalMusic.finalEndTimesLoopAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesLoopAudio) ? otherConfig.GlobalMusic.finalEndTimesLoopAudio : GlobalMusic.finalEndTimesLoopAudio; + GlobalMusic.finalEndTimesBrambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesBrambleDimensionAudio) ? otherConfig.GlobalMusic.finalEndTimesBrambleDimensionAudio : GlobalMusic.finalEndTimesBrambleDimensionAudio; + } + else + { + GlobalMusic ??= otherConfig.GlobalMusic; + } + entryPositions = Concatenate(entryPositions, otherConfig.entryPositions); curiosities = Concatenate(curiosities, otherConfig.curiosities); initialReveal = Concatenate(initialReveal, otherConfig.initialReveal); @@ -314,6 +389,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) @@ -348,6 +428,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/BaseModule.cs b/NewHorizons/External/Modules/BaseModule.cs index 1e1c28d1..78a41fee 100644 --- a/NewHorizons/External/Modules/BaseModule.cs +++ b/NewHorizons/External/Modules/BaseModule.cs @@ -36,11 +36,6 @@ namespace NewHorizons.External.Modules /// public float groundSize; - /// - /// If the body should have a marker on the map screen. - /// - public bool hasMapMarker; - /// /// Can this planet survive entering a star? /// @@ -108,6 +103,9 @@ namespace NewHorizons.External.Modules [Obsolete("AmbientLight is deprecated, please use AmbientLightModule instead")] public float ambientLight; + [Obsolete("HasMapMarker is deprecated, please use MapMarkerModule instead")] + public bool hasMapMarker; + [Obsolete("HasReferenceFrame is deprecated, please use ReferenceModule instead")] [DefaultValue(true)] public bool hasReferenceFrame = true; 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/Props/DetailInfo.cs b/NewHorizons/External/Modules/Props/DetailInfo.cs index d9654562..171f1b64 100644 --- a/NewHorizons/External/Modules/Props/DetailInfo.cs +++ b/NewHorizons/External/Modules/Props/DetailInfo.cs @@ -54,7 +54,9 @@ namespace NewHorizons.External.Modules.Props public string quantumGroupID; /// - /// Should this detail stay loaded even if you're outside the sector (good for very large props) + /// Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)? + /// Also makes this detail visible on the map. + /// Most logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided. /// 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 06207150..d3395938 100644 --- a/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs +++ b/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs @@ -31,6 +31,11 @@ namespace NewHorizons.External.Modules.Props.Dialogue /// public string pathToAnimController; + /// + /// If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here + /// + public string pathToExistingDialogue; + /// /// Radius of the spherical collision volume where you get the "talk to" prompt when looking at. If you use a /// remoteTrigger, you can set this to 0 to make the dialogue only trigger remotely. @@ -42,6 +47,16 @@ namespace NewHorizons.External.Modules.Props.Dialogue /// [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/ProjectionInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs index f56e2cac..ba6a98d4 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionInfo.cs @@ -21,6 +21,28 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye } + [JsonConverter(typeof(StringEnumConverter))] + public enum SlideReelType + { + [EnumMember(Value = @"sixSlides")] SixSlides = 6, + + [EnumMember(Value = @"sevenSlides")] SevenSlides = 7, + + [EnumMember(Value = @"eightSlides")] EightSlides = 8, + + [EnumMember(Value = @"whole")] Whole = 9, + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum SlideReelCondition + { + [EnumMember(Value = @"antique")] Antique = 0, + + [EnumMember(Value = @"pristine")] Pristine = 1, + + [EnumMember(Value = @"rusted")] Rusted = 2, + } + /// /// The ship log facts revealed after finishing this slide reel. /// @@ -43,6 +65,16 @@ 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; } } 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/Item/ItemInfo.cs b/NewHorizons/External/Modules/Props/Item/ItemInfo.cs index 6876bd4c..23253119 100644 --- a/NewHorizons/External/Modules/Props/Item/ItemInfo.cs +++ b/NewHorizons/External/Modules/Props/Item/ItemInfo.cs @@ -19,7 +19,7 @@ namespace NewHorizons.External.Modules.Props.Item /// public string name; /// - /// 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, WarpCode, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch). Defaults to the item name. + /// 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. /// public string itemType; /// @@ -27,6 +27,11 @@ namespace NewHorizons.External.Modules.Props.Item /// [DefaultValue(2f)] public float interactRange = 2f; /// + /// The radius that the added sphere collider will use for collision and hover detection. + /// If there's already a collider on the detail, you can make this 0. + /// + [DefaultValue(0.5f)] public float colliderRadius = 0.5f; + /// /// Whether the item can be dropped. Defaults to true. /// [DefaultValue(true)] public bool droppable = true; @@ -39,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/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/NewHorizonBody.cs b/NewHorizons/External/NewHorizonBody.cs index 590c5b91..6779e526 100644 --- a/NewHorizons/External/NewHorizonBody.cs +++ b/NewHorizons/External/NewHorizonBody.cs @@ -28,11 +28,20 @@ namespace NewHorizons.External public bool RequiresDLC() { - var detailPaths = Config.Props.details.Select(x => x.path); - return Config.Cloak != null - || Config.Props?.rafts != null - || Config.Props?.slideShows != null - || detailPaths.Any(x => x.StartsWith("Ringworld") || x.StartsWith("Dreamworld")); + try + { + var detailPaths = Config?.Props?.details?.Select(x => x.path) ?? Array.Empty(); + return Config?.Cloak != null + || Config?.Props?.rafts != null + || Config?.Props?.slideShows != null + || detailPaths.Any(x => x.StartsWith("RingWorld_Body") || x.StartsWith("DreamWorld_Body")); + } + catch + { + NHLogger.LogWarning($"Failed to check if {Mod.ModHelper.Manifest.Name} requires the DLC"); + return false; + } + } #region Cache diff --git a/NewHorizons/External/NewHorizonsData.cs b/NewHorizons/External/NewHorizonsData.cs index b691b622..cedb624a 100644 --- a/NewHorizons/External/NewHorizonsData.cs +++ b/NewHorizons/External/NewHorizonsData.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using NewHorizons.Builder.Props.Audio; using NewHorizons.Utility.OWML; namespace NewHorizons.External @@ -124,7 +126,6 @@ namespace NewHorizons.External if (!KnowsFrequency(frequency)) { _activeProfile.KnownFrequencies.Add(frequency); - Save(); } } @@ -134,13 +135,12 @@ namespace NewHorizons.External if (KnowsFrequency(frequency)) { _activeProfile.KnownFrequencies.Remove(frequency); - Save(); } } public static bool KnowsMultipleFrequencies() { - return _activeProfile != null && _activeProfile.KnownFrequencies.Count > 0; + return _activeProfile?.KnownFrequencies != null && _activeProfile.KnownFrequencies.Count(SignalBuilder.IsFrequencyInUse) > 1; } #endregion @@ -159,7 +159,6 @@ namespace NewHorizons.External if (!KnowsSignal(signal)) { _activeProfile.KnownSignals.Add(signal); - Save(); } } @@ -170,7 +169,6 @@ namespace NewHorizons.External public static void AddNewlyRevealedFactID(string id) { _activeProfile?.NewlyRevealedFactIDs.Add(id); - Save(); } public static List GetNewlyRevealedFactIDs() @@ -181,7 +179,6 @@ namespace NewHorizons.External public static void ClearNewlyRevealedFactIDs() { _activeProfile?.NewlyRevealedFactIDs.Clear(); - Save(); } #endregion @@ -190,8 +187,11 @@ namespace NewHorizons.External public static void ReadOneTimePopup(string id) { - _activeProfile?.PopupsRead.Add(id); - Save(); + // else it re-adds it each time + if (_activeProfile != null && !_activeProfile.PopupsRead.Contains(id)) + { + _activeProfile.PopupsRead.Add(id); + } } public static bool HasReadOneTimePopup(string id) @@ -208,7 +208,6 @@ namespace NewHorizons.External { if (name == CharacterDialogueTree.RECORDING_NAME || name == CharacterDialogueTree.SIGN_NAME) return; _activeProfile?.CharactersTalkedTo.SafeAdd(name); - Save(); } public static bool HasTalkedToFiveCharacters() diff --git a/NewHorizons/External/NewHorizonsSystem.cs b/NewHorizons/External/NewHorizonsSystem.cs index 0724a027..07f1ba08 100644 --- a/NewHorizons/External/NewHorizonsSystem.cs +++ b/NewHorizons/External/NewHorizonsSystem.cs @@ -1,6 +1,7 @@ using NewHorizons.External.Configs; using NewHorizons.External.Modules; using OWML.Common; +using System.Linq; namespace NewHorizons.External { @@ -19,6 +20,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 77c3eccc..0e4aadd6 100644 --- a/NewHorizons/External/SerializableData/MVector2.cs +++ b/NewHorizons/External/SerializableData/MVector2.cs @@ -1,6 +1,9 @@ #region +using NewHorizons.Utility.DebugTools.Menu; +using NewHorizons.Utility.OWML; using Newtonsoft.Json; +using System; using UnityEngine; #endregion @@ -26,6 +29,11 @@ namespace NewHorizons.External.SerializableData public static implicit operator Vector2(MVector2 vec) { + if (vec == null) + { + NHLogger.LogWarning($"Null MVector2 can't be turned into a non-nullable Vector2, returning Vector2.zero - {Environment.StackTrace}"); + return Vector2.zero; + } return new Vector2(vec.x, vec.y); } } diff --git a/NewHorizons/Handlers/EyeDetailCacher.cs b/NewHorizons/Handlers/EyeDetailCacher.cs new file mode 100644 index 00000000..62dfb460 --- /dev/null +++ b/NewHorizons/Handlers/EyeDetailCacher.cs @@ -0,0 +1,63 @@ +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?.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/FadeHandler.cs b/NewHorizons/Handlers/FadeHandler.cs index fbaa0b5b..f9d6d4ff 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)); @@ -17,11 +21,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/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index c23649a8..8ede325a 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -3,15 +3,19 @@ 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; @@ -345,6 +349,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; } @@ -367,12 +378,12 @@ 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); ao._rootSector = sector; @@ -448,7 +459,7 @@ 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); ao._rootSector = sector; @@ -460,7 +471,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); } @@ -791,7 +802,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; @@ -931,7 +942,7 @@ 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); + var distanceToCenter = go.transform.position.magnitude / (24000 / 30000f); if (distanceToCenter > SolarSystemRadius) { SolarSystemRadius = distanceToCenter; 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 22380665..dd4f4f27 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -115,6 +115,17 @@ namespace NewHorizons.Handlers } // For some reason none of this seems to apply to the Player. // If somebody ever makes a sound volume thats somehow always applying to the player tho then itd probably be this + + // Sometimes the ship isn't added to the volumes it's meant to now be in + foreach (var volume in SpawnPointBuilder.ShipSpawn.GetAttachedOWRigidbody().GetComponentsInChildren()) + { + if (volume.GetOWTriggerVolume().GetPenetrationDistance(ship.transform.position) > 0) + { + // Add ship to volume + // If it's already tracking it it will complain here but thats fine + volume.GetOWTriggerVolume().AddObjectToVolume(Locator.GetShipDetector()); + } + } } } else if (Main.Instance.CurrentStarSystem != "SolarSystem" && !Main.Instance.IsWarpingFromShip) @@ -126,30 +137,39 @@ 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); } - 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); 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/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs index b1ef9eea..d063abcc 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 /// @@ -113,21 +132,56 @@ namespace NewHorizons.Handlers else if (system.Equals("EyeOfTheUniverse")) canWarpTo = false; else if (config.Spawn?.shipSpawn != null) 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) + { Main.Instance.EnableWarpDrive(); - if (ShipLogStarChartMode != null) - ShipLogStarChartMode.AddSystemCard(systemUnlocked); + // Add all cards that now work + foreach (var starSystem in Main.SystemDict.Keys) + { + if (CanWarpToSystem(starSystem)) + { + ShipLogStarChartMode.AddSystemCard(starSystem); + } + } + } + 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(); + } + ShipLogStarChartMode?.AddSystemCard(systemUnlocked); } } diff --git a/NewHorizons/Handlers/SubtitlesHandler.cs b/NewHorizons/Handlers/SubtitlesHandler.cs index fc4bfe94..d3fe3fb8 100644 --- a/NewHorizons/Handlers/SubtitlesHandler.cs +++ b/NewHorizons/Handlers/SubtitlesHandler.cs @@ -11,11 +11,8 @@ namespace NewHorizons.Handlers { class SubtitlesHandler : MonoBehaviour { - public static int SUBTITLE_HEIGHT = 97; - public static int SUBTITLE_WIDTH = 669; // nice - - public Graphic graphic; - public Image image; + public static float SUBTITLE_HEIGHT = 97; + public static float SUBTITLE_WIDTH = 669; // nice public float fadeSpeed = 0.005f; public float fade = 1; @@ -29,13 +26,27 @@ namespace NewHorizons.Handlers public static readonly int PAUSE_TIMER_MAX = 50; public int pauseTimer = PAUSE_TIMER_MAX; + private Image _subtitleDisplay; + private Graphic _graphic; + + private static List<(IModBehaviour mod, string filePath)> _additionalSubtitles = new(); + + public static void RegisterAdditionalSubtitle(IModBehaviour mod, string filePath) + { + _additionalSubtitles.Add((mod, filePath)); + } + public void CheckForEOTE() { if (!eoteSubtitleHasBeenInserted) { if (Main.HasDLC) { - if (eoteSprite != null) possibleSubtitles.Insert(0, eoteSprite); // ensure that the Echoes of the Eye subtitle always appears first + if (eoteSprite != null) + { + // Don't make it appear first actually because we have mods to display! + possibleSubtitles.Add(eoteSprite); + } eoteSubtitleHasBeenInserted = true; } } @@ -43,27 +54,46 @@ namespace NewHorizons.Handlers public void Start() { + // We preserve the current image to add it to our custom subtitle + // We also need this element to preserve its size GetComponent().alpha = 1; - graphic = GetComponent(); - image = GetComponent(); - - graphic.enabled = true; - image.enabled = true; - + var image = GetComponent(); eoteSprite = image.sprite; + image.sprite = null; + image.enabled = false; + var layout = GetComponent(); + layout.minHeight = SUBTITLE_HEIGHT; CheckForEOTE(); - image.sprite = null; // Just in case. I don't know how not having the dlc changes the subtitle game object + // We add our subtitles as a child object so that their sizing doesnt shift the layout of the main menu + _subtitleDisplay = new GameObject("SubtitleDisplay").AddComponent(); + _subtitleDisplay.transform.parent = transform; + _subtitleDisplay.transform.localPosition = new Vector3(0, 0, 0); + _subtitleDisplay.transform.localScale = new Vector3(0.75f, 0.75f, 0.75f); + _graphic = _subtitleDisplay.gameObject.GetAddComponent(); + _subtitleDisplay.gameObject.GetAddComponent().minWidth = SUBTITLE_WIDTH; AddSubtitles(); } private void AddSubtitles() { - foreach (var mod in Main.MountedAddons.Where(mod => File.Exists($"{mod.ModHelper.Manifest.ModFolderPath}subtitle.png"))) + foreach (var mod in Main.MountedAddons) { - AddSubtitle(mod, "subtitle.png"); + if (Main.AddonConfigs.TryGetValue(mod, out var addonConfig) && File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, addonConfig.subtitlePath))) + { + AddSubtitle(mod, addonConfig.subtitlePath); + } + // Else default to subtitle.png + else if (File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "subtitle.png"))) + { + AddSubtitle(mod, "subtitle.png"); + } + } + foreach (var pair in _additionalSubtitles) + { + AddSubtitle(pair.mod, pair.filePath); } } @@ -74,7 +104,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, SUBTITLE_HEIGHT), new Vector2(0.5f, 0.5f), 100.0f); + 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); AddSubtitle(sprite); } @@ -87,12 +117,25 @@ namespace NewHorizons.Handlers { CheckForEOTE(); - if (possibleSubtitles.Count == 0) return; + if (possibleSubtitles.Count == 0) + { + return; + } - if (image.sprite == null) image.sprite = possibleSubtitles[0]; + _subtitleDisplay.transform.localPosition = new Vector3(0, -36, 0); + + if (_subtitleDisplay.sprite == null) + { + _subtitleDisplay.sprite = possibleSubtitles[0]; + // Always call this in case we stop changing subtitles after + ChangeSubtitle(); + } // don't fade transition subtitles if there's only one subtitle - if (possibleSubtitles.Count <= 1) return; + if (possibleSubtitles.Count <= 1) + { + return; + } if (pauseTimer > 0) { @@ -103,7 +146,7 @@ namespace NewHorizons.Handlers if (fadingAway) { fade -= fadeSpeed; - + if (fade <= 0) { fade = 0; @@ -114,7 +157,7 @@ namespace NewHorizons.Handlers else { fade += fadeSpeed; - + if (fade >= 1) { fade = 1; @@ -123,14 +166,19 @@ namespace NewHorizons.Handlers } } - graphic.color = new Color(1, 1, 1, fade); + _graphic.color = new Color(1, 1, 1, fade); } public void ChangeSubtitle() { subtitleIndex = (subtitleIndex + 1) % possibleSubtitles.Count; - - image.sprite = possibleSubtitles[subtitleIndex]; + + var subtitle = possibleSubtitles[subtitleIndex]; + _subtitleDisplay.sprite = subtitle; + var width = subtitle.texture.width; + var height = subtitle.texture.height; + var ratio = SUBTITLE_WIDTH / width; // one of these needs to be a float so that compiler doesn't think "oh 2 integers! let's round to nearest whole" + _subtitleDisplay.rectTransform.sizeDelta = new Vector2(width, height) * ratio; } } } diff --git a/NewHorizons/Handlers/SystemCreationHandler.cs b/NewHorizons/Handlers/SystemCreationHandler.cs index debd14e7..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 { @@ -34,7 +36,8 @@ namespace NewHorizons.Handlers if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") return; // Small mod compat change for StopTime - do nothing if it's enabled - if (system.Config.enableTimeLoop && !OtherModUtil.IsEnabled("_nebula.StopTime")) + // Do not add our custom time loop controller in the base game system: It will handle itself + if (Main.Instance.CurrentStarSystem != "SolarSystem" && system.Config.enableTimeLoop && !OtherModUtil.IsEnabled("_nebula.StopTime")) { var timeLoopController = new GameObject("TimeLoopController"); timeLoopController.AddComponent(); @@ -45,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 2db44131..43f787a0 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -1,8 +1,12 @@ 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 { @@ -48,23 +52,48 @@ namespace NewHorizons.Handlers } // Get the translated text - if (dictionary.TryGetValue(language, out var table)) - if (table.TryGetValue(text, out var translatedText)) - return translatedText; + if (TryGetTranslatedText(dictionary, language, text, out var translatedText)) + { + return translatedText; + } - if (warn) NHLogger.LogVerbose($"Defaulting to english for {text}"); + 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 (TryGetTranslatedText(dictionary, Language.ENGLISH, text, out translatedText)) + { + return translatedText; + } - if (warn) NHLogger.LogVerbose($"Defaulting to key for {text}"); + if (warn) + { + NHLogger.LogVerbose($"Defaulting to key for {text}"); + } - // Default to the key 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 true; + } + // Try without whitespace if its missing + else if (table.TryGetValue(text.TruncateWhitespaceAndToLower(), out translatedText)) + { + return true; + } + } + + translatedText = null; + return false; + } + public static void RegisterTranslation(TextTranslation.Language language, TranslationConfig config) { if (config.ShipLogDictionary != null && config.ShipLogDictionary.Count > 0) @@ -85,8 +114,11 @@ namespace NewHorizons.Handlers if (!_dialogueTranslationDictionary.ContainsKey(language)) _dialogueTranslationDictionary.Add(language, new Dictionary()); foreach (var originalKey in config.DialogueDictionary.Keys) { - var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("", ""); - var value = config.DialogueDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("", ""); + // Fix new lines in dialogue translations, remove whitespace from keys else if the dialogue has weird whitespace and line breaks it gets really annoying + // to write translation keys for (can't just copy paste out of xml, have to start adding \\n and \\r and stuff + // If any of these issues become relevant to other dictionaries we can bring this code over, but for now why fix what isnt broke + var key = originalKey.Replace("\\n", "\n").Replace("<", "<").Replace(">", ">").Replace("", "").TruncateWhitespaceAndToLower(); + var value = config.DialogueDictionary[originalKey].Replace("\\n", "\n").Replace("<", "<").Replace(">", ">").Replace("", ""); if (!_dialogueTranslationDictionary[language].ContainsKey(key)) _dialogueTranslationDictionary[language].Add(key, value); else _dialogueTranslationDictionary[language][key] = value; @@ -121,29 +153,63 @@ namespace NewHorizons.Handlers } } + public static (string, string) FixKeyValue(string key, string value) + { + key = key.Replace("<", "<").Replace(">", ">").Replace("", ""); + value = value.Replace("<", "<").Replace(">", ">").Replace("", ""); + + return (key, value); + } + public static void AddDialogue(string rawText, bool trimRawTextForKey = false, params string[] rawPreText) { var key = string.Join(string.Empty, rawPreText) + (trimRawTextForKey? rawText.Trim() : rawText); - var text = GetTranslation(rawText, TextType.DIALOGUE); + var value = GetTranslation(rawText, TextType.DIALOGUE); - TextTranslation.Get().m_table.Insert(key, text); + // Manually insert directly into the dictionary, otherwise it logs errors about duplicates but we want to allow replacing + (key, value) = FixKeyValue(key, value); + + 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; - string text = GetTranslation(rawText, TextType.SHIPLOG); + string value = GetTranslation(rawText, TextType.SHIPLOG); - TextTranslation.Get().m_table.InsertShipLog(key, text); + // Manually insert directly into the dictionary, otherwise it logs errors about duplicates but we want to allow replacing + (key, value) = FixKeyValue(key, value); + + TextTranslation.Get().m_table.theShipLogTable[key] = value; } public static int AddUI(string rawText) { 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 @@ -154,7 +220,7 @@ namespace NewHorizons.Handlers } catch (Exception) { } - TextTranslation.Get().m_table.Insert_UI(key, text); + TextTranslation.Get().m_table.theUITable[key] = text; return key; } diff --git a/NewHorizons/Handlers/VesselWarpHandler.cs b/NewHorizons/Handlers/VesselWarpHandler.cs index 12859c3a..26195e5c 100644 --- a/NewHorizons/Handlers/VesselWarpHandler.cs +++ b/NewHorizons/Handlers/VesselWarpHandler.cs @@ -41,7 +41,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() @@ -56,18 +56,51 @@ 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();//Delay.FireOnNextUpdate(vesselSpawnPoint.WarpPlayer); + } + else + { + NHLogger.LogVerbose("Debug warping into vessel"); + playerSpawner.DebugWarp(_vesselSpawnPoint); + } Builder.General.SpawnPointBuilder.SuitUp(); + LoadDB(); + } + + 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 +114,7 @@ namespace NewHorizons.Handlers } } - public static EyeSpawnPoint CreateVessel() + public static VesselSpawnPoint CreateVessel() { var system = SystemDict[Instance.CurrentStarSystem]; @@ -131,16 +164,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 +205,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 +237,22 @@ namespace NewHorizons.Handlers } } - EyeSpawnPoint eyeSpawnPoint = vesselObject.GetComponentInChildren(true); + VesselSpawnPoint spawnPoint = vesselObject.GetComponentInChildren(true); if (ShouldSpawnAtVessel()) { - system.SpawnPoint = eyeSpawnPoint; + system.SpawnPoint = spawnPoint; } vesselObject.SetActive(true); - Delay.FireOnNextUpdate(() => SetupWarpController(vesselWarpController)); + var power = vesselWarpController.transform.Find("PowerSwitchInterface"); + var orb = power.GetComponentInChildren(true); + Delay.FireOnNextUpdate(() => SetupWarpController(vesselWarpController, orb)); - return eyeSpawnPoint; + return spawnPoint; } - public static SpawnPoint UpdateVessel() + public static VesselSpawnPoint UpdateVessel() { var system = SystemDict[Instance.CurrentStarSystem]; @@ -207,7 +260,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 +272,29 @@ 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); + Delay.FireOnNextUpdate(() => SetupWarpController(vesselWarpController, orb, true)); + + 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 +329,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; + Delay.RunWhen(() => !vesselWarpController._coordinateInterface._pillarRaised, () => 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 22132568..71337802 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; @@ -207,5 +206,13 @@ namespace NewHorizons /// string GetTranslationForOtherText(string text); #endregion + + /// + /// Registers a subtitle for the main menu. + /// Call this once before the main menu finishes loading + /// + /// + /// + void AddSubtitle(IModBehaviour mod, string filePath); } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 92509469..b9d0ecbb 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -46,19 +46,21 @@ namespace NewHorizons // Settings public static bool Debug { 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 Dictionary(); - public static Dictionary> BodyDict = new Dictionary>(); - public static List MountedAddons = new List(); + public static Dictionary SystemDict = new(); + public static Dictionary> BodyDict = new(); + public static List MountedAddons = new(); + public static Dictionary AddonConfigs = new(); public static float SecondsElapsedInLoop = -1; 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 @@ -109,7 +111,12 @@ namespace NewHorizons public StarSystemEvent OnStarSystemLoaded = new(); public StarSystemEvent OnPlanetLoaded = new(); - public static bool HasDLC { get => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned; } + /// + /// Depending on platform, the AsyncOwnershipStatus might not be ready by the time we go to check it. + /// If that happens, I guess we just have to assume they do own the DLC. + /// Better to false positive than false negative and annoy people every time they launch the game when they do own the DLC + /// + public static bool HasDLC { get => EntitlementsManager.IsDlcOwned() != EntitlementsManager.AsyncOwnershipStatus.NotOwned; } public static StarSystemConfig GetCurrentSystemConfig => SystemDict[Instance.CurrentStarSystem].Config; @@ -124,8 +131,9 @@ namespace NewHorizons var currentScene = SceneManager.GetActiveScene().name; - Debug = config.GetSettingsValue("Debug"); - VerboseLogs = config.GetSettingsValue("Verbose Logs"); + Debug = config.GetSettingsValue(nameof(Debug)); + VerboseLogs = config.GetSettingsValue(nameof(VerboseLogs)); + SequentialPreCaching = config.GetSettingsValue(nameof(SequentialPreCaching)); if (currentScene == "SolarSystem") { @@ -137,19 +145,19 @@ namespace NewHorizons 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); @@ -186,7 +194,7 @@ namespace NewHorizons Config = { destroyStockPlanets = false, - factRequiredForWarp = "OPC_EYE_COORDINATES_X1", + //factRequiredForWarp = "OPC_EYE_COORDINATES_X1", Vessel = new StarSystemConfig.VesselModule() { coords = new StarSystemConfig.NomaiCoordinates @@ -195,7 +203,8 @@ namespace NewHorizons y = new int[4] { 3, 0, 1, 4 }, z = new int[6] { 1, 2, 3, 0, 5, 4 } } - } + }, + canEnterViaWarpDrive = false } }; @@ -220,6 +229,13 @@ 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; @@ -256,7 +272,6 @@ namespace NewHorizons // Call this from the menu since we hadn't hooked onto the event yet Delay.FireOnNextUpdate(() => OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single)); Delay.FireOnNextUpdate(() => _firstLoad = false); - Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu; MenuHandler.Init(); AchievementHandler.Init(); @@ -268,6 +283,13 @@ namespace NewHorizons LoadAddonManifest("Assets/addon-manifest.json", this); } + public override void SetupPauseMenu(IPauseMenuManager pauseMenu) + { + base.SetupPauseMenu(pauseMenu); + DebugReload.InitializePauseMenu(pauseMenu); + DebugMenu.InitializePauseMenu(pauseMenu); + } + public void OnDestroy() { NHLogger.Log($"Destroying NewHorizons"); @@ -321,6 +343,8 @@ namespace NewHorizons { try { + EyeDetailCacher.Init(); + AtmosphereBuilder.InitPrefabs(); BrambleDimensionBuilder.InitPrefabs(); BrambleNodeBuilder.InitPrefabs(); @@ -374,9 +398,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; } @@ -404,7 +430,7 @@ namespace NewHorizons IsChangingStarSystem = false; - if (isTitleScreen && _useCustomTitleScreen) + if (isTitleScreen && CustomTitleScreen) { try { @@ -505,10 +531,14 @@ namespace NewHorizons // We are in a custom system on the first loop -> The time loop isn't active, that's not very good // TimeLoop uses the launch codes condition to know if the loop is active or not + // We also skip them to loop 2, else if they enter a credits volume in this loop they get reset if (CurrentStarSystem != "SolarSystem" && PlayerData.LoadLoopCount() == 1) { + PlayerData.SaveLoopCount(2); PlayerData.SetPersistentCondition("LAUNCH_CODES_GIVEN", true); } + + if (shouldWarpInFromVessel) VesselWarpHandler.LoadDB(); } else if (isEyeOfTheUniverse) { @@ -588,6 +618,7 @@ namespace NewHorizons Locator.GetPlayerBody().gameObject.AddComponent(); Locator.GetPlayerBody().gameObject.AddComponent(); Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); @@ -611,6 +642,39 @@ 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) @@ -630,8 +694,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 @@ -734,6 +815,12 @@ namespace NewHorizons var addonConfig = mod.ModHelper.Storage.Load(file, false); + if (addonConfig == null) + { + NHLogger.LogError($"Addon manifest for {mod.ModHelper.Manifest.Name} could not load, check your JSON"); + return; + } + if (addonConfig.achievements != null) { AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); @@ -747,6 +834,15 @@ namespace NewHorizons { MenuHandler.RegisterOneTimePopup(mod, TranslationHandler.GetTranslation(addonConfig.popupMessage, TranslationHandler.TextType.UI), addonConfig.repeatPopup); } + if (addonConfig.preloadAssetBundles != null) + { + foreach (var bundle in addonConfig.preloadAssetBundles) + { + AssetBundleUtilities.PreloadBundle(bundle, mod); + } + } + + AddonConfigs[mod] = addonConfig; } private void LoadTranslations(string folder, IModBehaviour mod) @@ -756,7 +852,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))) { @@ -839,6 +935,10 @@ namespace NewHorizons { _defaultStarSystem = defaultSystem; } + if (LoadManager.GetCurrentScene() != OWScene.SolarSystem && LoadManager.GetCurrentScene() != OWScene.EyeOfTheUniverse) + { + CurrentStarSystem = _defaultStarSystem; + } } #endregion Load @@ -876,6 +976,7 @@ namespace NewHorizons IsWarpingFromVessel = vessel; DidWarpFromVessel = false; OnChangeStarSystem?.Invoke(newStarSystem); + VesselWarpController.s_relativeLocationSaved = false; NHLogger.Log($"Warping to {newStarSystem}"); if (warp && ShipWarpController) ShipWarpController.WarpOut(); @@ -891,7 +992,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; @@ -909,13 +1011,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) { @@ -927,13 +1060,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?.shipSpawn != null)) { - // #738 - Sometimes the override will not support spawning regularly, so always warp in if possible IsWarpingFromShip = true; } else @@ -944,9 +1081,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 c1e195b3..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 e5507abc..6c46ac3c 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -28,7 +28,6 @@ using static NewHorizons.External.Modules.ShipLogModule; namespace NewHorizons { - public class NewHorizonsApi : INewHorizons { [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] @@ -144,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) @@ -337,5 +338,7 @@ namespace NewHorizons public string GetTranslationForUI(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.UI); public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); + + public void AddSubtitle(IModBehaviour mod, string filePath) => SubtitlesHandler.RegisterAdditionalSubtitle(mod, filePath); } } diff --git a/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs b/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs deleted file mode 100644 index f44aecdf..00000000 --- a/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -namespace NewHorizons.OtherMods.MenuFramework -{ - public interface IMenuAPI - { - GameObject TitleScreen_MakeMenuOpenButton(string name, int index, Menu menuToOpen); - GameObject TitleScreen_MakeSceneLoadButton(string name, int index, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); - Button TitleScreen_MakeSimpleButton(string name, int index); - GameObject PauseMenu_MakeMenuOpenButton(string name, Menu menuToOpen, Menu customMenu = null); - GameObject PauseMenu_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null, Menu customMenu = null); - Button PauseMenu_MakeSimpleButton(string name, Menu customMenu = null); - Menu PauseMenu_MakePauseListMenu(string title); - PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); - PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); - PopupMenu MakeInfoPopup(string message, string continueButtonText); - void RegisterStartupPopup(string message); - } -} diff --git a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs index 101a0789..1a54fc46 100644 --- a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs +++ b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs @@ -11,15 +11,11 @@ namespace NewHorizons.OtherMods.MenuFramework { public static class MenuHandler { - private static IMenuAPI _menuApi; - private static List<(IModBehaviour mod, string message, bool repeat)> _registeredPopups = new(); private static List _failedFiles = new(); public static void Init() { - _menuApi = Main.Instance.ModHelper.Interaction.TryGetModApi("_nebula.MenuFramework"); - TextTranslation.Get().OnLanguageChanged += OnLanguageChanged; } @@ -35,14 +31,14 @@ namespace NewHorizons.OtherMods.MenuFramework Application.version); NHLogger.LogError(warning); - _menuApi.RegisterStartupPopup(warning); + Main.Instance.ModHelper.MenuHelper.PopupMenuManager.RegisterStartupPopup(warning); } foreach(var (mod, message, repeat) in _registeredPopups) { if (repeat || !NewHorizonsData.HasReadOneTimePopup(mod.ModHelper.Manifest.UniqueName)) { - _menuApi.RegisterStartupPopup(TranslationHandler.GetTranslation(message, TranslationHandler.TextType.UI)); + Main.Instance.ModHelper.MenuHelper.PopupMenuManager.RegisterStartupPopup(TranslationHandler.GetTranslation(message, TranslationHandler.TextType.UI)); NewHorizonsData.ReadOneTimePopup(mod.ModHelper.Manifest.UniqueName); } } @@ -52,7 +48,7 @@ namespace NewHorizons.OtherMods.MenuFramework var message = TranslationHandler.GetTranslation("JSON_FAILED_TO_LOAD", TranslationHandler.TextType.UI); var mods = string.Join(",", _failedFiles.Take(10)); if (_failedFiles.Count > 10) mods += "..."; - _menuApi.RegisterStartupPopup(string.Format(message, mods)); + Main.Instance.ModHelper.MenuHelper.PopupMenuManager.RegisterStartupPopup(string.Format(message, mods)); } _registeredPopups.Clear(); diff --git a/NewHorizons/Patches/BrambleProjectionFixPatches.cs b/NewHorizons/Patches/BrambleProjectionFixPatches.cs new file mode 100644 index 00000000..9d41a251 --- /dev/null +++ b/NewHorizons/Patches/BrambleProjectionFixPatches.cs @@ -0,0 +1,26 @@ +using HarmonyLib; + +namespace NewHorizons.Patches; + +/// +/// Bug fix from the Outsider +/// +[HarmonyPatch] +internal class BrambleProjectionFixPatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(FogWarpVolume), nameof(FogWarpVolume.WarpDetector))] + public static bool FogWarpVolume_WarpDetector() + { + // Do not warp the player if they have entered the fog via a projection + return !PlayerState.UsingNomaiRemoteCamera(); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(FogWarpDetector), nameof(FogWarpDetector.FixedUpdate))] + public static bool FogWarpDetector_FixedUpdate() + { + // Do not warp the player if they have entered the fog via a projection + return !PlayerState.UsingNomaiRemoteCamera(); + } +} 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/DialoguePatches/RemoteDialogueTriggerPatches.cs b/NewHorizons/Patches/DialoguePatches/RemoteDialogueTriggerPatches.cs index 330e578e..c01240f2 100644 --- a/NewHorizons/Patches/DialoguePatches/RemoteDialogueTriggerPatches.cs +++ b/NewHorizons/Patches/DialoguePatches/RemoteDialogueTriggerPatches.cs @@ -1,4 +1,6 @@ using HarmonyLib; +using System.Collections; +using UnityEngine.InputSystem; namespace NewHorizons.Patches.DialoguePatches { @@ -7,6 +9,29 @@ namespace NewHorizons.Patches.DialoguePatches { private static bool _wasLastDialogueInactive = false; + [HarmonyPostfix] + [HarmonyPatch(nameof(RemoteDialogueTrigger.Awake))] + public static void RemoteDialogueTrigger_Awake(RemoteDialogueTrigger __instance) + { + // Wait for player to be up and moving before allowing them to trigger remote dialogue + // Stops you getting locked into dialogue while waking up + if (OWInput.GetInputMode() != InputMode.Character) + { + __instance._collider.enabled = false; + __instance.StartCoroutine(AwakeCoroutine(__instance)); + } + } + + private static IEnumerator AwakeCoroutine(RemoteDialogueTrigger instance) + { + while (OWInput.GetInputMode() != InputMode.Character) + { + yield return null; + } + + instance._collider.enabled = true; + } + /// /// Should fix a bug where disabled a CharacterDialogueTree makes its related RemoteDialogueTriggers softlock your game /// diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs index a0b40a99..e615c201 100644 --- a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs @@ -63,7 +63,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); diff --git a/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs b/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs index 9feba169..931389f0 100644 --- a/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs +++ b/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs @@ -1,4 +1,6 @@ +using Autodesk.Fbx; using HarmonyLib; +using NewHorizons.Utility.Files; using NewHorizons.Utility.OWML; namespace NewHorizons.Patches.EyeScenePatches @@ -6,10 +8,18 @@ namespace NewHorizons.Patches.EyeScenePatches [HarmonyPatch(typeof(SubmitActionLoadScene))] public static class SubmitActionLoadScenePatches { + // To call the base method + [HarmonyReversePatch] + [HarmonyPatch(typeof(SubmitActionConfirm), nameof(SubmitActionConfirm.ConfirmSubmit))] + public static void SubmitActionConfirm_ConfirmSubmit(SubmitActionConfirm instance) { } + + [HarmonyPrefix] [HarmonyPatch(nameof(SubmitActionLoadScene.ConfirmSubmit))] - public static void SubmitActionLoadScene_ConfirmSubmit(SubmitActionLoadScene __instance) + public static bool SubmitActionLoadScene_ConfirmSubmit(SubmitActionLoadScene __instance) { + if (__instance._receivedSubmitAction) return false; + // Title screen can warp you to eye and cause problems. if (__instance._sceneToLoad == SubmitActionLoadScene.LoadableScenes.EYE) { @@ -17,6 +27,47 @@ namespace NewHorizons.Patches.EyeScenePatches Main.Instance.IsWarpingBackToEye = true; __instance._sceneToLoad = SubmitActionLoadScene.LoadableScenes.GAME; } + + // Don't bother going through this stuff if we don't have to + if (AssetBundleUtilities.AreRequiredAssetsLoaded()) return true; + + // modified from patched function + SubmitActionConfirm_ConfirmSubmit(__instance); + __instance._receivedSubmitAction = true; + Locator.GetMenuInputModule().DisableInputs(); + + Delay.RunWhen(() => + { + // update text. just use 0% + __instance.ResetStringBuilder(); + __instance._nowLoadingSB.Append(UITextLibrary.GetString(UITextType.LoadingMessage)); + __instance._nowLoadingSB.Append(0.ToString("P0")); + __instance._loadingText.text = __instance._nowLoadingSB.ToString(); + + return AssetBundleUtilities.AreRequiredAssetsLoaded(); + }, () => + { + switch (__instance._sceneToLoad) + { + case SubmitActionLoadScene.LoadableScenes.GAME: + LoadManager.LoadSceneAsync(OWScene.SolarSystem, false, LoadManager.FadeType.ToBlack, 1f, false); + __instance.ResetStringBuilder(); + __instance._waitingOnStreaming = true; + break; + case SubmitActionLoadScene.LoadableScenes.EYE: + LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, true, LoadManager.FadeType.ToBlack, 1f, false); + __instance.ResetStringBuilder(); + break; + case SubmitActionLoadScene.LoadableScenes.TITLE: + LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f, true); + break; + case SubmitActionLoadScene.LoadableScenes.CREDITS: + LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack, 1f, false); + break; + } + }); + + 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/ProbeHUDMarkerPatches.cs b/NewHorizons/Patches/HUDPatches/ProbeHUDMarkerPatches.cs index 15da0b12..b0c31daa 100644 --- a/NewHorizons/Patches/HUDPatches/ProbeHUDMarkerPatches.cs +++ b/NewHorizons/Patches/HUDPatches/ProbeHUDMarkerPatches.cs @@ -1,6 +1,7 @@ using HarmonyLib; using NewHorizons.Components.Sectored; using NewHorizons.Handlers; +using NewHorizons.Utility.OWML; namespace NewHorizons.Patches.HUDPatches { @@ -27,15 +28,21 @@ namespace NewHorizons.Patches.HUDPatches [HarmonyPatch(nameof(ProbeHUDMarker.RefreshOwnVisibility))] public static bool ProbeHUDMarker_RefreshOwnVisibility(ProbeHUDMarker __instance) { + // Probe marker seems to never appear in the eye or QM in base game (inside eye being past the vortex) ?? at least thats what its code implies bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsProbeInside()); - bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside == Locator.GetRingWorldController().isProbeInside; - bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isProbeInsideCloak; - bool insideCloak = CloakSectorController.isPlayerInside == CloakSectorController.isProbeInside; + + // Either the controllers wtv are null or the player and probe state are the same + bool sameRW = Locator.GetRingWorldController() == null || Locator.GetRingWorldController().isPlayerInside == Locator.GetRingWorldController().isProbeInside; + bool sameIP = Locator.GetCloakFieldController() == null || Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isProbeInsideCloak; + bool sameCloak = CloakSectorController.isPlayerInside == CloakSectorController.isProbeInside; bool sameInterference = InterferenceHandler.IsPlayerSameAsProbe(); + bool isActive = __instance.gameObject.activeInHierarchy || __instance._isTLCDuplicate; - __instance._isVisible = isActive && !insideEYE && !insideQM && !__instance._translatorEquipped && !__instance._inConversation && __instance._launched && (__instance._isWearingHelmet || __instance._atFlightConsole) && insideRW && insideIP && insideCloak && sameInterference; + __instance._isVisible = isActive && !insideEYE && !insideQM && !__instance._translatorEquipped + && !__instance._inConversation && __instance._launched && (__instance._isWearingHelmet || __instance._atFlightConsole) + && sameRW && sameIP && sameCloak && sameInterference; if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); 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/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/PlayerPatches/PlayerDataPatches.cs b/NewHorizons/Patches/PlayerPatches/PlayerDataPatches.cs index 16056c7b..6785a8a2 100644 --- a/NewHorizons/Patches/PlayerPatches/PlayerDataPatches.cs +++ b/NewHorizons/Patches/PlayerPatches/PlayerDataPatches.cs @@ -85,12 +85,8 @@ namespace NewHorizons.Patches.PlayerPatches [HarmonyPatch(nameof(PlayerData.KnowsMultipleFrequencies))] public static bool PlayerData_KnowsMultipleFrequencies(ref bool __result) { - if (NewHorizonsData.KnowsMultipleFrequencies()) - { - __result = true; - return false; - } - return true; + __result = NewHorizonsData.KnowsMultipleFrequencies(); + return false; } [HarmonyPrefix] @@ -140,5 +136,12 @@ namespace NewHorizons.Patches.PlayerPatches { NewHorizonsData.Reset(); } + + [HarmonyPostfix] + [HarmonyPatch(nameof(PlayerData.SaveCurrentGame))] + public static void PlayerData_SaveCurrentGame() + { + NewHorizonsData.Save(); + } } } diff --git a/NewHorizons/Patches/RigidbodyPatches.cs b/NewHorizons/Patches/RigidbodyPatches.cs index c0e8d3e3..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; @@ -20,6 +20,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.Awake))] private static bool Awake(OWRigidbody __instance) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return true; + } + __instance._transform = __instance.transform; if (!__instance._scaleRoot) @@ -63,6 +69,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.Start))] private static void Start(OWRigidbody __instance) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return; + } + if (_setParentQueue.TryGetValue(__instance, out var parent)) { __instance._transform.parent = parent; @@ -74,6 +86,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.OnDestroy))] private static void OnDestroy(OWRigidbody __instance) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return; + } + _setParentQueue.Remove(__instance); } @@ -81,6 +99,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.Suspend), typeof(Transform), typeof(OWRigidbody))] private static bool Suspend(OWRigidbody __instance, Transform suspensionParent, OWRigidbody suspensionBody) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return true; + } + if (!__instance._suspended || __instance._unsuspendNextUpdate) { __instance._suspensionBody = suspensionBody; @@ -133,6 +157,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.ChangeSuspensionBody))] private static bool ChangeSuspensionBody(OWRigidbody __instance, OWRigidbody newSuspensionBody) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return true; + } + if (__instance._suspended) { __instance._cachedRelativeVelocity = Vector3.zero; @@ -154,6 +184,12 @@ public static class OWRigidbodyPatches [HarmonyPatch(nameof(OWRigidbody.UnsuspendImmediate))] private static bool UnsuspendImmediate(OWRigidbody __instance, bool restoreCachedVelocity) { + if (Main.Instance.ModHelper.Interaction.ModExists("Raicuparta.QuantumSpaceBuddies")) + { + // QSB already does all this, so don't run it again if it's installed. + return true; + } + if (__instance._suspended) { if (__instance.RunningKinematicSimulation()) diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs index ba3c0e33..abfc67a8 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogAstroObjectPatches.cs @@ -25,9 +25,32 @@ namespace NewHorizons.Patches.ShipLogPatches } } + [HarmonyPrefix] + [HarmonyPatch(nameof(ShipLogAstroObject.UpdateState))] + public static bool ShipLogAstroObject_UpdateState_Pre(ShipLogAstroObject __instance) + { + // 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) + { + __instance._state = ShipLogEntry.State.Explored; + __instance._imageObj.SetActive(true); + __instance._outlineObj?.SetActive(false); + if (__instance._image != null) + { + __instance.SetMaterialGreyscale(false); + __instance._image.color = Color.white; + } + + return false; + } + + return true; + } + [HarmonyPostfix] [HarmonyPatch(nameof(ShipLogAstroObject.UpdateState))] - public static void ShipLogAstroObject_UpdateState(ShipLogAstroObject __instance) + public static void ShipLogAstroObject_UpdateState_Post(ShipLogAstroObject __instance) { Transform detailsParent = __instance.transform.Find("Details"); if (detailsParent != null) diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs index 12a37e8e..b76d61bb 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogManagerPatches.cs @@ -123,6 +123,9 @@ namespace NewHorizons.Patches.ShipLogPatches else { EntryLocationBuilder.InitializeLocations(); + // Start method disables the ShipLogManager + // Else it thinks its meant to be waiting to post a SHIP LOG UPDATED notif (and then does so) #779 + __instance.enabled = false; return false; } } diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs index 8f70fbfe..e6fd50ad 100644 --- a/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogMapModePatches.cs @@ -26,7 +26,7 @@ namespace NewHorizons.Patches.ShipLogPatches else { __instance._astroObjects = navMatrix; - __instance._startingAstroObjectID = navMatrix[1][0].GetID(); + __instance._startingAstroObjectID = Main.SystemDict[Main.Instance.CurrentStarSystem].Config.shipLogStartingPlanetID ?? navMatrix[1][0].GetID(); if (Main.Instance.CurrentStarSystem != "SolarSystem") { List delete = panRoot.GetAllChildren().Where(g => g.name.Contains("_ShipLog") == false).ToList(); 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/SignalscopePatches.cs b/NewHorizons/Patches/SignalPatches/SignalscopePatches.cs index 12c54db5..2b91dc72 100644 --- a/NewHorizons/Patches/SignalPatches/SignalscopePatches.cs +++ b/NewHorizons/Patches/SignalPatches/SignalscopePatches.cs @@ -1,5 +1,6 @@ using HarmonyLib; using NewHorizons.Builder.Props.Audio; +using NewHorizons.Utility.OWML; namespace NewHorizons.Patches.SignalPatches { @@ -19,13 +20,18 @@ namespace NewHorizons.Patches.SignalPatches { var count = SignalBuilder.NumberOfFrequencies; __instance._frequencyFilterIndex += increment; + // Base game does 1 here but we use frequency index 0 as "default" or "???" __instance._frequencyFilterIndex = __instance._frequencyFilterIndex >= count ? 0 : __instance._frequencyFilterIndex; __instance._frequencyFilterIndex = __instance._frequencyFilterIndex < 0 ? count - 1 : __instance._frequencyFilterIndex; var signalFrequency = AudioSignal.IndexToFrequency(__instance._frequencyFilterIndex); + NHLogger.Log($"Changed freq to {signalFrequency} at {__instance._frequencyFilterIndex}"); + // Skip over this frequency - var isUnknown = !PlayerData.KnowsFrequency(signalFrequency) && !(__instance._isUnknownFreqNearby && __instance._unknownFrequency == signalFrequency); - if (isUnknown || !SignalBuilder.IsFrequencyInUse(signalFrequency)) + // Never skip traveler (always known) + var isTraveler = __instance._frequencyFilterIndex == 1; + var isUnknown = !PlayerData.KnowsFrequency(signalFrequency) && (!__instance._isUnknownFreqNearby || __instance._unknownFrequency != signalFrequency); + if (!isTraveler && (isUnknown || !SignalBuilder.IsFrequencyInUse(signalFrequency))) { __instance.SwitchFrequencyFilter(increment); } 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/VolumePatches/DestructionVolumePatches.cs b/NewHorizons/Patches/VolumePatches/DestructionVolumePatches.cs index 0dd86a57..3091d1fd 100644 --- a/NewHorizons/Patches/VolumePatches/DestructionVolumePatches.cs +++ b/NewHorizons/Patches/VolumePatches/DestructionVolumePatches.cs @@ -26,5 +26,25 @@ namespace NewHorizons.Patches.VolumePatches return false; } + + /// + /// This method detects Nomai shuttles that are inactive + /// When active, it swaps the position of the NomaiShuttleController and the Rigidbody, so its not found as a child here and explodes continuously forever + /// Just ignore the shuttle if its inactive + /// + [HarmonyPrefix] + [HarmonyPatch(nameof(DestructionVolume.VanishNomaiShuttle))] + public static bool DestructionVolume_VanishNomaiShuttle(DestructionVolume __instance, OWRigidbody shuttleBody, RelativeLocationData entryLocation) + { + if (shuttleBody.GetComponentInChildren() == null) + { + if (__instance._nomaiShuttleBody == shuttleBody) + { + __instance._nomaiShuttleBody = null; + } + return false; + } + return true; + } } } diff --git a/NewHorizons/Patches/VolumePatches/FogWarpVolumePatches.cs b/NewHorizons/Patches/VolumePatches/FogWarpVolumePatches.cs index f63bad81..41f3e652 100644 --- a/NewHorizons/Patches/VolumePatches/FogWarpVolumePatches.cs +++ b/NewHorizons/Patches/VolumePatches/FogWarpVolumePatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Builder.Props; using UnityEngine; namespace NewHorizons.Patches.VolumePatches @@ -8,20 +9,38 @@ namespace NewHorizons.Patches.VolumePatches { [HarmonyPrefix] [HarmonyPatch(typeof(SphericalFogWarpVolume), nameof(SphericalFogWarpVolume.IsProbeOnly))] - public static bool SphericalFogWarpVolume_IsProbeOnly(SphericalFogWarpVolume __instance, out bool __result) + public static bool SphericalFogWarpVolume_IsProbeOnly(SphericalFogWarpVolume __instance, ref bool __result) { - __result = Mathf.Approximately(__instance._exitRadius / __instance._warpRadius, 2f); // Check the ratio between these to determine if seed, instead of just < 10 + // Do not affect base game volumes + if (!BrambleNodeBuilder.IsNHFogWarpVolume(__instance)) + { + return true; + } + + // Check the ratio between these to determine if seed, instead of just < 10 + __result = Mathf.Approximately(__instance._exitRadius / __instance._warpRadius, 2f); return false; } [HarmonyPrefix] [HarmonyPatch(typeof(FogWarpVolume), nameof(FogWarpVolume.GetFogThickness))] - public static bool FogWarpVolume_GetFogThickness(FogWarpVolume __instance, out float __result) + public static bool FogWarpVolume_GetFogThickness(FogWarpVolume __instance, ref float __result) { - if (__instance is InnerFogWarpVolume sph) __result = sph._exitRadius; - else __result = 50; // 50f is hardcoded as the return value in the base game + // Do not affect base game volumes + if (!BrambleNodeBuilder.IsNHFogWarpVolume(__instance)) + { + return true; + } - return false; + if (__instance is InnerFogWarpVolume sph) + { + __result = sph._exitRadius; + return false; + } + else + { + 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..e4f993a3 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,7 @@ namespace NewHorizons.Patches.WarpPatches if (!Main.Instance.IsWarpingFromVessel) PlayerData.SaveWarpedToTheEye(TimeLoopUtilities.GetVanillaSecondsRemaining()); + Locator.GetPlayerSectorDetector().RemoveFromAllSectors(); LoadManager.EnableAsyncLoadTransition(); return false; @@ -64,10 +68,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 aada816a..dec9022f 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -27,6 +27,17 @@ "type": "boolean", "description": "If popupMessage is set, should it repeat every time the game starts or only once" }, + "preloadAssetBundles": { + "type": "array", + "description": "These asset bundles will be loaded on the title screen and stay loaded. Will improve initial load time at the cost of increased memory use.\nThe path is the relative directory of the asset bundle in the mod folder.", + "items": { + "type": "string" + } + }, + "subtitlePath": { + "type": "string", + "description": "The path to the addons subtitle for the main menu.\nDefaults to \"subtitle.png\"." + }, "$schema": { "type": "string", "description": "The schema to validate with" diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 35fc04b2..1f2fd960 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -85,6 +85,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" @@ -543,10 +547,6 @@ "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": { - "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?" @@ -692,14 +692,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" @@ -1022,6 +1022,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, @@ -1295,14 +1315,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" @@ -1342,7 +1362,7 @@ }, "keepLoaded": { "type": "boolean", - "description": "Should this detail stay loaded even if you're outside the sector (good for very large props)" + "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." }, "hasPhysics": { "type": "boolean", @@ -1402,7 +1422,7 @@ }, "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, WarpCode, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch). Defaults to the item name." + "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", @@ -1410,6 +1430,12 @@ "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.", @@ -1423,6 +1449,22 @@ "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." @@ -1477,14 +1519,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" @@ -1543,14 +1585,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" @@ -1568,6 +1610,10 @@ "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.", @@ -1579,6 +1625,17 @@ "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" @@ -1594,6 +1651,73 @@ } } }, + "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, @@ -1602,14 +1726,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" @@ -1647,14 +1771,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" @@ -1688,14 +1812,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" @@ -1767,14 +1891,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" @@ -1936,14 +2060,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" @@ -2020,6 +2144,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)." } } }, @@ -2042,14 +2170,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" @@ -2079,6 +2207,16 @@ "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" } } }, @@ -2144,6 +2282,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 } } }, @@ -2163,6 +2306,36 @@ "standingVisionTorch" ] }, + "SlideReelType": { + "type": "string", + "description": "", + "x-enumNames": [ + "SixSlides", + "SevenSlides", + "EightSlides", + "Whole" + ], + "enum": [ + "sixSlides", + "sevenSlides", + "eightSlides", + "whole" + ] + }, + "SlideReelCondition": { + "type": "string", + "description": "", + "x-enumNames": [ + "Antique", + "Pristine", + "Rusted" + ], + "enum": [ + "antique", + "pristine", + "rusted" + ] + }, "QuantumGroupInfo": { "type": "object", "additionalProperties": false, @@ -2228,14 +2401,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" @@ -2267,14 +2440,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" @@ -2435,14 +2608,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" @@ -2505,14 +2678,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" @@ -2606,14 +2779,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" @@ -2679,13 +2852,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" } } } @@ -2709,14 +2882,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" @@ -2769,7 +2942,7 @@ } } }, - "PlatformInfo": { + "RemotePlatformInfo": { "type": "object", "additionalProperties": false, "properties": { @@ -2788,14 +2961,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" @@ -2815,7 +2988,7 @@ } } }, - "StoneInfo": { + "ProjectionStoneInfo": { "type": "object", "additionalProperties": false, "properties": { @@ -2834,14 +3007,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" @@ -2871,14 +3044,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" @@ -2916,14 +3089,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" @@ -2953,14 +3126,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" @@ -3006,14 +3179,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" @@ -3084,14 +3257,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" @@ -3131,14 +3304,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" @@ -3175,14 +3348,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" @@ -3478,14 +3651,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" @@ -3523,14 +3696,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" @@ -4003,14 +4176,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" @@ -4101,14 +4274,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" @@ -4171,14 +4344,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" @@ -4254,14 +4427,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" @@ -4305,14 +4478,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" @@ -4348,14 +4521,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" @@ -4382,14 +4555,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" @@ -4453,14 +4626,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" @@ -4510,14 +4683,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" @@ -4633,14 +4806,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" @@ -4673,14 +4846,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" @@ -4723,14 +4896,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" @@ -4767,14 +4940,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" @@ -4839,14 +5012,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" @@ -4893,14 +5066,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" @@ -4933,14 +5106,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" @@ -4973,14 +5146,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" @@ -5006,14 +5179,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..1c68acde 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,49 +29,56 @@ - + 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 @@ -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 0cba8563..68d3f216 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -20,9 +20,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 +46,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 +68,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", @@ -81,6 +90,10 @@ "type": "string" } }, + "shipLogStartingPlanetID": { + "type": "string", + "description": "The planet to focus on when entering the ship log for the first time in a loop. If not set this will be the planet at navtigation position (1, 0)" + }, "curiosities": { "type": "array", "description": "List colors of curiosity entries", @@ -139,6 +152,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, @@ -242,14 +289,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" @@ -297,14 +344,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 index 4a107eaf..088d3ec5 100644 --- a/NewHorizons/Utility/DebugTools/DebugPropPlacer.cs +++ b/NewHorizons/Utility/DebugTools/DebugPropPlacer.cs @@ -156,6 +156,7 @@ namespace NewHorizons.Utility.DebugTools { position = data.pos, rotation = data.rot.eulerAngles, + keepLoaded = true }; var prop = DetailBuilder.Make(planetGO, sector, null, prefab, detailInfo); diff --git a/NewHorizons/Utility/DebugTools/DebugReload.cs b/NewHorizons/Utility/DebugTools/DebugReload.cs index 87dc99cf..97a40178 100644 --- a/NewHorizons/Utility/DebugTools/DebugReload.cs +++ b/NewHorizons/Utility/DebugTools/DebugReload.cs @@ -3,6 +3,7 @@ using NewHorizons.Utility.Files; using NewHorizons.Utility.OWML; using OWML.Common; using OWML.Common.Menus; +using OWML.Utils; using System; namespace NewHorizons.Utility.DebugTools @@ -10,22 +11,18 @@ namespace NewHorizons.Utility.DebugTools public static class DebugReload { - private static IModButton _reloadButton; + private static SubmitAction _reloadButton; - public static void InitializePauseMenu() + public static void InitializePauseMenu(IPauseMenuManager pauseMenu) { - _reloadButton = Main.Instance.ModHelper.Menus.PauseMenu.OptionsButton.Duplicate(TranslationHandler.GetTranslation("Reload Configs", TranslationHandler.TextType.UI).ToUpper()); - _reloadButton.OnClick += ReloadConfigs; + _reloadButton = pauseMenu.MakeSimpleButton(TranslationHandler.GetTranslation("Reload Configs", TranslationHandler.TextType.UI).ToUpperFixed(), 3, true); + _reloadButton.OnSubmitAction += ReloadConfigs; UpdateReloadButton(); } public static void UpdateReloadButton() { - if (_reloadButton != null) - { - if (Main.Debug) _reloadButton.Show(); - else _reloadButton.Hide(); - } + _reloadButton?.SetButtonVisible(Main.Debug); } private static void ReloadConfigs() @@ -46,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 index 07bd39f6..882b9cee 100644 --- a/NewHorizons/Utility/DebugTools/Menu/DebugMenu.cs +++ b/NewHorizons/Utility/DebugTools/Menu/DebugMenu.cs @@ -5,6 +5,7 @@ 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; @@ -15,7 +16,7 @@ namespace NewHorizons.Utility.DebugTools.Menu { class DebugMenu : MonoBehaviour { - private static IModButton pauseMenuButton; + private static SubmitAction pauseMenuButton; public GUIStyle _editorMenuStyle; public GUIStyle _tabBarStyle; @@ -23,7 +24,6 @@ namespace NewHorizons.Utility.DebugTools.Menu internal Vector2 EditorMenuSize = new Vector2(600, 900); bool menuOpen = false; static bool openMenuOnPause; - static bool staticInitialized; // Menu params internal static IModBehaviour loadedMod = null; @@ -34,6 +34,8 @@ namespace NewHorizons.Utility.DebugTools.Menu // Submenus private List submenus; private int activeSubmenu = 0; + + private static DebugMenu _instance; internal static JsonSerializerSettings jsonSettings = new JsonSerializerSettings { @@ -55,28 +57,13 @@ namespace NewHorizons.Utility.DebugTools.Menu private void Start() { - if (!staticInitialized) - { - staticInitialized = true; + _instance = this; - Main.Instance.ModHelper.Menus.PauseMenu.OnInit += PauseMenuInitHook; - Main.Instance.ModHelper.Menus.PauseMenu.OnClosed += CloseMenu; - Main.Instance.ModHelper.Menus.PauseMenu.OnOpened += RestoreMenuOpennessState; + Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuOpened += OnOpenMenu; + Main.Instance.ModHelper.MenuHelper.PauseMenuManager.PauseMenuClosed += OnCloseMenu; + Main.Instance.OnChangeStarSystem.AddListener(OnChangeStarSystem); - PauseMenuInitHook(); - - Main.Instance.OnChangeStarSystem.AddListener((string s) => { - if (saveButtonUnlocked) - { - SaveLoadedConfigsForRecentSystem(); - saveButtonUnlocked = false; - } - }); - } - else - { - InitMenu(); - } + InitMenu(); if (loadedMod != null) { @@ -84,26 +71,38 @@ namespace NewHorizons.Utility.DebugTools.Menu } } - private void PauseMenuInitHook() + public void OnDestroy() { - pauseMenuButton = Main.Instance.ModHelper.Menus.PauseMenu.OptionsButton.Duplicate(TranslationHandler.GetTranslation("Toggle Dev Tools Menu", TranslationHandler.TextType.UI).ToUpper()); - InitMenu(); + 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).ToUpperFixed(), 3, true); + _instance?.InitMenu(); } public static void UpdatePauseMenuButton() { - if (pauseMenuButton != null) - { - if (Main.Debug) pauseMenuButton.Show(); - else pauseMenuButton.Hide(); - } + pauseMenuButton?.SetButtonVisible(Main.Debug); } - private void RestoreMenuOpennessState() { menuOpen = openMenuOnPause; } + private void OnOpenMenu() { menuOpen = openMenuOnPause; } private void ToggleMenu() { menuOpen = !menuOpen; openMenuOnPause = !openMenuOnPause; } - private void CloseMenu() { menuOpen = false; } + private void OnCloseMenu() { menuOpen = false; } private void OnGUI() { @@ -284,7 +283,7 @@ namespace NewHorizons.Utility.DebugTools.Menu 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.OnClick += ToggleMenu; + pauseMenuButton.OnSubmitAction += ToggleMenu; submenus.ForEach(submenu => submenu.OnInit(this)); diff --git a/NewHorizons/Utility/Files/AssetBundleUtilities.cs b/NewHorizons/Utility/Files/AssetBundleUtilities.cs index 182826ba..12083f5b 100644 --- a/NewHorizons/Utility/Files/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/Files/AssetBundleUtilities.cs @@ -3,24 +3,57 @@ using OWML.Common; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using UnityEngine; namespace NewHorizons.Utility.Files { public static class AssetBundleUtilities { - public static Dictionary AssetBundles = new Dictionary(); + public static Dictionary AssetBundles = new(); + + private static readonly List _loadingBundles = new(); public static void ClearCache() { foreach (var pair in AssetBundles) { - if (pair.Value == null) NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); - else pair.Value.Unload(true); + if (!pair.Value.keepLoaded) + { + if (pair.Value.bundle == null) + { + NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); + } + else + { + pair.Value.bundle.Unload(true); + } + } + } - AssetBundles.Clear(); + AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value); } + public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod) + { + string key = Path.GetFileName(assetBundleRelativeDir); + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); + var request = AssetBundle.LoadFromFileAsync(completePath); + _loadingBundles.Add(request); + NHLogger.Log($"Preloading bundle {assetBundleRelativeDir} - {_loadingBundles.Count} left"); + request.completed += _ => + { + _loadingBundles.Remove(request); + NHLogger.Log($"Finshed preloading bundle {assetBundleRelativeDir} - {_loadingBundles.Count} left"); + AssetBundles[key] = (request.assetBundle, true); + }; + } + + /// + /// are preloaded bundles done loading? + /// + public static bool AreRequiredAssetsLoaded() => _loadingBundles.Count == 0; + public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object { string key = Path.GetFileName(assetBundleRelativeDir); @@ -32,7 +65,7 @@ namespace NewHorizons.Utility.Files if (AssetBundles.ContainsKey(key)) { - bundle = AssetBundles[key]; + bundle = AssetBundles[key].bundle; } else { @@ -44,7 +77,7 @@ namespace NewHorizons.Utility.Files return null; } - AssetBundles[key] = bundle; + AssetBundles[key] = (bundle, false); } obj = bundle.LoadAsset(pathInBundle); @@ -122,6 +155,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 ff03f14b..e7a76d64 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,10 @@ 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(string path) => + path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length + 1).Replace('\\', '/'); public static bool IsTextureLoaded(IModBehaviour mod, string filename) { @@ -28,8 +25,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) { @@ -81,6 +82,11 @@ namespace NewHorizons.Utility.Files { 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) @@ -105,9 +111,23 @@ namespace NewHorizons.Utility.Files _textureCache.Clear(); } - public static Texture2D Invert(Texture2D texture) + /// + /// 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). + /// + 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.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); + key = GetKey(cachedPath); + } + if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; var pixels = texture.GetPixels(); @@ -132,7 +152,7 @@ namespace NewHorizons.Utility.Files } } - var newTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount != 1); + var newTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount != 1, true); newTexture.name = key; newTexture.SetPixels(pixels); newTexture.Apply(); @@ -141,18 +161,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++) @@ -194,7 +222,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.ATLAS_SLIDE_CACHE_FOLDER, $"{uniqueSlideReelID}.png"); + NHLogger.LogVerbose($"Caching atlas image to {path}"); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllBytes(path, texture.EncodeToPNG()); return texture; } @@ -307,6 +342,40 @@ namespace NewHorizons.Utility.Files return newImage; } + public static Color LerpColor(Color start, Color end, float amount) + { + return new Color(Mathf.Lerp(start.r, end.r, amount), Mathf.Lerp(start.g, end.g, amount), Mathf.Lerp(start.b, end.b, amount)); + } + + public static Texture2D LerpGreyscaleImageAlongX(Texture2D image, Color lightTintStart, Color darkTintStart, Color lightTintEnd, Color darkTintEnd) + { + var key = $"{image.name} > lerp greyscale {lightTintStart} {darkTintStart} {lightTintEnd} {darkTintEnd}"; + if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; + + var pixels = image.GetPixels(); + for (int i = 0; i < pixels.Length; i++) + { + var amount = (i % image.width) / (float) image.width; + var lightTint = LerpColor(lightTintStart, lightTintEnd, amount); + var darkTint = LerpColor(darkTintStart, darkTintEnd, amount); + + pixels[i].r = Mathf.Lerp(darkTint.r, lightTint.r, pixels[i].r); + pixels[i].g = Mathf.Lerp(darkTint.g, lightTint.g, pixels[i].g); + pixels[i].b = Mathf.Lerp(darkTint.b, lightTint.b, pixels[i].b); + } + + var newImage = new Texture2D(image.width, image.height, image.format, image.mipmapCount != 1); + newImage.name = key; + newImage.SetPixels(pixels); + newImage.Apply(); + + newImage.wrapMode = image.wrapMode; + + _textureCache.Add(key, newImage); + + return newImage; + } + public static Texture2D ClearTexture(int width, int height, bool wrap = false) { var key = $"Clear {width} {height} {wrap}"; @@ -380,6 +449,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++) @@ -390,6 +462,9 @@ namespace NewHorizons.Utility.Files var newTexture = new Texture2D(width, height); newTexture.SetPixels(pixels); newTexture.Apply(); + + _textureCache.Add(key, newTexture); + return newTexture; } @@ -401,96 +476,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..7674ccdc --- /dev/null +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -0,0 +1,212 @@ +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; + + 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("SlideReelCache")) + { + 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/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index dd907c45..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,11 +8,13 @@ using Newtonsoft.Json; using OWML.Utils; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using System.Xml; using UnityEngine; using static NewHorizons.External.Modules.ParticleFieldModule; using NomaiCoordinates = NewHorizons.External.Configs.StarSystemConfig.NomaiCoordinates; @@ -24,7 +27,7 @@ namespace NewHorizons.Utility { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, - Formatting = Formatting.Indented, + Formatting = Newtonsoft.Json.Formatting.Indented, }; private static StringBuilder stringBuilder = new StringBuilder(); @@ -36,7 +39,7 @@ namespace NewHorizons.Utility { using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter) { - Formatting = Formatting.Indented, + Formatting = Newtonsoft.Json.Formatting.Indented, IndentChar = '\t', Indentation = 1 }) @@ -68,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 @@ -306,38 +383,81 @@ namespace NewHorizons.Utility return curve; } - // From QSB - public static void RaiseEvent(this T instance, string eventName, params object[] args) + public static List GetChildNodes(this XmlNode parentNode, string tagName) { - 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); + return parentNode.ChildNodes.Cast().Where(node => node.LocalName == tagName).ToList(); } - // From QSB - public static void SafeInvoke(this MulticastDelegate multicast, params object[] args) + public static XmlNode GetChildNode(this XmlNode parentNode, string tagName) { - foreach (var del in multicast.GetInvocationList()) - { - try - { - del.DynamicInvoke(args); - } - catch (TargetInvocationException ex) - { - NHLogger.LogError($"Error invoking delegate! {ex.InnerException}"); - } - } + return parentNode.ChildNodes.Cast().FirstOrDefault(node => node.LocalName == tagName); + } + + public static string TruncateWhitespaceAndToLower(this string text) + { + // 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/OWML/NHLogger.cs b/NewHorizons/Utility/OWML/NHLogger.cs index 080cdcad..c539f43f 100644 --- a/NewHorizons/Utility/OWML/NHLogger.cs +++ b/NewHorizons/Utility/OWML/NHLogger.cs @@ -19,10 +19,17 @@ namespace NewHorizons.Utility.OWML Main.Instance.ModHelper.Console.WriteLine($"{Enum.GetName(typeof(LogType), type)} : {text}", LogTypeToMessageType(type)); } + public static void LogVerbose(params object[] obj) => LogVerbose(string.Join(", ", obj)); public static void LogVerbose(object text) => Log(text, LogType.Verbose); + public static void Log(object text) => Log(text, LogType.Log); + public static void Log(params object[] obj) => Log(string.Join(", ", obj)); + public static void LogWarning(object text) => Log(text, LogType.Warning); + public static void LogWarning(params object[] obj) => LogWarning(string.Join(", ", obj)); + public static void LogError(object text) => Log(text, LogType.Error); + public static void LogError(params object[] obj) => LogError(string.Join(", ", obj)); public enum LogType { 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..e4b87a61 100644 --- a/NewHorizons/default-config.json +++ b/NewHorizons/default-config.json @@ -1,9 +1,38 @@ { - "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." + }, + "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 c75c1b1c..c75a3c1f 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, clay, MegaPiggy, John, Trifid, Hawkbar, Book", + "author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.18.4", - "owmlVersion": "2.9.8", - "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], - "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ], + "version": "1.22.7", + "owmlVersion": "2.12.1", + "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], + "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..9c5b60cd 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 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/package.json b/docs/package.json index cc2a0bf7..2d8ea72f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,18 +12,18 @@ "format": "prettier --write ." }, "dependencies": { - "@astrojs/starlight": "^0.15.1", - "astro": "4.0.8", + "@astrojs/starlight": "^0.19.1", + "astro": "4.4.1", "rehype-external-links": "^3.0.0", - "sharp": "^0.33.1" + "sharp": "^0.33.2" }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.1.2", - "fast-xml-parser": "^4.3.2", - "prettier": "^3.1.1", - "prettier-plugin-astro": "^0.12.3", + "eslint-plugin-prettier": "^5.1.3", + "fast-xml-parser": "^4.3.4", + "prettier": "^3.2.5", + "prettier-plugin-astro": "^0.13.0", "xml-js": "^1.6.11" } } diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 3f0a3cc9..75ffd0cc 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -6,17 +6,17 @@ settings: dependencies: "@astrojs/starlight": - specifier: ^0.15.1 - version: 0.15.1(astro@4.0.8) + specifier: ^0.19.1 + version: 0.19.1(astro@4.4.1) astro: - specifier: 4.0.8 - version: 4.0.8 + specifier: 4.4.1 + version: 4.4.1 rehype-external-links: specifier: ^3.0.0 version: 3.0.0 sharp: - specifier: ^0.33.1 - version: 0.33.1 + specifier: ^0.33.2 + version: 0.33.2 devDependencies: "@apidevtools/json-schema-ref-parser": @@ -26,17 +26,17 @@ devDependencies: specifier: ^8.56.0 version: 8.56.0 eslint-plugin-prettier: - specifier: ^5.1.2 - version: 5.1.2(eslint@8.56.0)(prettier@3.1.1) + specifier: ^5.1.3 + version: 5.1.3(eslint@8.56.0)(prettier@3.2.5) fast-xml-parser: - specifier: ^4.3.2 - version: 4.3.2 + specifier: ^4.3.4 + version: 4.3.4 prettier: - specifier: ^3.1.1 - version: 3.1.1 + specifier: ^3.2.5 + version: 3.2.5 prettier-plugin-astro: - specifier: ^0.12.3 - version: 0.12.3 + specifier: ^0.13.0 + version: 0.13.0 xml-js: specifier: ^1.6.11 version: 1.6.11 @@ -58,7 +58,7 @@ packages: engines: { node: ">=6.0.0" } dependencies: "@jridgewell/gen-mapping": 0.3.3 - "@jridgewell/trace-mapping": 0.3.20 + "@jridgewell/trace-mapping": 0.3.22 dev: false /@apidevtools/json-schema-ref-parser@11.1.0: @@ -82,10 +82,10 @@ packages: } dev: true - /@astrojs/compiler@2.3.4: + /@astrojs/compiler@2.5.3: resolution: { - integrity: sha512-33/YtWoBCE0cBUNy1kh78FCDXBoBANX87ShgATlAHECYbG2+buNTAgq4Xgz4t5NgnEHPN21GIBC2Mvvwisoutw== + integrity: sha512-jzj01BRv/fmo+9Mr2FhocywGzEYiyiP2GVHje1ziGNU6c97kwhYGsnvwMkHrncAy9T9Vi54cjaMK7UE4ClX4vA== } dev: false @@ -96,10 +96,10 @@ packages: } dev: false - /@astrojs/markdown-remark@4.0.1: + /@astrojs/markdown-remark@4.2.1: resolution: { - integrity: sha512-RU4ESnqvyLpj8WZs0n5elS6idaDdtIIm7mIpMaRNPCebpxMjfcfdwcmBwz83ktAj5d2eO5bC3z92TcGdli+lRw== + integrity: sha512-2RQBIwrq+2qPYtp99bH+eL5hfbK0BoxXla85lHsRpIX/IsGqFrPX6pXI2cbWPihBwGbKCdxS6uZNX2QerZWwpQ== } dependencies: "@astrojs/prism": 3.0.0 @@ -110,9 +110,9 @@ packages: rehype-stringify: 10.0.0 remark-gfm: 4.0.0 remark-parse: 11.0.0 - remark-rehype: 11.0.0 - remark-smartypants: 2.0.0 - shikiji: 0.6.13 + 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 @@ -120,19 +120,19 @@ packages: - supports-color dev: false - /@astrojs/mdx@2.0.3(astro@4.0.8): + /@astrojs/mdx@2.1.1(astro@4.4.1): resolution: { - integrity: sha512-wFjQX5CihU5B4UAQNwc2R48ph0flpc6/yvDCFANE0agtgI2+BaVcAjuW0EhGOQCZ65dQDqnFKE0lvGs7EADYpg== + integrity: sha512-AgGFdE7HOGmoFooGvMSatkA9FiSKwyVW7ImHot/bXJ6uAbFfu6iG2ht18Cf1pT22Hda/6iSCGWusFvBv0/EnKQ== } engines: { node: ">=18.14.1" } peerDependencies: astro: ^4.0.0 dependencies: - "@astrojs/markdown-remark": 4.0.1 - "@mdx-js/mdx": 3.0.0 - acorn: 8.11.2 - astro: 4.0.8 + "@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 @@ -141,7 +141,7 @@ packages: kleur: 4.1.5 rehype-raw: 7.0.0 remark-gfm: 4.0.0 - remark-smartypants: 2.0.0 + remark-smartypants: 2.1.0 source-map: 0.7.4 unist-util-visit: 5.0.0 vfile: 6.0.1 @@ -159,36 +159,36 @@ packages: prismjs: 1.29.0 dev: false - /@astrojs/sitemap@3.0.4: + /@astrojs/sitemap@3.1.0: resolution: { - integrity: sha512-RSqiqs0oD8zTGaClHM0YB8n7e5En+Ihi+6qKthWf47pRkzHpENwlPGvKuEL0kUFXq+GzYot9e2JYH58gtr2q0w== + integrity: sha512-eAlD3ISaESByanYtr08yfwIYNZdLEvF8shSne5SaxSdrvtK7fS8UQTF0GpDFUpU/NOyqwB7YmndjlTplhYLP1A== } dependencies: sitemap: 7.1.1 zod: 3.22.4 dev: false - /@astrojs/starlight@0.15.1(astro@4.0.8): + /@astrojs/starlight@0.19.1(astro@4.4.1): resolution: { - integrity: sha512-5AdySPQOG/+r1vHSNotQAGqhQIfF68hS8PfV+z4M2ewnrWoeCsHI3yYRFYgSRsVofcOEGTHiIryTRG/uRIvDkQ== + integrity: sha512-HcrgWAdOJRVRhiOf4TySzpCek/6N56jq5Iz0ahDTE5Fv2tHHA2cLIRAOvVF+iPNVZa349hqv6QSCmzUHA2JK4Q== } peerDependencies: - astro: ^4.0.0 + astro: ^4.2.7 dependencies: - "@astrojs/mdx": 2.0.3(astro@4.0.8) - "@astrojs/sitemap": 3.0.4 + "@astrojs/mdx": 2.1.1(astro@4.4.1) + "@astrojs/sitemap": 3.1.0 "@pagefind/default-ui": 1.0.4 - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 "@types/mdast": 4.0.3 - astro: 4.0.8 - astro-expressive-code: 0.30.1(astro@4.0.8) + astro: 4.4.1 + astro-expressive-code: 0.32.4(astro@4.4.1) bcp-47: 2.1.0 - execa: 8.0.1 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 @@ -237,10 +237,10 @@ packages: engines: { node: ">=6.9.0" } dev: false - /@babel/core@7.23.6: + /@babel/core@7.23.9: resolution: { - integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw== + integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== } engines: { node: ">=6.9.0" } dependencies: @@ -248,12 +248,12 @@ packages: "@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.6) - "@babel/helpers": 7.23.6 - "@babel/parser": 7.23.6 - "@babel/template": 7.22.15 - "@babel/traverse": 7.23.6 - "@babel/types": 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 @@ -270,9 +270,9 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 "@jridgewell/gen-mapping": 0.3.3 - "@jridgewell/trace-mapping": 0.3.20 + "@jridgewell/trace-mapping": 0.3.22 jsesc: 2.5.2 dev: false @@ -283,7 +283,7 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@babel/helper-compilation-targets@7.23.6: @@ -295,7 +295,7 @@ packages: dependencies: "@babel/compat-data": 7.23.5 "@babel/helper-validator-option": 7.23.5 - browserslist: 4.22.2 + browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 dev: false @@ -315,8 +315,8 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/template": 7.22.15 - "@babel/types": 7.23.6 + "@babel/template": 7.23.9 + "@babel/types": 7.23.9 dev: false /@babel/helper-hoist-variables@7.22.5: @@ -326,7 +326,7 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@babel/helper-module-imports@7.22.15: @@ -336,10 +336,10 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6): + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9): resolution: { integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== @@ -348,7 +348,7 @@ packages: peerDependencies: "@babel/core": ^7.0.0 dependencies: - "@babel/core": 7.23.6 + "@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 @@ -371,7 +371,7 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@babel/helper-split-export-declaration@7.22.6: @@ -381,7 +381,7 @@ packages: } engines: { node: ">=6.9.0" } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@babel/helper-string-parser@7.23.4: @@ -408,16 +408,16 @@ packages: engines: { node: ">=6.9.0" } dev: false - /@babel/helpers@7.23.6: + /@babel/helpers@7.23.9: resolution: { - integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA== + integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== } engines: { node: ">=6.9.0" } dependencies: - "@babel/template": 7.22.15 - "@babel/traverse": 7.23.6 - "@babel/types": 7.23.6 + "@babel/template": 7.23.9 + "@babel/traverse": 7.23.9 + "@babel/types": 7.23.9 transitivePeerDependencies: - supports-color dev: false @@ -434,18 +434,18 @@ packages: js-tokens: 4.0.0 dev: false - /@babel/parser@7.23.6: + /@babel/parser@7.23.9: resolution: { - integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== + integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== } engines: { node: ">=6.0.0" } hasBin: true dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.6): + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9): resolution: { integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== @@ -454,11 +454,11 @@ packages: peerDependencies: "@babel/core": ^7.0.0-0 dependencies: - "@babel/core": 7.23.6 + "@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.6): + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.23.9): resolution: { integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== @@ -467,30 +467,30 @@ packages: peerDependencies: "@babel/core": ^7.0.0-0 dependencies: - "@babel/core": 7.23.6 + "@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.6) - "@babel/types": 7.23.6 + "@babel/plugin-syntax-jsx": 7.23.3(@babel/core@7.23.9) + "@babel/types": 7.23.9 dev: false - /@babel/template@7.22.15: + /@babel/template@7.23.9: resolution: { - integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== } engines: { node: ">=6.9.0" } dependencies: "@babel/code-frame": 7.23.5 - "@babel/parser": 7.23.6 - "@babel/types": 7.23.6 + "@babel/parser": 7.23.9 + "@babel/types": 7.23.9 dev: false - /@babel/traverse@7.23.6: + /@babel/traverse@7.23.9: resolution: { - integrity: sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ== + integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== } engines: { node: ">=6.9.0" } dependencies: @@ -500,18 +500,18 @@ packages: "@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.6 - "@babel/types": 7.23.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.6: + /@babel/types@7.23.9: resolution: { - integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== + integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== } engines: { node: ">=6.9.0" } dependencies: @@ -528,10 +528,10 @@ packages: engines: { node: ">=10" } dev: false - /@emnapi/runtime@0.44.0: + /@emnapi/runtime@0.45.0: resolution: { - integrity: sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw== + integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w== } requiresBuild: true dependencies: @@ -539,10 +539,10 @@ packages: dev: false optional: true - /@esbuild/aix-ppc64@0.19.10: + /@esbuild/aix-ppc64@0.19.12: resolution: { - integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q== + integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== } engines: { node: ">=12" } cpu: [ppc64] @@ -551,10 +551,10 @@ packages: dev: false optional: true - /@esbuild/android-arm64@0.19.10: + /@esbuild/android-arm64@0.19.12: resolution: { - integrity: sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q== + integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== } engines: { node: ">=12" } cpu: [arm64] @@ -563,10 +563,10 @@ packages: dev: false optional: true - /@esbuild/android-arm@0.19.10: + /@esbuild/android-arm@0.19.12: resolution: { - integrity: sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w== + integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== } engines: { node: ">=12" } cpu: [arm] @@ -575,10 +575,10 @@ packages: dev: false optional: true - /@esbuild/android-x64@0.19.10: + /@esbuild/android-x64@0.19.12: resolution: { - integrity: sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw== + integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== } engines: { node: ">=12" } cpu: [x64] @@ -587,10 +587,10 @@ packages: dev: false optional: true - /@esbuild/darwin-arm64@0.19.10: + /@esbuild/darwin-arm64@0.19.12: resolution: { - integrity: sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA== + integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== } engines: { node: ">=12" } cpu: [arm64] @@ -599,10 +599,10 @@ packages: dev: false optional: true - /@esbuild/darwin-x64@0.19.10: + /@esbuild/darwin-x64@0.19.12: resolution: { - integrity: sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA== + integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== } engines: { node: ">=12" } cpu: [x64] @@ -611,10 +611,10 @@ packages: dev: false optional: true - /@esbuild/freebsd-arm64@0.19.10: + /@esbuild/freebsd-arm64@0.19.12: resolution: { - integrity: sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg== + integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== } engines: { node: ">=12" } cpu: [arm64] @@ -623,10 +623,10 @@ packages: dev: false optional: true - /@esbuild/freebsd-x64@0.19.10: + /@esbuild/freebsd-x64@0.19.12: resolution: { - integrity: sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag== + integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== } engines: { node: ">=12" } cpu: [x64] @@ -635,10 +635,10 @@ packages: dev: false optional: true - /@esbuild/linux-arm64@0.19.10: + /@esbuild/linux-arm64@0.19.12: resolution: { - integrity: sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ== + integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== } engines: { node: ">=12" } cpu: [arm64] @@ -647,10 +647,10 @@ packages: dev: false optional: true - /@esbuild/linux-arm@0.19.10: + /@esbuild/linux-arm@0.19.12: resolution: { - integrity: sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg== + integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== } engines: { node: ">=12" } cpu: [arm] @@ -659,10 +659,10 @@ packages: dev: false optional: true - /@esbuild/linux-ia32@0.19.10: + /@esbuild/linux-ia32@0.19.12: resolution: { - integrity: sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg== + integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== } engines: { node: ">=12" } cpu: [ia32] @@ -671,10 +671,10 @@ packages: dev: false optional: true - /@esbuild/linux-loong64@0.19.10: + /@esbuild/linux-loong64@0.19.12: resolution: { - integrity: sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA== + integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== } engines: { node: ">=12" } cpu: [loong64] @@ -683,10 +683,10 @@ packages: dev: false optional: true - /@esbuild/linux-mips64el@0.19.10: + /@esbuild/linux-mips64el@0.19.12: resolution: { - integrity: sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A== + integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== } engines: { node: ">=12" } cpu: [mips64el] @@ -695,10 +695,10 @@ packages: dev: false optional: true - /@esbuild/linux-ppc64@0.19.10: + /@esbuild/linux-ppc64@0.19.12: resolution: { - integrity: sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ== + integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== } engines: { node: ">=12" } cpu: [ppc64] @@ -707,10 +707,10 @@ packages: dev: false optional: true - /@esbuild/linux-riscv64@0.19.10: + /@esbuild/linux-riscv64@0.19.12: resolution: { - integrity: sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA== + integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== } engines: { node: ">=12" } cpu: [riscv64] @@ -719,10 +719,10 @@ packages: dev: false optional: true - /@esbuild/linux-s390x@0.19.10: + /@esbuild/linux-s390x@0.19.12: resolution: { - integrity: sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA== + integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== } engines: { node: ">=12" } cpu: [s390x] @@ -731,10 +731,10 @@ packages: dev: false optional: true - /@esbuild/linux-x64@0.19.10: + /@esbuild/linux-x64@0.19.12: resolution: { - integrity: sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA== + integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== } engines: { node: ">=12" } cpu: [x64] @@ -743,10 +743,10 @@ packages: dev: false optional: true - /@esbuild/netbsd-x64@0.19.10: + /@esbuild/netbsd-x64@0.19.12: resolution: { - integrity: sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q== + integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== } engines: { node: ">=12" } cpu: [x64] @@ -755,10 +755,10 @@ packages: dev: false optional: true - /@esbuild/openbsd-x64@0.19.10: + /@esbuild/openbsd-x64@0.19.12: resolution: { - integrity: sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg== + integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== } engines: { node: ">=12" } cpu: [x64] @@ -767,10 +767,10 @@ packages: dev: false optional: true - /@esbuild/sunos-x64@0.19.10: + /@esbuild/sunos-x64@0.19.12: resolution: { - integrity: sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA== + integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== } engines: { node: ">=12" } cpu: [x64] @@ -779,10 +779,10 @@ packages: dev: false optional: true - /@esbuild/win32-arm64@0.19.10: + /@esbuild/win32-arm64@0.19.12: resolution: { - integrity: sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw== + integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== } engines: { node: ">=12" } cpu: [arm64] @@ -791,10 +791,10 @@ packages: dev: false optional: true - /@esbuild/win32-ia32@0.19.10: + /@esbuild/win32-ia32@0.19.12: resolution: { - integrity: sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ== + integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== } engines: { node: ">=12" } cpu: [ia32] @@ -803,10 +803,10 @@ packages: dev: false optional: true - /@esbuild/win32-x64@0.19.10: + /@esbuild/win32-x64@0.19.12: resolution: { - integrity: sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA== + integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== } engines: { node: ">=12" } cpu: [x64] @@ -847,7 +847,7 @@ packages: debug: 4.3.4 espree: 9.6.1 globals: 13.24.0 - ignore: 5.3.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -864,58 +864,58 @@ packages: engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } dev: true - /@expressive-code/core@0.30.1: + /@expressive-code/core@0.32.4: resolution: { - integrity: sha512-z11G1lfzzuTJ63C4dGPUIEKxJse5eKrVxqxQJRiuCDH8hPqfIQs/+by04UceSWB0dt5SIFsL5J+7HvCycbc1iA== + integrity: sha512-S0OwgZCy29OCcwFUBTLDrShUovIUWZcQn3EvSoKsGfzf/wTisK7XqZ1uH0Y7Mlof3Hf9uJMjOhJZvxTLtQUdSQ== } dependencies: "@ctrl/tinycolor": 3.6.1 hast-util-to-html: 8.0.4 hastscript: 7.2.0 - postcss: 8.4.32 - postcss-nested: 6.0.1(postcss@8.4.32) + postcss: 8.4.35 + postcss-nested: 6.0.1(postcss@8.4.35) dev: false - /@expressive-code/plugin-frames@0.30.1: + /@expressive-code/plugin-frames@0.32.4: resolution: { - integrity: sha512-fBqd+NWcmlP63q8kNi73b6EuwWZwb+8OIhqWEsEQ/lsoOmT8FVYqsnH+M+TRC9or+U0PNd7Po/ojcFNnS0TAIQ== + integrity: sha512-XOQrLqlVEy5JbqsBhDcSJQinceQ5j/Z8cE0/27Lnlcj4oXRdiQNjMVtstC/xZUeWEbm+FI9ZZP4Z9yihol61Aw== } dependencies: - "@expressive-code/core": 0.30.1 + "@expressive-code/core": 0.32.4 hastscript: 7.2.0 dev: false - /@expressive-code/plugin-shiki@0.30.1: + /@expressive-code/plugin-shiki@0.32.4: resolution: { - integrity: sha512-VoJZWZ1wjW/I+CsHg4/RwurrHdtnF5C5fbXESQCQlnXWOhWBfJnOX1ilx5B11gsH5kEWNoD5WRAt8t0L0V/VJA== + integrity: sha512-zZzTXFFTpG+fmBG6C+4KzIyh1nFPdn4gLJ8E9LhBVufmRkn3gZplkE99lulfillsKyUZTRw3+dC3xYZWEZKzPw== } dependencies: - "@expressive-code/core": 0.30.1 + "@expressive-code/core": 0.32.4 shikiji: 0.8.7 dev: false - /@expressive-code/plugin-text-markers@0.30.1: + /@expressive-code/plugin-text-markers@0.32.4: resolution: { - integrity: sha512-Dtw2lIsAfcfWy/qUhrEW1NwZdgMsI+qeHQ/7Do+W9fMBIoIIHh2GLwmhwkBitoOvLgOJQWsNoBvT42LUp2+16g== + integrity: sha512-lFlo3uwTp7vUmfXtLPn2aXs0CPFqdFvKiR3y8gtNzmBeYWPqVahF4RFUCN9ZpztCmXp5V8p2ADvNHzoNwCBwzA== } dependencies: - "@expressive-code/core": 0.30.1 + "@expressive-code/core": 0.32.4 hastscript: 7.2.0 unist-util-visit-parents: 5.1.3 dev: false - /@humanwhocodes/config-array@0.11.13: + /@humanwhocodes/config-array@0.11.14: resolution: { - integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== + integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== } engines: { node: ">=10.10.0" } dependencies: - "@humanwhocodes/object-schema": 2.0.1 + "@humanwhocodes/object-schema": 2.0.2 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -930,17 +930,17 @@ packages: engines: { node: ">=12.22" } dev: true - /@humanwhocodes/object-schema@2.0.1: + /@humanwhocodes/object-schema@2.0.2: resolution: { - integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== } dev: true - /@img/sharp-darwin-arm64@0.33.1: + /@img/sharp-darwin-arm64@0.33.2: resolution: { - integrity: sha512-esr2BZ1x0bo+wl7Gx2hjssYhjrhUsD88VQulI0FrG8/otRQUOxLWHMBd1Y1qo2Gfg2KUvXNpT0ASnV9BzJCexw== + integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w== } engines: { @@ -954,14 +954,14 @@ packages: os: [darwin] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-darwin-arm64": 1.0.0 + "@img/sharp-libvips-darwin-arm64": 1.0.1 dev: false optional: true - /@img/sharp-darwin-x64@0.33.1: + /@img/sharp-darwin-x64@0.33.2: resolution: { - integrity: sha512-YrnuB3bXuWdG+hJlXtq7C73lF8ampkhU3tMxg5Hh+E7ikxbUVOU9nlNtVTloDXz6pRHt2y2oKJq7DY/yt+UXYw== + integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg== } engines: { @@ -975,14 +975,14 @@ packages: os: [darwin] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-darwin-x64": 1.0.0 + "@img/sharp-libvips-darwin-x64": 1.0.1 dev: false optional: true - /@img/sharp-libvips-darwin-arm64@1.0.0: + /@img/sharp-libvips-darwin-arm64@1.0.1: resolution: { - integrity: sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw== + integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw== } engines: { macos: ">=11", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [arm64] @@ -991,10 +991,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-darwin-x64@1.0.0: + /@img/sharp-libvips-darwin-x64@1.0.1: resolution: { - integrity: sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA== + 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] @@ -1003,10 +1003,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linux-arm64@1.0.0: + /@img/sharp-libvips-linux-arm64@1.0.1: resolution: { - integrity: sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA== + 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] @@ -1015,10 +1015,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linux-arm@1.0.0: + /@img/sharp-libvips-linux-arm@1.0.1: resolution: { - integrity: sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw== + integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ== } engines: { glibc: ">=2.28", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [arm] @@ -1027,10 +1027,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linux-s390x@1.0.0: + /@img/sharp-libvips-linux-s390x@1.0.1: resolution: { - integrity: sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw== + integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ== } engines: { glibc: ">=2.28", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [s390x] @@ -1039,10 +1039,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linux-x64@1.0.0: + /@img/sharp-libvips-linux-x64@1.0.1: resolution: { - integrity: sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q== + integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw== } engines: { glibc: ">=2.26", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [x64] @@ -1051,10 +1051,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linuxmusl-arm64@1.0.0: + /@img/sharp-libvips-linuxmusl-arm64@1.0.1: resolution: { - integrity: sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ== + integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg== } engines: { musl: ">=1.2.2", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [arm64] @@ -1063,10 +1063,10 @@ packages: dev: false optional: true - /@img/sharp-libvips-linuxmusl-x64@1.0.0: + /@img/sharp-libvips-linuxmusl-x64@1.0.1: resolution: { - integrity: sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg== + integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw== } engines: { musl: ">=1.2.2", npm: ">=9.6.5", pnpm: ">=7.1.0", yarn: ">=3.2.0" } cpu: [x64] @@ -1075,10 +1075,10 @@ packages: dev: false optional: true - /@img/sharp-linux-arm64@0.33.1: + /@img/sharp-linux-arm64@0.33.2: resolution: { - integrity: sha512-59B5GRO2d5N3tIfeGHAbJps7cLpuWEQv/8ySd9109ohQ3kzyCACENkFVAnGPX00HwPTQcaBNF7HQYEfZyZUFfw== + integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew== } engines: { @@ -1092,14 +1092,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linux-arm64": 1.0.0 + "@img/sharp-libvips-linux-arm64": 1.0.1 dev: false optional: true - /@img/sharp-linux-arm@0.33.1: + /@img/sharp-linux-arm@0.33.2: resolution: { - integrity: sha512-Ii4X1vnzzI4j0+cucsrYA5ctrzU9ciXERfJR633S2r39CiD8npqH2GMj63uFZRCFt3E687IenAdbwIpQOJ5BNA== + integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA== } engines: { @@ -1113,14 +1113,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linux-arm": 1.0.0 + "@img/sharp-libvips-linux-arm": 1.0.1 dev: false optional: true - /@img/sharp-linux-s390x@0.33.1: + /@img/sharp-linux-s390x@0.33.2: resolution: { - integrity: sha512-tRGrb2pHnFUXpOAj84orYNxHADBDIr0J7rrjwQrTNMQMWA4zy3StKmMvwsI7u3dEZcgwuMMooIIGWEWOjnmG8A== + integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA== } engines: { @@ -1134,14 +1134,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linux-s390x": 1.0.0 + "@img/sharp-libvips-linux-s390x": 1.0.1 dev: false optional: true - /@img/sharp-linux-x64@0.33.1: + /@img/sharp-linux-x64@0.33.2: resolution: { - integrity: sha512-4y8osC0cAc1TRpy02yn5omBeloZZwS62fPZ0WUAYQiLhSFSpWJfY/gMrzKzLcHB9ulUV6ExFiu2elMaixKDbeg== + integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A== } engines: { @@ -1155,14 +1155,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linux-x64": 1.0.0 + "@img/sharp-libvips-linux-x64": 1.0.1 dev: false optional: true - /@img/sharp-linuxmusl-arm64@0.33.1: + /@img/sharp-linuxmusl-arm64@0.33.2: resolution: { - integrity: sha512-D3lV6clkqIKUizNS8K6pkuCKNGmWoKlBGh5p0sLO2jQERzbakhu4bVX1Gz+RS4vTZBprKlWaf+/Rdp3ni2jLfA== + integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA== } engines: { @@ -1176,14 +1176,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64": 1.0.0 + "@img/sharp-libvips-linuxmusl-arm64": 1.0.1 dev: false optional: true - /@img/sharp-linuxmusl-x64@0.33.1: + /@img/sharp-linuxmusl-x64@0.33.2: resolution: { - integrity: sha512-LOGKNu5w8uu1evVqUAUKTix2sQu1XDRIYbsi5Q0c/SrXhvJ4QyOx+GaajxmOg5PZSsSnCYPSmhjHHsRBx06/wQ== + integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A== } engines: { @@ -1197,14 +1197,14 @@ packages: os: [linux] requiresBuild: true optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64": 1.0.0 + "@img/sharp-libvips-linuxmusl-x64": 1.0.1 dev: false optional: true - /@img/sharp-wasm32@0.33.1: + /@img/sharp-wasm32@0.33.2: resolution: { - integrity: sha512-vWI/sA+0p+92DLkpAMb5T6I8dg4z2vzCUnp8yvxHlwBpzN8CIcO3xlSXrLltSvK6iMsVMNswAv+ub77rsf25lA== + integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ== } engines: { @@ -1216,14 +1216,14 @@ packages: cpu: [wasm32] requiresBuild: true dependencies: - "@emnapi/runtime": 0.44.0 + "@emnapi/runtime": 0.45.0 dev: false optional: true - /@img/sharp-win32-ia32@0.33.1: + /@img/sharp-win32-ia32@0.33.2: resolution: { - integrity: sha512-/xhYkylsKL05R+NXGJc9xr2Tuw6WIVl2lubFJaFYfW4/MQ4J+dgjIo/T4qjNRizrqs/szF/lC9a5+updmY9jaQ== + integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g== } engines: { @@ -1238,10 +1238,10 @@ packages: dev: false optional: true - /@img/sharp-win32-x64@0.33.1: + /@img/sharp-win32-x64@0.33.2: resolution: { - integrity: sha512-XaM69X0n6kTEsp9tVYYLhXdg7Qj32vYJlAKRutxUsm1UlgQNx6BOhHwZPwukCGXBU2+tH87ip2eV1I/E8MQnZg== + integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg== } engines: { @@ -1265,13 +1265,13 @@ packages: dependencies: "@jridgewell/set-array": 1.1.2 "@jridgewell/sourcemap-codec": 1.4.15 - "@jridgewell/trace-mapping": 0.3.20 + "@jridgewell/trace-mapping": 0.3.22 dev: false - /@jridgewell/resolve-uri@3.1.1: + /@jridgewell/resolve-uri@3.1.2: resolution: { - integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } engines: { node: ">=6.0.0" } dev: false @@ -1291,13 +1291,13 @@ packages: } dev: false - /@jridgewell/trace-mapping@0.3.20: + /@jridgewell/trace-mapping@0.3.22: resolution: { - integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== } dependencies: - "@jridgewell/resolve-uri": 3.1.1 + "@jridgewell/resolve-uri": 3.1.2 "@jridgewell/sourcemap-codec": 1.4.15 dev: false @@ -1308,16 +1308,16 @@ packages: } dev: true - /@mdx-js/mdx@3.0.0: + /@mdx-js/mdx@3.0.1: resolution: { - integrity: sha512-Icm0TBKBLYqroYbNW3BPnzMGn+7mwpQOK310aZ7+fkCtiU3aqv2cdcX+nd0Ydo3wI5Rx8bX2Z2QmGb/XcAClCw== + integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA== } dependencies: "@types/estree": 1.0.5 - "@types/estree-jsx": 1.0.3 - "@types/hast": 3.0.3 - "@types/mdx": 2.0.10 + "@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 @@ -1328,9 +1328,9 @@ packages: hast-util-to-jsx-runtime: 2.3.0 markdown-extensions: 2.0.0 periscopic: 3.1.0 - remark-mdx: 3.0.0 + remark-mdx: 3.0.1 remark-parse: 11.0.0 - remark-rehype: 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 @@ -1341,6 +1341,13 @@ packages: - supports-color dev: false + /@medv/finder@3.1.0: + resolution: + { + integrity: sha512-ojkXjR3K0Zz3jnCR80tqPL+0yvbZk/lEodb6RIVjLz7W8RVA2wrw8ym/CzCpXO9SYVUIKHFUpc7jvf8UKfIM3w== + } + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: { @@ -1366,7 +1373,7 @@ packages: engines: { node: ">= 8" } dependencies: "@nodelib/fs.scandir": 2.1.5 - fastq: 1.16.0 + fastq: 1.17.1 /@pagefind/darwin-arm64@1.0.4: resolution: @@ -1430,18 +1437,18 @@ packages: dev: false optional: true - /@pkgr/core@0.1.0: + /@pkgr/core@0.1.1: resolution: { - integrity: sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ== + integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== } engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } dev: true - /@rollup/rollup-android-arm-eabi@4.9.1: + /@rollup/rollup-android-arm-eabi@4.12.0: resolution: { - integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig== + integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w== } cpu: [arm] os: [android] @@ -1449,10 +1456,10 @@ packages: dev: false optional: true - /@rollup/rollup-android-arm64@4.9.1: + /@rollup/rollup-android-arm64@4.12.0: resolution: { - integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ== + integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ== } cpu: [arm64] os: [android] @@ -1460,10 +1467,10 @@ packages: dev: false optional: true - /@rollup/rollup-darwin-arm64@4.9.1: + /@rollup/rollup-darwin-arm64@4.12.0: resolution: { - integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA== + integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ== } cpu: [arm64] os: [darwin] @@ -1471,10 +1478,10 @@ packages: dev: false optional: true - /@rollup/rollup-darwin-x64@4.9.1: + /@rollup/rollup-darwin-x64@4.12.0: resolution: { - integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og== + integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg== } cpu: [x64] os: [darwin] @@ -1482,10 +1489,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.1: + /@rollup/rollup-linux-arm-gnueabihf@4.12.0: resolution: { - integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q== + integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA== } cpu: [arm] os: [linux] @@ -1493,10 +1500,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.1: + /@rollup/rollup-linux-arm64-gnu@4.12.0: resolution: { - integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q== + integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA== } cpu: [arm64] os: [linux] @@ -1504,10 +1511,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-arm64-musl@4.9.1: + /@rollup/rollup-linux-arm64-musl@4.12.0: resolution: { - integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw== + integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ== } cpu: [arm64] os: [linux] @@ -1515,10 +1522,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.1: + /@rollup/rollup-linux-riscv64-gnu@4.12.0: resolution: { - integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw== + integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw== } cpu: [riscv64] os: [linux] @@ -1526,10 +1533,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-x64-gnu@4.9.1: + /@rollup/rollup-linux-x64-gnu@4.12.0: resolution: { - integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg== + integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA== } cpu: [x64] os: [linux] @@ -1537,10 +1544,10 @@ packages: dev: false optional: true - /@rollup/rollup-linux-x64-musl@4.9.1: + /@rollup/rollup-linux-x64-musl@4.12.0: resolution: { - integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw== + integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw== } cpu: [x64] os: [linux] @@ -1548,10 +1555,10 @@ packages: dev: false optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.1: + /@rollup/rollup-win32-arm64-msvc@4.12.0: resolution: { - integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g== + integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw== } cpu: [arm64] os: [win32] @@ -1559,10 +1566,10 @@ packages: dev: false optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.1: + /@rollup/rollup-win32-ia32-msvc@4.12.0: resolution: { - integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg== + integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA== } cpu: [ia32] os: [win32] @@ -1570,10 +1577,10 @@ packages: dev: false optional: true - /@rollup/rollup-win32-x64-msvc@4.9.1: + /@rollup/rollup-win32-x64-msvc@4.12.0: resolution: { - integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA== + integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg== } cpu: [x64] os: [win32] @@ -1596,11 +1603,11 @@ packages: integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== } dependencies: - "@babel/parser": 7.23.6 - "@babel/types": 7.23.6 + "@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.4 + "@types/babel__traverse": 7.20.5 dev: false /@types/babel__generator@7.6.8: @@ -1609,7 +1616,7 @@ packages: integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@types/babel__template@7.4.4: @@ -1618,17 +1625,17 @@ packages: integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== } dependencies: - "@babel/parser": 7.23.6 - "@babel/types": 7.23.6 + "@babel/parser": 7.23.9 + "@babel/types": 7.23.9 dev: false - /@types/babel__traverse@7.20.4: + /@types/babel__traverse@7.20.5: resolution: { - integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== + integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== } dependencies: - "@babel/types": 7.23.6 + "@babel/types": 7.23.9 dev: false /@types/debug@4.1.12: @@ -1640,10 +1647,10 @@ packages: "@types/ms": 0.7.34 dev: false - /@types/estree-jsx@1.0.3: + /@types/estree-jsx@1.0.4: resolution: { - integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w== + integrity: sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ== } dependencies: "@types/estree": 1.0.5 @@ -1656,19 +1663,19 @@ packages: } dev: false - /@types/hast@2.3.9: + /@types/hast@2.3.10: resolution: { - integrity: sha512-pTHyNlaMD/oKJmS+ZZUyFUcsZeBZpC0lmGquw98CqRVNgAdJZJeD7GoeLiT6Xbx5rU9VCjSt0RwEvDgzh4obFw== + integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw== } dependencies: "@types/unist": 2.0.10 dev: false - /@types/hast@3.0.3: + /@types/hast@3.0.4: resolution: { - integrity: sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ== + integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== } dependencies: "@types/unist": 3.0.2 @@ -1706,10 +1713,10 @@ packages: "@types/unist": 3.0.2 dev: false - /@types/mdx@2.0.10: + /@types/mdx@2.0.11: resolution: { - integrity: sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg== + integrity: sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw== } dev: false @@ -1772,7 +1779,7 @@ packages: integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== } - /acorn-jsx@5.3.2(acorn@8.11.2): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -1780,12 +1787,12 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.11.2 + acorn: 8.11.3 - /acorn@8.11.2: + /acorn@8.11.3: resolution: { - integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== } engines: { node: ">=0.4.0" } hasBin: true @@ -1887,6 +1894,15 @@ packages: integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } + /aria-query@5.3.0: + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + } + dependencies: + dequal: 2.0.3 + dev: false + /array-iterate@2.0.1: resolution: { @@ -1902,52 +1918,57 @@ packages: hasBin: true dev: false - /astro-expressive-code@0.30.1(astro@4.0.8): + /astro-expressive-code@0.32.4(astro@4.4.1): resolution: { - integrity: sha512-Dr3VbK4HvIXTT8rsvd8PuRW4uZOpfRz9J8noVSSxmx7/M7o73SBtqauBYvxXpGHnagTJPGzYOa9BQS7jDHNUVw== + integrity: sha512-/Kq8wLMz0X2gbLWGmPryqEdFV/om/GROsoLtPFqLrLCRD5CpwxXAW185BIGZKf4iYsyJim1vvcpQm5Y9hV5B1g== } peerDependencies: astro: ^3.3.0 || ^4.0.0-beta dependencies: - astro: 4.0.8 - remark-expressive-code: 0.30.1 + astro: 4.4.1 + hast-util-to-html: 8.0.4 + remark-expressive-code: 0.32.4 dev: false - /astro@4.0.8: + /astro@4.4.1: resolution: { - integrity: sha512-LwbYUfdrfys5mtI6cA5V8UvPRUVVTRQ68uF1ylrEge6BkPcHHJ7qCYLNdjnNPxZDR9bWi1awILJESB6j+w0P4w== + integrity: sha512-nJLgNg8UXKBJYXjWtekgv1TYZES++LAdShgyKL8L5yJMeiqlDSO+/Laq5VfRKoL9hzBdyolJMB0WDE/+bRZytg== } engines: { node: ">=18.14.1", npm: ">=6.14.0" } hasBin: true dependencies: - "@astrojs/compiler": 2.3.4 + "@astrojs/compiler": 2.5.3 "@astrojs/internal-helpers": 0.2.1 - "@astrojs/markdown-remark": 4.0.1 + "@astrojs/markdown-remark": 4.2.1 "@astrojs/telemetry": 3.0.4 - "@babel/core": 7.23.6 + "@babel/core": 7.23.9 "@babel/generator": 7.23.6 - "@babel/parser": 7.23.6 - "@babel/plugin-transform-react-jsx": 7.23.4(@babel/core@7.23.6) - "@babel/traverse": 7.23.6 - "@babel/types": 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.2 + acorn: 8.11.3 + aria-query: 5.3.0 + axobject-query: 4.0.0 boxen: 7.1.1 - chokidar: 3.5.3 + chokidar: 3.6.0 ci-info: 4.0.0 - clsx: 2.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.1.0 + diff: 5.2.0 dlv: 1.1.3 dset: 3.1.3 es-module-lexer: 1.4.1 - esbuild: 0.19.10 + esbuild: 0.19.12 estree-walker: 3.0.3 execa: 8.0.1 fast-glob: 3.3.2 @@ -1958,7 +1979,7 @@ packages: http-cache-semantics: 4.1.1 js-yaml: 4.1.0 kleur: 4.1.5 - magic-string: 0.30.5 + magic-string: 0.30.7 mdast-util-to-hast: 13.0.2 mime: 3.0.0 ora: 7.0.1 @@ -1966,25 +1987,24 @@ packages: p-queue: 8.0.1 path-to-regexp: 6.2.1 preferred-pm: 3.1.2 - probe-image-size: 7.2.3 prompts: 2.4.2 rehype: 13.0.1 resolve: 1.22.8 - semver: 7.5.4 - server-destroy: 1.0.1 - shikiji: 0.6.13 - string-width: 7.0.0 + 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.0 + tsconfck: 3.0.2 unist-util-visit: 5.0.0 vfile: 6.0.1 - vite: 5.0.10 - vitefu: 0.2.5(vite@5.0.10) + 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.33.1 + sharp: 0.32.6 transitivePeerDependencies: - "@types/node" - less @@ -1997,6 +2017,24 @@ packages: - typescript dev: false + /axobject-query@4.0.0: + resolution: + { + integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw== + } + dependencies: + dequal: 2.0.3 + dev: false + + /b4a@1.6.6: + resolution: + { + integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== + } + requiresBuild: true + dev: false + optional: true + /bail@2.0.2: resolution: { @@ -2011,6 +2049,49 @@ packages: } 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: resolution: { @@ -2051,6 +2132,19 @@ packages: 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: { @@ -2106,20 +2200,32 @@ packages: fill-range: 7.0.1 dev: false - /browserslist@4.22.2: + /browserslist@4.23.0: resolution: { - integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== } engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true dependencies: - caniuse-lite: 1.0.30001572 - electron-to-chromium: 1.4.616 + caniuse-lite: 1.0.30001588 + electron-to-chromium: 1.4.677 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.22.2) + 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: { @@ -2146,10 +2252,10 @@ packages: engines: { node: ">=14.16" } dev: false - /caniuse-lite@1.0.30001572: + /caniuse-lite@1.0.30001588: resolution: { - integrity: sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw== + integrity: sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ== } dev: false @@ -2219,10 +2325,10 @@ packages: } dev: false - /chokidar@3.5.3: + /chokidar@3.6.0: resolution: { - integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== } engines: { node: ">= 8.10.0" } dependencies: @@ -2237,6 +2343,15 @@ packages: fsevents: 2.3.3 dev: false + /chownr@1.1.4: + resolution: + { + integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + } + requiresBuild: true + dev: false + optional: true + /ci-info@3.9.0: resolution: { @@ -2279,10 +2394,10 @@ packages: engines: { node: ">=6" } dev: false - /clsx@2.0.0: + /clsx@2.1.0: resolution: { - integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== } engines: { node: ">=6" } dev: false @@ -2409,34 +2524,6 @@ packages: hasBin: true dev: false - /debug@2.6.9: - resolution: - { - integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - } - peerDependencies: - supports-color: "*" - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.0.0 - dev: false - - /debug@3.2.7: - resolution: - { - integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - } - peerDependencies: - supports-color: "*" - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: false - /debug@4.3.4: resolution: { @@ -2460,6 +2547,28 @@ packages: 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: resolution: { @@ -2509,10 +2618,10 @@ packages: dequal: 2.0.3 dev: false - /diff@5.1.0: + /diff@5.2.0: resolution: { - integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== } engines: { node: ">=0.3.1" } dev: false @@ -2557,10 +2666,10 @@ packages: } dev: false - /electron-to-chromium@1.4.616: + /electron-to-chromium@1.4.677: resolution: { - integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg== + integrity: sha512-erDa3CaDzwJOpyvfKhOiJjBVNnMM0qxHq47RheVVwsSQrgBA9ZSGV9kdaOfZDPXcHzhG7lBxhj6A7KvfLJBd6Q== } dev: false @@ -2585,6 +2694,17 @@ packages: } 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: resolution: { @@ -2600,44 +2720,44 @@ packages: } dev: false - /esbuild@0.19.10: + /esbuild@0.19.12: resolution: { - integrity: sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA== + integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== } engines: { node: ">=12" } hasBin: true requiresBuild: true optionalDependencies: - "@esbuild/aix-ppc64": 0.19.10 - "@esbuild/android-arm": 0.19.10 - "@esbuild/android-arm64": 0.19.10 - "@esbuild/android-x64": 0.19.10 - "@esbuild/darwin-arm64": 0.19.10 - "@esbuild/darwin-x64": 0.19.10 - "@esbuild/freebsd-arm64": 0.19.10 - "@esbuild/freebsd-x64": 0.19.10 - "@esbuild/linux-arm": 0.19.10 - "@esbuild/linux-arm64": 0.19.10 - "@esbuild/linux-ia32": 0.19.10 - "@esbuild/linux-loong64": 0.19.10 - "@esbuild/linux-mips64el": 0.19.10 - "@esbuild/linux-ppc64": 0.19.10 - "@esbuild/linux-riscv64": 0.19.10 - "@esbuild/linux-s390x": 0.19.10 - "@esbuild/linux-x64": 0.19.10 - "@esbuild/netbsd-x64": 0.19.10 - "@esbuild/openbsd-x64": 0.19.10 - "@esbuild/sunos-x64": 0.19.10 - "@esbuild/win32-arm64": 0.19.10 - "@esbuild/win32-ia32": 0.19.10 - "@esbuild/win32-x64": 0.19.10 + "@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.1: + /escalade@3.1.2: resolution: { - integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== } engines: { node: ">=6" } dev: false @@ -2666,10 +2786,10 @@ packages: engines: { node: ">=12" } dev: false - /eslint-plugin-prettier@5.1.2(eslint@8.56.0)(prettier@3.1.1): + /eslint-plugin-prettier@5.1.3(eslint@8.56.0)(prettier@3.2.5): resolution: { - integrity: sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg== + integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== } engines: { node: ^14.18.0 || >=16.0.0 } peerDependencies: @@ -2684,7 +2804,7 @@ packages: optional: true dependencies: eslint: 8.56.0 - prettier: 3.1.1 + prettier: 3.2.5 prettier-linter-helpers: 1.0.0 synckit: 0.8.8 dev: true @@ -2720,7 +2840,7 @@ packages: "@eslint-community/regexpp": 4.10.0 "@eslint/eslintrc": 2.1.4 "@eslint/js": 8.56.0 - "@humanwhocodes/config-array": 0.11.13 + "@humanwhocodes/config-array": 0.11.14 "@humanwhocodes/module-importer": 1.0.1 "@nodelib/fs.walk": 1.2.8 "@ungap/structured-clone": 1.2.0 @@ -2741,7 +2861,7 @@ packages: glob-parent: 6.0.2 globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.3.0 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -2765,8 +2885,8 @@ packages: } engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } dependencies: - acorn: 8.11.2 - acorn-jsx: 5.3.2(acorn@8.11.2) + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 dev: true @@ -2822,7 +2942,7 @@ packages: integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== } dependencies: - "@types/estree-jsx": 1.0.3 + "@types/estree-jsx": 1.0.4 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 estree-walker: 3.0.3 @@ -2841,7 +2961,7 @@ packages: integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== } dependencies: - "@types/estree-jsx": 1.0.3 + "@types/estree-jsx": 1.0.4 astring: 1.8.6 source-map: 0.7.4 dev: false @@ -2852,7 +2972,7 @@ packages: integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== } dependencies: - "@types/estree-jsx": 1.0.3 + "@types/estree-jsx": 1.0.4 "@types/unist": 3.0.2 dev: false @@ -2898,16 +3018,26 @@ packages: strip-final-newline: 3.0.0 dev: false - /expressive-code@0.30.1: + /expand-template@2.0.3: resolution: { - integrity: sha512-iD8xtfN8X29jfk0B2U4/ws1pV/8GgRVIGOIDm9f6U9Zcnse1OnD/i+cYkcaOr5MLpVe8eLEr8GvVtZDOBxekrg== + 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.30.1 - "@expressive-code/plugin-frames": 0.30.1 - "@expressive-code/plugin-shiki": 0.30.1 - "@expressive-code/plugin-text-markers": 0.30.1 + "@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: @@ -2941,6 +3071,15 @@ packages: } dev: true + /fast-fifo@1.3.2: + resolution: + { + integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + } + requiresBuild: true + dev: false + optional: true + /fast-glob@3.3.2: resolution: { @@ -2969,20 +3108,20 @@ packages: } dev: true - /fast-xml-parser@4.3.2: + /fast-xml-parser@4.3.4: resolution: { - integrity: sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg== + integrity: sha512-utnwm92SyozgA3hhH2I8qldf2lBqm6qHOICawRNRFu1qMe3+oqr+GcXjGqTmXTMGE5T4eC03kr/rlh5C1IRdZA== } hasBin: true dependencies: strnum: 1.0.5 dev: true - /fastq@1.16.0: + /fastq@1.17.1: resolution: { - integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== + integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== } dependencies: reusify: 1.0.4 @@ -3045,15 +3184,15 @@ packages: } engines: { node: ^10.12.0 || >=12.0.0 } dependencies: - flatted: 3.2.9 + flatted: 3.3.0 keyv: 4.5.4 rimraf: 3.0.2 dev: true - /flatted@3.2.9: + /flatted@3.3.0: resolution: { - integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + integrity: sha512-noqGuLw158+DuD9UPRKHpJ2hGxpFyDlYYrfM0mWt4XhT4n0lwzTLh70Tkdyy4kyTmyTT9Bv7bWAJqw7cgkEXDg== } dev: true @@ -3065,6 +3204,15 @@ packages: 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: { @@ -3114,6 +3262,15 @@ packages: 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: resolution: { @@ -3216,10 +3373,10 @@ packages: engines: { node: ">=8" } dev: true - /hasown@2.0.0: + /hasown@2.0.1: resolution: { - integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== } engines: { node: ">= 0.4" } dependencies: @@ -3232,7 +3389,7 @@ packages: integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 devlop: 1.1.0 hast-util-from-parse5: 8.0.1 parse5: 7.1.2 @@ -3246,10 +3403,10 @@ packages: integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw== } dependencies: - "@types/hast": 2.3.9 + "@types/hast": 2.3.10 "@types/unist": 2.0.10 hastscript: 7.2.0 - property-information: 6.4.0 + property-information: 6.4.1 vfile: 5.3.7 vfile-location: 4.1.0 web-namespaces: 2.0.1 @@ -3261,11 +3418,11 @@ packages: integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 "@types/unist": 3.0.2 devlop: 1.1.0 hastscript: 8.0.0 - property-information: 6.4.0 + property-information: 6.4.1 vfile: 6.0.1 vfile-location: 5.0.2 web-namespaces: 2.0.1 @@ -3277,7 +3434,7 @@ packages: integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 dev: false /hast-util-is-element@3.0.0: @@ -3286,7 +3443,7 @@ packages: integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 dev: false /hast-util-parse-selector@3.1.1: @@ -3295,7 +3452,7 @@ packages: integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA== } dependencies: - "@types/hast": 2.3.9 + "@types/hast": 2.3.10 dev: false /hast-util-parse-selector@4.0.0: @@ -3304,7 +3461,7 @@ packages: integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 dev: false /hast-util-raw@7.2.3: @@ -3313,7 +3470,7 @@ packages: integrity: sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg== } dependencies: - "@types/hast": 2.3.9 + "@types/hast": 2.3.10 "@types/parse5": 6.0.3 hast-util-from-parse5: 7.1.2 hast-util-to-parse5: 7.1.0 @@ -3326,19 +3483,19 @@ packages: zwitch: 2.0.4 dev: false - /hast-util-raw@9.0.1: + /hast-util-raw@9.0.2: resolution: { - integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA== + integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA== } dependencies: - "@types/hast": 3.0.3 + "@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.0.2 + mdast-util-to-hast: 13.1.0 parse5: 7.1.2 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 @@ -3353,7 +3510,7 @@ packages: integrity: sha512-hT/SD/d/Meu+iobvgkffo1QecV8WeKWxwsNMzcTJsKw1cKTQKSR/7ArJeURLNJF9HDjp9nVoORyNNJxrvBye8Q== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 "@types/unist": 3.0.2 bcp-47-match: 2.0.3 comma-separated-tokens: 2.0.3 @@ -3365,7 +3522,7 @@ packages: hast-util-whitespace: 3.0.0 not: 0.1.0 nth-check: 2.1.1 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 unist-util-visit: 5.0.0 zwitch: 2.0.4 @@ -3378,17 +3535,17 @@ packages: } dependencies: "@types/estree": 1.0.5 - "@types/estree-jsx": 1.0.3 - "@types/hast": 3.0.3 + "@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.0.0 + mdast-util-mdx-jsx: 3.1.0 mdast-util-mdxjs-esm: 2.0.1 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 style-to-object: 0.4.4 unist-util-position: 5.0.0 @@ -3403,14 +3560,14 @@ packages: integrity: sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA== } dependencies: - "@types/hast": 2.3.9 + "@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.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 stringify-entities: 4.0.3 zwitch: 2.0.4 @@ -3422,15 +3579,15 @@ packages: integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw== } dependencies: - "@types/hast": 3.0.3 + "@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.1 + hast-util-raw: 9.0.2 hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 - property-information: 6.4.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 @@ -3443,16 +3600,16 @@ packages: } dependencies: "@types/estree": 1.0.5 - "@types/hast": 3.0.3 + "@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.0.0 + mdast-util-mdx-jsx: 3.1.0 mdast-util-mdxjs-esm: 2.0.1 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 style-to-object: 1.0.5 unist-util-position: 5.0.0 @@ -3467,9 +3624,9 @@ packages: integrity: sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw== } dependencies: - "@types/hast": 2.3.9 + "@types/hast": 2.3.10 comma-separated-tokens: 2.0.3 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -3481,10 +3638,10 @@ packages: integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 comma-separated-tokens: 2.0.3 devlop: 1.1.0 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -3496,7 +3653,7 @@ packages: integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 dev: false /hast-util-whitespace@2.0.1: @@ -3512,7 +3669,7 @@ packages: integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 dev: false /hastscript@7.2.0: @@ -3521,10 +3678,10 @@ packages: integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw== } dependencies: - "@types/hast": 2.3.9 + "@types/hast": 2.3.10 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 3.1.1 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 dev: false @@ -3534,10 +3691,10 @@ packages: integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 - property-information: 6.4.0 + property-information: 6.4.1 space-separated-tokens: 2.0.2 dev: false @@ -3577,16 +3734,6 @@ packages: engines: { node: ">=16.17.0" } dev: false - /iconv-lite@0.4.24: - resolution: - { - integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - } - engines: { node: ">=0.10.0" } - dependencies: - safer-buffer: 2.1.2 - dev: false - /ieee754@1.2.1: resolution: { @@ -3594,10 +3741,10 @@ packages: } dev: false - /ignore@5.3.0: + /ignore@5.3.1: resolution: { - integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== } engines: { node: ">= 4" } dev: true @@ -3644,6 +3791,15 @@ packages: integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } + /ini@1.3.8: + resolution: + { + integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + } + requiresBuild: true + dev: false + optional: true + /inline-style-parser@0.1.1: resolution: { @@ -3714,7 +3870,7 @@ packages: integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== } dependencies: - hasown: 2.0.0 + hasown: 2.0.1 dev: false /is-decimal@2.0.1: @@ -4010,6 +4166,7 @@ packages: { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } + dev: true /log-symbols@5.1.0: resolution: @@ -4048,10 +4205,10 @@ packages: yallist: 4.0.0 dev: false - /magic-string@0.30.5: + /magic-string@0.30.7: resolution: { - integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA== } engines: { node: ">=12" } dependencies: @@ -4146,7 +4303,7 @@ packages: ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 dev: false /mdast-util-gfm-footnote@2.0.0: @@ -4229,8 +4386,8 @@ packages: integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw== } dependencies: - "@types/estree-jsx": 1.0.3 - "@types/hast": 3.0.3 + "@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 @@ -4239,14 +4396,14 @@ packages: - supports-color dev: false - /mdast-util-mdx-jsx@3.0.0: + /mdast-util-mdx-jsx@3.1.0: resolution: { - integrity: sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA== + integrity: sha512-A8AJHlR7/wPQ3+Jre1+1rq040fX9A4Q1jG8JxmSNp/PLPHg80A6475wxTp3KzHpApFH6yWxFotHrJQA3dXP6/w== } dependencies: - "@types/estree-jsx": 1.0.3 - "@types/hast": 3.0.3 + "@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 @@ -4270,7 +4427,7 @@ packages: dependencies: mdast-util-from-markdown: 2.0.0 mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.0.0 + mdast-util-mdx-jsx: 3.1.0 mdast-util-mdxjs-esm: 2.0.1 mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: @@ -4283,8 +4440,8 @@ packages: integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== } dependencies: - "@types/estree-jsx": 1.0.3 - "@types/hast": 3.0.3 + "@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 @@ -4293,10 +4450,10 @@ packages: - supports-color dev: false - /mdast-util-phrasing@4.0.0: + /mdast-util-phrasing@4.1.0: resolution: { - integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA== + integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== } dependencies: "@types/mdast": 4.0.3 @@ -4309,7 +4466,7 @@ packages: integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 "@types/mdast": 4.0.3 "@ungap/structured-clone": 1.2.0 devlop: 1.1.0 @@ -4319,6 +4476,23 @@ packages: unist-util-visit: 5.0.0 dev: false + /mdast-util-to-hast@13.1.0: + resolution: + { + integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== + } + 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: { @@ -4328,7 +4502,7 @@ packages: "@types/mdast": 4.0.3 "@types/unist": 3.0.2 longest-streak: 3.1.0 - mdast-util-phrasing: 4.0.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 @@ -4372,7 +4546,7 @@ packages: micromark-factory-space: 2.0.0 micromark-factory-title: 2.0.0 micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.0.1 + 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 @@ -4392,7 +4566,7 @@ packages: devlop: 1.1.0 micromark-factory-space: 2.0.0 micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 parse-entities: 4.0.1 @@ -4404,7 +4578,7 @@ packages: integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== } dependencies: - micromark-util-character: 2.0.1 + 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 @@ -4419,7 +4593,7 @@ packages: devlop: 1.1.0 micromark-core-commonmark: 2.0.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + 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 @@ -4448,7 +4622,7 @@ packages: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4470,7 +4644,7 @@ packages: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4501,7 +4675,7 @@ packages: devlop: 1.1.0 micromark-factory-mdx-expression: 2.0.1 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + 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 @@ -4519,7 +4693,7 @@ packages: 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.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 vfile-message: 4.0.2 @@ -4543,7 +4717,7 @@ packages: "@types/estree": 1.0.5 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 - micromark-util-character: 2.0.1 + 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 @@ -4557,8 +4731,8 @@ packages: integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== } dependencies: - acorn: 8.11.2 - acorn-jsx: 5.3.2(acorn@8.11.2) + 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 @@ -4573,7 +4747,7 @@ packages: integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== } dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4585,7 +4759,7 @@ packages: } dependencies: devlop: 1.1.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4598,7 +4772,7 @@ packages: dependencies: "@types/estree": 1.0.5 devlop: 1.1.0 - micromark-util-character: 2.0.1 + 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 @@ -4612,7 +4786,7 @@ packages: integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== } dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-types: 2.0.0 dev: false @@ -4623,7 +4797,7 @@ packages: } dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4635,15 +4809,15 @@ packages: } dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false - /micromark-util-character@2.0.1: + /micromark-util-character@2.1.0: resolution: { - integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw== + integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== } dependencies: micromark-util-symbol: 2.0.0 @@ -4665,7 +4839,7 @@ packages: integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== } dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 dev: false @@ -4696,7 +4870,7 @@ packages: } dependencies: decode-named-character-reference: 1.0.2 - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-decode-numeric-character-reference: 2.0.1 micromark-util-symbol: 2.0.0 dev: false @@ -4755,7 +4929,7 @@ packages: integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== } dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.0 micromark-util-encode: 2.0.0 micromark-util-symbol: 2.0.0 dev: false @@ -4798,7 +4972,7 @@ packages: devlop: 1.1.0 micromark-core-commonmark: 2.0.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 + 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 @@ -4849,6 +5023,16 @@ packages: 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: resolution: { @@ -4858,12 +5042,23 @@ packages: brace-expansion: 1.1.11 dev: true - /ms@2.0.0: + /minimist@1.2.8: resolution: { - integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } + requiresBuild: true dev: false + optional: true + + /mkdirp-classic@0.5.3: + resolution: + { + integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + } + requiresBuild: true + dev: false + optional: true /ms@2.1.2: resolution: @@ -4871,13 +5066,6 @@ packages: integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== } - /ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - } - dev: false - /nanoid@3.3.7: resolution: { @@ -4887,6 +5075,15 @@ packages: 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: resolution: { @@ -4894,21 +5091,6 @@ packages: } dev: true - /needle@2.9.1: - resolution: - { - integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== - } - engines: { node: ">= 4.4.x" } - hasBin: true - dependencies: - debug: 3.2.7 - iconv-lite: 0.4.24 - sax: 1.3.0 - transitivePeerDependencies: - - supports-color - dev: false - /nlcst-to-string@3.1.1: resolution: { @@ -4918,6 +5100,27 @@ packages: "@types/nlcst": 1.0.4 dev: false + /node-abi@3.55.0: + resolution: + { + integrity: sha512-uPEjtyh2tFEvWYt4Jw7McOD5FPcHkcxm/tHZc5PWaDB3JYq0rGFUbgaAK+CT5pYpQddBfsZVWI08OwoRfdfbcQ== + } + engines: { node: ">=10" } + requiresBuild: true + dependencies: + semver: 7.6.0 + dev: false + optional: true + + /node-addon-api@6.1.0: + resolution: + { + integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + } + requiresBuild: true + dev: false + optional: true + /node-releases@2.0.14: resolution: { @@ -4966,7 +5169,6 @@ packages: } dependencies: wrappy: 1.0.2 - dev: true /onetime@5.1.2: resolution: @@ -5251,7 +5453,7 @@ packages: find-up: 4.1.0 dev: false - /postcss-nested@6.0.1(postcss@8.4.32): + /postcss-nested@6.0.1(postcss@8.4.35): resolution: { integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== @@ -5260,14 +5462,14 @@ packages: peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.32 - postcss-selector-parser: 6.0.14 + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 dev: false - /postcss-selector-parser@6.0.14: + /postcss-selector-parser@6.0.15: resolution: { - integrity: sha512-65xXYsT40i9GyWzlHQ5ShZoK7JZdySeOozi/tz2EezDo6c04q6+ckYMeoY7idaie1qp2dT5KoYQ2yky6JuoHnA== + integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== } engines: { node: ">=4" } dependencies: @@ -5275,10 +5477,10 @@ packages: util-deprecate: 1.0.2 dev: false - /postcss@8.4.32: + /postcss@8.4.35: resolution: { - integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== + integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== } engines: { node: ^10 || ^12 || >=14 } dependencies: @@ -5287,6 +5489,30 @@ packages: source-map-js: 1.0.2 dev: false + /prebuild-install@7.1.1: + resolution: + { + integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + } + 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 + /preferred-pm@3.1.2: resolution: { @@ -5318,22 +5544,22 @@ packages: fast-diff: 1.3.0 dev: true - /prettier-plugin-astro@0.12.3: + /prettier-plugin-astro@0.13.0: resolution: { - integrity: sha512-GthUSu3zCvmtVyqlArosez0xE08vSJ0R1sWurxIWpABaCkNGYFANoUdFkqmIo54EV2uPLGcVJzOucWvCjPBWvg== + integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g== } engines: { node: ^14.15.0 || >=16.0.0 } dependencies: "@astrojs/compiler": 1.8.2 - prettier: 3.1.1 - sass-formatter: 0.7.8 + prettier: 3.2.5 + sass-formatter: 0.7.9 dev: true - /prettier@3.1.1: + /prettier@3.2.5: resolution: { - integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw== + integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== } engines: { node: ">=14" } hasBin: true @@ -5347,19 +5573,6 @@ packages: engines: { node: ">=6" } dev: false - /probe-image-size@7.2.3: - resolution: - { - integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w== - } - dependencies: - lodash.merge: 4.6.2 - needle: 2.9.1 - stream-parser: 0.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /prompts@2.4.2: resolution: { @@ -5371,13 +5584,25 @@ packages: sisteransi: 1.0.5 dev: false - /property-information@6.4.0: + /property-information@6.4.1: resolution: { - integrity: sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ== + integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w== } 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: resolution: { @@ -5392,6 +5617,30 @@ packages: integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== } + /queue-tick@1.0.1: + resolution: + { + integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + } + 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: { @@ -5420,7 +5669,7 @@ packages: integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 "@ungap/structured-clone": 1.2.0 hast-util-is-element: 3.0.0 is-absolute-url: 4.0.1 @@ -5434,7 +5683,7 @@ packages: integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 hast-util-from-html: 2.0.1 unified: 11.0.4 dev: false @@ -5445,8 +5694,8 @@ packages: integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== } dependencies: - "@types/hast": 3.0.3 - hast-util-raw: 9.0.1 + "@types/hast": 3.0.4 + hast-util-raw: 9.0.2 vfile: 6.0.1 dev: false @@ -5456,7 +5705,7 @@ packages: integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 hast-util-to-html: 9.0.0 unified: 11.0.4 dev: false @@ -5467,7 +5716,7 @@ packages: integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg== } dependencies: - "@types/hast": 3.0.3 + "@types/hast": 3.0.4 rehype-parse: 9.0.0 rehype-stringify: 10.0.0 unified: 11.0.4 @@ -5487,13 +5736,13 @@ packages: - supports-color dev: false - /remark-expressive-code@0.30.1: + /remark-expressive-code@0.32.4: resolution: { - integrity: sha512-1cxsvXn1FyZsgaZXvkV+uoJLkg0rcS/k9Yemw211oD8Fblhh67Gb6ThxbuQdPf346kgg5D4R/iVyR49oAS5kbg== + integrity: sha512-khV7fVBpVDOyz9EXU+6MFwLj7BtY3DLVlNMMJYQcfp9ksLMxG/i83rIJbMUZCRof9bDBmFFlrF0VDvqJ0/MNeQ== } dependencies: - expressive-code: 0.30.1 + expressive-code: 0.32.4 hast-util-to-html: 8.0.4 unist-util-visit: 4.1.2 dev: false @@ -5514,10 +5763,10 @@ packages: - supports-color dev: false - /remark-mdx@3.0.0: + /remark-mdx@3.0.1: resolution: { - integrity: sha512-O7yfjuC6ra3NHPbRVxfflafAj3LTwx3b73aBvkEFU5z4PsD6FD4vrqJAkE5iNGLz71GdjXfgRqm3SQ0h0VuE7g== + integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA== } dependencies: mdast-util-mdx: 3.0.0 @@ -5540,29 +5789,29 @@ packages: - supports-color dev: false - /remark-rehype@11.0.0: + /remark-rehype@11.1.0: resolution: { - integrity: sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw== + integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== } dependencies: - "@types/hast": 3.0.3 + "@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.0.0: + /remark-smartypants@2.1.0: resolution: { - integrity: sha512-Rc0VDmr/yhnMQIz8n2ACYXlfw/P/XZev884QU1I5u+5DgJls32o97Vc1RbK3pfumLsJomS2yy8eT4Fxj/2MDVA== + integrity: sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } dependencies: retext: 8.1.0 retext-smartypants: 5.2.0 - unist-util-visit: 4.1.2 + unist-util-visit: 5.0.0 dev: false /remark-stringify@11.0.0: @@ -5671,27 +5920,29 @@ packages: glob: 7.2.3 dev: true - /rollup@4.9.1: + /rollup@4.12.0: resolution: { - integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw== + integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q== } engines: { node: ">=18.0.0", npm: ">=8.0.0" } hasBin: true + dependencies: + "@types/estree": 1.0.5 optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.9.1 - "@rollup/rollup-android-arm64": 4.9.1 - "@rollup/rollup-darwin-arm64": 4.9.1 - "@rollup/rollup-darwin-x64": 4.9.1 - "@rollup/rollup-linux-arm-gnueabihf": 4.9.1 - "@rollup/rollup-linux-arm64-gnu": 4.9.1 - "@rollup/rollup-linux-arm64-musl": 4.9.1 - "@rollup/rollup-linux-riscv64-gnu": 4.9.1 - "@rollup/rollup-linux-x64-gnu": 4.9.1 - "@rollup/rollup-linux-x64-musl": 4.9.1 - "@rollup/rollup-win32-arm64-msvc": 4.9.1 - "@rollup/rollup-win32-ia32-msvc": 4.9.1 - "@rollup/rollup-win32-x64-msvc": 4.9.1 + "@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 @@ -5717,17 +5968,10 @@ packages: } dev: false - /safer-buffer@2.1.2: + /sass-formatter@0.7.9: resolution: { - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - } - dev: false - - /sass-formatter@0.7.8: - resolution: - { - integrity: sha512-7fI2a8THglflhhYis7k06eUf92VQuJoXzEs2KRP0r1bluFxKFvLx0Ns7c478oYGM0fPfrr846ZRWVi2MAgHt9Q== + integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw== } dependencies: suf-log: 2.5.3 @@ -5758,10 +6002,10 @@ packages: hasBin: true dev: false - /semver@7.5.4: + /semver@7.6.0: resolution: { - integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== } engines: { node: ">=10" } hasBin: true @@ -5769,44 +6013,56 @@ packages: lru-cache: 6.0.0 dev: false - /server-destroy@1.0.1: + /sharp@0.32.6: resolution: { - integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== + integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== } - dev: false - - /sharp@0.33.1: - resolution: - { - integrity: sha512-iAYUnOdTqqZDb3QjMneBKINTllCJDZ3em6WaWy7NPECM4aHncvqHRm0v0bN9nqJxMiwamv5KIdauJ6lUzKDpTQ== - } - engines: { libvips: ">=8.15.0", node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + engines: { node: ">=14.15.0" } requiresBuild: true dependencies: color: 4.2.3 detect-libc: 2.0.2 - semver: 7.5.4 + 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 + + /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.1 - "@img/sharp-darwin-x64": 0.33.1 - "@img/sharp-libvips-darwin-arm64": 1.0.0 - "@img/sharp-libvips-darwin-x64": 1.0.0 - "@img/sharp-libvips-linux-arm": 1.0.0 - "@img/sharp-libvips-linux-arm64": 1.0.0 - "@img/sharp-libvips-linux-s390x": 1.0.0 - "@img/sharp-libvips-linux-x64": 1.0.0 - "@img/sharp-libvips-linuxmusl-arm64": 1.0.0 - "@img/sharp-libvips-linuxmusl-x64": 1.0.0 - "@img/sharp-linux-arm": 0.33.1 - "@img/sharp-linux-arm64": 0.33.1 - "@img/sharp-linux-s390x": 0.33.1 - "@img/sharp-linux-x64": 0.33.1 - "@img/sharp-linuxmusl-arm64": 0.33.1 - "@img/sharp-linuxmusl-x64": 0.33.1 - "@img/sharp-wasm32": 0.33.1 - "@img/sharp-win32-ia32": 0.33.1 - "@img/sharp-win32-x64": 0.33.1 + "@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: @@ -5825,13 +6081,11 @@ packages: } engines: { node: ">=8" } - /shikiji@0.6.13: + /shikiji-core@0.9.19: resolution: { - integrity: sha512-4T7X39csvhT0p7GDnq9vysWddf2b6BeioiN3Ymhnt3xcy9tXmDcnsEFVxX18Z4YcQgEE/w48dLJ4pPPUcG9KkA== + integrity: sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw== } - dependencies: - hast-util-to-html: 9.0.0 dev: false /shikiji@0.8.7: @@ -5843,6 +6097,15 @@ packages: 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: { @@ -5858,6 +6121,28 @@ packages: 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: resolution: { @@ -5928,16 +6213,19 @@ packages: bl: 5.1.0 dev: false - /stream-parser@0.3.1: + /streamx@2.16.1: resolution: { - integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ== + integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== } + requiresBuild: true dependencies: - debug: 2.6.9 - transitivePeerDependencies: - - supports-color + 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: resolution: @@ -5975,10 +6263,10 @@ packages: strip-ansi: 7.1.0 dev: false - /string-width@7.0.0: + /string-width@7.1.0: resolution: { - integrity: sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw== + integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== } engines: { node: ">=18" } dependencies: @@ -6049,6 +6337,16 @@ packages: 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: resolution: { @@ -6126,10 +6424,68 @@ packages: } engines: { node: ^14.18.0 || >=16.0.0 } dependencies: - "@pkgr/core": 0.1.0 + "@pkgr/core": 0.1.1 tslib: 2.6.2 dev: true + /tar-fs@2.1.1: + resolution: + { + integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + } + 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: { @@ -6162,17 +6518,17 @@ packages: } dev: false - /trough@2.1.0: + /trough@2.2.0: resolution: { - integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== + integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== } dev: false - /tsconfck@3.0.0: + /tsconfck@3.0.2: resolution: { - integrity: sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A== + integrity: sha512-6lWtFjwuhS3XI4HsX4Zg0izOI3FU/AI9EGVlPEUMDIhvLPMD4wkiof0WCoDgW7qY+Dy198g4d9miAqUHWHFH6Q== } engines: { node: ^18 || >=20 } hasBin: true @@ -6189,6 +6545,17 @@ packages: integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== } + /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: resolution: { @@ -6233,7 +6600,7 @@ packages: extend: 3.0.2 is-buffer: 2.0.5 is-plain-obj: 4.1.0 - trough: 2.1.0 + trough: 2.2.0 vfile: 5.3.7 dev: false @@ -6248,7 +6615,7 @@ packages: devlop: 1.1.0 extend: 3.0.2 is-plain-obj: 4.1.0 - trough: 2.1.0 + trough: 2.2.0 vfile: 6.0.1 dev: false @@ -6397,7 +6764,7 @@ packages: unist-util-visit-parents: 6.0.1 dev: false - /update-browserslist-db@1.0.13(browserslist@4.22.2): + /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: { integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== @@ -6406,8 +6773,8 @@ packages: peerDependencies: browserslist: ">= 4.21.0" dependencies: - browserslist: 4.22.2 - escalade: 3.1.1 + browserslist: 4.23.0 + escalade: 3.1.2 picocolors: 1.0.0 dev: false @@ -6490,10 +6857,10 @@ packages: vfile-message: 4.0.2 dev: false - /vite@5.0.10: + /vite@5.1.3: resolution: { - integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw== + integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew== } engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true @@ -6521,14 +6888,14 @@ packages: terser: optional: true dependencies: - esbuild: 0.19.10 - postcss: 8.4.32 - rollup: 4.9.1 + esbuild: 0.19.12 + postcss: 8.4.35 + rollup: 4.12.0 optionalDependencies: fsevents: 2.3.3 dev: false - /vitefu@0.2.5(vite@5.0.10): + /vitefu@0.2.5(vite@5.1.3): resolution: { integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== @@ -6539,7 +6906,7 @@ packages: vite: optional: true dependencies: - vite: 5.0.10 + vite: 5.1.3 dev: false /web-namespaces@2.0.1: @@ -6616,7 +6983,6 @@ packages: { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } - dev: true /xml-js@1.6.11: resolution: diff --git a/docs/src/content/docs/guides/details.md b/docs/src/content/docs/guides/details.md index 35549a0f..ea05b3d0 100644 --- a/docs/src/content/docs/guides/details.md +++ b/docs/src/content/docs/guides/details.md @@ -88,7 +88,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 +97,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 +109,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 +124,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 +132,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", diff --git a/docs/src/content/docs/guides/dialogue.md b/docs/src/content/docs/guides/dialogue.md index a2f1aa86..77d8631d 100644 --- a/docs/src/content/docs/guides/dialogue.md +++ b/docs/src/content/docs/guides/dialogue.md @@ -159,3 +159,44 @@ Defining `` in the `` tag instead of a ` + + Scientist5 + + + Hi how are you? + example_new_slate_Text + + + + + example_new_slate_Text + + I'm good! + + + +``` + +NH will merge together `` nodes that have the same `` field, adding their `` together. No other changes will be merged. + +NH can also add new `` nodes into the text, however you have to add ``s that link to them for them to ever be read by the player. + +Be careful to use unique names to ensure optimal compatibility between mods. Consider prefixing the names of your nodes with the name of your mod. + +To use this additional dialogue you need to reference it in a planet config file: + +```json +"dialogue": [ + { + "pathToExistingDialogue": "Sector_TH/Sector_Village/Sector_StartingCamp/Characters_StartingCamp/Villager_HEA_Slate/ConversationZone_RSci", + "xmlFile": "planets/text/Slate.xml" + } +] +``` 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/ship-log.md b/docs/src/content/docs/guides/ship-log.md index 15dafd5c..efcc2d42 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, 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/secret.md b/docs/src/content/docs/secret.md new file mode 100644 index 00000000..9855c1eb --- /dev/null +++ b/docs/src/content/docs/secret.md @@ -0,0 +1,15 @@ +--- +title: "Secret" +--- + +# Hello!! + +![Cow](https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F964831e7eccb34007e82c065a50679ef%2Ftenor.gif%3Fitemid%3D18924714&f=1&nofb=1) + +## It's Morbin' Time + +![Morb](https://user-images.githubusercontent.com/25644444/178856213-44cb0a38-6d3d-4af6-b7f8-0ae6cda8d44a.png) + +## Help + +They made me recreate this page diff --git a/docs/src/content/docs/start-here/getting-started.md b/docs/src/content/docs/start-here/getting-started.md index 8f4b709d..c37163bb 100644 --- a/docs/src/content/docs/start-here/getting-started.md +++ b/docs/src/content/docs/start-here/getting-started.md @@ -30,7 +30,7 @@ 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,