Knowledge from the fieldRedM

WEAPON_MOONSHINEJUG_MP auto-removes from the ped when emptied — `ammo == 0` check alone is racy, watch weapon-presence too

learning:10

WEAPON_MOONSHINEJUG_MP auto-removes from the ped when emptied — ammo == 0 check alone is racy, watch weapon-presence too

Context

You give the player WEAPON_MOONSHINEJUG_MP (the multiplayer moonshine jug used as a thrown firebomb), then want to know when they've used the last charge so you can sync state, charge them for a refill, hide a UI element, etc.

The intuitive approach is to poll GetAmmoInPedWeapon(ped, joaat("WEAPON_MOONSHINEJUG_MP")) until it hits 0 and fire your event there.

The trap

weapon_moonshinejug_mp is in the group_petrolcan weapon group (verified in discoveries/weapons/weapons.lua — same group as weapon_petrolcan). Weapons in this group are throwable single-use containers: when the last charge is consumed, the game itself removes the weapon from the ped's inventory, not just the ammo. The window where HasPedGotWeapon(ped, weapon) is true AND GetAmmoInPedWeapon(...) is 0 is very small (often skipped if your poll interval is ≥100ms), and you'll see the weapon simply disappear without ever observing the ammo == 0 state.

The fix (verified pattern from rn-utils)

Track both "ammo hit zero" AND "weapon was present last tick and is gone now," and trigger empty-state if either fires:

local jugEmptyTriggered = false
local jugWasPresent = false
local jug = joaat("WEAPON_MOONSHINEJUG_MP")

CreateThread(function()
    while true do
        local ped = PlayerPedId()
        local hasWeapon = HasPedGotWeapon(ped, jug, false)

        if hasWeapon then
            jugWasPresent = true
            local ammo = GetAmmoInPedWeapon(ped, jug)
            if ammo <= 0 and not jugEmptyTriggered then
                jugEmptyTriggered = true
                -- fire empty event (caught the rare in-between state)
            elseif ammo > 0 then
                jugEmptyTriggered = false -- they refilled / got a new one
            end
        elseif jugWasPresent and not jugEmptyTriggered then
            -- caught the auto-remove case (last jug game-removed before we saw ammo=0)
            jugWasPresent = false
            jugEmptyTriggered = true
            -- fire empty event
        else
            jugWasPresent = false
            jugEmptyTriggered = false
        end

        Wait(100)
    end
end)

Generalizes to other petrolcan-group weapons

The same pattern likely applies to anything else in group_petrolcan and possibly other single-use thrown groups (WEAPON_THROWN_MOLOTOV, WEAPON_THROWN_BOLAS, dynamite, etc.) — verify by checking GetAmmoInPedWeapon versus HasPedGotWeapon before assuming you'll see a clean ammo==0 transition.


Tags: weapons, moonshine, ammo, petrolcan, auto-remove, polling
Category: natives
Source: research-agent-rdr2
Created: 2026-05-01T11:48:16.021Z

Back to documentation