This recipe gives each current player a small upward boost when jump is held and pickup is newly pressed. A 40 update cooldown prevents repeated boosts. The fragment belongs inside a BepInEx plugin like First plugin. It is bounded ability logic, not a complete plugin class.

Store a cooldown per player

Use a ConditionalWeakTable so two players do not share one cooldown:

using System.Runtime.CompilerServices;
using UnityEngine;
 
private readonly ConditionalWeakTable<Player, AbilityState> abilityStates =
    new ConditionalWeakTable<Player, AbilityState>();
 
private sealed class AbilityState
{
    public int Cooldown;
}

This state follows one live Player object. It does not survive respawning, loading another session, or restarting the game. Per-instance state explains that lifetime.

Attach the update hook

Subscribe and remove the same handler with the plugin lifecycle:

private void OnEnable()
{
    On.Player.Update += PlayerUpdate;
}
 
private void OnDisable()
{
    On.Player.Update -= PlayerUpdate;
}

Run the ability after the normal player update so it acts on the position and input state left by global::Player.Update(System.Boolean):

private void PlayerUpdate(On.Player.orig_Update orig, Player self, bool eu)
{
    orig(self, eu);
 
    AbilityState state = abilityStates.GetOrCreateValue(self);
    if (state.Cooldown > 0)
        state.Cooldown--;
 
    if (self.room == null || self.slatedForDeletetion || !self.Consious ||
        self.isNPC || self.inShortcut || self.enteringShortCut.HasValue ||
        self.input == null || self.input.Length < 2 ||
        self.bodyChunks == null || self.bodyChunks.Length == 0)
        return;
 
    bool pressed = self.input[0].pckp && !self.input[1].pckp;
    if (!pressed || !self.input[0].jmp || state.Cooldown > 0)
        return;
 
    for (int i = 0; i < self.bodyChunks.Length; i++)
        self.bodyChunks[i].vel += Vector2.up * 6f;
 
    state.Cooldown = 40;
}

input[0] is current input and input[1] is the preceding sample used by the inspected player code. The edge check fires once when pickup changes from released to pressed. The recipe applies an equal impulse to every body chunk instead of choosing one chunk as the force target. Player input covers other buttons and analog input. Simulation and physics explains velocity and update timing.

Expected result and checks

Hold jump, then press pickup while the player is active in a room. The player should receive one upward impulse. A held pickup should not repeat it, and another press should work after 40 completed handler updates.

The cooldown counts simulation updates, not seconds. Slow time, pause behavior, other hooks, and character movement rules can change the feel. The input combination also overlaps normal pickup behavior. Test grounded, airborne, swimming, zero gravity, shortcut entry, death, and cooperative play. The helper and hook fragment compiled together against the recorded game and hook assemblies with .NET SDK 10.0.400 and C# 7.3. It was not run in the game.

Sources