341 lines
11 KiB
C++
341 lines
11 KiB
C++
![]() |
//
|
||
|
// weapons/firingpattern.cpp
|
||
|
//
|
||
|
// Copyright (C) 1999-2010 Rockstar North. All Rights Reserved.
|
||
|
//
|
||
|
|
||
|
// File header
|
||
|
#include "Weapons/FiringPattern.h"
|
||
|
|
||
|
// Rage headers
|
||
|
#include "bank/bank.h"
|
||
|
#include "fwsys/fileExts.h"
|
||
|
#include "parser/manager.h"
|
||
|
|
||
|
// Parser headers
|
||
|
#include "FiringPattern_parser.h"
|
||
|
|
||
|
// Game headers
|
||
|
#include "Peds/Ped.h"
|
||
|
#include "Scene/DataFileMgr.h"
|
||
|
#include "Weapons/WeaponDebug.h"
|
||
|
|
||
|
// Macro to disable optimisations if set
|
||
|
WEAPON_OPTIMISATIONS()
|
||
|
|
||
|
#if __BANK
|
||
|
PARAM(rawFiringPatternMetadata, "Load the raw XML firing pattern metadata from the assets folder");
|
||
|
const char* g_FiringPatternMetadataAssetsSubPath = "export/data/metadata/ai/firingpatterns.pso.meta";
|
||
|
#endif // __BANK
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// CFiringPatternInfo
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CFiringPatternInfo::CFiringPatternInfo()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// CFiringPatternInfoManager
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Static manager object
|
||
|
CFiringPatternInfoManager CFiringPatternInfoManager::ms_Instance;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPatternInfoManager::Init()
|
||
|
{
|
||
|
// Load
|
||
|
Load();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPatternInfoManager::Shutdown()
|
||
|
{
|
||
|
// Delete
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if __BANK
|
||
|
void CFiringPatternInfoManager::AddWidgets(bkBank& bank)
|
||
|
{
|
||
|
bank.PushGroup("Firing Patterns");
|
||
|
|
||
|
bank.AddButton("Load", Load);
|
||
|
bank.AddButton("Save", Save);
|
||
|
|
||
|
bank.PushGroup("Infos");
|
||
|
for(s32 i = 0; i < ms_Instance.m_Infos.GetCount(); i++)
|
||
|
{
|
||
|
bank.PushGroup(ms_Instance.m_Infos[i]->GetName());
|
||
|
PARSER.AddWidgets(bank, ms_Instance.m_Infos[i]);
|
||
|
bank.PopGroup(); // ms_Instance.m_Infos[i]->GetName()
|
||
|
}
|
||
|
bank.PopGroup(); // "Infos"
|
||
|
bank.PopGroup(); // "Firing Patterns"
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPatternInfoManager::Reset()
|
||
|
{
|
||
|
// Delete the infos
|
||
|
for(s32 i = 0; i < ms_Instance.m_Infos.GetCount(); i++)
|
||
|
{
|
||
|
delete ms_Instance.m_Infos[i];
|
||
|
}
|
||
|
ms_Instance.m_Infos.Reset();
|
||
|
ms_Instance.m_InfoPtrs.Reset();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPatternInfoManager::Load()
|
||
|
{
|
||
|
// Delete any existing data
|
||
|
Reset();
|
||
|
|
||
|
char metadataFilename[RAGE_MAX_PATH];
|
||
|
|
||
|
#if __BANK
|
||
|
if(PARAM_rawFiringPatternMetadata.Get())
|
||
|
{
|
||
|
formatf(metadataFilename, RAGE_MAX_PATH, "%s%s", CFileMgr::GetAssetsFolder(), g_FiringPatternMetadataAssetsSubPath);
|
||
|
|
||
|
ASSERT_ONLY(bool hasSucceeded = )PARSER.InitAndLoadObject(metadataFilename, "", ms_Instance);
|
||
|
weaponFatalAssertf(hasSucceeded, "Failed to parse the metadata XML file (%s)", metadataFilename);
|
||
|
}
|
||
|
else
|
||
|
#endif // __BANK
|
||
|
{
|
||
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::FIRINGPATTERNS_FILE);
|
||
|
weaponFatalAssertf(pData && DATAFILEMGR.IsValid(pData), "Failed to find the firing pattern metadata PSO file");
|
||
|
|
||
|
formatf(metadataFilename, RAGE_MAX_PATH, "%s.%s", pData->m_filename, META_FILE_EXT);
|
||
|
|
||
|
psoFile* pPsoFile = psoLoadFile(metadataFilename, PSOLOAD_PREP_FOR_PARSER_LOADING, atFixedBitSet32().Set(psoFile::REQUIRE_CHECKSUM));
|
||
|
weaponFatalAssertf(pPsoFile, "Failed to load the metadata PSO file (%s)", metadataFilename);
|
||
|
|
||
|
ASSERT_ONLY(const bool bHasSucceeded = ) psoInitAndLoadObject(*pPsoFile, ms_Instance);
|
||
|
weaponFatalAssertf(bHasSucceeded, "Failed to parse the metadata PSO file (%s)", metadataFilename);
|
||
|
|
||
|
delete pPsoFile;
|
||
|
}
|
||
|
|
||
|
ms_Instance.m_InfoPtrs.Reserve(ms_Instance.m_Infos.GetCount());
|
||
|
|
||
|
// Copy the array and sort it for searching
|
||
|
for(s32 i = 0; i < ms_Instance.m_Infos.GetCount(); i++)
|
||
|
{
|
||
|
ms_Instance.m_InfoPtrs.PushAndGrow(ms_Instance.m_Infos[i]);
|
||
|
}
|
||
|
ms_Instance.m_InfoPtrs.QSort(0, ms_Instance.m_InfoPtrs.GetCount(), CFiringPatternInfo::PairSort);
|
||
|
|
||
|
// Init the widgets
|
||
|
BANK_ONLY(CWeaponDebug::InitBank());
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if __BANK
|
||
|
void CFiringPatternInfoManager::Save()
|
||
|
{
|
||
|
char filename[RAGE_MAX_PATH];
|
||
|
formatf(filename, RAGE_MAX_PATH, "%s%s", CFileMgr::GetAssetsFolder(), g_FiringPatternMetadataAssetsSubPath);
|
||
|
weaponVerifyf(PARSER.SaveObject(filename, "meta", &ms_Instance, parManager::XML), "Failed to save firing patterns");
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// CFiringPattern
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const s32 CFiringPattern::INFINITE_NUMBER_OF_BURSTS = -1;
|
||
|
const s32 CFiringPattern::INFINITE_NUMBER_OF_SHOTS_PER_BURST = -1;
|
||
|
const s32 CFiringPattern::DEFAULT_NUMBER_OF_BURSTS = -1;
|
||
|
const s32 CFiringPattern::DEFAULT_NUMBER_OF_SHOTS_PER_BURST = -1;
|
||
|
const f32 CFiringPattern::DEFAULT_TIME_TO_DELAY_FIRING = -1.0f;
|
||
|
const f32 CFiringPattern::DEFAULT_TIME_TILL_NEXT_SHOT = -1.0f;
|
||
|
const f32 CFiringPattern::DEFAULT_TIME_TILL_NEXT_SHOT_MOD = 1.0f;
|
||
|
const f32 CFiringPattern::DEFAULT_TIME_TILL_NEXT_BURST = -1.0f;
|
||
|
const f32 CFiringPattern::DEFAULT_TIME_TILL_NEXT_BURST_MOD = 1.0f;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CFiringPattern::CFiringPattern()
|
||
|
: m_uFiringPatternHash(0)
|
||
|
, m_EquippedWeaponHash(u32(0))
|
||
|
, m_pFiringPatternInfo(NULL)
|
||
|
, m_iNumberOfBursts(DEFAULT_NUMBER_OF_BURSTS)
|
||
|
, m_iNumberOfShotsPerBurst(DEFAULT_NUMBER_OF_SHOTS_PER_BURST)
|
||
|
, m_fTimeToDelayFiring(DEFAULT_TIME_TO_DELAY_FIRING)
|
||
|
, m_fTimeTillNextShot(DEFAULT_TIME_TILL_NEXT_SHOT)
|
||
|
, m_fTimeTillNextShotModifier(DEFAULT_TIME_TILL_NEXT_SHOT_MOD)
|
||
|
, m_fTimeTillNextBurst(DEFAULT_TIME_TILL_NEXT_BURST)
|
||
|
, m_fTimeTillNextBurstModifier(DEFAULT_TIME_TILL_NEXT_BURST_MOD)
|
||
|
, m_fBurstCooldownOnUnsetTimer(0.0f)
|
||
|
, m_uBurstCooldownOnUnsetPatternHash(0)
|
||
|
, m_bForceReset(false)
|
||
|
, m_bBurstFinishedThisFrame(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CFiringPattern::CFiringPattern(const CFiringPattern& other)
|
||
|
: m_uFiringPatternHash(other.m_uFiringPatternHash)
|
||
|
, m_EquippedWeaponHash(other.m_EquippedWeaponHash)
|
||
|
, m_pFiringPatternInfo(other.m_pFiringPatternInfo)
|
||
|
, m_iNumberOfBursts(other.m_iNumberOfBursts)
|
||
|
, m_iNumberOfShotsPerBurst(other.m_iNumberOfShotsPerBurst)
|
||
|
, m_fTimeToDelayFiring(other.m_fTimeToDelayFiring)
|
||
|
, m_fTimeTillNextShot(other.m_fTimeTillNextShot)
|
||
|
, m_fTimeTillNextShotModifier(other.m_fTimeTillNextShotModifier)
|
||
|
, m_fTimeTillNextBurst(other.m_fTimeTillNextBurst)
|
||
|
, m_fTimeTillNextBurstModifier(other.m_fTimeTillNextBurstModifier)
|
||
|
, m_fBurstCooldownOnUnsetTimer(other.m_fBurstCooldownOnUnsetTimer)
|
||
|
, m_uBurstCooldownOnUnsetPatternHash(other.m_uBurstCooldownOnUnsetPatternHash)
|
||
|
, m_bForceReset(other.m_bForceReset)
|
||
|
, m_bBurstFinishedThisFrame(other.m_bBurstFinishedThisFrame)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPattern::SetFiringPattern(u32 uFiringPatternHash, bool bForceReset)
|
||
|
{
|
||
|
m_bForceReset = bForceReset || (m_uFiringPatternHash != uFiringPatternHash);
|
||
|
m_uFiringPatternHash = uFiringPatternHash;
|
||
|
m_EquippedWeaponHash.Clear();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPattern::PreProcess(const CPed& ped)
|
||
|
{
|
||
|
weaponFatalAssertf(ShouldProcess(ped), "Shouldn't be processing");
|
||
|
|
||
|
// Query if the weapon has an alias
|
||
|
const CWeaponInfo* pEquippedWeaponInfo = ped.GetWeaponManager()->GetEquippedWeaponInfo();
|
||
|
if(m_bForceReset || m_EquippedWeaponHash.GetHash() != pEquippedWeaponInfo->GetHash())
|
||
|
{
|
||
|
// Store the weapon
|
||
|
m_EquippedWeaponHash = pEquippedWeaponInfo->GetHash();
|
||
|
|
||
|
// Get the firing pattern we are supposed to be using
|
||
|
const CFiringPatternInfo* pFiringPatternInfo = CFiringPatternInfoManager::GetInfo(pEquippedWeaponInfo->GetFiringPatternAlias(m_uFiringPatternHash));
|
||
|
|
||
|
// Do we need to reset the data?
|
||
|
if(m_bForceReset || m_pFiringPatternInfo != pFiringPatternInfo)
|
||
|
{
|
||
|
// When unsetting a flagged pattern, kick off a separate timer that prevents this specific hash from firing until over, no matter how many times we switch pattern
|
||
|
if(m_pFiringPatternInfo && m_pFiringPatternInfo->GetBurstCooldownOnUnset() && m_uBurstCooldownOnUnsetPatternHash == 0 && m_pFiringPatternInfo != pFiringPatternInfo)
|
||
|
{
|
||
|
if(m_fTimeTillNextBurst > 0.0f)
|
||
|
{
|
||
|
// If we're already on a burst cooldown, copy the current timer
|
||
|
m_fBurstCooldownOnUnsetTimer = m_fTimeTillNextBurst;
|
||
|
}
|
||
|
else if (m_pFiringPatternInfo->GetNumberOfShotsPerBurst() != INFINITE_NUMBER_OF_SHOTS_PER_BURST)
|
||
|
{
|
||
|
// Set the cooldown based on the number of rockets that have been fired
|
||
|
s32 iShotsFired = m_pFiringPatternInfo->GetNumberOfShotsPerBurst() - m_iNumberOfShotsPerBurst;
|
||
|
m_fBurstCooldownOnUnsetTimer = m_pFiringPatternInfo->GetTimeBetweenBursts(m_fTimeTillNextBurstModifier) * ((float)iShotsFired/(float)m_pFiringPatternInfo->GetNumberOfShotsPerBurst());
|
||
|
}
|
||
|
|
||
|
m_uBurstCooldownOnUnsetPatternHash = m_pFiringPatternInfo->GetHash();
|
||
|
}
|
||
|
|
||
|
// Assign the new firing pattern
|
||
|
m_pFiringPatternInfo = pFiringPatternInfo;
|
||
|
if(m_pFiringPatternInfo)
|
||
|
{
|
||
|
m_iNumberOfBursts = m_pFiringPatternInfo->GetNumberOfBursts();
|
||
|
m_iNumberOfShotsPerBurst = m_pFiringPatternInfo->GetNumberOfShotsPerBurst();
|
||
|
m_fTimeToDelayFiring = m_pFiringPatternInfo->GetTimeBeforeFiring();
|
||
|
m_fTimeTillNextShot = 0.0f;
|
||
|
m_fTimeTillNextBurst = 0.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
// Clear flag
|
||
|
m_bForceReset = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPattern::ProcessFired()
|
||
|
{
|
||
|
// Only decrement if not set to infinite shots per burst
|
||
|
if(m_iNumberOfShotsPerBurst != INFINITE_NUMBER_OF_SHOTS_PER_BURST)
|
||
|
{
|
||
|
m_iNumberOfShotsPerBurst--;
|
||
|
m_iNumberOfShotsPerBurst = MAX(m_iNumberOfShotsPerBurst, 0);
|
||
|
}
|
||
|
|
||
|
if(m_iNumberOfShotsPerBurst != 0)
|
||
|
{
|
||
|
// Re-init the time between shots
|
||
|
if(m_pFiringPatternInfo)
|
||
|
{
|
||
|
m_fTimeTillNextShot = m_pFiringPatternInfo->GetTimeBetweenShots(m_fTimeTillNextShotModifier);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(IsBurstFinished())
|
||
|
{
|
||
|
ProcessBurstFinished();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void CFiringPattern::ProcessBurstFinished()
|
||
|
{
|
||
|
// Mark the firing pattern as having finished the burst
|
||
|
m_bBurstFinishedThisFrame = true;
|
||
|
|
||
|
// Only decrement if not set to infinite bursts
|
||
|
if(m_iNumberOfBursts != INFINITE_NUMBER_OF_BURSTS)
|
||
|
{
|
||
|
m_iNumberOfBursts--;
|
||
|
m_iNumberOfBursts = MAX(m_iNumberOfBursts, 0);
|
||
|
}
|
||
|
|
||
|
if(m_iNumberOfBursts != 0)
|
||
|
{
|
||
|
// Re-init the number of shots per burst
|
||
|
if(m_iNumberOfShotsPerBurst != INFINITE_NUMBER_OF_SHOTS_PER_BURST)
|
||
|
{
|
||
|
if(m_pFiringPatternInfo)
|
||
|
{
|
||
|
m_iNumberOfShotsPerBurst = m_pFiringPatternInfo->GetNumberOfShotsPerBurst();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Re-init the time between bursts
|
||
|
if(m_pFiringPatternInfo)
|
||
|
{
|
||
|
m_fTimeTillNextBurst = m_pFiringPatternInfo->GetTimeBetweenBursts(m_fTimeTillNextBurstModifier);
|
||
|
}
|
||
|
|
||
|
// Clear any timer till next shot, as when the burst timer finishes we want to fire straight away
|
||
|
m_fTimeTillNextShot = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|