Files
GTASource/game/weapons/FiringPattern.cpp

341 lines
11 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// 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;
}
}
////////////////////////////////////////////////////////////////////////////////