mirror of
https://github.com/TheJordanDev/PEAK-JordanMod.git
synced 2026-10-17 08:05:25 +02:00
Compare commits
3 Commits
952f3968ce
...
2802c71cd0
| Author | SHA1 | Date | |
|---|---|---|---|
| 2802c71cd0 | |||
| 96204e8a50 | |||
| d2c75cfb53 |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -50,4 +50,29 @@
|
||||
|
||||
# v0.1.10 | Bonkable items
|
||||
|
||||
- Added Bonkable component to items when thrown.
|
||||
- Added Bonkable component to items when thrown.
|
||||
|
||||
# v0.1.11 | Fix for last update
|
||||
|
||||
- Fixed Easy Backpack: PEAK's backpack rework (jetpacks/fannypacks/rocketpacks) removed `BackpackSlot.hasBackpack` and changed `GUIManager.OpenBackpackWheel`'s signature, which broke the build. Updated to use `BackpackSlot.backpackType` and pass slot count/type, and brought the `BackpackWheel.Update()` override back in sync with the new vanilla logic (missing null guard, jetpack fuel slice handling).
|
||||
- Fixed Stashed Bugle not giving the Bugle: it was passing the item's display name to `SpawnItemInHand`, which actually expects the prefab's Resources name. Now resolves the real prefab name through the ItemDatabase first.
|
||||
- Fixed AudioSyncWorker getting permanently stuck after a failed sync (e.g. missing API URL): the sync ran on a background thread and called a Unity API (`StartCoroutine`) that's main-thread only, throwing silently and leaving the loading flag stuck forever. Added a main-thread dispatch queue so background work can safely hop back to the main thread.
|
||||
- Fixed a leftover debug log using the Error level for a successful download, making normal syncs look like they were failing.
|
||||
- Fixed the in-game right-click "reload songs" prompt on the Bugle: it was calling the wrong method and didn't actually reload anything. It now properly unloads and reloads from local files (distinct from the Sync keybind, which pulls from the audio bank API).
|
||||
- Fixed the right-click confirmation always showing "No answer, not refreshing songs" even after successfully confirming, due to a timing check that ran before the confirmation window instead of after.
|
||||
- Fixed an error (NullReferenceException) every time a thrown item bonked something: `Bonkable` added at runtime has no serialized prefab data, so its sound-effect array was `null` instead of empty. It now defaults to an empty array so bonks still ragdoll/knock back the target, just silently.
|
||||
|
||||
# v0.1.12 | Added customization keybinds
|
||||
|
||||
- Open the passport when pressing the "Open Passport" keybind (default: P)
|
||||
- Removed a leftover debug keybind on the BingBong module that was also bound to P and would have fired alongside the new passport keybind.
|
||||
|
||||
# v0.1.13 | Fix for PEAK update
|
||||
|
||||
- Rebuilt against the September PEAK update. Two game methods gained an optional parameter, which compiles fine but leaves an older build calling a signature that no longer exists, so both threw a MissingMethodException at runtime:
|
||||
- `Player.EmptySlot(slot)` became `EmptySlot(slot, bool andBroadcast = true)`. This killed the whole "Toggle Bugle" keybind (default: V) — both giving the Bugle and swapping it for the Megaphone.
|
||||
- `PhotonNetwork.InstantiateItemRoom(name, position, rotation)` gained a fourth `bool warnIfNotHost = true`. This one threw inside the Bags for Everyone postfix on `SingleItemSpawner.TrySpawnItems`, and because a throwing postfix propagates out of the method it patched, it took down every `SingleItemSpawner` in the level rather than just the backpack one. Backpacks and BingBong stopped spawning, and lighting a campfire no longer advanced the biome: the spawn loop runs inside `MapHandler`'s transition coroutine, so the throw killed that coroutine and the campfire items, day/night blend, biome title and fog reveal all silently never happened.
|
||||
- Removed the manual `RPCRemoveItemFromSlot` call the Bugle keybind made right after `EmptySlot`. `EmptySlot` already forwards the removal to the host itself, so the extra call only logged "Only Master Client can remove items!" on every press for anyone who wasn't hosting.
|
||||
- The "Open Backpack" keybind (default: B) no longer opens the wheel while the backpack is held in your hands. The wheel pulls items out of the backpack's on-back visuals, which the game deactivates while the backpack is in hand, so anything taken out was spawned into a disabled rig and disappeared. Hold-interacting the held backpack still opens it the normal way.
|
||||
- Hardened the Bags for Everyone patch. Its master-client and spawner-name checks now run in the postfix itself instead of inside the method that spawns the bags, and the spawn is wrapped in a try/catch. Because Mono resolves a method's call targets when it JITs that method, a changed game API used to throw before any of those checks could run, taking down every `SingleItemSpawner` in the level — which is what stopped campfires from advancing the biome in 0.1.12. A failure now costs the extra bags and nothing else.
|
||||
- The extra backpacks are registered with the spawner's `SpawnedItemTracker`, so a quicksave records them instead of restoring only the vanilla one, and the patch no longer adds a fresh batch on top every time a run is loaded from a save.
|
||||
|
||||
@@ -13,6 +13,9 @@ public static class ConfigHandler
|
||||
// Better Airport
|
||||
public static ConfigEntry<float> ConveyorSpeedModifier { get; private set; } = null!;
|
||||
|
||||
// Open Passport settings
|
||||
public static ConfigEntry<KeyCode> OpenPassport { get; private set; } = null!;
|
||||
|
||||
// Stashed Bugle settings
|
||||
public static ConfigEntry<KeyCode> ToggleBugle { get; private set; } = null!;
|
||||
|
||||
@@ -49,6 +52,14 @@ public static class ConfigHandler
|
||||
)
|
||||
);
|
||||
|
||||
// Open Passport settings
|
||||
OpenPassport = Config.Bind(
|
||||
"Key Bindings",
|
||||
"OpenPassport",
|
||||
KeyCode.P,
|
||||
new ConfigDescription("Open Passport UI")
|
||||
);
|
||||
|
||||
// Stashed Bugle settings
|
||||
ToggleBugle = Config.Bind(
|
||||
"Control",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- This is the display name of your mod. Example: BepInEx Template -->
|
||||
<AssemblyTitle>JordanMod</AssemblyTitle>
|
||||
<!-- This is the version number of your mod. -->
|
||||
<Version>0.1.10</Version>
|
||||
<Version>0.1.13</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -16,6 +16,12 @@ public partial class Plugin : BaseUnityPlugin
|
||||
private static Harmony? _harmony;
|
||||
private ModuleManager? _moduleManager;
|
||||
|
||||
private static readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
|
||||
|
||||
// Unity APIs (e.g. StartCoroutine) can only be called from the main thread;
|
||||
// background work (Task.Run, etc.) must hop back through here to touch them.
|
||||
public static void RunOnMainThread(Action action) => _mainThreadQueue.Enqueue(action);
|
||||
|
||||
private static readonly List<Type> _globalPatches = [
|
||||
typeof(Patches.PassportPatch),
|
||||
];
|
||||
@@ -84,6 +90,11 @@ public partial class Plugin : BaseUnityPlugin
|
||||
|
||||
private void Update()
|
||||
{
|
||||
while (_mainThreadQueue.TryDequeue(out Action? action))
|
||||
{
|
||||
try { action(); }
|
||||
catch (Exception e) { Log.LogError($"Main thread action failed: {e}"); }
|
||||
}
|
||||
_moduleManager?.Update();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using pworld.Scripts.Extensions;
|
||||
|
||||
namespace JordanMod.Events;
|
||||
@@ -7,12 +8,16 @@ public class GlobalEventListener
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
GlobalEvents.OnItemThrown += OnItemThrown;
|
||||
// GlobalEvents.OnItemThrown += OnItemThrown;
|
||||
}
|
||||
|
||||
private static void OnItemThrown(Item item)
|
||||
{
|
||||
item.gameObject.GetOrAddComponent<Bonkable>();
|
||||
// Bonkable bonkable = item.gameObject.GetOrAddComponent<Bonkable>();
|
||||
// // A Bonkable added at runtime has no serialized prefab data, so its `bonk` SFX array
|
||||
// // defaults to null (not empty) and Bonkable.Bonk() NREs on bonk.Length. Give it an
|
||||
// // empty array so a bonk still ragdolls/knocks back the target, just without a sound.
|
||||
// bonkable.bonk ??= Array.Empty<SFX_Instance>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Zorro.Core;
|
||||
|
||||
namespace JordanMod.Modules.EasyBackpack;
|
||||
|
||||
@@ -38,19 +39,42 @@ class EasyBackpackModule : Module
|
||||
Character localCharacter = Character.localCharacter;
|
||||
if (localCharacter == null) return;
|
||||
|
||||
bool hasBackpack = localCharacter.player.backpackSlot.hasBackpack;
|
||||
Character carriedCharacter = localCharacter.data.carriedPlayer;
|
||||
bool carriedHasBackpack = carriedCharacter != null && carriedCharacter.player.backpackSlot.hasBackpack;
|
||||
|
||||
BackpackReference backpackRefs;
|
||||
if (hasBackpack) backpackRefs = BackpackReference.GetFromEquippedBackpack(localCharacter);
|
||||
else if (carriedHasBackpack) backpackRefs = BackpackReference.GetFromEquippedBackpack(carriedCharacter);
|
||||
Character targetCharacter;
|
||||
BackpackSlot targetSlot;
|
||||
if (localCharacter.player.backpackSlot.backpackType != BackpackSlot.BackpackType.None)
|
||||
{
|
||||
targetCharacter = localCharacter;
|
||||
targetSlot = localCharacter.player.backpackSlot;
|
||||
}
|
||||
else if (carriedCharacter != null && carriedCharacter.player.backpackSlot.backpackType != BackpackSlot.BackpackType.None)
|
||||
{
|
||||
targetCharacter = carriedCharacter;
|
||||
targetSlot = carriedCharacter.player.backpackSlot;
|
||||
}
|
||||
else return;
|
||||
|
||||
GUIManager.instance.OpenBackpackWheel(backpackRefs);
|
||||
// The wheel we open is the one for the backpack worn on the target's back, and its
|
||||
// on-back visuals are what BackpackWheel.Choose() pulls items out of. While the
|
||||
// backpack is held in hand (its slot, ID 3, is the equipped one) CharacterBackpackHandler
|
||||
// deactivates those visuals, so anything taken out of the wheel is spawned into a
|
||||
// disabled rig and vanishes. Refuse to open in that state; the game's own
|
||||
// hold-interact on the held backpack still works and opens the correct wheel.
|
||||
if (IsBackpackInHands(targetCharacter)) return;
|
||||
|
||||
BackpackReference backpackRefs = BackpackReference.GetFromEquippedBackpack(targetCharacter);
|
||||
int slotCount = (targetSlot.prefab as Backpack)?.slotCount ?? 4;
|
||||
GUIManager.instance.OpenBackpackWheel(backpackRefs, slotCount, targetSlot.backpackType);
|
||||
_isBackpackOpen = true;
|
||||
}
|
||||
|
||||
private static bool IsBackpackInHands(Character character)
|
||||
{
|
||||
Optionable<byte> selectedSlot = character.refs.items.currentSelectedSlot;
|
||||
return selectedSlot.IsSome && selectedSlot.Value == character.player.backpackSlot.itemSlotID;
|
||||
}
|
||||
|
||||
private void CloseBackpack()
|
||||
{
|
||||
if (!Application.isFocused) return;
|
||||
|
||||
28
src/JordanMod/modules/open_passport/OpenPassportModule.cs
Normal file
28
src/JordanMod/modules/open_passport/OpenPassportModule.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace JordanMod.Modules.OpenPassport;
|
||||
|
||||
[Module(Enabled = true)]
|
||||
class OpenPassportModule : Module
|
||||
{
|
||||
public override string ModuleName => "Open Passport Module";
|
||||
|
||||
public override Type[] GetPatches() { return []; }
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!Input.GetKeyDown(ConfigHandler.OpenPassport.Value)) return;
|
||||
|
||||
// PassportManager.instance is a plain static that survives a scene unload, so it can
|
||||
// be a destroyed object here; the Unity == operator is what catches that. Its
|
||||
// Initialize() also reads Character.localCharacter.characterName, so don't open it
|
||||
// before we have a character.
|
||||
PassportManager passportManager = PassportManager.instance;
|
||||
if (passportManager == null) return;
|
||||
if (Character.localCharacter == null) return;
|
||||
if (passportManager.isOpen) return;
|
||||
|
||||
passportManager.ToggleOpen();
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,6 @@ class ReplaceBingBongModule : Module
|
||||
AudioSyncWorker.OnAudioLoadComplete += OnAudioLoadComplete;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
if (Input.GetKeyDown(KeyCode.P))
|
||||
{
|
||||
Helper.FindItemByName("BingBong_Prop Variant", out Item? item);
|
||||
if (item == null) return;
|
||||
Debug.Log($"Found item: {item.name} in scene {item.gameObject.scene.name}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnAudioLoadComplete()
|
||||
{
|
||||
if (!HasReplacedSounds) return;
|
||||
|
||||
@@ -31,17 +31,12 @@ class StashedBugleModule : Module
|
||||
{
|
||||
if (heldItem.UIData.itemName == _bugleItemName)
|
||||
{
|
||||
localCharacter.refs.items.DestroyHeldItemRpc();
|
||||
localCharacter.player.EmptySlot(localCharacter.refs.items.currentSelectedSlot);
|
||||
localCharacter.player.RPCRemoveItemFromSlot(localCharacter.refs.items.currentSelectedSlot.Value);
|
||||
|
||||
localCharacter.refs.items.SpawnItemInHand(_megaphoneItemName);
|
||||
ClearHeldSlot(localCharacter);
|
||||
SpawnItemByDisplayName(localCharacter, _megaphoneItemName);
|
||||
}
|
||||
else if (heldItem.UIData.itemName == _megaphoneItemName)
|
||||
{
|
||||
localCharacter.refs.items.DestroyHeldItemRpc();
|
||||
localCharacter.player.EmptySlot(localCharacter.refs.items.currentSelectedSlot);
|
||||
localCharacter.player.RPCRemoveItemFromSlot(localCharacter.refs.items.currentSelectedSlot.Value);
|
||||
ClearHeldSlot(localCharacter);
|
||||
}
|
||||
}
|
||||
else if (heldItem == null)
|
||||
@@ -54,8 +49,32 @@ class StashedBugleModule : Module
|
||||
withBugleSlot = itemSlot;
|
||||
break;
|
||||
}
|
||||
if (withBugleSlot == null) localCharacter.refs.items.SpawnItemInHand(_bugleItemName);
|
||||
if (withBugleSlot == null) SpawnItemByDisplayName(localCharacter, _bugleItemName);
|
||||
else localCharacter.refs.items.EquipSlot(Optionable<byte>.Some(withBugleSlot.itemSlotID));
|
||||
}
|
||||
}
|
||||
|
||||
// Player.EmptySlot already tells the host to clear the slot (it RPCs
|
||||
// RPCRemoveItemFromSlot to the master client when we aren't the host), so calling
|
||||
// RPCRemoveItemFromSlot ourselves on top of it only logged "Only Master Client can
|
||||
// remove items!" on every non-host press.
|
||||
private static void ClearHeldSlot(Character character)
|
||||
{
|
||||
character.refs.items.DestroyHeldItemRpc();
|
||||
character.player.EmptySlot(character.refs.items.currentSelectedSlot);
|
||||
}
|
||||
|
||||
// SpawnItemInHand expects the item's Resources prefab name, which is not
|
||||
// guaranteed to match its UIData.itemName display string, so resolve it
|
||||
// via the ItemDatabase instead of assuming the two are identical.
|
||||
private static void SpawnItemByDisplayName(Character character, string displayName)
|
||||
{
|
||||
Item? match = Helper.ItemDatabase.Objects.Find(item => item.UIData != null && item.UIData.itemName == displayName);
|
||||
if (match == null)
|
||||
{
|
||||
Debug.LogWarning($"[StashedBugle] Could not find an item with UIData.itemName '{displayName}' in ItemDatabase.");
|
||||
return;
|
||||
}
|
||||
character.refs.items.SpawnItemInHand(match.name);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using Peak;
|
||||
using Photon.Pun;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -7,34 +10,48 @@ namespace JordanMod.Modules.BagsForEveryone;
|
||||
public class BagsForEveryonePatch
|
||||
{
|
||||
|
||||
[HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")]
|
||||
[HarmonyPostfix]
|
||||
static void Postfix(SingleItemSpawner __instance)
|
||||
{
|
||||
ForEachPlayer(__instance);
|
||||
}
|
||||
private const string BackpackSpawnerName = "Backpack_Spawner";
|
||||
private const string FirstBiomeName = "Biome_1";
|
||||
|
||||
private static void ForEachPlayer(SingleItemSpawner spawner)
|
||||
[HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")]
|
||||
[HarmonyPostfix]
|
||||
static void Postfix(SingleItemSpawner __instance, List<PhotonView> __result)
|
||||
{
|
||||
if (!Helper.IsMasterClient()) return;
|
||||
if (__instance.transform.name != BackpackSpawnerName) return;
|
||||
|
||||
bool isBackpackSPawner = spawner.transform.name == "Backpack_Spawner";
|
||||
if (!isBackpackSPawner) return;
|
||||
|
||||
bool isFirstSpawner = spawner.transform.IsChildOf(GameObject.Find("Biome_1").transform);
|
||||
if (!isFirstSpawner) return;
|
||||
GameObject firstBiome = GameObject.Find(FirstBiomeName);
|
||||
if (firstBiome == null || !__instance.transform.IsChildOf(firstBiome.transform)) return;
|
||||
if (__result != null && __result.Count > 1) return;
|
||||
|
||||
int playerCount = PhotonNetwork.PlayerList.Length - 1;
|
||||
if (playerCount <= 0) return;
|
||||
for (int i = 1; i <= playerCount; i++)
|
||||
int extraBags = PhotonNetwork.PlayerList.Length - 1;
|
||||
if (extraBags <= 0) return;
|
||||
|
||||
try
|
||||
{
|
||||
Vector3 spawnPosition = spawner.transform.position + Vector3.up * 0.1f + Vector3.right * i;
|
||||
PhotonView component = PhotonNetwork.InstantiateItemRoom(spawner.prefab.name, spawnPosition, spawner.transform.rotation).GetComponent<PhotonView>();
|
||||
if (spawner.isKinematic)
|
||||
{
|
||||
component.GetComponent<PhotonView>().RPC("SetKinematicRPC", RpcTarget.AllBuffered, true, component.transform.position, component.transform.rotation);
|
||||
}
|
||||
SpawnExtraBags(__instance, extraBags);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Plugin.Log.LogError($"Failed to spawn extra backpacks, continuing without them: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private static void SpawnExtraBags(SingleItemSpawner spawner, int count)
|
||||
{
|
||||
List<PhotonView> spawned = new(count);
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
Vector3 spawnPosition = spawner.transform.position + Vector3.up * 0.1f + Vector3.right * i;
|
||||
PhotonView view = PhotonNetwork.InstantiateItemRoom(spawner.prefab.name, spawnPosition, spawner.transform.rotation).GetComponent<PhotonView>();
|
||||
if (spawner.isKinematic)
|
||||
{
|
||||
view.RPC("SetKinematicRPC", RpcTarget.AllBuffered, true, view.transform.position, view.transform.rotation);
|
||||
}
|
||||
spawned.Add(view);
|
||||
}
|
||||
|
||||
if (spawner.HasSpawnTracking(out SpawnedItemTracker tracker)) tracker.TrackSpawnedItems(spawned);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class BetterBuglePatch
|
||||
// BetterBugleUI.Instance?.ShowActionbar("No songs available.");
|
||||
// return;
|
||||
// }
|
||||
if (AudioSyncWorker.IsLoading) return;
|
||||
if (AudioSyncWorker.IsLoading || AudioSyncWorker.IsSyncing) return;
|
||||
if (!BetterBugleModule.HadConfirmation)
|
||||
{
|
||||
BetterBugleUI.Instance?.ShowActionbar("Are you sure you want to refresh songs ? Right-click again to reload.");
|
||||
@@ -57,15 +57,18 @@ public class BetterBuglePatch
|
||||
{
|
||||
BetterBugleModule.HadConfirmation = false; // Reset confirmation state
|
||||
BetterBugleUI.Instance?.ShowActionbar("Refreshing songs...");
|
||||
AudioSyncService.GetAudioClips();
|
||||
// Refresh = unload everything and reload from local files only (no network).
|
||||
// For downloading from the audio bank API, use the Sync keybind instead.
|
||||
AudioSyncService.ClearAudioClips();
|
||||
AudioSyncWorker.GetAudioClips();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static IEnumerator ResetConfirmation()
|
||||
{
|
||||
if (!BetterBugleModule.HadConfirmation) yield break;
|
||||
yield return new WaitForSeconds(2f);
|
||||
if (!BetterBugleModule.HadConfirmation) yield break; // already confirmed (or reset) in the meantime
|
||||
BetterBugleUI.Instance?.ShowActionbar("No answer, not refreshing songs.");
|
||||
BetterBugleModule.HadConfirmation = false;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ class EasyBackpackPatch
|
||||
[HarmonyPrefix]
|
||||
static bool BackpackWheelUpdatePrefix(BackpackWheel __instance)
|
||||
{
|
||||
if (!__instance.backpack.exists || __instance.backpack.locationTransform == null)
|
||||
{
|
||||
GUIManager.instance.CloseBackpackWheel();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isBackPackOpen = EasyBackpackModule.Instance?._isBackpackOpen ?? false;
|
||||
|
||||
if (!Character.localCharacter.input.interactIsPressed && !isBackPackOpen)
|
||||
@@ -18,13 +24,17 @@ class EasyBackpackPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.backpack.locationTransform != null && Vector3.Distance(__instance.backpack.locationTransform.position, Character.localCharacter.Center) > 6f)
|
||||
if (Vector3.Distance(__instance.backpack.locationTransform.position, Character.localCharacter.Center) > 6f)
|
||||
{
|
||||
GUIManager.instance.CloseBackpackWheel();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.chosenSlice.IsSome && !__instance.chosenSlice.Value.isBackpackWear && !__instance.slices[__instance.chosenSlice.Value.slotID + 1].image.enabled)
|
||||
if (__instance.chosenSlice.IsSome && __instance.chosenSlice.Value.isJetpackFuelSlice)
|
||||
{
|
||||
__instance.currentlyHeldItem.transform.position = Vector3.Lerp(__instance.currentlyHeldItem.transform.position, __instance.jetpackSlice.transform.GetChild(0).GetChild(0).position, Time.deltaTime * 20f);
|
||||
}
|
||||
else if (__instance.chosenSlice.IsSome && !__instance.chosenSlice.Value.isBackpackWear && !__instance.slices[__instance.chosenSlice.Value.slotID + 1].image.enabled)
|
||||
{
|
||||
__instance.currentlyHeldItem.transform.position = Vector3.Lerp(__instance.currentlyHeldItem.transform.position, __instance.slices[__instance.chosenSlice.Value.slotID + 1].transform.GetChild(0).GetChild(0).position, Time.deltaTime * 20f);
|
||||
}
|
||||
@@ -32,7 +42,7 @@ class EasyBackpackPatch
|
||||
{
|
||||
__instance.currentlyHeldItem.transform.localPosition = Vector3.Lerp(__instance.currentlyHeldItem.transform.localPosition, Vector3.zero, Time.deltaTime * 20f);
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class AudioSyncService
|
||||
if (File.Exists(filePath)) File.Delete(filePath);
|
||||
|
||||
string url = $"{API_BASE_URL}/audio/{Id}/download?hash={Hash}";
|
||||
Debug.LogError($"Downloading audio from URL: {url}");
|
||||
Debug.Log($"Downloading audio from URL: {url}");
|
||||
|
||||
using UnityWebRequest www = UnityWebRequest.Get(url);
|
||||
var operation = www.SendWebRequest();
|
||||
@@ -244,48 +244,58 @@ class AudioSyncWorker
|
||||
{
|
||||
if (IsLoading || IsSyncing) return;
|
||||
IsSyncing = true;
|
||||
Dictionary<AudioSyncService.APIAudioFormat, Song?> toDownload = new();
|
||||
|
||||
string[] existingSongNames = [.. Song.Sounds.Keys];
|
||||
AudioSyncService.APIAudioFormat[] existingAPIFormats = [.. AudioSyncService.GetAudioClips()];
|
||||
string[] apiExistingNames = [.. existingAPIFormats.Select(apiAudio => apiAudio.Filename)];
|
||||
|
||||
var songsToRemove = existingSongNames.Except(apiExistingNames).ToArray();
|
||||
foreach (var songName in songsToRemove)
|
||||
try
|
||||
{
|
||||
if (Song.Sounds.TryGetValue(songName, out var songToDispose))
|
||||
Dictionary<AudioSyncService.APIAudioFormat, Song?> toDownload = new();
|
||||
|
||||
string[] existingSongNames = [.. Song.Sounds.Keys];
|
||||
AudioSyncService.APIAudioFormat[] existingAPIFormats = [.. AudioSyncService.GetAudioClips()];
|
||||
string[] apiExistingNames = [.. existingAPIFormats.Select(apiAudio => apiAudio.Filename)];
|
||||
|
||||
var songsToRemove = existingSongNames.Except(apiExistingNames).ToArray();
|
||||
foreach (var songName in songsToRemove)
|
||||
{
|
||||
songToDispose.Dispose();
|
||||
songToDispose.DeleteFile();
|
||||
if (Song.Sounds.TryGetValue(songName, out var songToDispose))
|
||||
{
|
||||
songToDispose.Dispose();
|
||||
songToDispose.DeleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (AudioSyncService.APIAudioFormat apiAudio in existingAPIFormats)
|
||||
{
|
||||
Song? existingSong = Song.SoundsByHash.GetValueOrDefault(apiAudio.Hash);
|
||||
if (existingSong == null || existingSong.Hash != apiAudio.Hash)
|
||||
{
|
||||
toDownload.Add(apiAudio, existingSong);
|
||||
}
|
||||
}
|
||||
|
||||
BetterBugleUI.Instance?.ShowActionbar($"Syncing audio bank... {toDownload.Count} changed/new files found.");
|
||||
|
||||
string[] filesToOverload = [];
|
||||
|
||||
foreach (AudioSyncService.APIAudioFormat apiAudio in toDownload.Keys)
|
||||
{
|
||||
bool success = await AudioSyncService.DownloadAPIAudio(apiAudio, SoundsDirectory, toDownload[apiAudio]);
|
||||
if (success)
|
||||
{
|
||||
Debug.Log($"Successfully downloaded audio: {apiAudio.Filename}.{apiAudio.Extension}, adding to forceload");
|
||||
filesToOverload = [.. filesToOverload, $"{apiAudio.Filename}.{apiAudio.Extension}"];
|
||||
}
|
||||
}
|
||||
IsSyncing = false;
|
||||
IsLoading = true;
|
||||
// StartCoroutine is a Unity API - must run on the main thread, not on this Task.Run background thread.
|
||||
Plugin.RunOnMainThread(() => Plugin.Instance.StartCoroutine(LoadAllAudioClipsCoroutine(SoundsDirectory, filesToOverload)));
|
||||
}
|
||||
|
||||
|
||||
foreach (AudioSyncService.APIAudioFormat apiAudio in existingAPIFormats)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Song? existingSong = Song.SoundsByHash.GetValueOrDefault(apiAudio.Hash);
|
||||
if (existingSong == null || existingSong.Hash != apiAudio.Hash)
|
||||
{
|
||||
toDownload.Add(apiAudio, existingSong);
|
||||
}
|
||||
Debug.LogError($"Audio sync failed: {ex}");
|
||||
IsSyncing = false;
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
BetterBugleUI.Instance?.ShowActionbar($"Syncing audio bank... {toDownload.Count} changed/new files found.");
|
||||
|
||||
string[] filesToOverload = [];
|
||||
|
||||
foreach (AudioSyncService.APIAudioFormat apiAudio in toDownload.Keys)
|
||||
{
|
||||
bool success = await AudioSyncService.DownloadAPIAudio(apiAudio, SoundsDirectory, toDownload[apiAudio]);
|
||||
if (success)
|
||||
{
|
||||
Debug.Log($"Successfully downloaded audio: {apiAudio.Filename}.{apiAudio.Extension}, adding to forceload");
|
||||
filesToOverload = [.. filesToOverload, $"{apiAudio.Filename}.{apiAudio.Extension}"];
|
||||
}
|
||||
}
|
||||
IsSyncing = false;
|
||||
IsLoading = true;
|
||||
Plugin.Instance.StartCoroutine(LoadAllAudioClipsCoroutine(SoundsDirectory, filesToOverload));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user