mirror of
https://github.com/TheJordanDev/PEAK-JordanMod.git
synced 2026-10-17 08:05:25 +02:00
Compare commits
2 Commits
main
...
2802c71cd0
| Author | SHA1 | Date | |
|---|---|---|---|
| 2802c71cd0 | |||
| 96204e8a50 |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -62,3 +62,17 @@
|
|||||||
- 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 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.
|
- 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
|
// Better Airport
|
||||||
public static ConfigEntry<float> ConveyorSpeedModifier { get; private set; } = null!;
|
public static ConfigEntry<float> ConveyorSpeedModifier { get; private set; } = null!;
|
||||||
|
|
||||||
|
// Open Passport settings
|
||||||
|
public static ConfigEntry<KeyCode> OpenPassport { get; private set; } = null!;
|
||||||
|
|
||||||
// Stashed Bugle settings
|
// Stashed Bugle settings
|
||||||
public static ConfigEntry<KeyCode> ToggleBugle { get; private set; } = null!;
|
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
|
// Stashed Bugle settings
|
||||||
ToggleBugle = Config.Bind(
|
ToggleBugle = Config.Bind(
|
||||||
"Control",
|
"Control",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<!-- This is the display name of your mod. Example: BepInEx Template -->
|
<!-- This is the display name of your mod. Example: BepInEx Template -->
|
||||||
<AssemblyTitle>JordanMod</AssemblyTitle>
|
<AssemblyTitle>JordanMod</AssemblyTitle>
|
||||||
<!-- This is the version number of your mod. -->
|
<!-- This is the version number of your mod. -->
|
||||||
<Version>0.1.11</Version>
|
<Version>0.1.13</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Zorro.Core;
|
||||||
|
|
||||||
namespace JordanMod.Modules.EasyBackpack;
|
namespace JordanMod.Modules.EasyBackpack;
|
||||||
|
|
||||||
@@ -54,12 +55,26 @@ class EasyBackpackModule : Module
|
|||||||
}
|
}
|
||||||
else return;
|
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);
|
BackpackReference backpackRefs = BackpackReference.GetFromEquippedBackpack(targetCharacter);
|
||||||
int slotCount = (targetSlot.prefab as Backpack)?.slotCount ?? 4;
|
int slotCount = (targetSlot.prefab as Backpack)?.slotCount ?? 4;
|
||||||
GUIManager.instance.OpenBackpackWheel(backpackRefs, slotCount, targetSlot.backpackType);
|
GUIManager.instance.OpenBackpackWheel(backpackRefs, slotCount, targetSlot.backpackType);
|
||||||
_isBackpackOpen = true;
|
_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()
|
private void CloseBackpack()
|
||||||
{
|
{
|
||||||
if (!Application.isFocused) return;
|
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;
|
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()
|
private static void OnAudioLoadComplete()
|
||||||
{
|
{
|
||||||
if (!HasReplacedSounds) return;
|
if (!HasReplacedSounds) return;
|
||||||
|
|||||||
@@ -31,17 +31,12 @@ class StashedBugleModule : Module
|
|||||||
{
|
{
|
||||||
if (heldItem.UIData.itemName == _bugleItemName)
|
if (heldItem.UIData.itemName == _bugleItemName)
|
||||||
{
|
{
|
||||||
localCharacter.refs.items.DestroyHeldItemRpc();
|
ClearHeldSlot(localCharacter);
|
||||||
localCharacter.player.EmptySlot(localCharacter.refs.items.currentSelectedSlot);
|
|
||||||
localCharacter.player.RPCRemoveItemFromSlot(localCharacter.refs.items.currentSelectedSlot.Value);
|
|
||||||
|
|
||||||
SpawnItemByDisplayName(localCharacter, _megaphoneItemName);
|
SpawnItemByDisplayName(localCharacter, _megaphoneItemName);
|
||||||
}
|
}
|
||||||
else if (heldItem.UIData.itemName == _megaphoneItemName)
|
else if (heldItem.UIData.itemName == _megaphoneItemName)
|
||||||
{
|
{
|
||||||
localCharacter.refs.items.DestroyHeldItemRpc();
|
ClearHeldSlot(localCharacter);
|
||||||
localCharacter.player.EmptySlot(localCharacter.refs.items.currentSelectedSlot);
|
|
||||||
localCharacter.player.RPCRemoveItemFromSlot(localCharacter.refs.items.currentSelectedSlot.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (heldItem == null)
|
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
|
// SpawnItemInHand expects the item's Resources prefab name, which is not
|
||||||
// guaranteed to match its UIData.itemName display string, so resolve it
|
// guaranteed to match its UIData.itemName display string, so resolve it
|
||||||
// via the ItemDatabase instead of assuming the two are identical.
|
// via the ItemDatabase instead of assuming the two are identical.
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using Peak;
|
||||||
using Photon.Pun;
|
using Photon.Pun;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -7,34 +10,48 @@ namespace JordanMod.Modules.BagsForEveryone;
|
|||||||
public class BagsForEveryonePatch
|
public class BagsForEveryonePatch
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private const string BackpackSpawnerName = "Backpack_Spawner";
|
||||||
|
private const string FirstBiomeName = "Biome_1";
|
||||||
|
|
||||||
[HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")]
|
[HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
static void Postfix(SingleItemSpawner __instance)
|
static void Postfix(SingleItemSpawner __instance, List<PhotonView> __result)
|
||||||
{
|
|
||||||
ForEachPlayer(__instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ForEachPlayer(SingleItemSpawner spawner)
|
|
||||||
{
|
{
|
||||||
if (!Helper.IsMasterClient()) return;
|
if (!Helper.IsMasterClient()) return;
|
||||||
|
if (__instance.transform.name != BackpackSpawnerName) return;
|
||||||
|
|
||||||
bool isBackpackSPawner = spawner.transform.name == "Backpack_Spawner";
|
GameObject firstBiome = GameObject.Find(FirstBiomeName);
|
||||||
if (!isBackpackSPawner) return;
|
if (firstBiome == null || !__instance.transform.IsChildOf(firstBiome.transform)) return;
|
||||||
|
if (__result != null && __result.Count > 1) return;
|
||||||
|
|
||||||
bool isFirstSpawner = spawner.transform.IsChildOf(GameObject.Find("Biome_1").transform);
|
int extraBags = PhotonNetwork.PlayerList.Length - 1;
|
||||||
if (!isFirstSpawner) return;
|
if (extraBags <= 0) return;
|
||||||
|
|
||||||
int playerCount = PhotonNetwork.PlayerList.Length - 1; // Already one bag
|
try
|
||||||
if (playerCount <= 0) return;
|
{
|
||||||
for (int i = 1; i <= playerCount; i++)
|
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;
|
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>();
|
PhotonView view = PhotonNetwork.InstantiateItemRoom(spawner.prefab.name, spawnPosition, spawner.transform.rotation).GetComponent<PhotonView>();
|
||||||
if (spawner.isKinematic)
|
if (spawner.isKinematic)
|
||||||
{
|
{
|
||||||
component.GetComponent<PhotonView>().RPC("SetKinematicRPC", RpcTarget.AllBuffered, true, component.transform.position, component.transform.rotation);
|
view.RPC("SetKinematicRPC", RpcTarget.AllBuffered, true, view.transform.position, view.transform.rotation);
|
||||||
}
|
}
|
||||||
|
spawned.Add(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (spawner.HasSpawnTracking(out SpawnedItemTracker tracker)) tracker.TrackSpawnedItems(spawned);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user