diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index d12768c1..06c55089 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -6351,6 +6351,44 @@ CBaseEntity *CBasePlayer::GiveNamedItemEx(const char *pszName) return pEntity; } +// Creates a copy of the specified entity (pEntitySource) and gives it to the player +// The cloned entity inherits base properties (entvars) of the original entity +// Returns Pointer to the cloned entity, or NULL if the entity cannot be created +CBaseEntity *CBasePlayer::GiveCopyItem(CBaseEntity *pEntitySource) +{ + edict_t *pEdict = CREATE_NAMED_ENTITY(pEntitySource->pev->classname); + if (FNullEnt(pEdict)) + { + ALERT(at_console, "NULL Ent in GiveCloneItem classname `%s`!\n", STRING(pEntitySource->pev->classname)); + return nullptr; + } + + // copy entity properties + Q_memcpy(&pEdict->v, pEntitySource->pev, sizeof(pEdict->v)); + + pEdict->v.pContainingEntity = pEdict; + pEdict->v.origin = pev->origin; + pEdict->v.spawnflags |= SF_NORESPAWN; + pEdict->v.owner = NULL; // will re-link owner after touching + pEdict->v.chain = ENT(pEntitySource->pev); // refer to source copy entity + + DispatchSpawn(pEdict); + DispatchTouch(pEdict, ENT(pev)); + pEdict->v.chain = NULL; + + CBaseEntity *pEntity = GET_PRIVATE<CBaseEntity>(pEdict); + + // not allow the item to fall to the ground. + if (FNullEnt(pEdict->v.owner) || pEdict->v.owner != edict()) + { + pEdict->v.flags |= FL_KILLME; + UTIL_Remove(pEntity); + return nullptr; + } + + return pEntity; +} + CBaseEntity *FindEntityForward(CBaseEntity *pEntity) { TraceResult tr; diff --git a/regamedll/dlls/player.h b/regamedll/dlls/player.h index 4595bf22..8908e44c 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -536,6 +536,7 @@ public: void ItemPostFrame(); CBaseEntity *GiveNamedItem(const char *pszName); CBaseEntity *GiveNamedItemEx(const char *pszName); + CBaseEntity *GiveCopyItem(CBaseEntity *pEntityBase); void EnableControl(BOOL fControl); bool HintMessage(const char *pMessage, BOOL bDisplayIfPlayerDead = FALSE, BOOL bOverride = FALSE); bool HintMessageEx(const char *pMessage, float duration = 6.0f, bool bDisplayIfPlayerDead = false, bool bOverride = false); diff --git a/regamedll/dlls/weapons.cpp b/regamedll/dlls/weapons.cpp index e6524a96..2c035c74 100644 --- a/regamedll/dlls/weapons.cpp +++ b/regamedll/dlls/weapons.cpp @@ -1852,39 +1852,72 @@ bool CWeaponBox::GiveAmmoToPlayer(CBasePlayer *pPlayer, CBasePlayerWeapon *pWeap return false; // can't pickup more, these ammo are full in backpack // If already have a weapon in backpack, just refill ammo for it - if (iCurrentAmmo > 0) + int iAmmoIndex = GetAmmoIndex(pszAmmo); + if (iAmmoIndex > 0) { - int iAmmoIndex = GetAmmoIndex(pszAmmo); - if (iAmmoIndex > 0) + // How many weapon ammo can pick up? + int iAmmoPickup = min(m_rgAmmo[iAmmoIndex], iMaxAmmo - iCurrentAmmo); + if (iAmmoPickup > 0) { - // how many gren ammo can pick up? - int iAmmoPickup = min(m_rgAmmo[iAmmoIndex], iMaxAmmo - iCurrentAmmo); - if (iAmmoPickup > 0) + if (iCurrentAmmo == 0 && !(pPlayer->pev->weapons & (1<<pWeapon->m_iId)) && (pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE)) { - if (!FStringNull(m_rgiszAmmo[iAmmoIndex]) && - pPlayer->GiveAmmo(iAmmoPickup, STRING(m_rgiszAmmo[iAmmoIndex]), iMaxAmmo) != -1) + if (m_rgAmmo[iAmmoIndex] > iMaxAmmo) { - m_rgAmmo[iAmmoIndex] -= iAmmoPickup; + // If ammo capacity of the dropped weapon exceeds the player's backpack capacity, + // make a copy of dropped weapon and give it to the player + CBasePlayerItem *copyItem = (CBasePlayerItem *)pPlayer->GiveCopyItem(pWeapon); + if (copyItem) + { + // The cloned weapon must inherit properties from a dropped weapon, such as Item Info +#ifdef REGAMEDLL_API + ItemInfo info; + if (pWeapon->CSPlayerItem()->GetItemInfo(&info)) + copyItem->CSPlayerItem()->SetItemInfo(&info); +#endif + m_rgAmmo[iAmmoIndex]--; + iAmmoPickup--; + } + } + else + { + // If no weapon in backpack, then issue weapon + if (pPlayer->AddPlayerItem(pWeapon)) + { + pWeapon->AttachToPlayer(pPlayer); + if (pGivenItem) *pGivenItem = pWeapon; + } - if (m_rgAmmo[iAmmoIndex] < 0) - m_rgAmmo[iAmmoIndex] = 0; - - EMIT_SOUND(pPlayer->edict(), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); + // unlink this weapon from the box + return true; } } - // ammo exhausted, remove this weapon - if (m_rgAmmo[iAmmoIndex] <= 0) + Assert(iAmmoPickup != 0); + Assert(m_rgAmmo[iAmmoIndex] != 0); + + if (!FStringNull(m_rgiszAmmo[iAmmoIndex]) && + pPlayer->GiveAmmo(iAmmoPickup, STRING(m_rgiszAmmo[iAmmoIndex]), iMaxAmmo) != -1) { - pWeapon->Kill(); + m_rgAmmo[iAmmoIndex] -= iAmmoPickup; - // unlink this weapon from the box - return true; + if (m_rgAmmo[iAmmoIndex] < 0) + m_rgAmmo[iAmmoIndex] = 0; + + EMIT_SOUND(pPlayer->edict(), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); } - - // ammo has not been exhausted yet, keep this weapon in weaponbox - return false; } + + // ammo exhausted, remove this weapon + if (m_rgAmmo[iAmmoIndex] <= 0) + { + pWeapon->Kill(); + + // unlink this weapon from the box + return true; + } + + // ammo has not been exhausted yet, keep this weapon in weaponbox + return false; } // If no weapon in backpack, then issue weapon @@ -2045,9 +2078,20 @@ void CWeaponBox::Touch(CBaseEntity *pOther) #ifdef REGAMEDLL_FIXES CBasePlayerItem *pNext = m_rgpPlayerItems[i]->m_pNext; + // Determine the max ammo capacity for the picked-up grenade + int iMaxPickupAmmo = pGrenade->iMaxAmmo1(); + + // If the player already has the same weapon in inventory, + // prioritize the max ammo capacity value over the one from the dropped weapon + // When the pickup occurs, ammo will be granted up to + // the max ammo capacity of the weapon currently held by the player + CBasePlayerItem *pInventoryItem = (CBasePlayerItem *)pPlayer->GetItemById((WeaponIdType)pGrenade->m_iId); + if (pInventoryItem && !Q_stricmp(pInventoryItem->pszAmmo1(), pGrenade->pszAmmo1())) + iMaxPickupAmmo = pInventoryItem->iMaxAmmo1(); + // Pickup grenade item or refill ammo if (GiveAmmoToPlayer(pPlayer, pGrenade, - playerGrenades, pGrenade->pszAmmo1(), pGrenade->iMaxAmmo1(), &givenItem)) + playerGrenades, pGrenade->pszAmmo1(), iMaxPickupAmmo, &givenItem)) { // unlink this weapon from the box m_rgpPlayerItems[i] = pItem = pNext;