Files
GTASource/game/pickups/PickupManager.cpp

3323 lines
106 KiB
C++
Raw Normal View History

2025-02-23 17:40:52 +08:00
// File header
#include "pickups/PickupManager.h"
// Rage headers
#include "physics/simulator.h"
// Framework headers
#include "fwnet/nettypes.h"
#include "fwscene/world/WorldLimits.h"
#include "fwutil/quadtree.h"
#include "fwscene/stores/drawablestore.h"
// Game headers
#include "audio/scriptaudioentity.h"
#include "camera/CamInterface.h"
#include "control/replay/replay.h"
#include "Core/Game.h"
#include "Cutscene/CutSceneManagerNew.h"
#include "weapons/Weapon.h"
#include "event/EventGroup.h"
#include "event/EventNetwork.h"
#include "network/Network.h"
#include "network/Objects/Entities/NetObjGame.h"
#include "peds/PlayerInfo.h"
#include "pickups/Data/PickupDataManager.h"
#include "pickups/Data/scriptedglows_parser.h"
#include "pickups/Pickup.h"
#include "renderer/Lights/lights.h"
#include "scene/portals/InteriorInst.h"
#include "scene/world/GameWorld.h"
#include "script/Handlers/GameScriptEntity.h"
#include "script/commands_weapon.h"
#include "peds/PlayerInfo.h"
#include "peds/Ped.h"
#include "streaming/streamingengine.h"
#include "physics/gtaInst.h"
#include "physics/WorldProbe/shapetestcapsuledesc.h"
#include "Vfx/Misc/GameGlows.h"
#include "Weapons/WeaponFactory.h"
#if __BANK
#include "bank/bkmgr.h"
#include "scene/world/GameWorld.h"
#endif
WEAPON_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
// Static variable initialisation
CPickupManager::PickupTypeInfoMap CPickupManager::ms_pickupTypeInfoMap;
atFixedArray<CPickupManager::CCustomArchetypeInfo, CPickupManager::MAX_EXTRA_CUSTOM_ARCHETYPES> CPickupManager::ms_extraCustomArchetypes;
atQueue<CPickupManager::CCollectionInfo, CPickupManager::COLLECTION_HISTORY_SIZE> CPickupManager::ms_collectionHistory;
fwQuadTreeNode* CPickupManager::ms_pQuadTree = NULL;
Vector3 CPickupManager::ms_cachedPlayerPosition = Vector3(0.0f, 0.0f, 0.0f);
fwPtrListSingleLink CPickupManager::ms_pendingPickups;
fwPtrListSingleLink CPickupManager::ms_regeneratingPlacements;
fwPtrListSingleLink CPickupManager::ms_removedPlacements;
fwFlags32 CPickupManager::ms_SuppressionFlags;
bool CPickupManager::ms_bShuttingDown = false;
ScriptedGlowList CPickupManager::ms_glowList;
u32 CPickupManager::ms_prohibitedPickupModels[MAX_PROHIBITED_PICKUP_MODELS];
bool CPickupManager::ms_hasProhibitedPickupModels = false;
bool CPickupManager::ms_OnlyAllowAmmoCollectionWhenLowOnAmmo = false;
float CPickupManager::ms_fAmmoAmountScaler(1.0f);
FW_INSTANTIATE_BASECLASS_POOL(CPickupManager::CCollectionInfo, CPickupManager::COLLECTION_HISTORY_SIZE, atHashString("CCollectionInfo",0x857da83e), sizeof(CPickupManager::CCollectionInfo));
FW_INSTANTIATE_BASECLASS_POOL(CPickupManager::CRegenerationInfo, CONFIGURED_FROM_FILE, atHashString("CRegenerationInfo",0x24bead4e), sizeof(CPickupManager::CRegenerationInfo));
bool CPickupManager::ms_AllowNetworkedMoneyPickup = false;
#if __BANK
bkBank* CPickupManager::ms_pBank = NULL;
bkCombo* CPickupManager::ms_pPickupNamesCombo = NULL;
int CPickupManager::ms_pickupComboIndex = 0;
const char* CPickupManager::ms_pickupComboNames[CPickupData::MAX_STORAGE];
int CPickupManager::ms_numPickupComboNames = 0;
bool CPickupManager::ms_fixedPickups = true;
bool CPickupManager::ms_regeneratePickups = false;
bool CPickupManager::ms_blipPickups = false;
bool CPickupManager::ms_forceMPStyleWeaponPickups = false;
bool CPickupManager::ms_createAllPickupsOnGround = false;
bool CPickupManager::ms_createAllPickupsWithArrowMarkers = false;
bool CPickupManager::ms_createAllPickupsUpright = false;
bool CPickupManager::ms_allowPlayerToCarryPortablePickups = true;
bool CPickupManager::ms_displayAllPickupPlacements = false;
bool CPickupManager::ms_debugScriptedPickups = false;
bool CPickupManager::ms_NoMoneyDrop = false;
#if ENABLE_GLOWS
bool CPickupManager::ms_forcePickupGlow = false;
bool CPickupManager::ms_forceOffsetPickupGlow = false;
#endif // ENABLE_GLOWS
#endif
// settings
dev_float ARROW_MARKER_Z_OFFSET = 1.0f;
dev_float ARROW_MARKER_SCALE = 0.5f;
#if __DEV
Color32 ARROW_MARKER_COL = Color32(0.05f, 0.45f, 0.05f, 0.5f);
#else
const Color32 ARROW_MARKER_COL = Color32(0.05f, 0.45f, 0.05f, 0.5f);
#endif
dev_float ARROW_MARKER_FADE_START_RANGE = 35.0f;
dev_float ARROW_MARKER_FADE_OUT_RANGE = 50.0f;
dev_bool ARROW_MARKER_BOUNCE = true;
dev_bool ARROW_MARKER_FACE_CAM = true;
bool CPickupManager::ms_forceRotatePickupFaceUp = false;
#define SCRIPTED_PICKUP_GLOW_FADE_NEAR 22.0f
#define SCRIPTED_PICKUP_GLOW_FADE_FAR 30.0f
void CPickupManager::GlowListLoad()
{
if(ms_glowList.m_data.GetCount() == 0)
{
PARSER.LoadObject("common:/data/scriptedglow", "xml", ms_glowList, &parSettings::sm_StrictSettings);
}
}
#if __BANK
void CPickupManager::GlowListSave()
{
PARSER.SaveObject("common:/data/scriptedglow", "xml", &ms_glowList, parManager::XML);
}
void CPickupManager::GlowListAddWidgets(bkBank *bank)
{
GlowListLoad();
bank->PushGroup("Glows");
bank->AddButton("Save",GlowListSave);
for(int i=0;i<ms_glowList.m_data.GetCount();i++)
{
char title[128];
sprintf(title,"Glow type %d",i);
bank->PushGroup(title);
ms_glowList.m_data[i].AddWidgets(bank);
bank->PopGroup();
}
bank->PopGroup();
}
#endif // __BANK
void CPickupManager::RenderGlow( Vec3V_In pos, float fadeNear, float fadeFar, Vector3 &col, float intensity, float range )
{
Vec3V camPos = VECTOR3_TO_VEC3V(camInterface::GetPos());
float distToCam = Mag(pos - camPos).Getf();
if (distToCam < fadeFar && range > 0.0f)
{
// glow intensity scales depending on distance from the pickup to the camera
intensity *= rage::Clamp((fadeNear - distToCam)/ (fadeFar - fadeNear),0.0f,1.0f);
if( intensity > 0.0f)
{
GameGlows::Add(pos,range,col,intensity);
}
}
}
void CPickupManager::RenderScriptedGlow(Vec3V_In pos, int glowType)
{
#if !__FINAL
Assertf(glowType < ms_glowList.m_data.GetCount(),"Trying to render a non existing scripted glow (%d/%d)",glowType,ms_glowList.m_data.GetCount());
glowType = Clamp(glowType,0,ms_glowList.m_data.GetCount());
#endif
ScriptedGlow &glow = ms_glowList.m_data[glowType];
Vector3 col = Vector3(glow.color.GetRedf(),glow.color.GetGreenf(),glow.color.GetBluef());
RenderGlow(pos, SCRIPTED_PICKUP_GLOW_FADE_NEAR, SCRIPTED_PICKUP_GLOW_FADE_FAR, col, glow.intensity, glow.range);
}
void CPickupManager::MovePortablePickupsFromOnePedToAnother(CPed& ped1, CPed& ped2)
{
static const unsigned MAX_ATTACHED_PICKUPS = 20;
CPickup* attachedPickups[MAX_ATTACHED_PICKUPS];
u32 numAttachedPickups = 0;
CEntity* pChild = static_cast<CEntity*>(ped1.GetChildAttachment());
while (pChild)
{
if (pChild->GetIsTypeObject() && static_cast<CObject*>(pChild)->m_nObjectFlags.bIsPickUp)
{
if (AssertVerify(numAttachedPickups < MAX_ATTACHED_PICKUPS))
{
attachedPickups[numAttachedPickups++] = static_cast<CPickup*>(pChild);
}
}
pChild = static_cast<CEntity*>(pChild->GetSiblingAttachment());
}
for (u32 i=0; i<numAttachedPickups; i++)
{
if (AssertVerify(attachedPickups[i]->IsFlagSet(CPickup::PF_Portable)))
{
attachedPickups[i]->DetachPortablePickupFromPed("Moved from one ped to another");
attachedPickups[i]->AttachPortablePickupToPed(&ped2, "Moved from one ped to another");
}
}
}
void CPickupManager::DetachAllPortablePickupsFromPed(CPed& ped)
{
static const unsigned MAX_ATTACHED_PICKUPS = 20;
CPickup* attachedPickups[MAX_ATTACHED_PICKUPS];
u32 numAttachedPickups = 0;
CEntity* pChild = static_cast<CEntity*>(ped.GetChildAttachment());
while (pChild)
{
if (pChild->GetIsTypeObject() && static_cast<CObject*>(pChild)->m_nObjectFlags.bIsPickUp)
{
if (AssertVerify(numAttachedPickups < MAX_ATTACHED_PICKUPS))
{
attachedPickups[numAttachedPickups++] = static_cast<CPickup*>(pChild);
}
}
pChild = static_cast<CEntity*>(pChild->GetSiblingAttachment());
}
for (u32 i=0; i<numAttachedPickups; i++)
{
if (AssertVerify(attachedPickups[i]->IsFlagSet(CPickup::PF_Portable)))
{
if (!NetworkUtils::IsNetworkCloneOrMigrating(attachedPickups[i]))
{
attachedPickups[i]->DetachPortablePickupFromPed("DetachAllPortablePickupsFromPed(1)");
}
else
{
attachedPickups[i]->SetFlag(CPickup::PF_DetachWhenLocal);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
// CPickupManager::CPickupPositionInfo
///////////////////////////////////////////////////////////////////////////////////////
CPickupManager::CPickupPositionInfo::CPickupPositionInfo( const Vector3 &srcPos )
: m_MinDist( 1.2f ),
m_MaxDist( 1.5f ),
m_SrcPos( srcPos ),
m_pickupHeightOffGround(0.0f),
m_bOnGround(false)
{
}
///////////////////////////////////////////////////////////////////////////////////////
// CPickupManager::CRegenerationInfo
///////////////////////////////////////////////////////////////////////////////////////
CPickupManager::CRegenerationInfo::CRegenerationInfo(CPickupPlacement* pPlacement)
: m_pPlacement(pPlacement), m_forceRegenerate(false)
{
m_regenerationTime = NetworkInterface::GetSyncedTimeInMilliseconds() + pPlacement->GetRegenerationTime();
}
bool CPickupManager::CRegenerationInfo::IsReadyToRegenerate()
{
if (m_forceRegenerate)
return true;
u32 currTime = NetworkInterface::GetSyncedTimeInMilliseconds();
u32 signBit = 0x80000000u;
if (currTime >= m_regenerationTime)
{
return true;
}
else if ((m_regenerationTime & signBit) & !(currTime & signBit)) // handles the time wrapping
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////
// CPickupManager::CCustomArchetypeInfo
///////////////////////////////////////////////////////////////////////////////////////
void CPickupManager::CCustomArchetypeInfo::Set(phArchetypeDamp& archetype, u32 modelIndex)
{
Assert(!m_archetype);
m_archetype = &archetype;
m_modelIndex = modelIndex;
m_refCount = 0;
CBaseModelInfo* pMI = CModelInfo::GetModelInfoFromLocalIndex(modelIndex);
if (pMI->GetHasLoaded() && pMI->GetHasBoundInDrawable())
{
pMI->AddRef();
g_DrawableStore.AddRef(strLocalIndex(pMI->GetDrawableIndex()), REF_OTHER);
// char refString[16];
// g_DrawableStore.GetRefCountString(pMI->GetDrawableIndex(), refString, sizeof(refString));
// Displayf("Physics reference to %s raised to %s", pMI->GetModelName(), refString);
}
// add a ref here so that the archetype is kept until the pickup manager closes down
m_archetype->AddRef();
}
void CPickupManager::CCustomArchetypeInfo::Clear()
{
// If archetype exists release it and remove dictionary reference
if (IsSet())
{
if (m_archetype)
{
m_archetype->Release();
m_archetype = NULL;
}
CBaseModelInfo* pMI = CModelInfo::GetModelInfoFromLocalIndex(m_modelIndex.Get());
if (pMI && pMI->GetHasLoaded() && pMI->GetHasBoundInDrawable())
{
pMI->RemoveRef();
g_DrawableStore.RemoveRef(strLocalIndex(pMI->GetDrawableIndex()), REF_OTHER);
// char refString[16];
// g_DrawableStore.GetRefCountString(pMI->GetDrawableIndex(), refString, sizeof(refString));
// Displayf("Physics reference to %s dropped to %s", pMI->GetModelName(), refString);
}
m_modelIndex = fwModelId::MI_INVALID;
m_refCount = 0;
}
}
void CPickupManager::CCustomArchetypeInfo::RemoveRef()
{
if (AssertVerify(IsSet()) && AssertVerify(m_refCount > 0))
{
m_refCount--;
if (m_refCount == 0)
{
Clear();
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
// CPickupManager
///////////////////////////////////////////////////////////////////////////////////////
void CPickupManager::InitPools()
{
CPickup::InitPool( MEMBUCKET_GAMEPLAY );
CPickupPlacement::InitPool( MEMBUCKET_GAMEPLAY );
CPickupPlacement::CPickupPlacementCustomScriptData::InitPool( MEMBUCKET_GAMEPLAY );
CPickupManager::CCollectionInfo::InitPool( MEMBUCKET_GAMEPLAY );
CPickupManager::CRegenerationInfo::InitPool( MEMBUCKET_GAMEPLAY );
}
void CPickupManager::ShutdownPools()
{
CPickup::ShutdownPool();
CPickupPlacement::ShutdownPool();
CPickupPlacement::CPickupPlacementCustomScriptData::ShutdownPool();
CPickupManager::CCollectionInfo::ShutdownPool();
CPickupManager::CRegenerationInfo::ShutdownPool();
}
// Name : InitLevel
// Purpose : Initialises the manager when a level starts up.
void CPickupManager::Init(unsigned /*initMode*/)
{
gnetAssert(ms_pickupTypeInfoMap.GetNumUsed() == 0);
ms_extraCustomArchetypes.Resize(MAX_EXTRA_CUSTOM_ARCHETYPES);
Assert(!ms_pQuadTree);
fwRect worldBB(WORLDLIMITS_REP_XMIN, WORLDLIMITS_REP_YMIN, WORLDLIMITS_REP_XMAX, WORLDLIMITS_REP_YMAX);
ms_pQuadTree = rage_new fwQuadTreeNode(worldBB, 4);
ms_cachedPlayerPosition = Vector3(0.0f, 0.0f, 0.0f);
// load scripted glow definitions
GlowListLoad();
for (u32 i=0; i<MAX_PROHIBITED_PICKUP_MODELS; i++)
{
ms_prohibitedPickupModels[i] = 0;
}
ms_hasProhibitedPickupModels = false;
ms_AllowNetworkedMoneyPickup = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_NETWORKED_MONEY_PICKUPS", 0xAC79260F), false);
}
// Name : InitLevel
// Purpose : Initialises the manager when a level starts up.
void CPickupManager::InitDLCCommands()
{
UpdatePickupTypes();
}
// Name : ShutdownLevel
// Purpose : Shuts down the manager when a level ends.
void CPickupManager::Shutdown(unsigned /*shutdownMode*/)
{
Assert(!ms_bShuttingDown);
ms_bShuttingDown = true;
RemoveAllPickups(true);
// destroy all the pickups now, they will not be cleaned up properly otherwise
s32 i = CPickup::GetPool()->GetSize();
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup)
{
CObjectPopulation::DestroyObject(pPickup);
}
}
PickupTypeInfoMap::Iterator entry = ms_pickupTypeInfoMap.CreateIterator();
for (entry.Start(); !entry.AtEnd(); entry.Next())
{
entry.GetData().m_customArchetype.Clear();
}
ms_pickupTypeInfoMap.Reset();
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
ms_extraCustomArchetypes[i].Clear();
}
ms_extraCustomArchetypes.Reset();
ms_collectionHistory.Reset();
Assert(ms_pendingPickups.CountElements() == 0);
Assert(ms_regeneratingPlacements.CountElements() == 0);
Assert(ms_removedPlacements.CountElements() == 0);
ms_pendingPickups.Flush();
ms_regeneratingPlacements.Flush();
ms_removedPlacements.Flush();
delete ms_pQuadTree;
ms_pQuadTree = NULL;
ms_bShuttingDown = false;
}
// Name : Update
// Purpose : Main processing. Called once a frame.
void CPickupManager::Update()
{
if ((CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning())
REPLAY_ONLY(|| CReplayMgr::IsEditModeActive()) )
{
ProcessInactiveState();
}
else
{
ProcessRemovedPlacements();
ProcessPickupsInScope();
ProcessPendingPlacements();
ProcessRegeneratingPlacements();
UpdatePickups();
}
ms_fAmmoAmountScaler = 1.0f; // reset it every frame.
#if __BANK
DebugScriptedGlow();
DebugDisplay();
#endif // __BANK
}
void CPickupManager::SessionReset()
{
ClearSuppressionFlags();
PickupTypeInfoMap::Iterator entry = ms_pickupTypeInfoMap.CreateIterator();
for (entry.Start(); !entry.AtEnd(); entry.Next())
{
entry.GetData().m_prohibitedCollections = 0;
}
for (u32 i=0; i<MAX_PROHIBITED_PICKUP_MODELS; i++)
{
ms_prohibitedPickupModels[i] = 0;
}
ms_OnlyAllowAmmoCollectionWhenLowOnAmmo = false;
}
void CPickupManager::UpdatePickupTypes()
{
ms_pickupTypeInfoMap.Reset();
for (u32 i=0; i<CPickupDataManager::GetNumPickupTypes(); i++)
{
CPickupData* pPickupData = CPickupDataManager::GetPickupDataInSlot(i);
// make sure that the hash isn't conflicting with one of the old pickup types
Assert(pPickupData->GetHash() >= NUM_PICKUP_TYPES);
CPickupTypeInfo newInfo;
ms_pickupTypeInfoMap.Insert(pPickupData->GetHash(), newInfo);
if( pPickupData->GetDarkGlowIntensity() == -1.0f )
{
pPickupData->SetDarkGlowIntensity(pPickupData->GetGlowIntensity());
}
if( pPickupData->GetMPGlowIntensity() == -1.0f )
{
pPickupData->SetMPGlowIntensity(pPickupData->GetGlowIntensity());
}
if( pPickupData->GetMPDarkGlowIntensity() == -1.0f )
{
pPickupData->SetMPDarkGlowIntensity(pPickupData->GetDarkGlowIntensity());
}
#if __BANK
ms_pickupComboNames[i] = pPickupData->GetName();
#endif
}
#if __BANK
ms_numPickupComboNames = CPickupDataManager::GetNumPickupTypes();
if (ms_pPickupNamesCombo && ms_numPickupComboNames > 0)
{
ms_pPickupNamesCombo->UpdateCombo ("Pickup", &ms_pickupComboIndex, ms_numPickupComboNames, ms_pickupComboNames);
}
#endif
}
// Name : RegisterPickupPlacement
// Purpose : Pickup registration. Pickup placement data is added to the quad tree.
// Parameters : pickupHash - the hash of the pickup type
// pickupPosition - the position of the pickup
// pickupOrientation - the orientation of the pickup (in eulers)
// flags - placement flags determining whether the pickup is fixed, regenerates or is blipped.
// pNetObj - the network object to be assigned to the new placement (only used for network clones)
// Returns : the unique newly created pickup placement
CPickupPlacement* CPickupManager::RegisterPickupPlacement(u32 pickupHash,
Vector3& pickupPosition,
Vector3& pickupOrientation,
PlacementFlags flags,
CNetObjPickupPlacement* pNetObj,
u32 customModelIndex)
{
const CPickupData* pPickupData = CPickupDataManager::GetPickupData(pickupHash);
if (Verifyf(CPickupPlacement::GetPool()->GetNoOfFreeSpaces() > 0, "Can't register pickup - no spaces left in pool") &&
Verifyf(pPickupData, "Can't register pickup - pickup metadata does not exist for this pickup type (%u)", pickupHash))
{
#if __ASSERT
// check to see that there are no other placements at this position
int i = CPickupPlacement::GetPool()->GetSize();
// test pickup placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && !ms_removedPlacements.IsMemberOfList(pPlacement))
{
Vector3 placementPos = pPlacement->GetPickupPosition();
Vector3 vDiff = placementPos - pickupPosition;
if (vDiff.Mag2() < 0.01f)
{
Assertf(0, "A pickup of type %s is being created on top of another pickup of type %s at %f, %f, %f (%s)", pPickupData->GetName(), pPlacement->GetPickupData()->GetName(), placementPos.x, placementPos.y, placementPos.z, pPlacement->GetNetworkObject() ? pPlacement->GetNetworkObject()->GetLogName() : "");
break;
}
}
}
#endif
CPickupPlacement* pPlacement = rage_new CPickupPlacement(pickupHash, pickupPosition, pickupOrientation, flags, customModelIndex);
if (Verifyf(pPlacement, "Failed to allocate a new pickup placement"))
{
Vector2 position2D(pickupPosition.x, pickupPosition.y);
fwRect bb(position2D, 1.0f);
Assertf(!(pPlacement->GetRegenerates() && pPickupData->GetRegenerationTime() == 0.0f), "RegisterPickupPlacement: %s set to regenerate has no regeneration time!", pPickupData->GetName());
ms_pQuadTree->AddItem(pPlacement, bb);
if (pNetObj)
{
pPlacement->SetNetworkObject(pNetObj);
}
else if (!pPlacement->GetIsMapPlacement() && NetworkInterface::IsGameInProgress() && (pPlacement->GetFlags() & CPickupPlacement::PLACEMENT_CREATION_FLAG_LOCAL_ONLY)==0)
{
// placements are always created by scripts so they automatically become script network objects
NetworkInterface::GetObjectManager().RegisterGameObject(pPlacement, 0, CNetObjGame::GLOBALFLAG_SCRIPTOBJECT|CNetObjGame::GLOBALFLAG_SCRIPT_MIGRATION|CNetObjGame::GLOBALFLAG_CLONEONLY_SCRIPT);
Assert(pPlacement->GetNetworkObject());
}
// if the placement is networked, or in scope of the player, place on pending list so that its pickup object is created now
if (pPlacement->GetInScope() && !pPlacement->GetIsCollected() && !pPlacement->GetHasPickupBeenDestroyed())
{
Assert(!ms_pendingPickups.IsMemberOfList(pPlacement));
ms_pendingPickups.Add(pPlacement);
}
return pPlacement;
}
}
return NULL;
}
// Name : FindPickupPlacement
// Purpose : Finds the pickup placement at the given position
// Parameters : pickupPosition - the position of the pickup
CPickupPlacement* CPickupManager::FindPickupPlacement(const Vector3 &pickupPosition)
{
#define FIND_RANGE 0.2f
Vector2 pos2d(pickupPosition.x, pickupPosition.y);
fwRect bb(pos2d, FIND_RANGE);
// linked list of current pickup placements in scope
fwPtrListSingleLink pickupsInScope;
ms_pQuadTree->GetAllMatching(bb, pickupsInScope);
fwPtrNode* pNode=pickupsInScope.GetHeadPtr();
while(pNode)
{
CPickupPlacement* pPlacement = static_cast<CPickupPlacement*>(pNode->GetPtr());
Vector3 diff = pPlacement->GetPickupPosition() - pickupPosition;
if (diff.Mag2() < FIND_RANGE*FIND_RANGE)
return pPlacement;
pNode=pNode->GetNextPtr();
}
return NULL;
}
// Name : RemovePickupPlacement
// Purpose : Removes the pickup placement with the given script info.
// Parameters : info - the script info of the pickup to remove
// Returns : Nothing
void CPickupManager::RemovePickupPlacement(const CGameScriptObjInfo& info)
{
CPickupPlacement* pPlacement = GetPickupPlacementFromScriptInfo(info);
Assertf(pPlacement, "RemovePickupPlacement: pickup does not exist with this script info");
if (pPlacement)
{
bool bIsLocal = !pPlacement->GetNetworkObject() || (!pPlacement->IsNetworkClone() && !pPlacement->GetNetworkObject()->IsPendingOwnerChange()) || pPlacement->GetIsMapPlacement();
if (Verifyf(bIsLocal, "Trying to remove a pickup placement that is controlled by another machine"))
{
RemovePickupPlacement(pPlacement);
}
}
}
// Name : RemovePickupPlacement
// Purpose : Removes the given pickup placement
// Parameters : pPlacement - the pickup placement
// Returns : Nothing
void CPickupManager::RemovePickupPlacement(CPickupPlacement *pPlacement)
{
if (AssertVerify(pPlacement))
{
bool bIsLocal = !pPlacement->GetNetworkObject() || (!pPlacement->IsNetworkClone() && !pPlacement->GetNetworkObject()->IsPendingOwnerChange()) || pPlacement->GetIsMapPlacement();
if (Verifyf(bIsLocal, "Trying to remove a pickup placement that is controlled by another machine"))
{
pPlacement->SetIsBeingDestroyed();
if (pPlacement->GetScriptHandler())
{
pPlacement->GetScriptHandler()->UnregisterScriptObject(static_cast<scriptHandlerObject&>(*pPlacement));
}
if (pPlacement->GetNetworkObject())
{
NetworkInterface::GetObjectManager().UnregisterObject(pPlacement, pPlacement->IsNetworkClone());
}
// remove placement immediately if shutting down, if not add to removal list
if (ms_bShuttingDown)
{
DestroyPickupPlacement(pPlacement);
}
else if (!ms_removedPlacements.IsMemberOfList(pPlacement))
{
ms_removedPlacements.Add(pPlacement);
}
}
}
}
// Name : CreatePickup
// Purpose : Tries to creates a new pickup object. May fail if the model is not streamed in.
// Parameters : pickupHash - the hash of the new pickup type
// placementMatrix - the matrix specifying the position and orientation of the pickup
// pNetObj - if a network object is passed in this is assigned to the new pickup, otherwise it is registered as a new network object
// bRegisterAsNetworkObject - if true the pickup is registered as a network object and synced across the network
// customModelIndex - create a pickup with the given model index
// Returns : A pointer to the new pickup, if the creation was successful
CPickup* CPickupManager::CreatePickup(u32 pickupHash, const Matrix34& placementMatrix, netObject* pNetObj, bool bRegisterAsNetworkObject, u32 customModelIndex, bool bCreateDefaultWeaponAttachments, bool bCreateAsScriptEntity)
{
// Don't try to create a pickup if there are no objects free,
if(CObject::GetPool()->GetNoOfFreeSpaces() == 0)
{
#if !__FINAL
Errorf("Object pool is full. Unable to create a pickup \n");
CObject::DumpObjectPool();
#endif
return NULL;
}
CPickup* pPickup = NULL;
const CPickupData* pPickupData = CPickupDataManager::GetPickupData(pickupHash);
if (Verifyf(pPickupData, "Can't create pickup - pickup metadata does not exist for this pickup hash (%u)", pickupHash))
{
u32 modelIndex = customModelIndex != fwModelId::MI_INVALID ? customModelIndex : pPickupData->GetModelIndex();
if (modelIndex == fwModelId::MI_INVALID || !CModelInfo::GetStreamingModule()->IsObjectInImage(strLocalIndex(modelIndex)))
{
Errorf("Model for %s does not exist! (modelIndex=%d)", pPickupData->GetName(), modelIndex);
Assertf(0, "Model for %s does not exist! (modelIndex=%d)", pPickupData->GetName(), modelIndex);
}
else
{
u32 numRewards = pPickupData->GetNumRewards();
// Some pickups can have no rewards: these are pickups created a script that have rewards dictated by the script when it detects that the
// pickup has been collected. These must be created.
if (numRewards > 0)
{
bool bFoundValidReward = false;
// Walk through the pickup type rewards and make sure at least 1 reward exists
for( u32 i = 0; i < numRewards; i++ )
{
const CPickupRewardData* pReward = pPickupData->GetReward(i);
if( !IsSuppressionFlagSet( pReward->GetType() ) )
{
bFoundValidReward = true;
break;
}
}
// Did we fail to find a non-suppressed reward?
if( !bFoundValidReward && !pNetObj)
{
Errorf("Can't create pickup - all rewards suppressed");
Assertf(!NetworkInterface::IsGameInProgress(), "Can't create pickup - all rewards suppressed");
return NULL;
}
}
#if __DEV
// Have to set this flag to ensure objects are only created through the appropriate methods
CPickup::bInObjectCreate = true;
#endif // __DEV
if (pPickupData)
{
ClearSpaceInPickupPool(bRegisterAsNetworkObject);
if (CPickup::GetPool()->GetNoOfFreeSpaces() > 0 &&
(!NetworkInterface::IsGameInProgress() || CNetObjPickup::GetPool()->GetNoOfFreeSpaces() > 0))
{
CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(rage::fwModelId(strLocalIndex(modelIndex)));
if(pModelInfo == NULL || Verifyf(pModelInfo->GetFragType() == NULL,"Could not create pickup '%s' because it is a fragment ('%s').",pPickupData->GetName(),pModelInfo->GetFragType()->GetBaseName()))
{
pPickup = rage_new CPickup(ENTITY_OWNEDBY_RANDOM, pickupHash, customModelIndex, bCreateDefaultWeaponAttachments);
pPickup->SetForceAlphaAndUseAmbientScale();
pPickup->SetUseLightOverrideForGlow();
}
else
{
Errorf("Could not create pickup '%s' because it is a fragment ('%s').",pPickupData->GetName(),pModelInfo->GetFragType()->GetBaseName());
}
}
else
{
#if __DEV
static bool sbDumped = false;
// Dump pickup pool. Only do this once.
if (!sbDumped)
{
CPickup::_ms_pPool->PoolFullCallback();
sbDumped = true;
}
#endif // __DEV
if (CPickup::GetPool()->GetNoOfFreeSpaces() == 0)
{
Errorf("Can't create pickup - no spaces left in CPickup pool, see TTY for details");
Assertf(0, "Can't create pickup - no spaces left in CPickup pool, see TTY for details");
}
else
{
Errorf("Can't create pickup - no spaces left in CNetObjPickup pool, see TTY for details");
Assertf(0, "Can't create pickup - no spaces left in CNetObjPickup pool, see TTY for details");
}
}
}
#if __DEV
CPickup::bInObjectCreate = false;
#endif // __DEV
if(pPickup)
{
static dev_float TINY_PICKUP_OFFSET = 0.001f;
Matrix34 pickupMat(placementMatrix);
pickupMat.d.z += TINY_PICKUP_OFFSET;
pickupMat.Normalize();
// Set the matrix
pPickup->SetMatrix(pickupMat, true, true, true);
if (pPickup->GetPickupData()->GetAlwaysFixed())
{
pPickup->SetFixedPhysics(true);
}
// Add to the world, if no custom archetype needs assigned. If it does, it will be added later.
if (!pPickup->GetRequiresACustomArchetype())
{
CGameWorld::Add(pPickup, CGameWorld::OUTSIDE );
// pickups without placements have been dropped, so activate physics
if (!pPickup->GetPlacement() && !pPickup->GetPickupData()->GetAlwaysFixed())
{
pPickup->ActivatePhysics();
}
}
// Update portal tracker
if(pPickup->GetPortalTracker())
{
pPickup->GetPortalTracker()->SetLoadsCollisions(false);
pPickup->GetPortalTracker()->RequestRescanNextUpdate();
pPickup->GetPortalTracker()->Update(pickupMat.d);
}
// Initialise any stuff after pickup has been added to the world
pPickup->Init();
if (bCreateAsScriptEntity)
{
pPickup->SetOwnedBy(ENTITY_OWNEDBY_SCRIPT);
}
// assign or create a network object for this pickup
if (pNetObj)
{
Assert(pNetObj->IsClone());
// if a network object ptr is being passed in, then this is a clone pickup
pPickup->SetNetworkObject(pNetObj);
if (static_cast<CNetObjGame*>(pNetObj)->IsScriptObject())
{
pPickup->SetOwnedBy(ENTITY_OWNEDBY_SCRIPT);
}
}
else if (bRegisterAsNetworkObject && NetworkInterface::IsGameInProgress())
{
// placements are always created by scripts, so make placement pickups script objects too
NetObjFlags globalFlags = pPickup->GetPlacement() ? (CNetObjGame::GLOBALFLAG_SCRIPTOBJECT|CNetObjGame::GLOBALFLAG_SCRIPT_MIGRATION|CNetObjGame::GLOBALFLAG_CLONEONLY_SCRIPT) : 0;
if (bCreateAsScriptEntity)
{
globalFlags |= CNetObjGame::GLOBALFLAG_SCRIPTOBJECT;
}
if (!NetworkInterface::RegisterObject(pPickup, 0, globalFlags))
{
Errorf("Can't create pickup - failed to register it as a network object");
// pickup could not be registered as a network object (due to maximum number of CNetObjPickups being exceeded)
pPickup->Destroy();
pPickup = NULL;
NETWORK_QUITF(!bCreateAsScriptEntity, "Failed to register script pickup with network code - ran out of network objects");
}
else
{
Assert(pPickup->GetNetworkObject());
}
}
}
}
}
return pPickup;
}
// Name : CreatePickUpFromCurrentWeapon
// Purpose : Tries to creates a new pickup object from a peds current weapon. May fail if the model is not streamed in.
// Parameters : pPed - the ped who is holding the weapon
// Returns : Nothing
CPickup* CPickupManager::CreatePickUpFromCurrentWeapon(CPed* pPed, bool bUsePedAmmo, bool bDropWeaponIfUnarmed)
{
Assert( pPed );
// clones never generate pickups from dropped weapons
if (pPed->IsNetworkClone())
{
return NULL;
}
// If random peds are allowed to drop weapons, or we are the player, or we are a mission ped then do so.
if( CPed::GetRandomPedsDropWeapons() || pPed->IsLocalPlayer() || pPed->PopTypeIsMission() )
{
const CPedWeaponManager* pWeaponManager = pPed->GetWeaponManager();
if(pWeaponManager)
{
// Try to drop a weapon when a cop or a mission ped is killed unarmed.
// 1. Drop the last equipped weapon if it's non-unarmed. 2. Drop the best weapon if he never had a weapon equipped.
const CWeaponInfo* pDroppedWeaponInfo = NULL;
bool bUseMPPickups = ShouldUseMPPickups(pPed);
const CObject* pWeaponObject = pWeaponManager->GetEquippedWeaponObject();
if(bDropWeaponIfUnarmed)
{
// If no equipped weapon or the equipped weapon is unarmed
const CWeaponInfo* pEquippedWeaponInfo = pWeaponManager->GetEquippedWeaponInfo();
if(!pWeaponObject || !pEquippedWeaponInfo || pEquippedWeaponInfo->GetIsUnarmed())
{
pDroppedWeaponInfo = pPed->GetWeaponManager()->GetLastEquippedWeaponInfo();
// Use the best weapon if the last equipped weapon is NULL or doesn't have a valid pickup hash.
if(!pDroppedWeaponInfo || !pPed->GetInventory()->GetWeapon(pDroppedWeaponInfo->GetHash()) || (bUseMPPickups ? (pDroppedWeaponInfo->GetMPPickupHash() == 0) : (pDroppedWeaponInfo->GetPickupHash() == 0)))
{
pDroppedWeaponInfo = pPed->GetWeaponManager()->GetBestWeaponInfo();
if(pDroppedWeaponInfo && (bUseMPPickups ? (pDroppedWeaponInfo->GetMPPickupHash() == 0) : (pDroppedWeaponInfo->GetPickupHash() == 0)))
{
pDroppedWeaponInfo = NULL;
}
}
}
}
if(pWeaponObject || pDroppedWeaponInfo)
{
const CWeapon* pWeapon = pWeaponObject ? pWeaponObject->GetWeapon() : NULL;
const CWeaponInfo* pWeaponInfo = bDropWeaponIfUnarmed ? pDroppedWeaponInfo : (pWeapon ? pWeapon->GetWeaponInfo() : NULL);
if(pWeaponInfo)
{
u32 uPickupHash = bUseMPPickups ? pWeaponInfo->GetMPPickupHash() : pWeaponInfo->GetPickupHash();
if( Verifyf( uPickupHash != 0, "Weapon [%s] doesn't have a pickup associated with it. DropWeaponIfUnarmed: %s", pWeaponInfo->GetName(), bDropWeaponIfUnarmed ? "T":"F" ) )
{
// Only create a pickup if not a player, or player has ammo for their gun
if( !pPed->IsAPlayerPed() || (pWeapon && pWeapon->GetAmmoTotal() > 0) || pWeaponInfo->GetIsMelee() || bUsePedAmmo || pDroppedWeaponInfo )
{
const CPickupData* pPickupData = CPickupDataManager::GetPickupData( uPickupHash );
const bool bReusingWeaponModel = !pDroppedWeaponInfo && pPickupData && pWeaponInfo->GetModelHash() == pPickupData->GetModelHash();
Matrix34 m;
if(pDroppedWeaponInfo)
{
m.Identity();
m.d = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
}
else
{
m = MAT34V_TO_MATRIX34(pWeaponObject->GetMatrix());
}
// We don't want the pickup is created on the top of a car if the ped is killed inside.
if(pPed->GetIsAttachedInCar())
{
CVehicle* pMyVehicle = pPed->GetMyVehicle();
if(pMyVehicle && pMyVehicle->GetVehicleType() == VEHICLE_TYPE_CAR)
{
CPickupPositionInfo pos( m.d );
pos.m_MinDist = 0.0f;
pos.m_MaxDist = 2.0f;
CalculateDroppedPickupPosition( pos, true );
m.d = pos.m_Pos;
}
}
strLocalIndex customModelIndex = strLocalIndex(fwModelId::MI_INVALID);
if (!bUseMPPickups && pWeapon)
{
// If we have a variation model component on the weapon, use that model for the pickup
const CWeaponComponentVariantModelInfo* pVariantInfo = pWeapon->GetVariantModelComponent() ? pWeapon->GetVariantModelComponent()->GetInfo() : NULL;
if (pVariantInfo)
{
// Make sure the model exists
fwModelId variantModelId;
CModelInfo::GetBaseModelInfoFromHashKey(pVariantInfo->GetModelHash(), &variantModelId);
if (weaponVerifyf(variantModelId.IsValid(), "ModelId is invalid for VariantModel component on [%s], using original model for pickup instead.", pWeaponInfo->GetName()))
{
customModelIndex = variantModelId.GetModelIndex();
}
}
}
CPickup* pPickup = CPickupManager::CreatePickup( uPickupHash, m, NULL, true, customModelIndex.Get(), !bReusingWeaponModel );
if( pPickup )
{
// prevent the pickup from displaying help text immediately
if (pPed->IsLocalPlayer())
{
pPickup->SetFlag(CPickup::PF_HelpTextDisplayed);
pPickup->SetFlag(CPickup::PF_LocalPlayerCollision);
if( !NetworkInterface::IsGameInProgress() )
pPickup->SetFlag(CPickup::PF_DontGlow);
}
else if (pPed->PopTypeIsMission() || (pPed->GetNetworkObject() && pPed->GetNetworkObject()->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_WAS_SCRIPTOBJECT)))
{
pPickup->SetDroppedByPed();
}
if(!pDroppedWeaponInfo)
{
// The total ammo can be set to -1 for infinite ammo.
if( bUsePedAmmo && pWeapon->GetAmmoTotal() > 0 )
{
// Use the weapon ammo for the pickup
pPickup->SetAmount(pWeapon->GetAmmoTotal());
}
else if( bUseMPPickups )
{
u32 maxAmmoInClip = pWeapon->GetClipSize();
// The total ammo can be set to -1 for infinite ammo. Ensure we don't pass -1 into the unsigned pickup amount.
s32 iAmmoInWeapon = pWeapon->GetAmmoTotal();
if (iAmmoInWeapon == 0)
{
delete pPickup;
return NULL;
}
u32 uWeaponToDecrementAmmoFrom = pWeapon->GetWeaponHash();
s32 sWeaponToDecrementAmmoFromTotal = iAmmoInWeapon;
if( pWeaponInfo->GetHash() == WEAPONTYPE_STUNGUN )
{
uWeaponToDecrementAmmoFrom = pPed->GetWeaponManager()->GetBestWeaponHash( pWeaponInfo->GetHash() );
sWeaponToDecrementAmmoFromTotal = pPed->GetInventory()->GetWeaponAmmo( uWeaponToDecrementAmmoFrom );
}
// As infinite ammo, use max ammo.
if(sWeaponToDecrementAmmoFromTotal < 0)
{
sWeaponToDecrementAmmoFromTotal = maxAmmoInClip;
}
// Initialise the pickup to be the amount of ammo in the current peds weapon, up to a max of MAX_AMMO_MP
u32 uAmmoAmount = maxAmmoInClip;
if (uPickupHash == PICKUP_AMMO_MISSILE_MP ||
uPickupHash == PICKUP_AMMO_GRENADELAUNCHER_MP ||
uPickupHash == PICKUP_WEAPON_GRENADE ||
uPickupHash == PICKUP_WEAPON_SMOKEGRENADE ||
uPickupHash == PICKUP_WEAPON_STICKYBOMB ||
uPickupHash == PICKUP_WEAPON_MOLOTOV)
{
uAmmoAmount = 1;
}
else if( !pPed->IsAPlayerPed() )
{
// Randomise the amount for AI peds
uAmmoAmount = fwRandom::GetRandomNumberInRange(1, maxAmmoInClip);
// some peds are told how much ammo to drop by script
if (pPed->GetNetworkObject())
{
u32 ammoToDrop = static_cast<CNetObjPed*>(pPed->GetNetworkObject())->GetAmmoToDrop();
if (ammoToDrop > 0)
{
uAmmoAmount = ammoToDrop;
}
}
}
else
{
if(iAmmoInWeapon > -1)
{
uAmmoAmount = Min((u32)iAmmoInWeapon, maxAmmoInClip);
}
else // As infinite ammo, use max ammo.
{
uAmmoAmount = maxAmmoInClip;
}
}
// some weapons (like the minigun) only have a single large clip, so cap how much can be dropped
// matches hard-coded values in script func GET_AMMO_AMOUNT_FOR_MP_PICKUP of how much ammo is removed from player on kill
u32 uAmmoDroppedCap = 0;
if (pWeaponInfo->GetHash() == WEAPONTYPE_MINIGUN)
{
uAmmoDroppedCap = 200;
}
else if (pWeaponInfo->GetHash() == ATSTRINGHASH("WEAPON_RAYCARBINE", 0x476BF155))
{
uAmmoDroppedCap = 100; // iAW_WEAPON_CLIP_CAPACITY_UNHOLY_HELLBRINGER in mp_globals_tunables.sch
}
else if (pWeaponInfo->GetHash() == ATSTRINGHASH("WEAPON_RAYMINIGUN", 0xB62D1F67))
{
uAmmoDroppedCap = 50; // iAW_WEAPON_CLIP_CAPACITY_WIDOWMAKER in mp_globals_tunables.sch
}
if (uAmmoDroppedCap > 0 && uAmmoAmount > uAmmoDroppedCap)
{
uAmmoAmount = uAmmoDroppedCap;
}
// Set the pickup to only give this amount of ammo
Assert(uAmmoAmount < 65535);
pPickup->SetAmount(uAmmoAmount);
// Decrement this amount of ammo from the ped dropping the weapon
if( pPed->GetInventory() )
{
// Decrement one ammo for explosive weapons, otherwise decrement the ammo given to the pickup
u32 uAmmoDecrement = uAmmoAmount;
const CWeaponInfo* pWeaponInfoToDecrementFrom = CWeaponInfoManager::GetInfo<CWeaponInfo>(uWeaponToDecrementAmmoFrom);
if( pWeaponInfoToDecrementFrom && pWeaponInfoToDecrementFrom->GetDamageType() == DAMAGE_TYPE_EXPLOSIVE )
{
uAmmoDecrement = 1;
}
pPed->GetInventory()->NotifyAmmoChange( uWeaponToDecrementAmmoFrom, sWeaponToDecrementAmmoFromTotal-uAmmoDecrement );
}
}
SetStartingVelocities(pPickup, pPed, RAGDOLL_HAND_RIGHT, RAGDOLL_CLAVICLE_RIGHT);
// Only create and set weapon for weapon type pickups.
bool bIsWeaponPickup = false;
for (u32 i=0; i<pPickupData->GetNumRewards(); i++)
{
const CPickupRewardData* pReward = pPickupData->GetReward(i);
if (pReward && pReward->GetType() == PICKUP_REWARD_TYPE_WEAPON)
{
bIsWeaponPickup = true;
break;
}
}
if( bIsWeaponPickup && !pPickup->GetWeapon() )
{
pPickup->SetWeapon(WeaponFactory::Create(pWeapon->GetWeaponHash(), pWeapon->GetAmmoTotal(), pPickup, "CPickupManager::CreatePickUpFromCurrentWeapon"));
if (pWeapon->GetIsCooking()) //if this weapon was cooking transfer the love
{
pPickup->GetWeapon()->StartCookTimer(pWeapon->GetCookTime(), pPed);
}
if(pPickup->GetWeapon())
{
pPickup->GetWeapon()->GetAudioComponent().SetWasDropped(true);
}
}
// Only do this if the pickup uses the same model as the original weapon
if( bReusingWeaponModel )
{
// Make sure pickup has correct attachments
const CWeapon::Components& components = pWeapon->GetComponents();
for( s32 i = 0; i < components.GetCount(); i++ )
{
CPedEquippedWeapon::CreateWeaponComponent( components[i]->GetInfo()->GetHash(), pPickup );
}
bool bBlockFireOnImpact = false;
if( pWeapon->GetWeaponInfo()->GetCreateVisibleOrdnance() )
{
if( CPedEquippedWeapon::GetProjectileOrdnance( pWeaponObject ) )
{
CPedEquippedWeapon::CreateProjectileOrdnance( pPickup, NULL );
}
bBlockFireOnImpact = true;
}
if( !pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_StopWeaponFiringOnImpact ) &&
!pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_KilledByStealth ) &&
pPickup->GetDrawable() &&
!bBlockFireOnImpact )
{
// Only shoot on impacts 50% of the time
pPickup->m_nObjectFlags.bWeaponWillFireOnImpact = fwRandom::GetRandomTrueFalse();
weaponDebugf1("Pickup [0x%p] dropped by ped [0x%p]: bWeaponWillFireOnImpact %d", pPickup, pPed, pPickup->m_nObjectFlags.bWeaponWillFireOnImpact);
}
else
{
pPickup->m_nObjectFlags.bWeaponWillFireOnImpact = false;
}
}
// Set the correct rendering flags now whole hierarchy has been made (so attachments are set).
pPickup->SetForceAlphaAndUseAmbientScale();
// Set up the glow too.
pPickup->SetUseLightOverrideForGlow();
// Make sure pickup has correct tint
if( pPickup->GetWeapon() )
{
pPickup->GetWeapon()->UpdateShaderVariables( pWeapon->GetTintIndex() );
}
}
pPickup->SetLastOwner(pPed);
return pPickup;
}
}
}
}
}
}
}
return NULL;
}
// Name : GetPickupPlacementFromScriptInfo
// Purpose : Returns the pickup placement corresponding to the given script info
// Parameters : info - the pickup script info
// Returns : A pointer to the pickup placement.
CPickupPlacement* CPickupManager::GetPickupPlacementFromScriptInfo(const CGameScriptObjInfo& info)
{
s32 i = CPickupPlacement::GetPool()->GetSize();
// test pickup objects
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && pPlacement->GetScriptInfo() && *pPlacement->GetScriptInfo() == info)
{
return pPlacement;
}
}
return NULL;
}
//
// Name : RegisterPickupCollection
// Purpose : Called when the pickup for the given placement is collected
// Parameters : pPlacement - the pickup placement that spawned the pickup that was collected
// pPed - the ped who collected it
// Returns : Nothing
void CPickupManager::RegisterPickupCollection(CPickupPlacement* pPlacement, CPed* pPed)
{
if (AssertVerify(pPlacement) && AssertVerify(!pPlacement->GetIsCollected()))
{
if (pPlacement->GetScriptInfo())
{
// add to collection history
// TODO: remove this history. The script event should be used instead. At the moment the scripts are still using the old code and
// need to listen for the event instead.
CCollectionInfo collectInfo(*pPlacement->GetScriptInfo(), pPed);
if (ms_collectionHistory.IsFull())
{
ms_collectionHistory.Drop();
}
ms_collectionHistory.Push(collectInfo);
// Add network script event for the pickup event.
if (pPed && pPed->GetNetworkObject() && (pPed->IsLocalPlayer() || pPed->IsNetworkPlayer()))
{
CEventNetworkPlayerCollectedPickup pickupEvent(*pPed->GetNetworkObject()->GetPlayerOwner(),
pPlacement->GetScriptInfo()->GetObjectId(),
pPlacement->GetPickupHash(),
pPlacement->GetAmount(),
pPlacement->GetCustomModelHash(),
(pPlacement->GetAmountCollected() > 0) ? pPlacement->GetAmountCollected() : pPlacement->GetAmount());
GetEventScriptNetworkGroup()->Add(pickupEvent);
if(NetworkInterface::IsGameInProgress() && (pPed->IsNetworkPlayer() || pPed->IsNetworkBot()))
{
g_ScriptAudioEntity.PlayNPCPickup(pPlacement->GetPickupHash(),pPlacement->GetPickupPosition());
}
}
else
{
CEventNetworkPlayerCollectedPickup pickupEvent(pPlacement->GetScriptInfo()->GetObjectId(),
pPlacement->GetPickupHash(),
pPlacement->GetAmount(),
pPlacement->GetCustomModelHash(),
(pPlacement->GetAmountCollected() > 0) ? pPlacement->GetAmountCollected() : pPlacement->GetAmount());
GetEventScriptNetworkGroup()->Add(pickupEvent);
}
}
// if we are waiting to collect this pickup, then do it now
if (pPlacement->IsNetworkClone())
{
pPlacement->Collect(pPed);
}
else
{
// if this is a regenerating placement, add to the regenerating list, otherwise it is removed
if (pPlacement->GetRegenerates())
{
AddPlacementToRegenerationList(pPlacement);
}
pPlacement->Collect(pPed);
}
}
}
//
// Name : RegisterPickupDestruction
// Purpose : Called when the pickup for the given placement is destroyed
// Parameters : pPlacement - the pickup placement that spawned the pickup that was destroyed
// Returns : Nothing
void CPickupManager::RegisterPickupDestruction(CPickupPlacement *pPlacement)
{
if (AssertVerify(pPlacement) && pPlacement->GetRegenerates())
{
AddPlacementToRegenerationList(pPlacement);
}
}
// Name : HasPickupBeenCollected
// Purpose : Returns true if the pickup with the given script info has been picked up.
// Parameters : scriptInfo - the script info
// pPedWhoGotIt - set to the ped who collected the pickup, if it was collected
// Returns : True if the pickup has been collected
bool CPickupManager::HasPickupBeenCollected(const CGameScriptObjInfo& scriptInfo, CPed** ppPedWhoGotIt)
{
int index;
CCollectionInfo info(scriptInfo);
if (ms_collectionHistory.Find(info, &index))
{
if (ppPedWhoGotIt)
*ppPedWhoGotIt = ms_collectionHistory[index].GetPed();
// remove the collection history once it has been queried
ms_collectionHistory.Delete(index);
return true;
}
return false;
}
// Name : GetRegenerationTime
// Purpose : Returns the time the given placement will regenerate its pickup object
u32 CPickupManager::GetRegenerationTime(CPickupPlacement *pPlacement)
{
fwPtrNode* pNode = ms_regeneratingPlacements.GetHeadPtr();
while (pNode)
{
CRegenerationInfo* pRegenInfo = static_cast<CRegenerationInfo*>(pNode->GetPtr());
fwPtrNode* pNextNode = pNode->GetNextPtr();
if (pRegenInfo->GetPlacement() == pPlacement)
{
return pRegenInfo->GetRegenerationTime();
}
pNode = pNextNode;
}
if (AssertVerify(pPlacement))
{
if (pPlacement->GetNetworkObject())
{
Assertf(0, "CPickupManager::GetRegenerationTime: %s is not regenerating", pPlacement->GetNetworkObject()->GetLogName());
}
else
{
Assertf(0, "CPickupManager::GetRegenerationTime: placement at %f, %f, %f is not regenerating", pPlacement->GetPickupPosition().x, pPlacement->GetPickupPosition().y, pPlacement->GetPickupPosition().z);
}
}
return 0;
}
// Name : MigratePlacementOwnership
// Purpose : Called when the ownership of a placement migrates locally in a network game
// Parameters : pPlacement - the placement
// : regenerationTime - the time the given placement will regenerate its pickup object.
void CPickupManager::MigratePlacementOwnership(CPickupPlacement *pPlacement, u32 regenerationTime)
{
if (AssertVerify(pPlacement))
{
// if we are waiting to collect this pickup, then do it now
if (!pPlacement->GetIsCollected() && pPlacement->GetPickup() && pPlacement->GetPickup()->GetIsPendingCollection())
{
pPlacement->GetPickup()->ProcessCollectionResponse(NULL, true);
}
if ((pPlacement->GetIsCollected() || pPlacement->GetHasPickupBeenDestroyed()) &&
pPlacement->GetRegenerates() &&
!IsPlacementOnRegenerationList(pPlacement))
{
CRegenerationInfo* pRegenInfo = AddPlacementToRegenerationList(pPlacement);
if (regenerationTime != 0)
{
pRegenInfo->SetRegenerationTime(regenerationTime);
}
}
// if the placement is in scope of the player, place on pending list so that its pickup object is created now
if (pPlacement->GetInScope() && !pPlacement->GetIsCollected() && !pPlacement->GetHasPickupBeenDestroyed())
{
if (!ms_pendingPickups.IsMemberOfList(pPlacement))
{
ms_pendingPickups.Add(pPlacement);
}
}
}
}
// Name : GetIsAnyPickupInBounds
// Purpose : Returns true if any pickup objects or placements exist within the given bounding box
// Parameters : bounds - the bounding box
// Returns : True if any pickup objects exists within the given bounding box
bool CPickupManager::GetIsAnyPickupInBounds(const spdAABB& bounds)
{
s32 i = CPickup::GetPool()->GetSize();
// test pickup objects
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && bounds.ContainsPoint(pPickup->GetTransform().GetPosition()))
{
return true;
}
}
i = CPickupPlacement::GetPool()->GetSize();
// test pickup placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && bounds.ContainsPoint(RCC_VEC3V(pPlacement->GetPickupPosition())))
{
return true;
}
}
return false;
}
// Name : RemoveAllPickups
// Purpose : Removes all pickup objects
// Parameters : bIncludeScriptPickups - if set all script created pickups and placements are also removed
// Returns : Nothing
void CPickupManager::RemoveAllPickups(bool bIncludeScriptPickups, bool bImmediately)
{
s32 i = CPickup::GetPool()->GetSize();
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && !pPickup->IsNetworkClone() && (!pPickup->GetNetworkObject() || pPickup->GetNetworkObject()->CanDelete()))
{
if ((!pPickup->GetNetworkObject() || pPickup->GetNetworkObject()->CanDelete()) &&
(bIncludeScriptPickups || !pPickup->GetExtension<CScriptEntityExtension>()))
{
if (bImmediately)
{
if (pPickup->GetPlacement())
{
pPickup->GetPlacement()->SetPickup(NULL);
pPickup->SetPlacement(NULL);
}
CObjectPopulation::DestroyObject(pPickup);
}
else
{
pPickup->Destroy();
}
}
}
}
if (bIncludeScriptPickups)
{
s32 i = CPickupPlacement::GetPool()->GetSize();
// remove placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && !pPlacement->IsNetworkClone())
{
RemovePickupPlacement(pPlacement);
}
}
}
}
// Name : RemoveAllPickupsOfType
// Purpose : Removes all pickup objects and placements with the given type
// Parameters : pickupHash - the hash of the pickup type
// bIncludeScriptPickups - if set all script created pickups and placements are also removed
// Returns : Nothing
void CPickupManager::RemoveAllPickupsOfType(u32 pickupHash, bool bIncludeScriptPickups)
{
s32 i = CPickup::GetPool()->GetSize();
// remove pickups that have no corresponding placements
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && !pPickup->IsNetworkClone() && !pPickup->GetPlacement() && pPickup->GetPickupHash() == pickupHash)
{
if ((!pPickup->GetNetworkObject() || pPickup->GetNetworkObject()->CanDelete()) &&
(bIncludeScriptPickups || !pPickup->GetExtension<CScriptEntityExtension>()))
{
pPickup->Destroy();
}
}
}
if (bIncludeScriptPickups)
{
i = CPickupPlacement::GetPool()->GetSize();
// remove placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && !pPlacement->IsNetworkClone() && pPlacement->GetPickupHash() == pickupHash)
{
RemovePickupPlacement(pPlacement);
}
}
}
}
// Name : RemoveAllPickupsInBounds
// Purpose : Removes all pickup objects and placements within the given bounding box
// Parameters : bounds - the bounding box
// bLowPriorityOnly - if true only pickups flagged as low priority are removed
// bIncludeScriptPickups - if set all script created pickups and placements are also removed
// Returns : Nothing
void CPickupManager::RemoveAllPickupsInBounds(const spdAABB& bounds, bool bLowPriorityOnly, bool bIncludeScriptPickups)
{
s32 i = CPickup::GetPool()->GetSize();
// remove pickups that have no corresponding placements
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && !pPickup->IsNetworkClone() && !pPickup->GetPlacement() && bounds.ContainsPoint(pPickup->GetTransform().GetPosition()) && (pPickup->GetPickupData()->GetLowPriority() || !bLowPriorityOnly ))
{
if ((!pPickup->GetNetworkObject() || pPickup->GetNetworkObject()->CanDelete()) &&
(bIncludeScriptPickups || !pPickup->GetExtension<CScriptEntityExtension>()))
{
pPickup->Destroy();
}
}
}
if (bIncludeScriptPickups)
{
i = CPickupPlacement::GetPool()->GetSize();
// remove placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && !pPlacement->IsNetworkClone() )
{
const CPickupData* pData = CPickupDataManager::GetPickupData(pPlacement->GetPickupHash());
if (AssertVerify(pData) && bounds.ContainsPoint(RCC_VEC3V(pPlacement->GetPickupPosition())) && (pData->GetLowPriority() || !bLowPriorityOnly ))
{
RemovePickupPlacement(pPlacement);
}
}
}
}
}
// Name : DetachAllPortablePickupsFromPed
// Purpose : Finds and detaches all portable pickups from the given ped
void CPickupManager::DetachAllPortablePickupsFromPed(CPed* pPed)
{
s32 i = CPickup::GetPool()->GetSize();
// remove pickups that have no corresponding placements
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && pPickup->IsFlagSet(CPickup::PF_Portable) && pPickup->GetAttachParent() == pPed && !pPickup->IsNetworkClone())
{
pPickup->DetachPortablePickupFromPed("DetachAllPortablePickupsFromPed(2)");
}
}
}
// Name : CountPickupsOfType
// Purpose : Counts the number of placements (and pickups without a placement) with the given type
// Parameters : pickupHash - the pickup hash
// Returns : The count
s32 CPickupManager::CountPickupsOfType(u32 pickupHash)
{
s32 count = 0;
s32 i = CPickup::GetPool()->GetSize();
// count pickups that have no corresponding placements
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && !pPickup->GetPlacement() && pPickup->GetPickupHash() == pickupHash)
{
count++;
}
}
i = CPickupPlacement::GetPool()->GetSize();
// count placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && pPlacement->GetPickupHash() == pickupHash)
{
count++;
}
}
return count;
}
// Name : TestForPickupsInSphere
// Purpose : Tests to see if any pickups exist within the given sphere
// Parameters : SpherePos - the centre of the sphere
// SphereRadius - the radius of the sphere
// Returns : True if pickups exist with the sphere
bool CPickupManager::TestForPickupsInSphere(const Vector3& spherePos, float sphereRadius)
{
s32 i = CPickup::GetPool()->GetSize();
float sphereRadiusSqr = sphereRadius*sphereRadius;
// count pickups that have no corresponding placements
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && !pPickup->GetPlacement() && (VEC3V_TO_VECTOR3(pPickup->GetTransform().GetPosition()) - spherePos).Mag2() < sphereRadiusSqr)
{
return true;
}
}
i = CPickupPlacement::GetPool()->GetSize();
// count placements
while(i--)
{
CPickupPlacement* pPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPlacement && (pPlacement->GetPickupPosition() - spherePos).Mag2() < sphereRadiusSqr)
{
return true;
}
}
return false;
}
// Name : CalculateDroppedPickupPosition
// Purpose : Generates some coords suitable for placing a new pickup on
// Parameters : targetCoords - position that we ideally want the pickup to be placed
// result - the closest position to the target coords that a pickup could be placed at
// requestedDist - the furthest distance away from the target coords that we will consider for the result
// Returns : True if a valid position was found
bool CPickupManager::CalculateDroppedPickupPosition( CPickupPositionInfo &dat, bool bIgnoreInvalidePosWarning /*= false*/ )
{
Vector3 PickUpCoors, Temp, Temp2;
Vector3 localTargetCoords = dat.m_SrcPos;
s32 i;
bool bOnGround, bOnGround2;
float pickupHeightOffGround = dat.m_pickupHeightOffGround > 0.0f ? dat.m_pickupHeightOffGround : 0.25f;
if (!dat.m_bOnGround)
{
// Snap the startcoors to the map.
// 1.5 m up from us, downwards
float newZ = WorldProbe::FindGroundZFor3DCoord(TOP_SURFACE, localTargetCoords.x, localTargetCoords.y, localTargetCoords.z+1.5f, &bOnGround);
// If it hits ground in less than 1.5m from our start pos
if (bOnGround && rage::Abs(localTargetCoords.z-newZ) < 1.5f)
{
dat.m_bOnGround = true;
// Now we do a second line scan down - just below our original ped pos.
float newZ2 = WorldProbe::FindGroundZFor3DCoord(TOP_SURFACE, localTargetCoords.x, localTargetCoords.y, localTargetCoords.z - 0.05f, &bOnGround2);
// This is to fix the rare cases where the pickup ends up on the floor above the dying ped.
// If delta is closer than the original then original probably hit the ceiling above. Obviously still fails if we are > halfway between the floors
if (bOnGround2 && (rage::Abs(localTargetCoords.z-newZ2) < rage::Abs(localTargetCoords.z-newZ)))
{
localTargetCoords.z = newZ2;
}
else
{
// This is our new ground
localTargetCoords.z = newZ;
}
}
// Make sure the pickup above the ground.
localTargetCoords.z += pickupHeightOffGround;
}
#define TRIES (16)
float radius = dat.m_MinDist;
float RandomAngle = fwRandom::GetRandomNumberInRange(1.0f, 359.0f) * PI/180.0f;
CPed* pLocalPlayer = CGameWorld::FindLocalPlayer();
Vector3 localPlayerCoords = pLocalPlayer ? VEC3V_TO_VECTOR3(pLocalPlayer->GetTransform().GetPosition()) : CGameWorld::FindLocalPlayerCoors();
float minDistFromLocalPlayer = dat.m_MinDist*dat.m_MinDist*0.95f;
if (!pLocalPlayer || pLocalPlayer->IsDead())
{
minDistFromLocalPlayer = 0.0f;
}
for (i = 0; i < TRIES; i++)
{
PickUpCoors = localTargetCoords;
if ( radius > dat.m_MaxDist )
{
radius = dat.m_MinDist;
}
if (i!=0)
{
PickUpCoors.x += radius * rage::Sinf(RandomAngle);
PickUpCoors.y += radius * rage::Cosf(RandomAngle);
}
// Set the z coordinate to be above the ground a bit
PickUpCoors.z = WorldProbe::FindGroundZFor3DCoord(TOP_SURFACE, PickUpCoors.x, PickUpCoors.y, PickUpCoors.z+1.0f, &bOnGround);
if (bOnGround)
{
// We don't want to place pickups behind walls so:
// Do a test to make sure the LOS between the ped and the pickup is free.
Temp = localTargetCoords;
Temp.z += 0.3f;
// extend line a little bit
Temp2 = PickUpCoors;
Temp2.z += 0.3f; // Do linetest a bit above ground or the collision test will always fail.
Temp2 = Temp + (Temp2 - Temp) * (((Temp2 - Temp).Mag() + 0.3f) / (Temp2 - Temp).Mag());
// Make sure these coordinates are reasonably far away from the player
Vector3 vToLocalPlayer = PickUpCoors - localPlayerCoords;
float DistToLocalPlayer = vToLocalPlayer.Mag2();
if (DistToLocalPlayer > minDistFromLocalPlayer || i > (TRIES/2))
{
if (i > (TRIES/2) || !TestForPickupsInSphere(PickUpCoors, 1.3f))
{
// If the los from the ped to the potential pickup is clear we're laughing (test for cars for the first 16 go's)
int flags=ArchetypeFlags::GTA_ALL_MAP_TYPES|ArchetypeFlags::GTA_MAP_TYPE_WEAPON;
if(i < TRIES/2)
{
// flags|=ArchetypeFlags::GTA_VEHICLE_TYPE;
flags|=ArchetypeFlags::GTA_OBJECT_TYPE;
}
WorldProbe::CShapeTestProbeDesc probeDesc;
probeDesc.SetStartAndEnd(Temp, Temp2);
probeDesc.SetIncludeFlags(flags);
probeDesc.SetContext(WorldProbe::LOS_Unspecified);
if(!WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc))
{
// Do an extra check for collisions with cars. These should be avoided. Use a capsule to detect if the pickup is being placed underneath a vehicle, and possibly inaccessible
Vector3 capsuleStart = PickUpCoors;
Vector3 capsuleEnd = PickUpCoors;
float capsuleRadius = 0.5f;
float capsuleHeight = 2.0f;
capsuleStart.z -= capsuleRadius;
capsuleEnd.z += capsuleHeight+capsuleRadius;
WorldProbe::CShapeTestCapsuleDesc capsuleDesc;
capsuleDesc.SetCapsule(capsuleStart, capsuleEnd, capsuleRadius);
capsuleDesc.SetIncludeFlags(ArchetypeFlags::GTA_VEHICLE_TYPE);
if(!WorldProbe::GetShapeTestManager()->SubmitTest(capsuleDesc))
{
dat.m_Pos = PickUpCoors;
dat.m_Pos.z += pickupHeightOffGround; // Make sure the pickup above the ground.
dat.m_bOnGround = true;
return true;
}
}
}
}
}
RandomAngle += (137.1f * PI/180.0f);
RandomAngle = fwAngle::LimitRadianAngle(RandomAngle);
// And expand us out a bit
radius += 0.05f;
}
if(!bIgnoreInvalidePosWarning)
{
Assertf(0, "CPickupManager::CalculateDroppedPickupPosition - Failed to find valid pickup position:%f,%f,%f,MinDist:%f,MaxDist:%f,Ground:%i,PlayerPos:%f,%f\n",
dat.m_SrcPos.x,dat.m_SrcPos.y,dat.m_SrcPos.z,
dat.m_MinDist,dat.m_MaxDist,dat.m_bOnGround,
CGameWorld::FindLocalPlayerCoors().x,CGameWorld::FindLocalPlayerCoors().y);
}
else
{
aiWarningf("CPickupManager::CalculateDroppedPickupPosition - Failed to find valid pickup position:%f,%f,%f,MinDist:%f,MaxDist:%f,Ground:%i,PlayerPos:%f,%f\n",
dat.m_SrcPos.x,dat.m_SrcPos.y,dat.m_SrcPos.z,
dat.m_MinDist,dat.m_MaxDist,dat.m_bOnGround,
CGameWorld::FindLocalPlayerCoors().x,CGameWorld::FindLocalPlayerCoors().y);
}
dat.m_Pos = localTargetCoords;
return false;
}
// Name : CreateSomeMoney
// Purpose : Generates a bunch of money pickups up to the given amount, around the given coordinates
// Parameters : PickUpCoors - the position the money has to be generated around
// Amount - the total value of the money dropped
// MaxNumPickups - the max number of cash pickups dropped
// Returns : Nothing
void CPickupManager::CreateSomeMoney(const Vector3& targetCoords, u32 Amount, u32 MaxNumPickups, u32 customModelIndex, CPed* pPedForVelocity)
{
#if __BANK
if(ms_NoMoneyDrop)
{
return;
}
#endif // __BANK
s32 i;
const int maxMoneyPickups = 8;
Assertf(MaxNumPickups <= maxMoneyPickups, "CPickupManager::CreateSomeMoney - can only create a maximum of %d money pickups", maxMoneyPickups);
int AmountPerPickup = Amount / MaxNumPickups;
if (Amount % MaxNumPickups)
{
AmountPerPickup = 1 + (Amount / MaxNumPickups);
}
for (i = 0; i < maxMoneyPickups; i++)
{
if (Amount == 0)
break;
bool createPickup = false;
Matrix34 pickupM;
pickupM.Identity();
if(pPedForVelocity)
{
pPedForVelocity->GetBonePosition(pickupM.d,BONETAG_SPINE3);
// Add in some extra offset to avoid phLevel duplicate placement asserts.
float extraOffset = ((float)i)*0.001f;
pickupM.d += Vector3(0.0f, extraOffset, 0.05f);
createPickup = true;
}
else
{
// The money pickup originates from inside the ped
CPickupPositionInfo pos( targetCoords );
pos.m_MinDist = pPedForVelocity ? 0.1f : 1.5f;
pos.m_MaxDist = pPedForVelocity ? 0.15f : 1.55f;
CalculateDroppedPickupPosition( pos, true );
if(pos.m_bOnGround)
{
pickupM.d = pos.m_Pos;
pickupM.d.z += 0.05f;
createPickup = true;
}
}
if (createPickup)
{
CPickup* pPickup = CreatePickup(PICKUP_MONEY_VARIABLE, pickupM, NULL, false, customModelIndex);
if (pPickup)
{
int AmountThisPickup = MIN(AmountPerPickup, Amount);
pPickup->SetAmount(AmountThisPickup);
if (Amount > AmountThisPickup)
Amount -= AmountThisPickup;
else
Amount = 0;
bool bUseInitVelocities = false;
if(pPedForVelocity && pPedForVelocity->GetIsVisibleInSomeViewportThisFrame())
{
const ScalarV rotationAngle = ScalarVFromF32(fwRandom::GetRandomNumberInRange(0.0f,2.0f*PI));
const Vec3V extraVelocityDir = RotateAboutZAxis(Vec3V(V_X_AXIS_WZERO),rotationAngle);
TUNE_GROUP_FLOAT(PICKUP,PED_CASH_HORIZONTAL_VEL,3.5f,0.0f,10.0f,1.0f);
TUNE_GROUP_FLOAT(PICKUP,PED_CASH_VERTICAL_VEL, -3.5f,-10.0f,10.0f,1.0f);
const Vec3V extraVelocity = Add(Scale(ScalarVFromF32(PED_CASH_VERTICAL_VEL),Vec3V(V_Z_AXIS_WZERO)), Scale(ScalarVFromF32(PED_CASH_HORIZONTAL_VEL),extraVelocityDir));
bUseInitVelocities = SetStartingVelocities(pPickup, pPedForVelocity, RAGDOLL_SPINE0, RAGDOLL_SPINE3, VEC3V_TO_VECTOR3(extraVelocity));
pPickup->SetStartingAngularVelocity(VEC3_ZERO);
// Don't collide with ped until they don't touch each.
pPickup->SetNoCollision(pPedForVelocity, NO_COLLISION_RESET_WHEN_NO_IMPACTS);
}
if(!bUseInitVelocities)
{
pPickup->SetPlaceOnGround();
}
if (pPedForVelocity && !pPedForVelocity->IsAPlayerPed() && (pPedForVelocity->PopTypeIsMission() || (pPedForVelocity->GetNetworkObject() && pPedForVelocity->GetNetworkObject()->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_WAS_SCRIPTOBJECT))))
{
pPickup->SetDroppedByPed();
// use extended scope for money pickups dropped by dead script peds, so they are not cleaned up immediately when killed via a sniper rifle
pPickup->SetFlag(CPickup::PF_UseExtendedScope);
}
physicsDebugf1("CPickupManager::CreateSomeMoney - Pos(%f,%f,%f), Vel(%f,%f,%f), AVel(%f,%f,%f)", VEC3V_ARGS(RCC_VEC3V(pickupM.d)), VEC3V_ARGS(RCC_VEC3V(pPickup->m_StartingLinearVelocity)), VEC3V_ARGS(RCC_VEC3V(pPickup->m_StartingAngularVelocity)));
}
else
{
break;
}
}
}
}
// Name : GetCustomArchetype
// Purpose : Returns the stored custom archetype (the archetype containing an extra sphere bound used to detect collection) for the given pickup.
// Parameters : pickup - the pickup
// Returns : The custom archetype
phArchetypeDamp* CPickupManager::GetCustomArchetype(const CPickup& pickup)
{
if (pickup.IsFlagSet(CPickup::PF_HasCustomModel))
{
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
if (ms_extraCustomArchetypes[i].IsSet())
{
if (ms_extraCustomArchetypes[i].GetModelIndex().Get() == (s32)pickup.GetModelIndex())
{
return ms_extraCustomArchetypes[i].GetArchetype();
}
}
}
}
else
{
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickup.GetPickupHash());
if (AssertVerify(pTypeInfo) && pTypeInfo->m_customArchetype.IsSet())
{
return pTypeInfo->m_customArchetype.GetArchetype();
}
}
return NULL;
}
// Name : StoreCustomArchetype
// Purpose : Stores the custom archetype (the archetype containing an extra sphere bound used to detect collection) for the given pickup type.
// Parameters : pickup - the pickup
// pArchetype - the custom archetype
// Returns : Nothing
void CPickupManager::StoreCustomArchetype(CPickup& pickup, phArchetypeDamp* pArchetype)
{
if (pickup.IsFlagSet(CPickup::PF_HasCustomModel))
{
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
if (!ms_extraCustomArchetypes[i].IsSet())
{
ms_extraCustomArchetypes[i].Set(*pArchetype, pickup.GetModelIndex());
return;
}
}
Assertf(0, "CPickupManager: Ran out of extra custom archetypes for custom pickup models");
}
else
{
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickup.GetPickupHash());
if (AssertVerify(pTypeInfo))
{
pTypeInfo->m_customArchetype.Set(*pArchetype, pickup.GetModelIndex());
}
}
}
void CPickupManager::AddRefForExtraCustomArchetype(phArchetypeDamp* pArchetype)
{
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
CCustomArchetypeInfo* pInfo = &ms_extraCustomArchetypes[i];
if (pInfo->IsSet() && pInfo->GetArchetype() == pArchetype)
{
pInfo->AddRef();
return;
}
}
Assertf(0, "CPickupManager::AddRefForExtraCustomArchetype - Archetype not found!!");
}
void CPickupManager::RemoveRefForExtraCustomArchetype(u32 modelIndex)
{
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
CCustomArchetypeInfo* pInfo = &ms_extraCustomArchetypes[i];
if (pInfo->IsSet() && pInfo->GetModelIndex().Get() == (s32)modelIndex)
{
pInfo->RemoveRef();
return;
}
}
#if __ASSERT
for (u32 i=0; i<MAX_EXTRA_CUSTOM_ARCHETYPES; i++)
{
CCustomArchetypeInfo* pInfo = &ms_extraCustomArchetypes[i];
if(pInfo->IsSet())
{
Displayf("ms_extraCustomArchetypes[%d]: Model Index: %d", i, pInfo->GetModelIndex().Get());
}
if (pInfo->GetModelIndex().Get() == (s32)modelIndex)
{
if(!pInfo->IsSet())
{
Warningf("ms_extraCustomArchetypes[%d]: Pickup archetype has been deleted", i);
}
}
}
Assertf(0, "CPickupManager::RemoveRefForExtraCustomArchetype - Archetype not found!! Model index: %d", modelIndex);
#endif // __ASSERT
}
bool CPickupManager::IsSuppressionFlagSet( const s32 iFlag )
{
if( iFlag == PICKUP_REWARD_TYPE_NONE )
return ms_SuppressionFlags.GetAllFlags() == 0;
return ms_SuppressionFlags.IsFlagSet( iFlag );
}
// prevents a certain player from collecting a certain type of pickup
void CPickupManager::ProhibitCollectionForPlayer(u32 pickupHash, PhysicalPlayerIndex playerIndex)
{
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickupHash);
if (AssertVerify(pTypeInfo) && AssertVerify(playerIndex != INVALID_PLAYER_INDEX))
{
pTypeInfo->m_prohibitedCollections |= (1<<playerIndex);
#if ENABLE_NETWORK_LOGGING
if (NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("pTypeInfo", "0x%p", pTypeInfo);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("m_prohibitedCollections", "%d", pTypeInfo->m_prohibitedCollections);
}
#endif
}
else
{
#if ENABLE_NETWORK_LOGGING
if (NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Failed", "true");
}
#endif
}
}
// allows collection of a certain type of pickup for the given player
void CPickupManager::AllowCollectionForPlayer(u32 pickupHash, PhysicalPlayerIndex playerIndex)
{
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickupHash);
if (AssertVerify(pTypeInfo) && AssertVerify(playerIndex != INVALID_PLAYER_INDEX))
{
pTypeInfo->m_prohibitedCollections &= ~(1<<playerIndex);
#if ENABLE_NETWORK_LOGGING
if (NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("pTypeInfo", "0x%p", pTypeInfo);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("m_prohibitedCollections", "%d", pTypeInfo->m_prohibitedCollections);
}
#endif
}
else
{
#if ENABLE_NETWORK_LOGGING
if (NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Failed", "true");
}
#endif
}
}
// returns true if the given player is prohibited from collecting a certain type of pickup
bool CPickupManager::IsCollectionProhibited(u32 pickupHash, PhysicalPlayerIndex playerIndex)
{
bool bProhibited = false;
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickupHash);
if (AssertVerify(pTypeInfo) && AssertVerify(playerIndex != INVALID_PLAYER_INDEX))
{
bProhibited = (pTypeInfo->m_prohibitedCollections & (1<<playerIndex)) != 0;
}
return bProhibited;
}
// removes all prohibited collection for the given pickup type
void CPickupManager::RemoveProhibitedCollection(u32 pickupHash)
{
CPickupTypeInfo* pTypeInfo = ms_pickupTypeInfoMap.Access(pickupHash);
if (AssertVerify(pTypeInfo))
{
pTypeInfo->m_prohibitedCollections = 0;
}
}
// adds the given player to all prohibited collections for all pickup types
void CPickupManager::AddPlayerToAllProhibitedCollections(PhysicalPlayerIndex playerIndex)
{
PickupTypeInfoMap::Iterator entry = ms_pickupTypeInfoMap.CreateIterator();
for (entry.Start(); !entry.AtEnd(); entry.Next())
{
entry.GetData().m_prohibitedCollections |= (1<<playerIndex);
}
}
// removes the given player from all prohibited collections for all pickup types
void CPickupManager::RemovePlayerFromAllProhibitedCollections(PhysicalPlayerIndex playerIndex)
{
PickupTypeInfoMap::Iterator entry = ms_pickupTypeInfoMap.CreateIterator();
for (entry.Start(); !entry.AtEnd(); entry.Next())
{
entry.GetData().m_prohibitedCollections &= ~(1<<playerIndex);
}
}
// prevents the local player from collecting pickups with the given model
void CPickupManager::ProhibitPickupModelCollectionForLocalPlayer(u32 modelHash, const char* scriptName)
{
int freeSlot = -1;
if (!Verifyf(modelHash != 0, "CPickupManager::ProhibitModelCollectionForLocalPlayer - modelHash is 0"))
{
return;
}
u32 numProhibited = 0;
if (ms_hasProhibitedPickupModels)
{
for (u32 i=0; i<MAX_PROHIBITED_PICKUP_MODELS; i++)
{
if (ms_prohibitedPickupModels[i] == modelHash)
{
return;
}
else if (ms_prohibitedPickupModels[i] == 0)
{
if (freeSlot == -1)
{
freeSlot = i;
}
}
else
{
numProhibited++;
}
}
}
else
{
freeSlot = 0;
}
if (freeSlot != -1)
{
if (NetworkInterface::IsGameInProgress())
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetObjectManager().GetLog(), "ADD_PICKUP_MODEL_PROHIBIT", scriptName);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Model", "%d", modelHash);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Slot", "%d", freeSlot);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Curr num prohibited", "%d", numProhibited);
}
Assert(ms_prohibitedPickupModels[freeSlot] == 0);
ms_prohibitedPickupModels[freeSlot] = modelHash;
ms_hasProhibitedPickupModels = true;
}
else
{
Assertf(0, "CPickupManager::ProhibitModelCollectionForLocalPlayer - can't prohibit this model, too many prohibited models");
}
}
// returns true if the local player is prohibited from collecting pickups with the given model
bool CPickupManager::IsPickupModelCollectionProhibited(u32 modelHash)
{
if (ms_hasProhibitedPickupModels)
{
for (u32 i=0; i<MAX_PROHIBITED_PICKUP_MODELS; i++)
{
if (ms_prohibitedPickupModels[i] == modelHash)
{
return true;
}
}
}
return false;
}
// removes all prohibited collection for the given pickup model
void CPickupManager::RemoveProhibitedPickupModelCollection(u32 modelHash, const char* scriptName)
{
if (ms_hasProhibitedPickupModels)
{
u32 numProhibited = 0;
for (u32 i=0; i<MAX_PROHIBITED_PICKUP_MODELS; i++)
{
if (ms_prohibitedPickupModels[i] == modelHash)
{
ms_prohibitedPickupModels[i] = 0;
if (NetworkInterface::IsGameInProgress())
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetObjectManager().GetLog(), "REMOVE_PICKUP_MODEL_PROHIBIT", scriptName);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Model", "%d", modelHash);
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Slot", "%d", i);
}
numProhibited++;
}
else if (ms_prohibitedPickupModels[i] != 0)
{
numProhibited++;
}
}
if (numProhibited==0)
{
ms_hasProhibitedPickupModels = false;
}
if (NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetObjectManager().GetLog().WriteDataValue("Curr num prohibited", "%d", numProhibited);
}
}
}
#if __BANK
void CPickupManager::DebugScriptedGlow()
{
if( ms_debugScriptedPickups )
{
CPed * playerPed = FindPlayerPed();
if(playerPed)
{
float segment = (PI * 2.0f) / (float)ms_glowList.m_data.GetCount();
for (u32 i=0; i<ms_glowList.m_data.GetCount();i++)
{
Vec3V pickupPosition = playerPed->GetTransform().GetPosition();
if (ms_createAllPickupsOnGround)
pickupPosition.SetZf(playerPed->GetGroundPos().z);
pickupPosition += Vec3V(5.0f * rage::Sinf(i*segment),5.0f * rage::Cosf(i*segment),0.0f);
RenderScriptedGlow(pickupPosition,i);
}
}
}
}
void CPickupManager::DebugDisplay()
{
const float VISUALISE_RANGE = 30.0f;
if (ms_displayAllPickupPlacements)
{
Vector3 camPos = camInterface::GetPos();
for (s32 i=0; i<CPickupPlacement::GetPool()->GetSize(); i++)
{
CPickupPlacement* pPickupPlacement = CPickupPlacement::GetPool()->GetSlot(i);
if (pPickupPlacement)
{
Vector3 vDiff = camPos - pPickupPlacement->GetPickupPosition();
float fDist = vDiff.Mag();
if(fDist >= VISUALISE_RANGE)
continue;
char debugStr[100];
int line = -1;
sprintf(debugStr, "%s", pPickupPlacement->GetPickupData()->GetName());
grcDebugDraw::Text( pPickupPlacement->GetPickupPosition(), Color_red, 0, (line++)*grcDebugDraw::GetScreenSpaceTextHeight(), debugStr);
sprintf(debugStr, "Has pickup: %s", pPickupPlacement->GetPickup() ? "Yes" : "No");
grcDebugDraw::Text( pPickupPlacement->GetPickupPosition(), Color_red, 0, (line++)*grcDebugDraw::GetScreenSpaceTextHeight(), debugStr);
sprintf(debugStr, "Collected: %s", pPickupPlacement->GetIsCollected() ? "Yes" : "No");
grcDebugDraw::Text( pPickupPlacement->GetPickupPosition(), Color_red, 0, (line++)*grcDebugDraw::GetScreenSpaceTextHeight(), debugStr);
sprintf(debugStr, "In scope: %s", pPickupPlacement->GetInScope() ? "Yes" : "No");
grcDebugDraw::Text( pPickupPlacement->GetPickupPosition(), Color_red, 0, (line++)*grcDebugDraw::GetScreenSpaceTextHeight(), debugStr);
}
}
}
}
void CPickupManager::PickupsBank_CreatePickup()
{
CPed * playerPed = FindPlayerPed();
if(playerPed)
{
const CPickupData* pData = CPickupDataManager::GetPickupDataInSlot(ms_pickupComboIndex);
if (Verifyf(pData, "Pickup metadata does not exist"))
{
u32 modelIndex = pData->GetModelIndex();
fwModelId modelId((strLocalIndex(modelIndex)));
while (!CModelInfo::HaveAssetsLoaded(modelId))
{
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
strStreamingEngine::GetLoader().LoadAllRequestedObjects(true);
}
Matrix34 createM;
createM.Identity();
createM.d = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition()) + (VEC3V_TO_VECTOR3(playerPed->GetTransform().GetB()) * 2.0f);
CPickup* pPickup = CreatePickup(pData->GetHash(), createM);
if (pPickup)
{
if (playerPed->GetIsInInterior() && !pPickup->GetRequiresACustomArchetype())
{
CGameWorld::Remove(pPickup);
fwInteriorLocation interiorLocation = CInteriorInst::CreateLocation( playerPed->GetPortalTracker()->GetInteriorInst(), playerPed->GetPortalTracker()->m_roomIdx );
CGameWorld::Add(pPickup, interiorLocation);
}
if (pData->GetAttachmentBone() != 0)
{
pPickup->SetPortable();
}
if (ms_createAllPickupsOnGround)
{
pPickup->SetPlaceOnGround(true, ms_createAllPickupsUpright);
}
if (ms_createAllPickupsWithArrowMarkers)
{
pPickup->SetFlag(CPickup::PF_AddArrowMarker);
}
pPickup->SetDebugCreated();
}
}
}
}
void CPickupManager::PickupsBank_CreatePickupPlacement()
{
CPed * playerPed = FindPlayerPed();
if(playerPed)
{
const CPickupData* pData = CPickupDataManager::GetPickupDataInSlot(ms_pickupComboIndex);
if (Verifyf(pData, "Pickup metadata does not exist"))
{
u32 modelIndex = pData->GetModelIndex();
fwModelId modelId((strLocalIndex(modelIndex)));
while (!CModelInfo::HaveAssetsLoaded(modelId))
{
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
strStreamingEngine::GetLoader().LoadAllRequestedObjects(true);
}
Vector3 pickupPosition = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition()) + (VEC3V_TO_VECTOR3(playerPed->GetTransform().GetB()) * 2.0f);
Vector3 pickupOrientation = Vector3(1.0f, 1.0f, 1.0f);
PlacementFlags flags = 0;
if (ms_createAllPickupsOnGround)
pickupPosition.z = playerPed->GetGroundPos().z;
if (ms_fixedPickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_FIXED;
if (ms_regeneratePickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_REGENERATES;
if (ms_blipPickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_BLIPPED_SIMPLE;
if (ms_createAllPickupsOnGround)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_SNAP_TO_GROUND | CPickupPlacement::PLACEMENT_CREATION_FLAG_ORIENT_TO_GROUND;
if (ms_createAllPickupsUpright)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_UPRIGHT;
CPickupPlacement* pPlacement = RegisterPickupPlacement(pData->GetHash(), pickupPosition, pickupOrientation, flags);
if (playerPed->GetIsInInterior() && AssertVerify(pPlacement))
{
pPlacement->SetRoomHash(playerPed->GetPortalTracker()->GetInteriorInst()->GetRoomHashcode(playerPed->GetPortalTracker()->m_roomIdx));
}
pPlacement->SetDebugCreated();
}
}
}
void CPickupManager::PickupsBank_CreateAllPickups()
{
CPed * playerPed = FindPlayerPed();
if(playerPed)
{
float segment = (PI * 2.0f) / CPickupDataManager::GetNumPickupTypes();
for (u32 i=0; i<CPickupDataManager::GetNumPickupTypes(); i++)
{
const CPickupData* pData = CPickupDataManager::GetPickupDataInSlot(ms_pickupComboIndex);
if (Verifyf(pData, "Pickup metadata does not exist"))
{
Vector3 pickupPosition = VEC3V_TO_VECTOR3(playerPed->GetTransform().GetPosition());
if (ms_createAllPickupsOnGround)
pickupPosition.z = playerPed->GetGroundPos().z;
Vector3 pickupOrientation = Vector3(1.0f, 1.0f, 1.0f);
pickupPosition.x += 5.0f * rage::Sinf(i*segment);
pickupPosition.y += 5.0f * rage::Cosf(i*segment);
PlacementFlags flags = 0;
if (ms_fixedPickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_FIXED;
if (ms_regeneratePickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_REGENERATES;
if (ms_blipPickups)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_BLIPPED_SIMPLE;
if (ms_createAllPickupsOnGround)
flags |= CPickupPlacement::PLACEMENT_CREATION_FLAG_SNAP_TO_GROUND | CPickupPlacement::PLACEMENT_CREATION_FLAG_ORIENT_TO_GROUND;
RegisterPickupPlacement(pData->GetHash(), pickupPosition, pickupOrientation, flags);
}
}
}
}
void CPickupManager::PickupsBank_RemoveAllPickups()
{
RemoveAllPickups(true);
}
void CPickupManager::PickupsBank_DetachAllPickups()
{
s32 i = CPickup::GetPool()->GetSize();
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup && pPickup->IsFlagSet(CPickup::PF_Portable) && pPickup->GetAttachParent() == FindPlayerPed())
{
pPickup->DetachPortablePickupFromPed("PickupsBank_DetachAllPickups");
}
}
}
void CPickupManager::PickupsBank_ReloadPickupData()
{
CPickupDataManager::Shutdown(SHUTDOWN_SESSION);
CPickupDataManager::Init(INIT_SESSION);
}
void CPickupManager::PickupsBank_RecalculatePickupsInScope()
{
ms_cachedPlayerPosition = Vector3(0.0f, 0.0f, 0.0f);
}
void CPickupManager::PickupsBank_SetPlayerCanCarryPortablePickups()
{
static s8 maxPickupsCarried = 0;
if (ms_allowPlayerToCarryPortablePickups)
{
CGameWorld::GetMainPlayerInfo()->m_maxPortablePickupsCarried = maxPickupsCarried;
}
else
{
maxPickupsCarried = CGameWorld::GetMainPlayerInfo()->m_maxPortablePickupsCarried;
CGameWorld::GetMainPlayerInfo()->m_maxPortablePickupsCarried = 0;
}
}
void CPickupManager::PickupsBank_MakePickupFromPlayerWeapon()
{
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
int pedId = CTheScripts::GetGUIDFromEntity(*pPlayerPed);
static Vector3 offset(0.0f, 0.5f, 0.0f);
weapon_commands::CommandPedDropsInventoryWeapon(pedId, WEAPONTYPE_GOLFCLUB, offset, 3);
}
void CPickupManager::InitLevelWidgets()
{
if (AssertVerify(!ms_pBank))
{
ms_pBank = &BANKMGR.CreateBank("Pickups", 0, 0, false);
ms_pPickupNamesCombo = ms_pBank->AddCombo ("Pickup", &ms_pickupComboIndex, ms_numPickupComboNames, ms_pickupComboNames);
ms_pBank->AddToggle("Fix", &ms_fixedPickups);
ms_pBank->AddToggle("Regenerate", &ms_regeneratePickups);
ms_pBank->AddToggle("Blip", &ms_blipPickups);
ms_pBank->AddToggle("Force MP Style Weapon Pickups", &ms_forceMPStyleWeaponPickups);
ms_pBank->AddToggle("Snap to ground", &ms_createAllPickupsOnGround);
ms_pBank->AddToggle("Add Arrow Marker", &ms_createAllPickupsWithArrowMarkers);
ms_pBank->AddToggle("Upright", &ms_createAllPickupsUpright);
ms_pBank->AddToggle("Allow player to carry portable pickups", &ms_allowPlayerToCarryPortablePickups, datCallback(PickupsBank_SetPlayerCanCarryPortablePickups));
ms_pBank->AddToggle("No Money Drop", &ms_NoMoneyDrop);
ms_pBank->AddButton("Create pickup", datCallback(PickupsBank_CreatePickup));
ms_pBank->AddButton("Create pickup placement", datCallback(PickupsBank_CreatePickupPlacement));
ms_pBank->AddToggle("Display all pickup placements", &ms_displayAllPickupPlacements);
ms_pBank->AddButton("Create all pickups", datCallback(PickupsBank_CreateAllPickups));
ms_pBank->AddButton("Remove all pickups", datCallback(PickupsBank_RemoveAllPickups));
ms_pBank->AddButton("Detach all pickups from player", datCallback(PickupsBank_DetachAllPickups));
ms_pBank->AddToggle("Render all scripted glow", &ms_debugScriptedPickups);
ms_pBank->AddButton("Make pickup from players equipped weapon", datCallback(PickupsBank_MakePickupFromPlayerWeapon));
#if ENABLE_GLOWS
ms_pBank->AddToggle("Always Render pickup glows", &ms_forcePickupGlow);
ms_pBank->AddToggle("Always Offset Glows", &ms_forceOffsetPickupGlow);
#endif // ENABLE_GLOWS
#if __DEV
ms_pBank->AddSlider("Arrow Marker - Z Offset", &ARROW_MARKER_Z_OFFSET, 0.0f, 5.0f, 0.01f);
ms_pBank->AddSlider("Arrow Marker - Scale", &ARROW_MARKER_SCALE, 0.0f, 5.0f, 0.01f);
ms_pBank->AddColor("Arrow Marker - Colour", &ARROW_MARKER_COL);
ms_pBank->AddSlider("Arrow Marker - Fade Start Range", &ARROW_MARKER_FADE_START_RANGE, 0.0f, 50.0f, 0.1f);
ms_pBank->AddSlider("Arrow Marker - Fade Out Range", &ARROW_MARKER_FADE_OUT_RANGE, 0.0f, 50.0f, 0.1f);
ms_pBank->AddToggle("Arrow Marker - Bounce", &ARROW_MARKER_BOUNCE);
ms_pBank->AddToggle("Arrow Marker - Face Cam", &ARROW_MARKER_FACE_CAM);
#endif
ms_pBank->AddButton("Reload pickup data", datCallback(PickupsBank_ReloadPickupData));
#if __BANK && ENABLE_GLOWS
ms_pBank->PushGroup("Pickup Glow", false);
ms_pBank->AddSlider("Glow Red", &CPickup::ms_customGlowR, 0.0f, 1.0f, 0.05f, NullCB);
ms_pBank->AddSlider("Glow Green", &CPickup::ms_customGlowG, 0.0f, 1.0f, 0.05f, NullCB);
ms_pBank->AddSlider("Glow Blue", &CPickup::ms_customGlowB, 0.0f, 1.0f, 0.05f, NullCB);
ms_pBank->AddSlider("Glow Intensity", &CPickup::ms_customGlowI, 0.0f, 50.0f, 1.0f, NullCB);
ms_pBank->AddSlider("Glow Range", &CPickup::ms_customGlowRange, 0.0f, 5.0f, 0.1f, NullCB);
ms_pBank->AddSlider("Glow Fade Distance", &CPickup::ms_customGlowFadeDist, 0.0f, 50.0f, 1.0f, NullCB);
ms_pBank->AddSlider("Glow Offsert", &CPickup::ms_customGlowOffset, -3.0f, 3.0f, 0.1f, NullCB);
ms_pBank->PopGroup();
#endif // __BANK && ENABLE_GLOWS
GlowListAddWidgets(ms_pBank);
}
}
void CPickupManager::ShutdownLevelWidgets()
{
if (ms_pBank)
{
BANKMGR.DestroyBank(*ms_pBank);
ms_pBank = NULL;
ms_pPickupNamesCombo = NULL;
}
}
#endif
// Name : AddPlacementToRegenerationList
// Purpose : Adds a collected placement to the regeneration list
CPickupManager::CRegenerationInfo* CPickupManager::AddPlacementToRegenerationList(CPickupPlacement *pPlacement)
{
Assertf(CRegenerationInfo::GetPool()->GetNoOfFreeSpaces() > 0, "CPickupManager: Ran out of regeneration infos");
CRegenerationInfo* pNewInfo = NULL;
#if __ASSERT
// sanity check that it is not already on the list
if (IsPlacementOnRegenerationList(pPlacement, &pNewInfo))
{
Assertf(0, "CPickupManager::AddPlacementToRegenerationList: placement is already on the list");
return pNewInfo;
}
#endif // __ASSERT
if (CRegenerationInfo::GetPool()->GetNoOfFreeSpaces() > 0)
{
pNewInfo = rage_new CRegenerationInfo(pPlacement);
ms_regeneratingPlacements.Add(pNewInfo);
}
else
{
Assertf(0, "Ran out of CRegenerationInfos in CPickupManager");
}
return pNewInfo;
}
// Name : RemovePlacementFromRegenerationList
// Purpose : Removes a placement from the regeneration list
void CPickupManager::RemovePlacementFromRegenerationList(CPickupPlacement *pPlacement, bool ASSERT_ONLY(bAssert))
{
fwPtrNode* pNode = ms_regeneratingPlacements.GetHeadPtr();
while (pNode)
{
CRegenerationInfo* pRegenInfo = static_cast<CRegenerationInfo*>(pNode->GetPtr());
fwPtrNode* pNextNode = pNode->GetNextPtr();
if (pRegenInfo->GetPlacement() == pPlacement)
{
ms_regeneratingPlacements.Remove(pRegenInfo);
delete pRegenInfo;
return;
}
pNode = pNextNode;
}
Assertf(!bAssert || pNode, "CPickupManager::RemovePlacementToRegenerationList: placement is not on the list");
}
// Name : ForcePlacementFromRegenerationListToRegenerate
// Purpose : Sets flag on CRegenerationInfo to force regenerate pickup even if time left on timer
void CPickupManager::ForcePlacementFromRegenerationListToRegenerate(CPickupPlacement& pPlacement)
{
fwPtrNode* pNode = ms_regeneratingPlacements.GetHeadPtr();
while (pNode)
{
CRegenerationInfo* pRegenInfo = static_cast<CRegenerationInfo*>(pNode->GetPtr());
fwPtrNode* pNextNode = pNode->GetNextPtr();
if (pRegenInfo->GetPlacement() == &pPlacement)
{
pRegenInfo->SetForceRegenerate();
return;
}
pNode = pNextNode;
}
Assertf(0, "Can't find pickup placement(ID=%d)(NetID=%s) in regenerating placements list.", pPlacement.GetScriptInfo() ? pPlacement.GetScriptInfo()->GetObjectId() : -1, pPlacement.GetNetworkObject() ? pPlacement.GetNetworkObject()->GetLogName() : "N/A");
}
// Name : IsPlacementOnRegenerationList
// Purpose : Returns true if the placement is on the regeneration list
bool CPickupManager::IsPlacementOnRegenerationList(CPickupPlacement *pPlacement, CRegenerationInfo** pInfo)
{
fwPtrNode* pNode = ms_regeneratingPlacements.GetHeadPtr();
while (pNode)
{
CRegenerationInfo* pRegenInfo = static_cast<CRegenerationInfo*>(pNode->GetPtr());
if (pRegenInfo->GetPlacement() == pPlacement)
{
if (pInfo)
{
*pInfo = pRegenInfo;
}
return true;
}
pNode = pNode->GetNextPtr();
}
return false;
}
void CPickupManager::RemovePickupPlacementFromCollectionHistory(CPickupPlacement *pPlacement)
{
// remove from the collection history
if (pPlacement && pPlacement->GetScriptInfo())
{
int index;
CCollectionInfo info(*pPlacement->GetScriptInfo());
if (ms_collectionHistory.Find(info, &index))
{
ms_collectionHistory.Delete(index);
}
}
}
// Name : DestroyPickupPlacement
// Purpose : Destroys a placement
void CPickupManager::DestroyPickupPlacement(CPickupPlacement *pPlacement)
{
if (ms_removedPlacements.IsMemberOfList(pPlacement))
{
ms_removedPlacements.Remove(pPlacement);
}
if (ms_pendingPickups.IsMemberOfList(pPlacement))
{
ms_pendingPickups.Remove(pPlacement);
}
RemovePlacementFromRegenerationList(pPlacement, false);
ms_pQuadTree->DeleteItem(pPlacement);
delete pPlacement;
}
// Name : ProcessInactiveState
// Purpose : Called when the pickup manager is inactive and no pickups should exist
void CPickupManager::ProcessInactiveState()
{
if(CutSceneManager::GetInstance() && !(CutSceneManager::GetInstance()->GetOptionFlags().IsFlagSet(CUTSCENE_DO_NOT_CLEAR_PICKUPS)))
{
REPLAY_ONLY(if(CReplayMgr::IsOkayToDelete()))
RemoveAllPickups(false);
// flush pending list
ms_pendingPickups.Flush();
}
// uninitialized script pickups still need to be processed in MP, so that they can get created on other machines. Otherwise the script
// that created them will get stuck waiting for them.
if(CPickup::GetPool()->GetNoOfUsedSpaces() > 0)
{
for (s32 i=0; i<CPickup::GetPool()->GetSize(); i++)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup &&
!pPickup->IsFlagSet(CPickup::PF_Initialised) &&
pPickup->GetNetworkObject() &&
pPickup->GetNetworkObject()->IsScriptObject())
{
pPickup->Update();
}
}
}
// reset cached player pos so pickup scope is immediately recalculated when manager becomes active again
ms_cachedPlayerPosition = Vector3(0.0f, 0.0f, 0.0f);
}
// Name : ProcessPickupsInScope
// Purpose : Determines which pickups are in scope and adds them to the pending list
void CPickupManager::ProcessPickupsInScope()
{
if (AssertVerify(FindPlayerPed()))
{
Vector3 playerPosition;
playerPosition = VEC3V_TO_VECTOR3(FindPlayerPed()->GetTransform().GetPosition());
if (NetworkInterface::IsGameInProgress())
{
CNetGamePlayer* localPlayer = NetworkInterface::GetLocalPlayer();
if (localPlayer)
{
playerPosition = NetworkInterface::GetPlayerFocusPosition(*localPlayer);
}
}
// do we need to recalculate the current pickup placements in scope with the player?
Vector3 playerDist = playerPosition - ms_cachedPlayerPosition;
float scope = static_cast<float>(PLACEMENT_SCOPE_RANGE*PLACEMENT_SCOPE_RANGE);
if (ms_cachedPlayerPosition.Mag2() == 0.0f || playerDist.Mag2() >= scope)
{
// recalculate current pickup placements in scope with the player
Vector2 playerPos2d(playerPosition.x, playerPosition.y);
fwRect bb(playerPos2d, static_cast<float>(MAX_PLACEMENT_GENERATION_RANGE));
// linked list of current pickup placements in scope
fwPtrListSingleLink pickupsInScope;
ms_pQuadTree->GetAllMatching(bb, pickupsInScope);
ms_cachedPlayerPosition = playerPosition;
ms_pendingPickups.Flush();
fwPtrNode* pNode=pickupsInScope.GetHeadPtr();
while(pNode)
{
CPickupPlacement* pPlacement = static_cast<CPickupPlacement*>(pNode->GetPtr());
bool bClone = pPlacement->GetNetworkObject() && pPlacement->GetNetworkObject()->IsClone();
if ((!bClone || !pPlacement->GeneratesNetworkedPickups()) &&
!pPlacement->GetPickup() &&
!pPlacement->GetIsCollected() &&
!pPlacement->GetHasPickupBeenDestroyed() &&
!ms_pendingPickups.IsMemberOfList(pPlacement))
{
Vector2 pos2d(pPlacement->GetPickupPosition().x, pPlacement->GetPickupPosition().y);
Vector2 diff = pos2d - playerPos2d;
float dist = diff.Mag2();
float generationRange = pPlacement->GetGenerationRange();
if (dist < generationRange*generationRange)
{
// place pickup on pending list
ms_pendingPickups.Add(pPlacement);
}
}
pNode=pNode->GetNextPtr();
}
}
}
}
// Name : ProcessPendingPlacements
// Purpose : Processes the pending list (placements waiting to generate pickups)
void CPickupManager::ProcessPendingPlacements()
{
fwPtrNode* pNode = ms_pendingPickups.GetHeadPtr();
while (pNode)
{
CPickupPlacement* pPlacement = static_cast<CPickupPlacement*>(pNode->GetPtr());
pNode = pNode->GetNextPtr();
bool bClone = pPlacement->GetNetworkObject() && pPlacement->GetNetworkObject()->IsClone();
// leave on the pending list if the placement is a clone - either the pickup will be created for us or the placement will
// migrate to our machine and we can create one ourselves.
if (bClone && (pPlacement->GetIsCollected() || pPlacement->GetHasPickupBeenDestroyed()))
{
ms_pendingPickups.Remove(pPlacement);
}
else if (!pPlacement->GeneratesNetworkedPickups())
{
// remove from pending list if placement is controlled by another machine, goes out of scope or is successfully generated
if (!pPlacement->GetInScope() || pPlacement->GetPickup() || pPlacement->CreatePickup())
{
ms_pendingPickups.Remove(pPlacement);
}
}
}
}
// Name : ProcessRegeneratingPlacements
// Purpose : Processes placements that are due to regenerate a pickup
void CPickupManager::ProcessRegeneratingPlacements()
{
fwPtrNode* pNode = ms_regeneratingPlacements.GetHeadPtr();
while (pNode)
{
CRegenerationInfo* pRegenInfo = static_cast<CRegenerationInfo*>(pNode->GetPtr());
CPickupPlacement* pPlacement = pRegenInfo->GetPlacement();
netObject* pNetObj = pPlacement->GetNetObject();
pNode = pNode->GetNextPtr();
if (pNetObj)
{
if (pNetObj->IsClone())
{
// clones are told to regenerate by the machine which owns them (a placement may migrate or change network object after being placed on the list)
ms_regeneratingPlacements.Remove(pRegenInfo);
delete pRegenInfo;
pRegenInfo = NULL;
}
else if (!pNetObj->IsPendingOwnerChange() && pPlacement->GetPickup())
{
// it's possible for a placement to be on the regenerating list with a pickup if it migrated locally before the pickup was destroyed
pPlacement->DestroyPickup();
}
}
if (pRegenInfo)
{
if (!pPlacement->GetIsCollected() && !pPlacement->GetHasPickupBeenDestroyed())
{
if (pPlacement->GetNetObject())
{
Assertf(0, "%s is regenerating but is marked as not collected or destroyed!", pPlacement->GetNetObject()->GetLogName());
}
else
{
Assertf(0, "The pickup at %f, %f, %f is regenerating but is marked as not collected or destroyed!", pPlacement->GetPickupPosition().x, pPlacement->GetPickupPosition().y, pPlacement->GetPickupPosition().z);
}
}
if (pRegenInfo->IsReadyToRegenerate())
{
if (pPlacement->Regenerate())
{
ms_regeneratingPlacements.Remove(pRegenInfo);
delete pRegenInfo;
}
}
}
}
}
// Name : ProcessRemovedPlacements
// Purpose : Processes placements that are waiting to be removed
void CPickupManager::ProcessRemovedPlacements()
{
fwPtrNode* pNode = ms_removedPlacements.GetHeadPtr();
while (pNode)
{
CPickupPlacement* pPlacement = static_cast<CPickupPlacement*>(pNode->GetPtr());
pNode = pNode->GetNextPtr();
DestroyPickupPlacement(pPlacement);
}
}
// Name : UpdatePickups
// Purpose : Updates all the current pickups
void CPickupManager::UpdatePickups()
{
if(CPickup::GetPool()->GetNoOfUsedSpaces() > 0)
{
for (s32 i=0; i<CPickup::GetPool()->GetSize(); i++)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup)
{
bool bCanDestroy = false;
// destroy pickups that have gone out of scope
if (!pPickup->GetNetworkObject() || (!pPickup->IsNetworkClone() && pPickup->GetNetworkObject()->CanDelete()))
{
if (pPickup->GetPlacement())
{
if (!pPickup->GetPlacement()->GetInScope())
{
bCanDestroy = true;
}
}
else
{
if (!pPickup->IsAScriptEntity() && FindPlayerPed() && !pPickup->GetInScope(*FindPlayerPed()))
{
if (pPickup->GetNetworkObject() && static_cast<CNetObjGame*>(pPickup->GetNetworkObject())->IsScriptObject())
{
Assertf(0, "%s is not set as OWNEDBY_SCRIPT but has a script network object", pPickup->GetNetworkObject()->GetLogName());
}
else if (!pPickup->GetNetworkObject() || static_cast<CNetObjPickup*>(pPickup->GetNetworkObject())->TryToPassControlOutOfScope())
{
bCanDestroy = true;
}
}
}
}
if (bCanDestroy)
{
pPickup->Destroy();
}
else
{
pPickup->Update();
}
}
}
ms_forceRotatePickupFaceUp = false; // reset
}
// Map placement pickups are only allowed in networked games
if(CNetwork::IsGameInProgress())
{
for (s32 i=0; i<CPickupPlacement::GetPool()->GetSize(); i++)
{
CPickupPlacement* pPickupPlacement = CPickupPlacement::GetPool()->GetSlot(i);
// update map placements only
if (pPickupPlacement && pPickupPlacement->GetIsMapPlacement())
{
pPickupPlacement->Update();
}
}
}
}
void CPickupManager::ClearSpaceInPickupPool(bool network)
{
#if GTA_REPLAY
if(CReplayMgr::IsReplayInControlOfWorld())
return;
#endif // GTA_REPLAY
const s32 MAX_PICKUPS = 200;
const s32 NUM_PICKUPS_TO_FADE = 16;
const s32 NUM_PICKUPS_TO_DESTROY = 8;
u32 poolSize = CPickup::GetPool()->GetSize();
u32 usedSpaces = CPickup::GetPool()->GetNoOfUsedSpaces();
// Update pool size and used spaces in MP
if (network && NetworkInterface::IsGameInProgress())
{
poolSize = Min(poolSize, (u32)CNetObjPickup::GetPool()->GetSize());
usedSpaces = Max(usedSpaces, (u32)CNetObjPickup::GetPool()->GetNoOfUsedSpaces());
}
u32 desiredNumPickups = Min(poolSize-NUM_PICKUPS_TO_FADE, (u32)((float)poolSize * 0.9f));
// if the pickup pool is nearly full, try to dump some old pickups
if (usedSpaces > desiredNumPickups)
{
u32 numPickupsToFadeout = usedSpaces - desiredNumPickups;
u32 numPickupsToDestroy = 0;
CPickup* sortedPickups[MAX_PICKUPS];
u32 numPickupsToSort = 0;
s32 i = CPickup::GetPool()->GetSize();
// test pickup objects
while(i--)
{
CPickup* pPickup = CPickup::GetPool()->GetSlot(i);
if (pPickup &&
!pPickup->IsFlagSet(CPickup::PF_Destroyed) &&
!pPickup->GetPlacement() &&
!pPickup->IsNetworkClone() &&
!(pPickup->GetNetworkObject() && pPickup->GetNetworkObject()->IsPendingOwnerChange()))
{
if (AssertVerify(numPickupsToSort<MAX_PICKUPS-1))
{
sortedPickups[numPickupsToSort++] = pPickup;
}
}
}
std::sort(&sortedPickups[0], &sortedPickups[numPickupsToSort], PickupCompare());
s32 startFadeTime = CPickup::AMBIENT_PICKUP_LIFETIME - CPickup::PICKUP_FADEOUT_TIME;
if (numPickupsToFadeout > numPickupsToSort)
{
numPickupsToFadeout = numPickupsToSort;
}
// if the pool is getting dangerously full, start destroying pickups immediately
if (usedSpaces > poolSize - NUM_PICKUPS_TO_DESTROY)
{
numPickupsToDestroy = NUM_PICKUPS_TO_DESTROY - (poolSize - usedSpaces);
}
for (i=0; i<numPickupsToFadeout; i++)
{
if (AssertVerify(sortedPickups[i]))
{
if (i<numPickupsToDestroy)
{
sortedPickups[i]->Destroy();
}
else if (sortedPickups[i]->GetLifeTime() < startFadeTime)
{
sortedPickups[i]->SetLifeTime(startFadeTime);
}
}
}
}
}
bool CPickupManager::SetStartingVelocities(CPickup* pPickup, CPed *pPed, RagdollComponent primaryBone, RagdollComponent secondaryBone, const Vector3& vExtraLinearVelocity)
{
bool bVelocitySet = false;
if(AssertVerify(pPed && pPickup))
{
// Initialize the pickup weapon's velocity from the primary hand that held it
if( pPed->GetRagdollInst()->GetARTAssetID() >= 0 && pPed->GetRagdollInst()->GetCurrentPhysicsLOD() == fragInst::RAGDOLL_LOD_HIGH )
{
if( pPed->GetRagdollState() >= RAGDOLL_STATE_PHYS_ACTIVATE )
{
phArticulatedBody *body = pPed->GetRagdollInst()->GetCacheEntry()->GetHierInst()->body;
Assert(body);
if (body)
{
static dev_float maxRelativeVelocity = 8.0f;
const Vec3V rootVelocity = body->GetLinearVelocity(0);
const Vec3V handVelocity = body->GetLinearVelocity(primaryBone);
const Vec3V relativeVelocity = ClampMag(Subtract(handVelocity,rootVelocity),ScalarV(V_ZERO),ScalarVFromF32(maxRelativeVelocity));
const Vector3 startingLinearVelocity = VEC3V_TO_VECTOR3(Add(rootVelocity,relativeVelocity)) + vExtraLinearVelocity;
pPickup->SetStartingLinearVelocity(startingLinearVelocity);
pPickup->SetStartingAngularVelocity(VEC3V_TO_VECTOR3(body->GetAngularVelocity(primaryBone)));
Assertf(startingLinearVelocity.Dist2(pPed->GetVelocity()) < 20.0f*20.0f || startingLinearVelocity.Mag2() < 5.0f*5.0f, "Setting large relative pickup velocity (RAGDOLL_STATE_PHYS_ACTIVATE). PickupVel: <%f, %f, %f>, PedVel: <%f, %f, %f>",VEC3V_ARGS(RCC_VEC3V(startingLinearVelocity)),VEC3V_ARGS(RCC_VEC3V(pPed->GetVelocity())));
}
}
else
{
static bool forceInitialVelocity = true;
if (forceInitialVelocity)
{
phBoundComposite *bound = pPed->GetRagdollInst()->GetCacheEntry()->GetBound();
Assert(bound);
Matrix34 clavMat, handMat;
clavMat.Dot(RCC_MATRIX34(bound->GetCurrentMatrix(secondaryBone)), RCC_MATRIX34(pPed->GetRagdollInst()->GetMatrix()));
handMat.Dot(RCC_MATRIX34(bound->GetCurrentMatrix(primaryBone)), RCC_MATRIX34(pPed->GetRagdollInst()->GetMatrix()));
Vector3 toGun = handMat.d - clavMat.d;
toGun.z = 0.0f;
toGun.NormalizeSafe();
static float linMag = 1.0f;
static float angMag = 2.5f;
const Vector3 startingLinearVelocity = toGun * linMag + vExtraLinearVelocity;
pPickup->SetStartingLinearVelocity(startingLinearVelocity);
Vector3 vSide;
vSide.Cross(toGun, Vector3(0.0f,0.0f,-1.0f));
pPickup->SetStartingAngularVelocity( vSide * angMag );
Assertf(startingLinearVelocity.Dist2(pPed->GetVelocity()) < 20.0f*20.0f || startingLinearVelocity.Mag2() < 5.0f*5.0f, "Setting large relative pickup velocity (forceInitialVelocity). PickupVel: <%f, %f, %f>, PedVel: <%f, %f, %f>",VEC3V_ARGS(RCC_VEC3V(startingLinearVelocity)),VEC3V_ARGS(RCC_VEC3V(pPed->GetVelocity())));
}
else
{
phBoundComposite *bound = pPed->GetRagdollInst()->GetCacheEntry()->GetBound();
Assert(bound);
Matrix34 lastMat, currMat;
lastMat.Dot(RCC_MATRIX34(bound->GetCurrentMatrix(primaryBone)), RCC_MATRIX34(pPed->GetRagdollInst()->GetMatrix()));
currMat.Dot(RCC_MATRIX34(bound->GetLastMatrix(primaryBone)), RCC_MATRIX34(PHSIM->GetLastInstanceMatrix(pPed->GetRagdollInst())));
Vector3 linVel = (currMat.d - lastMat.d) * fwTimer::GetInvTimeStep();
Matrix34 lastToCurrent;
lastToCurrent.DotTranspose(currMat, lastMat);
Quaternion tempQuat;
lastToCurrent.ToQuaternion(tempQuat);
Vector3 angVel;
float angle;
tempQuat.ToRotation(angVel, angle);
angVel.Scale(angle * fwTimer::GetInvTimeStep());
pPickup->SetStartingLinearVelocity( linVel + vExtraLinearVelocity);
pPickup->SetStartingAngularVelocity( angVel );
}
}
bVelocitySet = true;
}
}
return bVelocitySet;
}
bool CPickupManager::CanCreatePickupIfUnarmed(const CPed* pPed)
{
if(pPed->IsAPlayerPed())
{
return false;
}
// Ignore animals
if(pPed->GetPedType() == PEDTYPE_ANIMAL)
{
return false;
}
// Always try to drop the last equipped weapon if it's non-unarmed.
// For cops or mission peds, they will try to drop the best weapon if they never equipped a non-unarmed weapon.
bool bCanCreatePickup = false;
const CPedWeaponManager* pWeaponMgr = pPed->GetWeaponManager();
if(pWeaponMgr)
{
bool bUseMPPickups = ShouldUseMPPickups(pPed);
const CWeaponInfo* pLastWeaponInfo = pWeaponMgr->GetLastEquippedWeaponInfo();
if(pLastWeaponInfo && pPed->GetInventory()->GetWeapon(pLastWeaponInfo->GetHash()) && (bUseMPPickups ? (pLastWeaponInfo->GetMPPickupHash() != 0) : (pLastWeaponInfo->GetPickupHash() != 0)))
{
bCanCreatePickup = true;
}
else if(!pPed->IsAPlayerPed() && (pPed->GetPedType() == PEDTYPE_COP || pPed->PopTypeIsMission()))
{
const CWeaponInfo* pBestWeaponInfo = pWeaponMgr->GetBestWeaponInfo();
if(pBestWeaponInfo)
{
bCanCreatePickup = bUseMPPickups ? (pBestWeaponInfo->GetMPPickupHash() != 0) : (pBestWeaponInfo->GetPickupHash() != 0);
}
}
}
return bCanCreatePickup;
}
bool CPickupManager::ShouldUseMPPickups(const CPed* pPed)
{
bool bUseMPPickups = false;
// only the player drops MP pickups now
if (NetworkInterface::IsGameInProgress())
{
if (pPed)
{
bUseMPPickups = pPed->IsLocalPlayer();
}
else
{
// If pPed is null return true if any ped is using MP pickups
bUseMPPickups = true;
}
}
#if __BANK
if(ms_forceMPStyleWeaponPickups)
bUseMPPickups = true;
#endif // __BANK
return bUseMPPickups;
}