From 5d2174fbbf546f033118bc6d65d55d2166f66980 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 5 Feb 2024 02:21:49 +0700 Subject: [PATCH] CWeaponBox::Touch: Reworked dropped grenade pickup --- regamedll/dlls/player.cpp | 38 +++++++++++++++++ regamedll/dlls/player.h | 1 + regamedll/dlls/weapons.cpp | 86 ++++++++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 21 deletions(-) diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index d12768c16..06c550890 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(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 4595bf226..8908e44c9 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -536,6 +536,7 @@ class CBasePlayer: public CBaseMonster { 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 e6524a96c..2c035c746 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<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 (m_rgAmmo[iAmmoIndex] < 0) - m_rgAmmo[iAmmoIndex] = 0; + // 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; + } - 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;