0.1.13 | Fix for PEAK update

Player.EmptySlot and PhotonNetwork.InstantiateItemRoom each gained an
optional parameter, which compiles fine but left the previous build
calling signatures that no longer exist. The InstantiateItemRoom throw
escaped the BagsForEveryone postfix and took down every
SingleItemSpawner in the level, which is why backpacks and BingBong
stopped spawning and campfires no longer advanced the biome.

Also hardens that postfix so a future API change costs the extra bags
and nothing else, registers them with the spawner's SpawnedItemTracker,
and stops the Open Backpack keybind from opening the wheel while the
backpack is held in hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 00:10:07 +02:00
parent 96204e8a50
commit 2802c71cd0
5 changed files with 77 additions and 30 deletions

View File

@@ -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.12</Version>
<Version>0.1.13</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -1,5 +1,6 @@
using System;
using UnityEngine;
using Zorro.Core;
namespace JordanMod.Modules.EasyBackpack;
@@ -54,12 +55,26 @@ class EasyBackpackModule : Module
}
else return;
// 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;

View File

@@ -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);
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)
@@ -59,6 +54,16 @@ class StashedBugleModule : Module
}
}
// 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.

View File

@@ -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; // Already one bag
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);
}
}