4063 lines
143 KiB
C++
4063 lines
143 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
// Title : PopCycle.h
|
|
// Author : Obbe, Adam Croston
|
|
// Started : 5/12/03
|
|
// Purpose : To keep track of the current population disposition.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
#include "PopCycle.h"
|
|
// Parser file
|
|
#include "PopCycle_parser.h"
|
|
|
|
|
|
// Std headers
|
|
#include <Stdio.h>
|
|
#include <String.h>
|
|
|
|
// Rage headers
|
|
#include "fwsys/fileExts.h"
|
|
#include "parser/restparserservices.h"
|
|
|
|
// Framework headers
|
|
#include "grcore/debugdraw.h"
|
|
#include "fwmaths/random.h"
|
|
#include "fwSys/fileExts.h"
|
|
|
|
// Game headers
|
|
#include "ai/ambient/AmbientModelSet.h"
|
|
#include "Control/gamelogic.h"
|
|
#include "Core/game.h"
|
|
#include "Cutscene/CutSceneManagerNew.h"
|
|
#include "Debug/debug.h"
|
|
#include "Game/cheat.h"
|
|
#include "Game/clock.h"
|
|
#include "Game/Dispatch/DispatchData.h"
|
|
#include "Game/modelindices.h"
|
|
#include "Game/Performance.h"
|
|
#include "game/PopMultiplierAreas.h"
|
|
#include "Game/weather.h"
|
|
#include "Game/zones.h"
|
|
#include "modelinfo/MloModelInfo.h"
|
|
#include "ModelInfo/pedmodelinfo.h"
|
|
#include "ModelInfo/vehiclemodelinfo.h"
|
|
#include "network/NetworkInterface.h"
|
|
#include "peds/Ped.h"
|
|
#include "Peds/pedpopulation.h"
|
|
#include "Peds/PedDebugVisualiser.h" // for CPedDebugVisualiserMenu::GetForceAllCops();
|
|
#include "peds/popzones.h"
|
|
#include "Peds/WildlifeManager.h"
|
|
//#include "Renderer/font.h"
|
|
#include "Renderer/zonecull.h"
|
|
#include "Scene/datafilemgr.h"
|
|
#include "Scene/fileloader.h"
|
|
#include "peds/PlayerInfo.h"
|
|
#include "scene/focusentity.h"
|
|
#include "scene/world/gameWorld.h"
|
|
#include "Script/script.h"
|
|
#include "script/script_cars_and_peds.h"
|
|
#include "Streaming/populationstreaming.h"
|
|
#include "Streaming/streaming.h"
|
|
#include "System/filemgr.h"
|
|
#include "vehicles/Automobile.h"
|
|
#include "vehicles/cargen.h"
|
|
#include "vehicles/vehicle.h"
|
|
#include "Vehicles/vehiclepopulation.h"
|
|
#include "Vfx/metadata/VfxRegionInfo.h"
|
|
#include "scene/dlc_channel.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
|
|
|
|
CPopModelAndVariations::~CPopModelAndVariations()
|
|
{
|
|
delete m_Variations;
|
|
}
|
|
|
|
CPopModelAndVariations& CPopModelAndVariations::CopyTo(const CPopModelAndVariations& rhs)
|
|
{
|
|
m_Name = rhs.m_Name;
|
|
|
|
if (rhs.m_Variations)
|
|
{
|
|
m_Variations = rhs.m_Variations->Clone();
|
|
Assertf(m_Variations, "CPopModelAndVariations - Clone failed!");
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CPopGroup
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#if __BANK
|
|
float CPopAllocation::sm_maxPedsMultiplier = 1.0f;
|
|
float CPopAllocation::sm_maxCarsMultiplier = 1.0f;
|
|
u8 CPopAllocation::sm_forceMaxPeds = 0;
|
|
u8 CPopAllocation::sm_forceMaxCars = 0;
|
|
bool CPopCycle::sm_enableOverridePedDirtScale = false;
|
|
float CPopCycle::sm_overridePedDirtScale = 0.0f;
|
|
bool CPopCycle::sm_enableOverrideVehDirtScale = false;
|
|
float CPopCycle::sm_overridevehDirtScale = 0.0f;
|
|
bool CPopCycle::sm_enableOverrideDirtColor = false;
|
|
Color32 CPopCycle::sm_overrideDirtColor;
|
|
#endif
|
|
|
|
u32 CPopCycle::sm_popSchedulesMaxNumCars = 0;
|
|
bool CPopCycle::sm_populationChanged = true;
|
|
int CPopCycle::sm_nCurrentZoneVehicleResponseType = VEHICLE_RESPONSE_DEFAULT;
|
|
s32 CPopCycle::sm_nCurrentZonePopScheduleIndex = -1;
|
|
s32 CPopCycle::sm_nCurrentDaySubdivision = 0;
|
|
s32 CPopCycle::sm_nCurrentWeekSubdivision = 0;
|
|
atArray<u8> CPopCycle::sm_currentPedPercentages;
|
|
atArray<u8> CPopCycle::sm_currentVehPercentages;
|
|
|
|
#if !__FINAL
|
|
atFinalHashString CPopCycle::sm_nCurrentZoneName;
|
|
#endif
|
|
|
|
CPopZone *CPopCycle::sm_pCurrZone = NULL;
|
|
const CPopAllocation *CPopCycle::sm_pCurrPopAllocation = NULL;
|
|
const CPopSchedule* CPopCycle::sm_pCurrPopSchedule = NULL;
|
|
s32 CPopCycle::sm_currentMaxNumCopPeds, CPopCycle::sm_currentMaxNumAmbientPeds, CPopCycle::sm_currentMaxNumScenarioPeds;
|
|
u32 CPopCycle::sm_currentNumPedsInCombat = 0;
|
|
bank_bool CPopCycle::sm_bUseCombatMultiplier = true;
|
|
bank_u32 CPopCycle::sm_iMinPedsInCombatForPopMultiplier = 8;
|
|
bank_u32 CPopCycle::sm_iMaxPedsInCombatForPopMultiplier = 24;
|
|
bank_float CPopCycle::sm_fPedsInCombatMinPopMultiplier = 0.5f;
|
|
bank_float CPopCycle::sm_fMinWantedMultiplierMP = 0.6f;
|
|
CPopGroupList CPopCycle::sm_popGroups;
|
|
atArray<atHashString> CPopCycle::sm_popGroupPatches;
|
|
CPopScheduleList CPopCycle::sm_popSchedules;
|
|
|
|
#define POPULATION_RECALCULATE_COUNTDOWN 3 // number of times to use the cached result (not including the time it was calculated) -> 0 = no caching
|
|
u8 CPopCycle::sm_PopulationRecalculateCountdown = 0;
|
|
u64 CPopCycle::sm_CurrentPopZoneId = 0;
|
|
u32 CPopCycle::sm_CachedMostWantedPopGroup = 0xFFFFFFFF;
|
|
|
|
s32 CPopCycle::sm_preferredSpawnGroupForVehs = -1;
|
|
|
|
float CPopCycle::sm_distanceDeltaForUpdateSqr = 5.f * 5.f; // update the pop zone data every 5 meters
|
|
u32 CPopCycle::sm_nextTimeForUpdate = 0;
|
|
Vector3 CPopCycle::sm_lastUpdatePos;
|
|
u32 CPopCycle::sm_lastTimePedsInCombatCounted = 0;
|
|
|
|
u32 CPopCycle::sm_commonVehicleGroup = ~0U;
|
|
u32 CPopCycle::sm_vehicleBikeGroup = ~0U;
|
|
|
|
#if !__FINAL
|
|
bool CPopCycle::sm_bDisplayDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplayScenarioDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplaySimpleDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplayInVehDeadDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplayInVehNoPretendModelDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplayCarDebugInfo = false;
|
|
bool CPopCycle::sm_bEnableVehPopDebugOutput = false;
|
|
bool CPopCycle::sm_bDisplayCarSimpleDebugInfo = false;
|
|
bool CPopCycle::sm_bDisplayCarPopulationFailureCounts = false;
|
|
bool CPopCycle::sm_bDisplayPedPopulationFailureCounts = false;
|
|
bool CPopCycle::sm_bDisplayScenarioPedPopulationFailureCountsOnVectorMap = false;
|
|
bool CPopCycle::sm_bDisplayScenarioVehiclePopulationFailureCountsOnVectorMap = false;
|
|
bool CPopCycle::sm_bDisplayWanderingPedPopulationFailureCountsOnVectorMap = false;
|
|
bool CPopCycle::sm_bDisplayPedPopulationFailureCountsDebugSpew= false;
|
|
bool CPopCycle::sm_bDisplayScenarioPedPopulationFailuresInTheWorld = false;
|
|
#endif
|
|
|
|
XPARAM(maxVehicles);
|
|
XPARAM(maxPeds);
|
|
PARAM(pedMult, "Multiplier for ped counts in pop-cycle");
|
|
PARAM(carMult, "Multiplier for car counts in pop-cycle");
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Data file loader interface
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
class CPopulationDataFileMounter : public CDataFileMountInterface
|
|
{
|
|
virtual bool LoadDataFile(const CDataFileMgr::DataFile& file)
|
|
{
|
|
dlcDebugf3("CPopulationDataFileMounter::LoadDataFile: %s type = %d", file.m_filename, file.m_fileType);
|
|
switch(file.m_fileType)
|
|
{
|
|
case CDataFileMgr::DLC_POP_GROUPS:
|
|
CPopCycle::LoadPopGroupsFile(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::POPGRP_FILE_PATCH:
|
|
CPopCycle::LoadPopGroupPatch(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::POPSCHED_FILE:
|
|
CPopCycle::LoadPopSchedFile(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::ZONEBIND_FILE:
|
|
CPopCycle::LoadZoneBindFile(file.m_filename);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual void UnloadDataFile(const CDataFileMgr::DataFile& file)
|
|
{
|
|
dlcDebugf3("CPopulationDataFileMounter::UnloadDataFile %s type = %d", file.m_filename, file.m_fileType);
|
|
switch(file.m_fileType)
|
|
{
|
|
case CDataFileMgr::DLC_POP_GROUPS:
|
|
CPopCycle::UnloadPopGroupsFile(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::POPGRP_FILE_PATCH:
|
|
CPopCycle::UnloadPopGroupPatch(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::POPSCHED_FILE:
|
|
CPopCycle::UnloadPopSchedFile(file.m_filename);
|
|
break;
|
|
|
|
case CDataFileMgr::ZONEBIND_FILE:
|
|
CPopCycle::UnloadZoneBindFile(file.m_filename);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} g_PopulationDataFileMounter;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CpopAllocation
|
|
// Purpose : To store a description of a current population disposition.
|
|
// Namely, their counts and percentages.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopAllocation::Reset()
|
|
{
|
|
m_nMaxNumAmbientPeds = 0;
|
|
m_nMaxNumScenarioPeds = 0;
|
|
m_nPercentageOfMaxCars = 0;
|
|
m_nMaxNumParkedCars = 0;
|
|
m_nMaxNumLowPrioParkedCars = 0;
|
|
m_nPercCopsCars = 0;
|
|
m_nPercCopsPeds = 0;
|
|
m_nMaxScenarioPedModels = 0;
|
|
m_nMaxScenarioVehicleModels = 2;
|
|
m_nMaxPreassignedParkedCars = 1;
|
|
m_pedGroupPercentages.Reset();
|
|
m_vehGroupPercentages.Reset();
|
|
}
|
|
|
|
void CPopAllocation::NormalizePercentages()
|
|
{
|
|
NormalizePercentages(m_pedGroupPercentages);
|
|
NormalizePercentages(m_vehGroupPercentages);
|
|
}
|
|
|
|
void CPopAllocation::NormalizePercentages(atArray<CPopGroupPercentage>& percentages)
|
|
{
|
|
// Normalize the values to 100%
|
|
const u32 groupCount = percentages.GetCount();
|
|
|
|
Assertf(groupCount != 0, "Empty pop group found - possibly a problem in popcycle.dat?");
|
|
if (groupCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s32 totalVal = 0;
|
|
for (u32 i = 0; i < groupCount; i++)
|
|
{
|
|
totalVal += percentages[i].m_percentage;
|
|
}
|
|
Assertf(totalVal > 0, "The percentages add up to 0");
|
|
for (u32 i = 0; i < groupCount; i++)
|
|
{
|
|
percentages[i].m_percentage = (u8)(percentages[i].m_percentage * (100.0f / totalVal));
|
|
}
|
|
|
|
// Because of rounding errors we might have less than 100. Give the remainder to the highest one.
|
|
totalVal = 0;
|
|
s32 highestVal = 0, indexOfHighestVal = 0;
|
|
for (u32 i = 0; i < groupCount; i++)
|
|
{
|
|
totalVal += percentages[i].m_percentage;
|
|
if (percentages[i].m_percentage >= highestVal)
|
|
{
|
|
highestVal = percentages[i].m_percentage;
|
|
indexOfHighestVal = i;
|
|
}
|
|
}
|
|
Assert(totalVal <= 100);
|
|
percentages[indexOfHighestVal].m_percentage += 100 - (u8)totalVal;
|
|
}
|
|
|
|
atHashWithStringNotFinal CPopAllocation::PickRandomGroup(const atArray<CPopGroupPercentage>& percentages) const
|
|
{
|
|
s32 random = fwRandom::GetRandomNumberInRange(0, 100);
|
|
u32 popGroupIndex = 0;
|
|
#if __ASSERT
|
|
const u32 groupCount = percentages.GetCount();
|
|
#endif
|
|
while(percentages[popGroupIndex].m_percentage < random)
|
|
{
|
|
random = random - (s32)percentages[popGroupIndex].m_percentage;
|
|
popGroupIndex++;
|
|
Assert(popGroupIndex < groupCount);
|
|
}
|
|
return popGroupIndex;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CPopSchedule
|
|
// Purpose : To store a series of a population dispositions over time.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
CPopSchedule::CPopSchedule()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CPopSchedule::Reset(void)
|
|
{
|
|
// Clear the info in the group so we can re-use it.
|
|
m_Name = atHashWithStringNotFinal::Null();
|
|
|
|
for (int dayType = 0; dayType < POPCYCLE_WEEKSUBDIVISIONS; dayType++)
|
|
{
|
|
for (int timeType = 0; timeType < POPCYCLE_DAYSUBDIVISIONS; timeType++)
|
|
{
|
|
CPopAllocation& popAllocation = GetAllocation(dayType, timeType);
|
|
popAllocation.Reset();
|
|
}
|
|
}
|
|
m_overridePedPercentage = 0;
|
|
m_overrideVehPercentage = 0;
|
|
m_overrideVehModel = fwModelId::MI_INVALID;
|
|
}
|
|
|
|
void CPopSchedule::SetOverridePedGroup(u32 overridePedGroup, u32 overridePercentage)
|
|
{
|
|
Assertf(overridePercentage <=100, "Override pedgroup percentage %d is out of range", overridePercentage);
|
|
m_overridePedPercentage = overridePercentage;
|
|
m_OverridePedGroup = overridePedGroup;
|
|
CPopCycle::SetPopulationChanged();
|
|
}
|
|
|
|
void CPopSchedule::SetOverrideVehGroup(u32 overrideVehGroup, u32 overridePercentage)
|
|
{
|
|
Assertf(overridePercentage <=100, "Override vehgroup percentage %d is out of range", overridePercentage);
|
|
m_overrideVehPercentage = overridePercentage;
|
|
m_OverrideVehGroup = overrideVehGroup;
|
|
CPopCycle::SetPopulationChanged();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CPopScheduleList
|
|
// Purpose : To store a series of a named population schedules.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPopScheduleList::CPopScheduleList()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CPopScheduleList::Reset()
|
|
{
|
|
const int popScheduleCount = m_schedules.GetCount();
|
|
for(int popScheduleIndex = 0; popScheduleIndex < popScheduleCount; ++popScheduleIndex)
|
|
{
|
|
m_schedules[popScheduleIndex].Reset();
|
|
}
|
|
m_schedules.Reset();
|
|
}
|
|
|
|
bool CPopScheduleList::GetIndexFromNameHash (u32 popScheduleNameHash, s32& outPopScheduleIndex) const
|
|
{
|
|
outPopScheduleIndex = INVALID_SCHEDULE;
|
|
const s32 popScheduleCount = m_schedules.GetCount();
|
|
for(s32 popScheduleIndex = 0; popScheduleIndex < popScheduleCount; ++popScheduleIndex)
|
|
{
|
|
if(m_schedules[popScheduleIndex].m_Name == popScheduleNameHash)
|
|
{
|
|
outPopScheduleIndex = popScheduleIndex;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CPopScheduleList::Load (const char* fileName)
|
|
{
|
|
FileHandle fd;
|
|
char line[1024];
|
|
|
|
fd = CFileMgr::OpenFile(fileName, "rb");
|
|
Assertf(CFileMgr::IsValidFileHandle(fd), "problem loading popcycle.dat");
|
|
|
|
bool bReadingSchedule = false;
|
|
bool bReadingpopAllocation = false;
|
|
int weekSubdivision = 0;
|
|
int daySubdivision = 0;
|
|
CPopSchedule popSchedule;
|
|
|
|
s32 iMaxCarsValue = 0;
|
|
|
|
|
|
while(CFileMgr::ReadLine(fd, &line[0], 1024))
|
|
{
|
|
// Check for empty or commented out lines...
|
|
if(line[0] == '/' || line[0] == '\0' || line[1] == '\0' || line[2] == '\0') // Note: ps3 returns empty lines as space,0
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check if this is the beginning or end of a schedule.
|
|
if(strncmp(line, "POP_SCHEDULE:", 13)==0)
|
|
{
|
|
// Update the reading state.
|
|
bReadingSchedule = true;
|
|
bReadingpopAllocation = false;
|
|
weekSubdivision = 0;
|
|
daySubdivision = 0;
|
|
iMaxCarsValue = 0;
|
|
continue;
|
|
}
|
|
else if(strncmp(line, "END_POP_SCHEDULE", 16)==0)
|
|
{
|
|
// Add the group.
|
|
m_schedules.PushAndGrow(popSchedule);
|
|
|
|
// Clear the info in the group so we can re-use it.
|
|
popSchedule.Reset();
|
|
|
|
// Update the reading state.
|
|
bReadingSchedule = false;
|
|
bReadingpopAllocation = false;
|
|
weekSubdivision = 0;
|
|
daySubdivision = 0;
|
|
continue;
|
|
}
|
|
|
|
// Depending on current state interpret the line.
|
|
if( bReadingSchedule )
|
|
{
|
|
// Read the schedule name.
|
|
popSchedule.m_Name = line;
|
|
|
|
// Update the reading state.
|
|
bReadingSchedule = false;
|
|
bReadingpopAllocation = true;
|
|
weekSubdivision = 0;
|
|
daySubdivision = 0;
|
|
continue;
|
|
}
|
|
else if( bReadingpopAllocation )
|
|
{
|
|
CPopAllocation& popAllocation = popSchedule.GetAllocation(weekSubdivision, daySubdivision);
|
|
|
|
// Read the allotment line.
|
|
const char* seps = " ,\t";
|
|
char* pToken = NULL;
|
|
pToken = strtok(line, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxNumAmbientPeds = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxNumScenarioPeds = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nPercentageOfMaxCars = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxNumParkedCars = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxNumLowPrioParkedCars= (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nPercCopsCars = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nPercCopsPeds = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxScenarioPedModels = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxScenarioVehicleModels = (u8)atoi(pToken);
|
|
pToken = strtok(NULL, seps);
|
|
Assert(pToken);
|
|
popAllocation.m_nMaxPreassignedParkedCars = (u8)atoi(pToken);
|
|
|
|
Assert(popAllocation.m_nPercentageOfMaxCars <= 100);
|
|
|
|
iMaxCarsValue = Max(iMaxCarsValue, (s32)popAllocation.m_nPercentageOfMaxCars);
|
|
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(!strcmp(pToken, "peds"), "corrupt popcycle.dat: couldn't find the ped tag");
|
|
char* pGroupName = strtok(NULL, seps);
|
|
bool bDoingPeds = true;
|
|
while(pGroupName)
|
|
{
|
|
if(!strcmp(pGroupName, "cars"))
|
|
{
|
|
bDoingPeds = false;
|
|
pGroupName = strtok(NULL, seps);
|
|
}
|
|
const char* pPercent = strtok(NULL, seps);
|
|
if(pGroupName == NULL || pPercent == NULL)
|
|
break;
|
|
u32 popGroup = 0;
|
|
int percentage = atoi(pPercent);
|
|
if(bDoingPeds)
|
|
{
|
|
if(Verifyf(CPopCycle::GetPopGroups().FindPedGroupFromNameHash(atHashValue(pGroupName), popGroup), "Cannot find ped group %s in schedule %s",
|
|
pGroupName, popSchedule.GetName().GetCStr()))
|
|
popAllocation.m_pedGroupPercentages.PushAndGrow(CPopGroupPercentage(pGroupName, (u8)percentage));
|
|
}
|
|
else
|
|
{
|
|
if(Verifyf(CPopCycle::GetPopGroups().FindVehGroupFromNameHash(atHashValue(pGroupName), popGroup), "Cannot find car group %s in schedule %s",
|
|
pGroupName, popSchedule.GetName().GetCStr()))
|
|
popAllocation.m_vehGroupPercentages.PushAndGrow(CPopGroupPercentage(pGroupName, (u8)percentage));
|
|
}
|
|
|
|
pGroupName = strtok(NULL, seps);
|
|
}
|
|
popAllocation.NormalizePercentages(); // disable when saving!
|
|
|
|
// Update the reading state.
|
|
// Increment the time of day and day of week.
|
|
bReadingSchedule = false;
|
|
bReadingpopAllocation = true;
|
|
++daySubdivision;
|
|
if(daySubdivision == CPopSchedule::POPCYCLE_DAYSUBDIVISIONS)
|
|
{
|
|
daySubdivision = 0;
|
|
++weekSubdivision;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CFileMgr::CloseFile(fd);
|
|
}
|
|
|
|
void CPopScheduleList::PostLoad()
|
|
{
|
|
/*
|
|
m_percentageOfMaxCars = 0;
|
|
for(int sch = 0; sch < m_schedules.GetCount(); sch++)
|
|
{
|
|
for (int dayType = 0; dayType < CPopSchedule::POPCYCLE_WEEKSUBDIVISIONS; dayType++)
|
|
{
|
|
for (int timeType = 0; timeType < CPopSchedule::POPCYCLE_DAYSUBDIVISIONS; timeType++)
|
|
{
|
|
CPopAllocation& popAllocation = m_schedules[sch].GetAllocation(dayType, timeType);
|
|
if(popAllocation.m_nPercentageOfMaxCars > m_percentageOfMaxCars)
|
|
{
|
|
m_percentageOfMaxCars = popAllocation.m_nPercentageOfMaxCars;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//Save("popcycle.datnew");
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CPopScheduleList::Save(const char* fileName)
|
|
{
|
|
char line_out[2048];
|
|
FileHandle fd_out=INVALID_FILE_HANDLE;
|
|
|
|
fd_out = CFileMgr::OpenFileForWriting(fileName);
|
|
Assertf(CFileMgr::IsValidFileHandle(fd_out), "problem opening popcycle.datnew");
|
|
|
|
sprintf(line_out, "// For each schedule (Northwood, North Holland etc) we have a set of numbers that controls the ped densities.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// There is a set of values for each time of the day in 2 hour increments (midnight, 2am, 4 am etc)\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// There are 2 sets of values: Weekday & Weekend.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #Peds is the maximum number of ambient peds you can ever have.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #Scenario is the maximum number of scenario peds you can ever have.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #Cars is the maximum number of cars you can ever have.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #prkdcrs is the maximum number of parked cars you can ever have.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #lowprkdcrs is the maximum number of low priority parked cars you can ever have.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #PercCopCars is percentage of cars that are cop cars.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #PercCopPed is percentage of peds that are cops.\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #MaxScenPeds is the max number of ped models that may be streamed in for scenarios.\r\n\r\n\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
|
|
// DEBUG!! -AC, update this to be data driven...
|
|
// Mention about the rest of the percentages.
|
|
|
|
// List the popgroups that the pop cycle system is aware of.
|
|
// END DEBUG!!
|
|
|
|
for(int sch=0; sch<m_schedules.GetCount(); sch++)
|
|
{
|
|
const CPopSchedule& schedule = m_schedules[sch];
|
|
sprintf(line_out, "\r\n////////////////////////////////////////////////////////////////////\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "POP_SCHEDULE:\r\n%s\r\n", schedule.GetName().GetCStr());
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
|
|
for(int dayType=0; dayType<CPopSchedule::POPCYCLE_WEEKSUBDIVISIONS; dayType++)
|
|
{
|
|
if (dayType == 0)
|
|
sprintf(line_out, "// Weekday\r\n//\r\n");
|
|
else
|
|
sprintf(line_out, "// Weekend\r\n//\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
sprintf(line_out, "// #Peds #Scenario #Cars #prkdcrs #lowprkdcrs PercCopCars PercCopPed MaxScenPeds MaxScenVehs MaxPreAssignedParked\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
|
|
for(int timeType=0; timeType<CPopSchedule::POPCYCLE_DAYSUBDIVISIONS; timeType++)
|
|
{
|
|
const CPopAllocation& popAllocation = schedule.GetAllocation(dayType, timeType);
|
|
// Write the pop allotment for this particular day subdivision.
|
|
sprintf(line_out, " %02d %3d %3d %02d %02d %3d %3d %3d %3d %3d ",
|
|
(s32)(popAllocation.m_nMaxNumAmbientPeds),
|
|
(s32)(popAllocation.m_nMaxNumScenarioPeds),
|
|
(s32)(popAllocation.m_nPercentageOfMaxCars),
|
|
(s32)(popAllocation.m_nMaxNumParkedCars),
|
|
(s32)(popAllocation.m_nMaxNumLowPrioParkedCars),
|
|
popAllocation.m_nPercCopsCars,
|
|
popAllocation.m_nPercCopsPeds,
|
|
popAllocation.m_nMaxScenarioPedModels,
|
|
popAllocation.m_nMaxScenarioVehicleModels,
|
|
popAllocation.m_nMaxPreassignedParkedCars);
|
|
strcat(line_out, " peds");
|
|
for(int i=0; i<popAllocation.m_pedGroupPercentages.GetCount(); i++)
|
|
{
|
|
char groupInfoBuff[128];
|
|
sprintf(groupInfoBuff, " %s %02d", popAllocation.m_pedGroupPercentages[i].m_GroupName.GetCStr(),
|
|
popAllocation.m_pedGroupPercentages[i].m_percentage);
|
|
strcat(line_out, groupInfoBuff);
|
|
}
|
|
strcat(line_out, " cars");
|
|
for(int i=0; i<popAllocation.m_vehGroupPercentages.GetCount(); i++)
|
|
{
|
|
char groupInfoBuff[128];
|
|
sprintf(groupInfoBuff, " %s %02d", popAllocation.m_vehGroupPercentages[i].m_GroupName.GetCStr(),
|
|
popAllocation.m_vehGroupPercentages[i].m_percentage);
|
|
strcat(line_out, groupInfoBuff);
|
|
}
|
|
strcat(line_out, "\r\n");
|
|
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
|
|
}
|
|
}
|
|
|
|
sprintf(line_out, "END_POP_SCHEDULE\r\n");
|
|
CFileMgr::Write(fd_out, line_out, istrlen(line_out));
|
|
}
|
|
CFileMgr::CloseFile(fd_out);
|
|
|
|
}
|
|
|
|
const char* CPopScheduleList::GetNameFromIndex (s32 popScheduleIndex) const
|
|
{
|
|
return m_schedules[popScheduleIndex].m_Name.GetCStr();
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CPopulationGroup
|
|
// Purpose : List of popgroups.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
CPopulationGroup::CPopulationGroup()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CPopulationGroup::Reset()
|
|
{
|
|
m_Name = atHashWithStringNotFinal::Null();
|
|
m_flags = POPGROUP_AMBIENT|POPGROUP_SCENARIO;
|
|
m_empty = false;
|
|
|
|
m_models.Reset();
|
|
}
|
|
|
|
u32 CPopulationGroup::GetModelIndex(u32 index) const
|
|
{
|
|
fwModelId modelId;
|
|
CModelInfo::GetBaseModelInfoFromHashKey(m_models[index].m_Name, &modelId);
|
|
return(modelId.GetModelIndex());
|
|
}
|
|
|
|
bool CPopulationGroup::IsIndexMember(u32 modelIndex) const
|
|
{
|
|
u32 modelHash = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetHashKey();
|
|
|
|
for (u32 i = 0; i < m_models.GetCount(); i++)
|
|
{
|
|
if (modelHash == m_models[i].m_Name)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
CAmbientModelVariations* CPopulationGroup::FindVariations(u32 modelIndex) const
|
|
{
|
|
u32 modelHash = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetHashKey();
|
|
|
|
for (u32 i = 0; i < m_models.GetCount(); i++)
|
|
{
|
|
if (modelHash == m_models[i].m_Name)
|
|
{
|
|
return m_models[i].m_Variations;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CPopGroupList
|
|
// Purpose : List of popgroups.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
CPopGroupList::CPopGroupList()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CPopGroupList::Flush()
|
|
{
|
|
// Purge if we already have something loaded
|
|
if (GetPedCount() > 0 || GetVehCount() > 0)
|
|
{
|
|
CPopulationStreaming::FlushAllVehicleModelsHard();
|
|
}
|
|
}
|
|
|
|
void CPopGroupList::Reset()
|
|
{
|
|
// m_popGroups.Reset();
|
|
m_pedGroups.Reset();
|
|
m_vehGroups.Reset();
|
|
}
|
|
|
|
void CPopGroupList::LoadPatch(const char* file)
|
|
{
|
|
CPopGroupList patchData;
|
|
|
|
Flush();
|
|
|
|
if (Verifyf(PARSER.LoadObject(file, NULL, patchData), "CPopGroupList::LoadPatch - could not load %s", file))
|
|
{
|
|
s32 wildLifeHabitatsCount = patchData.m_wildlifeHabitats.GetCount();
|
|
s32 vehGroupsCount = patchData.m_vehGroups.GetCount();
|
|
s32 pedGroupsCount = patchData.m_pedGroups.GetCount();
|
|
|
|
for (s32 i = 0; i < wildLifeHabitatsCount; i++)
|
|
{
|
|
atHashString& habitatPatchData = patchData.m_wildlifeHabitats[i];
|
|
|
|
if (Verifyf(m_wildlifeHabitats.Find(habitatPatchData) == -1, "CPopGroupList::LoadPatch - Duplicate wildlife %s in %s", habitatPatchData.GetCStr(), file))
|
|
m_wildlifeHabitats.PushAndGrow(habitatPatchData);
|
|
}
|
|
|
|
for (s32 i = 0; i < vehGroupsCount; i++)
|
|
{
|
|
CPopulationGroup& vehGroupPatchData = patchData.m_vehGroups[i];
|
|
|
|
if (Verifyf(m_vehGroups.Find(vehGroupPatchData) == -1, "CPopGroupList::LoadPatch - Duplicate vehicle %s in %s", vehGroupPatchData.m_Name.GetCStr(), file))
|
|
m_vehGroups.PushAndGrow(vehGroupPatchData);
|
|
}
|
|
|
|
for (s32 i = 0; i < pedGroupsCount; i++)
|
|
{
|
|
CPopulationGroup& pedGroupPatchData = patchData.m_pedGroups[i];
|
|
|
|
if (Verifyf(m_pedGroups.Find(pedGroupPatchData) == -1, "CPopGroupList::LoadPatch - Duplicate ped %s in %s", pedGroupPatchData.m_Name.GetCStr(), file))
|
|
m_pedGroups.PushAndGrow(pedGroupPatchData);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPopGroupList::UnloadPatch(const char* file)
|
|
{
|
|
CPopGroupList patchData;
|
|
|
|
Flush();
|
|
|
|
if (Verifyf(PARSER.LoadObject(file, NULL, patchData), "CPopGroupList::UnloadPatch - could not load %s", file))
|
|
{
|
|
s32 wildLifeHabitatsCount = patchData.m_wildlifeHabitats.GetCount();
|
|
s32 vehGroupsCount = patchData.m_vehGroups.GetCount();
|
|
s32 pedGroupsCount = patchData.m_pedGroups.GetCount();
|
|
|
|
for (s32 i = 0; i < wildLifeHabitatsCount; i++)
|
|
m_wildlifeHabitats.DeleteMatches(patchData.m_wildlifeHabitats[i]);
|
|
|
|
for (s32 i = 0; i < vehGroupsCount; i++)
|
|
{
|
|
s32 index = m_vehGroups.Find(patchData.m_vehGroups[i]);
|
|
|
|
if (index != -1)
|
|
{
|
|
m_vehGroups[index].Reset();
|
|
m_vehGroups.Delete(index);
|
|
}
|
|
}
|
|
|
|
for (s32 i = 0; i < pedGroupsCount; i++)
|
|
{
|
|
s32 index = m_pedGroups.Find(patchData.m_pedGroups[i]);
|
|
|
|
if (index != -1)
|
|
{
|
|
m_pedGroups[index].Reset();
|
|
m_pedGroups.Delete(index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*u32 CPopGroupList::GetPedHash(u32 popGroupIndex, u32 pedWithinGroup) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
return m_popGroups[popGroupIndex].m_pedModels[pedWithinGroup];
|
|
}
|
|
|
|
u32 CPopGroupList::GetPedIndex(u32 popGroupIndex, u32 pedWithinGroup) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
|
|
fwModelId modelId;
|
|
CModelInfo::GetBaseModelInfoFromHashKey(m_popGroups[popGroupIndex].m_pedModels[pedWithinGroup], &modelId);
|
|
return(modelId.GetModelIndex());
|
|
}
|
|
|
|
u32 CPopGroupList::GetNumPeds(u32 popGroupIndex) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
return m_popGroups[popGroupIndex].m_pedModels.GetCount();
|
|
}
|
|
|
|
u32 CPopGroupList::GetVehHash(u32 popGroupIndex, u32 vehWithinGroup) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
return m_popGroups[popGroupIndex].m_vehModels[vehWithinGroup];
|
|
}
|
|
|
|
u32 CPopGroupList::GetVehIndex(u32 popGroupIndex, u32 vehWithinGroup) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
|
|
fwModelId modelId;
|
|
CModelInfo::GetBaseModelInfoFromHashKey(m_popGroups[popGroupIndex].m_vehModels[vehWithinGroup], &modelId);
|
|
return(modelId.GetModelIndex());
|
|
}
|
|
|
|
u32 CPopGroupList::GetNumVehs(u32 popGroupIndex) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
return m_popGroups[popGroupIndex].m_vehModels.GetCount();
|
|
}
|
|
|
|
bool CPopGroupList::IsPedIndexMember(u32 popGroupIndex, u32 pedModelIndex) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
const CPopGroup& popGroup = m_popGroups[popGroupIndex];
|
|
u32 pedModelHash = CModelInfo::GetBaseModelInfo(fwModelId(pedModelIndex))->GetHashKey();
|
|
|
|
const u32 numPedsInGroup = popGroup.m_pedModels.GetCount();
|
|
for (u32 indexOfPedInGroup = 0; indexOfPedInGroup < numPedsInGroup; indexOfPedInGroup++)
|
|
{
|
|
if (pedModelHash == popGroup.m_pedModels[indexOfPedInGroup])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPopGroupList::IsVehIndexMember(u32 popGroupIndex, u32 vehModelIndex) const
|
|
{
|
|
Assert(popGroupIndex < (u32)m_popGroups.GetCount());
|
|
const CPopGroup& popGroup = m_popGroups[popGroupIndex];
|
|
u32 vehModelHash = CModelInfo::GetBaseModelInfo(fwModelId(vehModelIndex))->GetHashKey();
|
|
|
|
const u32 numVehsInGroup = popGroup.m_vehModels.GetCount();
|
|
for (u32 indexOfVehInGroup = 0; indexOfVehInGroup < numVehsInGroup; indexOfVehInGroup++)
|
|
{
|
|
if (vehModelHash == popGroup.m_vehModels[indexOfVehInGroup])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}*/
|
|
|
|
bool CPopGroupList::FindPedGroupFromNameHash(u32 pedGroupNameHash, u32& outPedGroupIndex) const
|
|
{
|
|
const u32 pedGroupCount = m_pedGroups.GetCount();
|
|
for(u32 pedGroupIndex = 0; pedGroupIndex < pedGroupCount; pedGroupIndex++)
|
|
{
|
|
if(m_pedGroups[pedGroupIndex].m_Name == pedGroupNameHash)
|
|
{
|
|
outPedGroupIndex = pedGroupIndex;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPopGroupList::FindVehGroupFromNameHash(u32 vehGroupNameHash, u32& outVehGroupIndex) const
|
|
{
|
|
const u32 vehGroupCount = m_vehGroups.GetCount();
|
|
for(u32 vehGroupIndex = 0; vehGroupIndex < vehGroupCount; vehGroupIndex++)
|
|
{
|
|
if(m_vehGroups[vehGroupIndex].m_Name == vehGroupNameHash)
|
|
{
|
|
outVehGroupIndex = vehGroupIndex;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CPopGroupList::PostLoad()
|
|
{
|
|
|
|
#if __ASSERT
|
|
for(u32 i=0; i<m_pedGroups.GetCount(); i++)
|
|
{
|
|
CPopulationGroup& popGroup = m_pedGroups[i];
|
|
for(u32 j=0; j<popGroup.GetCount(); j++)
|
|
{
|
|
Assertf(popGroup.GetModelIndex(j) != fwModelId::MI_INVALID, "Model %s in pedgroup %s doesn't exist", popGroup.GetModelName(j).GetCStr(), popGroup.GetName().GetCStr());
|
|
Assertf(popGroup.GetModelIndex(j) == fwModelId::MI_INVALID || CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(popGroup.GetModelIndex(j))))->GetModelType() == MI_TYPE_PED, "Model %s in pedgroup %s is not a ped", popGroup.GetModelName(j).GetCStr(), popGroup.GetName().GetCStr());
|
|
}
|
|
}
|
|
for(u32 i=0; i<m_vehGroups.GetCount(); i++)
|
|
{
|
|
CPopulationGroup& popGroup = m_vehGroups[i];
|
|
for(u32 j=0; j<popGroup.GetCount(); j++)
|
|
{
|
|
Assertf(popGroup.GetModelIndex(j) != fwModelId::MI_INVALID, "Model %s in vehicle group %s doesn't exist", popGroup.GetModelName(j).GetCStr(), popGroup.GetName().GetCStr());
|
|
Assertf(popGroup.GetModelIndex(j) == fwModelId::MI_INVALID || CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(popGroup.GetModelIndex(j))))->GetModelType() == MI_TYPE_VEHICLE, "Model %s in pedgroup %s is not a vehicle", popGroup.GetModelName(j).GetCStr(), popGroup.GetName().GetCStr());
|
|
}
|
|
}
|
|
#endif // __ASSERT
|
|
// setup gang cars for gang car groups
|
|
for (u32 i = 0; i < m_vehGroups.GetCount(); i++)
|
|
{
|
|
if(!GetVehGroup(i).IsFlagSet(POPGROUP_IS_GANG))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
m_vehGroups[i].SetIsEmpty(true);
|
|
for (u32 e = 0; e < GetVehGroup(i).GetCount(); e++)
|
|
{
|
|
u32 mi = GetVehGroup(i).GetModelIndex(e);
|
|
if (!CModelInfo::IsValidModelInfo(mi))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CVehicleModelInfo *pModelInfo = (CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
|
|
|
|
m_vehGroups[i].SetIsEmpty(false);
|
|
|
|
if (pModelInfo->GetGangPopGroup() >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pModelInfo->SetGangPopGroup(i);
|
|
|
|
for (s32 n = 0; n < MAX_VEH_POSSIBLE_COLOURS; n++)
|
|
{
|
|
for (s32 t = 0; t < NUM_VEH_BASE_COLOURS; t++)
|
|
{
|
|
pModelInfo->SetPossibleColours(t, n, pModelInfo->GetPossibleColours(t, n+1));
|
|
}
|
|
}
|
|
pModelInfo->SetNumPossibleColours(pModelInfo->GetNumPossibleColours()-1);
|
|
}
|
|
}
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CPopCycle
|
|
// Purpose : To keep track of the current population disposition, based
|
|
// on CPopSchedules and and CPopGroups.
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : InitLevel
|
|
// Purpose : Inits CPopCycle object
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::Init(unsigned initMode)
|
|
{
|
|
#if __BANK
|
|
float multiplier = 1.0f;
|
|
if(PARAM_maxVehicles.Get())
|
|
CPopAllocation::SetForceMaxCars((u32) CVehicle::GetPool()->GetSize());
|
|
if(PARAM_maxPeds.Get())
|
|
CPopAllocation::SetForceMaxPeds((u32) CPed::GetPool()->GetSize());
|
|
if(PARAM_pedMult.Get(multiplier))
|
|
CPopAllocation::SetMaxPedsMultiplier(multiplier);
|
|
if(PARAM_carMult.Get(multiplier))
|
|
CPopAllocation::SetMaxCarsMultiplier(multiplier);
|
|
#endif
|
|
|
|
Displayf("Intialising CPopCycle...\n");
|
|
|
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::DLC_POP_GROUPS, &g_PopulationDataFileMounter, eDFMI_UnloadFirst);
|
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::POPGRP_FILE_PATCH, &g_PopulationDataFileMounter, eDFMI_UnloadFirst);
|
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::POPSCHED_FILE, &g_PopulationDataFileMounter, eDFMI_UnloadLast);
|
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::ZONEBIND_FILE, &g_PopulationDataFileMounter, eDFMI_UnloadLast);
|
|
|
|
if(initMode == INIT_AFTER_MAP_LOADED)
|
|
{
|
|
// Load the groups before the time cycle data as the
|
|
// time cycle data references the groups.
|
|
ResetPopGroupsAndSchedules();
|
|
LoadPopGroups();
|
|
LoadPopSchedules();
|
|
LoadPopZoneToPopScheduleBindings();
|
|
}
|
|
else if(initMode == INIT_SESSION)
|
|
{
|
|
sm_nCurrentDaySubdivision = 0;
|
|
sm_nCurrentWeekSubdivision = 0;
|
|
sm_pCurrZone = 0;
|
|
sm_nCurrentZoneVehicleResponseType = VEHICLE_RESPONSE_DEFAULT;
|
|
sm_nCurrentZonePopScheduleIndex = -1;
|
|
sm_pCurrPopAllocation = NULL;
|
|
sm_pCurrPopSchedule = NULL;
|
|
sm_nextTimeForUpdate = 0;
|
|
sm_lastTimePedsInCombatCounted = 0;
|
|
|
|
#if !__FINAL
|
|
sm_nCurrentZoneName.Clear();
|
|
sm_bDisplayDebugInfo = false;
|
|
sm_bDisplaySimpleDebugInfo = false;
|
|
sm_bDisplayInVehDeadDebugInfo = false;
|
|
sm_bDisplayInVehNoPretendModelDebugInfo = false;
|
|
sm_bDisplayScenarioDebugInfo = false;
|
|
#endif
|
|
}
|
|
|
|
Displayf("CPopCycle ready\n");
|
|
}
|
|
|
|
#if __BANK
|
|
void CPopCycle::InitWidgets()
|
|
{
|
|
bkBank *bank = CGameLogic::GetGameLogicBank();
|
|
if(!bank)
|
|
{
|
|
Assertf(bank, "Somebody removed the CGameLogic RAG bank.");
|
|
return;
|
|
}
|
|
|
|
bank->PushGroup("Pop Cycle", false);
|
|
bank->AddButton("Reload zonebind.meta", ReloadPopZoneToPopScheduleBindings);
|
|
bank->AddSlider("WANTED minimum multiplier (MP)", &sm_fMinWantedMultiplierMP, 0.0f, 1.0f, 0.01f);
|
|
|
|
bank->AddToggle("Use COMBAT multiplier", &sm_bUseCombatMultiplier);
|
|
bank->AddSlider("COMBAT minimum multiplier", &sm_fPedsInCombatMinPopMultiplier, 0.0, 1.0f, 0.01f);
|
|
bank->AddSlider("COMBAT multiplier (min num peds)", &sm_iMinPedsInCombatForPopMultiplier, 0, 100, 1);
|
|
bank->AddSlider("COMBAT multiplier (max num peds)", &sm_iMaxPedsInCombatForPopMultiplier, 0, 100, 1);
|
|
|
|
bank->AddToggle("Overide ped dirt scale", &sm_enableOverridePedDirtScale);
|
|
bank->AddSlider("Ped dirt scale",&sm_overridePedDirtScale, 0.0f, 1.0f, 0.1f);
|
|
bank->AddToggle("Overide veh dirt scale", &sm_enableOverrideVehDirtScale);
|
|
bank->AddSlider("Veh dirt scale",&sm_overridevehDirtScale, 0.0f, 1.0f, 0.1f);
|
|
bank->AddToggle("Overide dirt color", &sm_enableOverrideDirtColor);
|
|
bank->AddColor("Veh color",&sm_overrideDirtColor);
|
|
|
|
bank->PopGroup();
|
|
}
|
|
#endif // __BANK
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : ShutdownLevel
|
|
// Purpose : Shuts down CPopCycle object
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::Shutdown(unsigned shutdownMode)
|
|
{
|
|
sm_popGroupPatches.Reset();
|
|
|
|
if(shutdownMode == SHUTDOWN_SESSION)
|
|
{
|
|
for(int i=0; i<sm_popSchedules.GetCount(); i++)
|
|
{
|
|
sm_popSchedules.GetSchedule(i).SetOverridePedGroup(0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : Update
|
|
// Purpose : Updates game population cycle
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::Update()
|
|
{
|
|
s32 OldDaySubdivision = sm_nCurrentDaySubdivision;
|
|
s32 OldTimeOfWeek = sm_nCurrentWeekSubdivision;
|
|
s32 OldZoneIndex = sm_nCurrentZonePopScheduleIndex;
|
|
|
|
sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKDAY;
|
|
|
|
/*
|
|
switch (CClock::GetDayOfWeek())
|
|
{
|
|
case Date::SATURDAY:
|
|
sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKEND;
|
|
break;
|
|
case Date::SUNDAY: // Sunday
|
|
if (CClock::GetHour() < 20) sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKEND; else sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKDAY;
|
|
break;
|
|
case Date::MONDAY:// Intentional fall though.
|
|
case Date::TUESDAY:// Intentional fall though.
|
|
case Date::WEDNESDAY:// Intentional fall though.
|
|
case Date::THURSDAY:
|
|
sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKDAY;
|
|
break;
|
|
case Date::FRIDAY: // Friday
|
|
if (CClock::GetHour() < 20) sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKDAY; else sm_nCurrentWeekSubdivision = CPopSchedule::POPCYCLE_WEEKEND;
|
|
break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
*/
|
|
|
|
sm_nCurrentDaySubdivision = CClock::GetHour() / 2;
|
|
|
|
UpdateCurrZone();
|
|
|
|
// If population info has changed we might want to load in new cars/peds etc.
|
|
if (sm_populationChanged ||
|
|
OldDaySubdivision != sm_nCurrentDaySubdivision ||
|
|
OldTimeOfWeek != sm_nCurrentWeekSubdivision ||
|
|
OldZoneIndex != sm_nCurrentZonePopScheduleIndex)
|
|
{
|
|
gPopStreaming.HandlePopCycleInfoChange(OldZoneIndex != sm_nCurrentZonePopScheduleIndex);
|
|
CVehiclePopulation::HandlePopCycleInfoChange();
|
|
sm_populationChanged = false;
|
|
}
|
|
|
|
UpdatePercentages();
|
|
|
|
CountNumPedsInCombat();
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : UpdateCurrZone
|
|
// Purpose : Updates game population zone
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::UpdateCurrZone(void)
|
|
{
|
|
UpdateCurrZoneFromCoors(CFocusEntityMgr::GetMgr().GetPopPos());
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : UpdateCurrZone
|
|
// Purpose : Updates game population zone
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::UpdateCurrZoneFromCoors(const Vector3 &playerPos)
|
|
{
|
|
u32 curTime = fwTimer::GetTimeInMilliseconds();
|
|
const float distSqr = playerPos.Dist2(sm_lastUpdatePos);
|
|
if (curTime < sm_nextTimeForUpdate && distSqr < sm_distanceDeltaForUpdateSqr)
|
|
return;
|
|
|
|
sm_lastUpdatePos = playerPos;
|
|
sm_nextTimeForUpdate = curTime + 1000; // don't update more often than once per second
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
sm_pCurrZone = CPopZones::FindSmallestForPosition(&playerPos, ZONECAT_ANY, ZONETYPE_MP);
|
|
sm_nCurrentZonePopScheduleIndex = (sm_pCurrZone && sm_popSchedules.GetCount() > 0) ? CPopZones::GetPopZonePopScheduleIndexForMP(sm_pCurrZone) : -1;
|
|
}
|
|
else
|
|
{
|
|
sm_pCurrZone = CPopZones::FindSmallestForPosition(&playerPos, ZONECAT_ANY, ZONETYPE_SP);
|
|
sm_nCurrentZonePopScheduleIndex = (sm_pCurrZone && sm_popSchedules.GetCount() > 0) ? CPopZones::GetPopZonePopScheduleIndexForSP(sm_pCurrZone) : -1;
|
|
}
|
|
|
|
sm_nCurrentZoneVehicleResponseType = sm_pCurrZone ? sm_pCurrZone->m_vehicleResponseType : VEHICLE_RESPONSE_DEFAULT;
|
|
|
|
if(sm_nCurrentZonePopScheduleIndex != -1)
|
|
{
|
|
sm_pCurrPopSchedule = &sm_popSchedules.GetSchedule(sm_nCurrentZonePopScheduleIndex);
|
|
sm_pCurrPopAllocation = &sm_pCurrPopSchedule->GetAllocation(sm_nCurrentWeekSubdivision, sm_nCurrentDaySubdivision);
|
|
}
|
|
else
|
|
{
|
|
sm_pCurrPopSchedule = NULL;
|
|
sm_pCurrPopAllocation = NULL;
|
|
}
|
|
|
|
gPopStreaming.SetMaxScenarioPedModelsLoadedPerSlot(sm_pCurrPopAllocation ? sm_pCurrPopAllocation->GetMaxScenarioPedModels() : MAX_NUMBER_STREAMED_SCENARIO_PEDS_PER_SLOT);
|
|
|
|
// Setup the current zone popgroup percentages
|
|
int pedSize = sm_popGroups.GetPedCount();
|
|
for(int i=0; i<pedSize; i++)
|
|
sm_currentPedPercentages[i] = 0;
|
|
int vehSize = sm_popGroups.GetVehCount();
|
|
for(int i=0; i<vehSize; i++)
|
|
sm_currentVehPercentages[i] = 0;
|
|
|
|
// if there is a valid pop allocation fill out percentages array
|
|
if(HasValidCurrentPopAllocation())
|
|
{
|
|
const CPopAllocation& allocation = GetCurrentPopAllocation();
|
|
|
|
// Set up ped group percentages (including overriding)
|
|
u32 overridePedPercentage = sm_pCurrPopSchedule->GetOverridePedPercentage();
|
|
float overridePedMultiplier = (100.0f - (float)overridePedPercentage) / 100.0f;
|
|
int overridePedGroup = sm_pCurrPopSchedule->GetOverridePedGroup();
|
|
u32 totalPedPercentage = 0;
|
|
|
|
for(int i=0; i<allocation.GetNumberOfPedGroups(); i++)
|
|
{
|
|
u32 pedGroupIndex;
|
|
if(sm_popGroups.FindPedGroupFromNameHash(allocation.GetPedGroupPercentage(i).m_GroupName, pedGroupIndex))
|
|
{
|
|
sm_currentPedPercentages[pedGroupIndex] = (u8)(allocation.GetPedGroupPercentage(i).m_percentage * overridePedMultiplier);
|
|
totalPedPercentage += sm_currentPedPercentages[pedGroupIndex];
|
|
}
|
|
}
|
|
if(overridePedPercentage > 0)
|
|
{
|
|
sm_currentPedPercentages[overridePedGroup] += (u8)(100 - totalPedPercentage);
|
|
}
|
|
|
|
// Set up veh group percentages (including overriding)
|
|
u32 overrideVehPercentage = sm_pCurrPopSchedule->GetOverrideVehPercentage();
|
|
float overrideVehMultiplier = (100.0f - (float)overrideVehPercentage) / 100.0f;
|
|
int overrideVehGroup = sm_pCurrPopSchedule->GetOverrideVehGroup();
|
|
u32 totalVehPercentage = 0;
|
|
|
|
for(int i=0; i<allocation.GetNumberOfVehGroups(); i++)
|
|
{
|
|
u32 vehGroupIndex;
|
|
if(sm_popGroups.FindVehGroupFromNameHash(allocation.GetVehGroupPercentage(i).m_GroupName, vehGroupIndex))
|
|
{
|
|
if (sm_popGroups.GetVehGroup(vehGroupIndex).IsEmpty())
|
|
{
|
|
sm_currentVehPercentages[vehGroupIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
sm_currentVehPercentages[vehGroupIndex] = (u8)(allocation.GetVehGroupPercentage(i).m_percentage * overrideVehMultiplier);
|
|
totalVehPercentage += sm_currentVehPercentages[vehGroupIndex];
|
|
}
|
|
}
|
|
}
|
|
if(overrideVehPercentage > 0)
|
|
{
|
|
sm_currentVehPercentages[overrideVehGroup] += (u8)(100 - totalVehPercentage);
|
|
}
|
|
}
|
|
|
|
#if !__FINAL
|
|
CPopZone *currentActiveZone = GetCurrentActiveZone();
|
|
if (currentActiveZone) sm_nCurrentZoneName = currentActiveZone->m_id;
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : Display
|
|
// Purpose : Displays game population cycle debug data
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::Display()
|
|
{
|
|
|
|
#if DEBUG_DRAW
|
|
CPopZone *currentActiveZone = GetCurrentActiveZone();
|
|
|
|
if (currentActiveZone)
|
|
{
|
|
if (sm_bDisplayDebugInfo || sm_bDisplayCarDebugInfo)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Current Zone (%s) popsched: (%s) ",
|
|
sm_nCurrentZoneName.TryGetCStr(), sm_popSchedules.GetNameFromIndex(sm_nCurrentZonePopScheduleIndex));
|
|
|
|
grcDebugDraw::AddDebugOutput("Current Day Subdivision:%d", sm_nCurrentDaySubdivision);
|
|
|
|
if (CCullZones::FewerPeds())
|
|
{
|
|
grcDebugDraw::AddDebugOutput("FEWER PEDS SET IN THIS AREA");
|
|
}
|
|
|
|
grcDebugDraw::AddDebugOutput("PercCopsCars:%d PercCopsPeds:%d NumAmbientPeds:%d NumScenarioPeds:%d", GetCurrentDesiredPercentageOfCopsCars(), GetCurrentDesiredPercentageOfCopsPeds(), (s32)(GetCurrentMaxNumAmbientPeds() * CalcWeatherMultiplier() * CPopCycle::CalcHighWayPedMultiplier()), (s32)(GetCurrentMaxNumScenarioPeds() * CalcWeatherMultiplier() * CPopCycle::CalcHighWayPedMultiplier()));
|
|
|
|
if (CalcWeatherMultiplier() < 0.99f || CalcHighWayPedMultiplier() < 0.9f || CPopCycle::GetInteriorPedMultiplier() < 0.99f || CPopCycle::GetWantedPedMultiplier() < 0.99f || CPopCycle::GetCombatPedMultiplier() < 0.99f || CPedPopulation::GetAmbientPedDensityMultiplier() < 0.99f)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("RANDOM MULTIPLIERS (Weather:%.2f HighWay:%.2f Interior:%.2f Wanted:%.2f Combat:%.2f AmbientPedDensity:%.2f)",
|
|
CalcWeatherMultiplier(),
|
|
CalcHighWayPedMultiplier(),
|
|
CPopCycle::GetInteriorPedMultiplier(),
|
|
CPopCycle::GetWantedPedMultiplier(),
|
|
CPopCycle::GetCombatPedMultiplier(),
|
|
CPedPopulation::GetAmbientPedDensityMultiplier());
|
|
}
|
|
|
|
float fInteriorMult = 1.0f;
|
|
float fExteriorMult = 1.0f;
|
|
CPedPopulation::GetTotalScenarioPedDensityMultipliers( fInteriorMult, fExteriorMult );
|
|
if(fInteriorMult < 0.99f || fExteriorMult < 0.99f)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("ScenarioPed Interior Mult:%.2f, ScenarioPed Exterior Mult:%.2f", fInteriorMult, fExteriorMult);
|
|
}
|
|
|
|
// Handle outputting group percentages.
|
|
if (HasValidCurrentPopAllocation())
|
|
{
|
|
bool lineHandled = false;
|
|
char lineBuff[1024];
|
|
lineBuff[0] = 0;
|
|
for(u32 i=0; i<sm_currentPedPercentages.GetCount(); i++)
|
|
{
|
|
if(sm_currentPedPercentages[i] > 0)
|
|
{
|
|
char itemBuff[128];
|
|
itemBuff[0] = 0;
|
|
|
|
lineHandled = false;
|
|
sprintf(itemBuff, "%s:%d", GetPopGroups().GetPedGroup(i).GetName().GetCStr(), sm_currentPedPercentages[i]);
|
|
strcat(lineBuff, itemBuff);
|
|
itemBuff[0] = 0;
|
|
if((i+1)%7 == 0)
|
|
{
|
|
grcDebugDraw::AddDebugOutput(lineBuff);
|
|
lineBuff[0] = 0;
|
|
lineHandled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!lineHandled)
|
|
{
|
|
grcDebugDraw::AddDebugOutput(lineBuff);
|
|
lineBuff[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(sm_bDisplayScenarioDebugInfo)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("");
|
|
int limit = gPopStreaming.GetMaxScenarioPedModelsLoadedPerSlot();
|
|
const int override = gPopStreaming.GetMaxScenarioPedModelsLoadedOverride();
|
|
const char* overrideStr = "";
|
|
if(override >= 0)
|
|
{
|
|
limit = override;
|
|
overrideStr = " [script override]";
|
|
}
|
|
|
|
fwArchetypeDynamicFactory<CPedModelInfo>& pedModelInfoStore = CModelInfo::GetPedModelInfoStore();
|
|
atArray<CPedModelInfo*> pedTypeArray;
|
|
pedModelInfoStore.GatherPtrs(pedTypeArray);
|
|
|
|
//Iterate over each of the peds, pushing the debug info into an array of character arrays based on streaming type.
|
|
atRangeArray<s16, NUM_SCENARIO_POP_SLOTS> aNumFound;
|
|
atRangeArray<atArray<ConstString>, NUM_SCENARIO_POP_SLOTS> aPedStrings;
|
|
for(unsigned int i=0; i < aNumFound.GetMaxCount(); i++)
|
|
{
|
|
aNumFound[i] = 0;
|
|
}
|
|
for(int i = 0; i < pedTypeArray.GetCount(); i++)
|
|
{
|
|
const CPedModelInfo* modelInfo = pedTypeArray[i];
|
|
if(!modelInfo)
|
|
{
|
|
continue;
|
|
}
|
|
if(!modelInfo->GetCountedAsScenarioPed())
|
|
{
|
|
continue;
|
|
}
|
|
eScenarioPopStreamingSlot eStreamSlot = modelInfo->GetScenarioPedStreamingSlot();
|
|
|
|
// Is this really the best way to get an fwModelId if we have a CPedModelInfo?
|
|
fwModelId pedModelId;
|
|
CModelInfo::GetBaseModelInfoFromHashKey(modelInfo->GetModelNameHash(), &pedModelId);
|
|
|
|
const bool loaded = CModelInfo::HaveAssetsLoaded(pedModelId);
|
|
const bool deletable = CModelInfo::GetAssetsAreDeletable(pedModelId);
|
|
const bool requested = !loaded && CModelInfo::AreAssetsRequested(pedModelId);
|
|
|
|
const char* name = modelInfo->GetModelName();
|
|
char pedDebugLine[1024];
|
|
// Print the debug info into a temp buffer and push it into the ped debug array.
|
|
formatf(pedDebugLine, " %-32s%s%s%s", name, loaded ? " loaded" : "", deletable ? " deletable" : "", requested ? " requested" : "");
|
|
aPedStrings[eStreamSlot].PushAndGrow(ConstString(pedDebugLine));
|
|
aNumFound[eStreamSlot]++;
|
|
}
|
|
#if !__FINAL
|
|
for(unsigned int i=0; i < aPedStrings.GetMaxCount(); i++)
|
|
{
|
|
//Draw label for the different scenario streaming types.
|
|
grcDebugDraw::AddDebugOutputEx(false, "%s (%d / %d%s):", CPopulationStreaming::GetScenarioSlotName((eScenarioPopStreamingSlot)i), gPopStreaming.GetNumScenarioPedModelsLoaded((eScenarioPopStreamingSlot)i), limit, overrideStr);
|
|
//Draw debug info for each ped inside the array associated with that streaming type.
|
|
for(int j=0; j < aPedStrings[i].GetCount(); j++)
|
|
{
|
|
grcDebugDraw::AddDebugOutputEx(false, aPedStrings[i][j].c_str());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Make sure that the number of peds found is in fact equal to the number of peds streamed in.
|
|
for(unsigned int i=0; i < aNumFound.GetMaxCount(); i++)
|
|
{
|
|
Assert(aNumFound[i] == gPopStreaming.GetNumScenarioPedModelsLoaded((eScenarioPopStreamingSlot)i));
|
|
}
|
|
}
|
|
|
|
if (sm_bDisplayDebugInfo || sm_bDisplaySimpleDebugInfo)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("");
|
|
grcDebugDraw::AddDebugOutput("We want (%d), [desired] Cops:%d Ambient:%d Scenario:%d",
|
|
GetCurrentMaxNumCopPeds()+GetCurrentMaxNumAmbientPeds()+GetCurrentMaxNumScenarioPeds(),
|
|
sm_currentMaxNumCopPeds,
|
|
sm_currentMaxNumAmbientPeds,
|
|
sm_currentMaxNumScenarioPeds);
|
|
|
|
int nRealPeds = CPed::GetPool()->GetNoOfUsedSpaces();
|
|
|
|
// Remove any playerpeds as they're not tallied in the breakdown.
|
|
int playerPedCount = 0;
|
|
CPed::Pool* pPool = CPed::GetPool();
|
|
for(int i=0;i<pPool->GetSize();i++)
|
|
{
|
|
CPed* pPed = pPool->GetSlot(i);
|
|
if( pPed )
|
|
{
|
|
if(pPed->IsPlayer())
|
|
{
|
|
playerPedCount++;
|
|
}
|
|
}
|
|
}
|
|
nRealPeds -= playerPedCount;
|
|
|
|
char szModeString[128];
|
|
if( CPerformance::GetInstance()->EstimateLowPerformance() )
|
|
{
|
|
sprintf(szModeString, "Reduced(");
|
|
if( CPerformance::GetInstance()->GetLowPerformanceFlags() & CPerformance::PF_MissionFlag )
|
|
strcat(szModeString, "Mission " );
|
|
if( CPerformance::GetInstance()->GetLowPerformanceFlags() & CPerformance::PF_PlayerSpeed )
|
|
strcat(szModeString, "Speed " );
|
|
if( CPerformance::GetInstance()->GetLowPerformanceFlags() & CPerformance::PF_WantedLevel )
|
|
strcat(szModeString, "WLevel " );
|
|
strcat(szModeString, ")" );
|
|
|
|
}
|
|
else
|
|
{
|
|
szModeString[0] = 0;
|
|
}
|
|
|
|
grcDebugDraw::AddDebugOutput("We have (%d/%d), [onFoot/InVeh] Cops:%d/%d, [onFoot/InVeh] Swat:%d/%d Ambient:%d/%d Scenario:%d/%d Other:%d/%d (Real:%d) (%s)",
|
|
(nRealPeds),
|
|
CPed::GetPool()->GetSize(),
|
|
CPedPopulation::ms_nNumOnFootCop, CPedPopulation::ms_nNumInVehCop,
|
|
CPedPopulation::ms_nNumOnFootSwat, CPedPopulation::ms_nNumInVehSwat,
|
|
CPedPopulation::ms_nNumOnFootAmbient,CPedPopulation::ms_nNumInVehAmbient,
|
|
CPedPopulation::ms_nNumOnFootScenario,CPedPopulation::ms_nNumInVehScenario,
|
|
CPedPopulation::ms_nNumOnFootOther, CPedPopulation::ms_nNumInVehOther,
|
|
nRealPeds, szModeString);
|
|
|
|
grcDebugDraw::AddDebugOutput("Peds Pending Deletion: %u", CPedPopulation::GetPedsPendingDeletion());
|
|
|
|
if(!sm_bDisplaySimpleDebugInfo)
|
|
{
|
|
gPopStreaming.PrintDebugForPeds();
|
|
}
|
|
}
|
|
|
|
if( sm_bDisplayInVehDeadDebugInfo )
|
|
{
|
|
grcDebugDraw::AddDebugOutput("PedsInVehAmbientDead: %d", CPedPopulation::ms_nNumInVehAmbientDead);
|
|
}
|
|
|
|
if( sm_bDisplayInVehNoPretendModelDebugInfo )
|
|
{
|
|
grcDebugDraw::AddDebugOutput("PedsInVehAmbientNoPretendModel: %d", CPedPopulation::ms_nNumInVehAmbientNoPretendModel);
|
|
}
|
|
|
|
if (sm_bDisplayCarDebugInfo || sm_bDisplayCarSimpleDebugInfo)
|
|
{
|
|
// Count the number of cars in the world.
|
|
s32 MissionCars = 0, OtherCars = 0, ParkedCars = 0, ParkedMissionCars = 0;
|
|
CVehicle::Pool& VehPool = *CVehicle::GetPool();
|
|
CVehicle* pVehicle;
|
|
s32 i = (s32) VehPool.GetSize();
|
|
while(i--)
|
|
{
|
|
pVehicle = VehPool.GetSlot(i);
|
|
if (pVehicle)
|
|
{
|
|
if (pVehicle->InheritsFromBike() ||
|
|
pVehicle->InheritsFromAutomobile() ||
|
|
pVehicle->InheritsFromBoat() ||
|
|
pVehicle->InheritsFromTrain())
|
|
{
|
|
if (pVehicle->PopTypeIsMission())
|
|
{
|
|
if (pVehicle->m_nVehicleFlags.bCountAsParkedCarForPopulation)
|
|
{
|
|
ParkedMissionCars++;
|
|
}
|
|
else
|
|
{
|
|
MissionCars++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pVehicle->PopTypeGet() == POPTYPE_RANDOM_PARKED)
|
|
{
|
|
ParkedCars++;
|
|
}
|
|
else
|
|
{
|
|
OtherCars++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CCullZones::FewerCars())
|
|
{
|
|
grcDebugDraw::AddDebugOutput("FEWER CARS SET IN THIS AREA");
|
|
}
|
|
|
|
const float fPercentage = CPopCycle::GetCurrentPercentageOfMaxCars();
|
|
|
|
int iVehiclesUpperLimit;
|
|
float fDesertedStreetsMult, fInteriorMult, fWantedMult, fCombatMult, fHighwayMult, fBudgetMult;
|
|
float fDesiredNumAmbientVehicles = CVehiclePopulation::GetDesiredNumberAmbientVehicles(&iVehiclesUpperLimit, &fDesertedStreetsMult, &fInteriorMult, &fWantedMult, &fCombatMult, &fHighwayMult, &fBudgetMult);
|
|
|
|
grcDebugDraw::AddDebugOutput("TOTAL num cars in pool : %d / %d", MissionCars + ParkedCars + OtherCars, CVehicle::GetPool()->GetSize());
|
|
grcDebugDraw::AddDebugOutput("Num AMBIENT cars desired : (%d) popcycle=%i%% (%i x %f x %f x %f x %f x %f x %f)"
|
|
,CVehiclePopulation::ms_overrideNumberOfCars != -1 ? CVehiclePopulation::ms_overrideNumberOfCars : (int)fDesiredNumAmbientVehicles
|
|
,(s32) fPercentage
|
|
,iVehiclesUpperLimit
|
|
,fDesertedStreetsMult
|
|
,fInteriorMult
|
|
,fWantedMult
|
|
,fCombatMult
|
|
,fHighwayMult
|
|
,fBudgetMult);
|
|
grcDebugDraw::AddDebugOutput("Num AMBIENT cars we have : (%d)", OtherCars);
|
|
grcDebugDraw::AddDebugOutput("Num PARKED cars we have : (%d) (of which %d are low prio)", ParkedCars, CVehiclePopulation::ms_numLowPrioParkedCars);
|
|
grcDebugDraw::AddDebugOutput("Num MISSION cars we have : (%d)", MissionCars);
|
|
grcDebugDraw::AddDebugOutput("Num cars in cargen queue : (%d)", CTheCarGenerators::GetNumQueuedVehs());
|
|
grcDebugDraw::AddDebugOutput("Num entries in automobile placement queue: (%d)", CAutomobile::GetAsyncQueueCount());
|
|
|
|
float areaMultiplier = CThePopMultiplierAreas::GetTrafficDensityMultiplier(CGameWorld::FindLocalPlayerCoors());
|
|
grcDebugDraw::AddDebugOutput("Area density multiplier: %.2f", areaMultiplier);
|
|
|
|
char buf[256] = {0};
|
|
char item[64] = {0};
|
|
for (s32 i = 0; i < CTheCarGenerators::GetPreassignedModels().GetCount(); ++i)
|
|
{
|
|
fwModelId mi((strLocalIndex(CTheCarGenerators::GetPreassignedModels()[i])));
|
|
if (mi.IsValid())
|
|
{
|
|
CBaseModelInfo* bmi = CModelInfo::GetBaseModelInfo(mi);
|
|
formatf(item, " %s", bmi->GetModelName());
|
|
safecat(buf, item);
|
|
}
|
|
}
|
|
grcDebugDraw::AddDebugOutput("Preassigned cargen models: (%d/%d)%s", CTheCarGenerators::GetPreassignedModels().GetCount(), GetMaxCargenModels(), buf);
|
|
|
|
buf[0] = '\0';
|
|
const atArray<u32>& scenarioModels = CTheCarGenerators::GetScenarioModels();
|
|
for(int i = 0; i < scenarioModels.GetCount(); i++)
|
|
{
|
|
fwModelId mi((strLocalIndex(scenarioModels[i])));
|
|
if (mi.IsValid())
|
|
{
|
|
CBaseModelInfo* bmi = CModelInfo::GetBaseModelInfo(mi);
|
|
formatf(item, " %s", bmi->GetModelName());
|
|
safecat(buf, item);
|
|
}
|
|
}
|
|
grcDebugDraw::AddDebugOutput("Scenario models: (%d/%d)%s", scenarioModels.GetCount(), GetMaxScenarioVehicleModels(), buf);
|
|
|
|
#if !__FINAL
|
|
grcDebugDraw::AddDebugOutput("ms_numGenerationLinks : (%i/%i) used, ms_iNumActiveLinks : %i. Range scale = %.1f",
|
|
CVehiclePopulation::GetNumGenerationLinks(),
|
|
MAX_GENERATION_LINKS,
|
|
CVehiclePopulation::GetNumActiveLinks(),
|
|
CVehiclePopulation::GetPopulationRangeScale() );
|
|
#endif
|
|
|
|
if (sm_bDisplayCarDebugInfo)
|
|
{
|
|
if (CVehiclePopulation::HasInterestingVehicle())
|
|
{
|
|
u32 timeLeft = CVehiclePopulation::GetInterestingVehicleClearTime() - fwTimer::GetTimeInMilliseconds();
|
|
u32 secs = timeLeft / 1000;
|
|
u32 mins = secs / 60;
|
|
secs = secs - (mins * 60);
|
|
grcDebugDraw::AddDebugOutput("Time until interesting vehicle is cleared: %2dm%2ds", mins, secs);
|
|
}
|
|
|
|
if (CVehiclePopulation::GetDesertedStreetsMultiplier() < 0.999f)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Deserted streets multiplier: %f", CVehiclePopulation::GetDesertedStreetsMultiplier());
|
|
}
|
|
if (CPopCycle::GetInteriorCarMultiplier() < 0.999f || CPopCycle::GetWantedCarMultiplier() < 0.99f || CPopCycle::GetCombatCarMultiplier() < 0.99f)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Interior car multiplier:%.2f WantedCarMult:%.2f CombatCarMult:%.2f", CPopCycle::GetInteriorCarMultiplier(), CPopCycle::GetWantedCarMultiplier(), CPopCycle::GetCombatCarMultiplier());
|
|
}
|
|
if (CPopCycle::GetTemporaryMaxNumScenarioPeds() < 1000 || CPopCycle::GetTemporaryMaxNumAmbientPeds() < 1000 || CPopCycle::GetTemporaryMaxNumCars() < 1000)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("TemporaryMaxNumScenarioPeds:%d TemporaryMaxNumAmbientPeds:%d TemporaryMaxNumCars:%d", CPopCycle::GetTemporaryMaxNumScenarioPeds(), CPopCycle::GetTemporaryMaxNumAmbientPeds(), CPopCycle::GetTemporaryMaxNumCars());
|
|
}
|
|
|
|
// Handle outputting group percentages.
|
|
if (HasValidCurrentPopAllocation())
|
|
{
|
|
CLoadedModelGroup loadedVehs;
|
|
loadedVehs.Merge(&gPopStreaming.GetAppropriateLoadedCars(), &gPopStreaming.GetLoadedBoats());
|
|
const s32 numCars = loadedVehs.CountMembers();
|
|
|
|
bool lineHandled = false;
|
|
char lineBuff[1024];
|
|
lineBuff[0] = 0;
|
|
const u32 vehGroupCount = GetCurrentPopAllocation().GetNumberOfVehGroups();
|
|
for(u32 i = 0; i < vehGroupCount; ++i)
|
|
{
|
|
lineHandled = false;
|
|
|
|
float currentPercentage = 0.f;
|
|
u32 vehGroupIndex;
|
|
if(sm_popGroups.FindVehGroupFromNameHash(GetCurrentPopAllocation().GetVehGroupPercentage(i).m_GroupName, vehGroupIndex))
|
|
{
|
|
s32 totalModels = 0;
|
|
for (s32 m = 0; m < numCars; m++)
|
|
{
|
|
u32 model = loadedVehs.GetMember(m);
|
|
fwModelId modelId((strLocalIndex(model)));
|
|
if (!CModelInfo::GetAssetsAreDeletable(modelId))
|
|
{
|
|
totalModels++;
|
|
if (GetPopGroups().IsVehIndexMember(vehGroupIndex, model))
|
|
{
|
|
currentPercentage += 1.f;
|
|
}
|
|
}
|
|
}
|
|
currentPercentage /= rage::Max(1, totalModels);
|
|
}
|
|
|
|
char itemBuff[255];
|
|
itemBuff[0] = 0;
|
|
|
|
u32 percent = GetCurrentPopAllocation().GetVehGroupPercentage(i).m_percentage;
|
|
const char* pGroupName = GetCurrentPopAllocation().GetVehGroupPercentage(i).m_GroupName.GetCStr();
|
|
sprintf(itemBuff, "%s:%d(%.2f) ", pGroupName, percent, currentPercentage);
|
|
|
|
strcat(lineBuff, itemBuff);
|
|
itemBuff[0] = 0;
|
|
|
|
if((i+1)%7 == 0)
|
|
{
|
|
grcDebugDraw::AddDebugOutput(lineBuff);
|
|
lineBuff[0] = 0;
|
|
lineHandled = true;
|
|
}
|
|
}
|
|
if(!lineHandled)
|
|
{
|
|
grcDebugDraw::AddDebugOutput(lineBuff);
|
|
lineBuff[0] = 0;
|
|
}
|
|
}
|
|
|
|
grcDebugDraw::AddDebugOutput("CVehiclePopulation::ms_NumRandomCars:%d ms_NumMissionCars:%d", CVehiclePopulation::ms_numRandomCars, CVehiclePopulation::ms_numMissionCars);
|
|
|
|
s32 maxParkedCars = GetCurrentMaxNumParkedCars();
|
|
s32 maxLowPrioCars = GetCurrentMaxNumLowPrioParkedCars();
|
|
grcDebugDraw::AddDebugOutput("Parked Cars we want(lowprio):%d(%d) We have(lowprio):%d(%d) (Parked:%d ParkedMission:%d)",
|
|
maxParkedCars, maxLowPrioCars, ParkedCars + ParkedMissionCars, CVehiclePopulation::ms_numLowPrioParkedCars, ParkedCars, ParkedMissionCars);
|
|
|
|
gPopStreaming.PrintDebugForVehicles();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
if(sm_bDisplayCarPopulationFailureCounts)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Car population failure reasons");
|
|
grcDebugDraw::AddDebugOutput("--------------------------");
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNoCreationPosFound?Color_red:Color_white, "No creation pos %d", CVehiclePopulation::m_populationFailureData.m_numNoCreationPosFound);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNoLinkBetweenNodes?Color_red:Color_white, "No link between nodes %d", CVehiclePopulation::m_populationFailureData.m_numNoLinkBetweenNodes);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numChosenModelNotLoaded?Color_red:Color_white, "Chosen car model not loaded %d", CVehiclePopulation::m_populationFailureData.m_numChosenModelNotLoaded);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numSpawnLocationInViewFrustum?Color_red:Color_white, "In view frustum %d", CVehiclePopulation::m_populationFailureData.m_numSpawnLocationInViewFrustum);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numSingleTrackRejection?Color_red:Color_white, "Single track rejection %d", CVehiclePopulation::m_populationFailureData.m_numSingleTrackRejection);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numTrajectoryOfMarkedVehicleRejection?Color_red:Color_white, "Trajectory Collision Rejection %d", CVehiclePopulation::m_populationFailureData.m_numTrajectoryOfMarkedVehicleRejection);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNoVehModelFound?Color_red:Color_white, "No car model %d", CVehiclePopulation::m_populationFailureData.m_numNoVehModelFound);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNetworkPopulationFail?Color_red:Color_white, "Network population %d", CVehiclePopulation::m_populationFailureData.m_numNetworkPopulationFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numVehsGoingAgainstTrafficFlow?Color_red:Color_white, "Going against traffic flow %d", CVehiclePopulation::m_populationFailureData.m_numVehsGoingAgainstTrafficFlow);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numDeadEndsChosen?Color_red:Color_white, "Dead end chosen %d", CVehiclePopulation::m_populationFailureData.m_numDeadEndsChosen);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNoPathNodesAvailable?Color_red:Color_white, "No path nodes available %d", CVehiclePopulation::m_populationFailureData.m_numNoPathNodesAvailable);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numGroundPlacementFail?Color_red:Color_white, "Ground placement %d", CVehiclePopulation::m_populationFailureData.m_numGroundPlacementFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numGroundPlacementSpecialFail?Color_red:Color_white, "Ground placement (special) %d", CVehiclePopulation::m_populationFailureData.m_numGroundPlacementFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNetworkVisibilityFail?Color_red:Color_white, "Network visibility %d", CVehiclePopulation::m_populationFailureData.m_numNetworkVisibilityFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numPointWithinCreationDistFail?Color_red:Color_white, "Point within creation distance %d", CVehiclePopulation::m_populationFailureData.m_numPointWithinCreationDistFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numPointWithinCreationDistSpecialFail?Color_red:Color_white, "Point within creation distance (special) %d", CVehiclePopulation::m_populationFailureData.m_numPointWithinCreationDistSpecialFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numRelativeMovementFail?Color_red:Color_white, "Relative movement %d", CVehiclePopulation::m_populationFailureData.m_numRelativeMovementFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numBoundingBoxFail?Color_red:Color_white, "Bounding box %d", CVehiclePopulation::m_populationFailureData.m_numBoundingBoxFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numBoundingBoxSpecialFail?Color_red:Color_white, "Bounding box (special) %d", CVehiclePopulation::m_populationFailureData.m_numBoundingBoxSpecialFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numVehicleCreateFail?Color_red:Color_white, "Vehicle creation %d", CVehiclePopulation::m_populationFailureData.m_numVehicleCreateFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numDriverAddFail?Color_red:Color_white, "Adding a driver %d", CVehiclePopulation::m_populationFailureData.m_numDriverAddFail);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numFallbackPedNotAvailable?Color_red:Color_white, "No fallback ped %d", CVehiclePopulation::m_populationFailureData.m_numFallbackPedNotAvailable);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numPopulationIsFull?Color_red:Color_white, "Population is full %d", CVehiclePopulation::m_populationFailureData.m_numPopulationIsFull);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNodesSwitchedOff?Color_red:Color_white, "Nodes switched off %d", CVehiclePopulation::m_populationFailureData.m_numNodesSwitchedOff);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numIdenticalModelTooClose?Color_red:Color_white, "Identical model too close %d", CVehiclePopulation::m_populationFailureData.m_numIdenticalModelTooClose);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numNotNetworkTurnToCreateVehicleAtPos?Color_red:Color_white, "Not network turn to create vehicle at pos %d", CVehiclePopulation::m_populationFailureData.m_numNotNetworkTurnToCreateVehicleAtPos);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_sameVehicleModelAsLastTime?Color_red:Color_white, "Same vehicle model as last time %d", CVehiclePopulation::m_populationFailureData.m_sameVehicleModelAsLastTime);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numChosenBoatModelNotLoaded?Color_red:Color_white, "Chosen boat model not loaded %d", CVehiclePopulation::m_populationFailureData.m_numChosenBoatModelNotLoaded);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numRandomBoatsNotAllowed?Color_red:Color_white, "Random boats not allowed %d", CVehiclePopulation::m_populationFailureData.m_numRandomBoatsNotAllowed);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numBoatAlreadyHere?Color_red:Color_white, "Boat already here %d", CVehiclePopulation::m_populationFailureData.m_numBoatAlreadyHere);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numWaterLevelFuckup?Color_red:Color_white, "Water level fuckup %d", CVehiclePopulation::m_populationFailureData.m_numWaterLevelFuckup);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numLinkDirectionInvalid?Color_red:Color_white, "Link Direction Invalid %d", CVehiclePopulation::m_populationFailureData.m_numLinkDirectionInvalid);
|
|
grcDebugDraw::AddDebugOutput(CVehiclePopulation::m_populationFailureData.m_numOutOfLocalPlayerScope? Color_red : Color_white, "Out of local player scope %d", CVehiclePopulation::m_populationFailureData.m_numOutOfLocalPlayerScope);
|
|
|
|
if (CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning())
|
|
grcDebugDraw::AddDebugOutput("Blocked by cutscene");
|
|
|
|
#if GTA_REPLAY
|
|
if( CReplayMgr::IsEditModeActive())
|
|
grcDebugDraw::AddDebugOutput("Blocked by replay edit mode");
|
|
#endif
|
|
|
|
if(fwTimer::IsGamePaused())
|
|
grcDebugDraw::AddDebugOutput("Blocked by game pause");
|
|
|
|
if(CVehiclePopulation::ms_bGenerationSkippedDueToNoLinksWithDisparity)
|
|
grcDebugDraw::AddDebugOutput("Blocked by no links with disparity");
|
|
}
|
|
|
|
if(sm_bDisplayPedPopulationFailureCounts)
|
|
{
|
|
grcDebugDraw::AddDebugOutput("Ped population failure reasons");
|
|
grcDebugDraw::AddDebugOutput("--------------------------");
|
|
|
|
for( int i = 0; i < CPedPopulation::PedPopulationFailureData::FR_Max; i++ )
|
|
{
|
|
CPedPopulation::PedPopulationFailureData::FailureInfo* pFailureInfo = CPedPopulation::ms_populationFailureData.GetFailureEnumDebugInfo((CPedPopulation::PedPopulationFailureData::FailureType)i);
|
|
if( pFailureInfo )
|
|
{
|
|
Color32 colour = pFailureInfo->m_colour;
|
|
if( !pFailureInfo->m_bActive )
|
|
{
|
|
colour = Color_grey;
|
|
}
|
|
else if( pFailureInfo->m_iCount == 0 )
|
|
{
|
|
colour = Color_white;
|
|
}
|
|
grcDebugDraw::AddDebugOutput(colour, "%s %d", pFailureInfo->m_szName, pFailureInfo->m_iCount);
|
|
}
|
|
}
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
grcDebugDraw::AddDebugOutput("MP Visibility Failure Count: %d", CPedPopulation::GetMPVisibilityFailureCount());
|
|
}
|
|
}
|
|
#endif // __DEV
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
|
|
static const u32 MaxPopCycleGroups = 250;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : FindNewPedType
|
|
// Purpose : This function will return the modelindex of the next ped to create.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool CPopCycle::FindNewPedModelIndex(u32 *pNewPedModelIndex, bool bNoCops, bool forceCivPedCreation, u32 requiredPopGroupFlags, CPopCycleConditions& conditions, const CAmbientModelVariations*& pVariationsOut, const Vector3* pos)
|
|
{
|
|
CPopZone *currentActiveZone = GetCurrentActiveZone();
|
|
if (!currentActiveZone) return false;
|
|
|
|
if(currentActiveZone->m_id.GetHash() != sm_CurrentPopZoneId)
|
|
{
|
|
// popzone has changed
|
|
sm_CurrentPopZoneId = currentActiveZone->m_id;
|
|
sm_PopulationRecalculateCountdown = 0;
|
|
sm_CachedMostWantedPopGroup = MaxPopCycleGroups;
|
|
}
|
|
|
|
// Determine the shortage of other peds if any.
|
|
s32 ambientShortage = sm_currentMaxNumAmbientPeds - CPedPopulation::ms_nNumOnFootAmbient;
|
|
|
|
// Determine the shortage of cops if any.
|
|
s32 copShortage = sm_currentMaxNumCopPeds - CPedPopulation::ms_nNumOnFootCop;
|
|
if(bNoCops
|
|
|| !CPedPopulation::GetAllowCreateRandomCops()
|
|
|| (currentActiveZone->m_bNoCops)
|
|
|| (CGameWorld::FindLocalPlayerWanted() ? CGameWorld::FindLocalPlayerWanted()->GetWantedLevel() > WANTED_CLEAN : false) // Don't want to create peds on foot with wanted level. They show up on radar.
|
|
|| MI_PED_COP == fwModelId::MI_INVALID) // If the cop model doesn't exist, we would otherwise crash on the HasObjectLoaded().
|
|
{
|
|
copShortage = 0;
|
|
}
|
|
|
|
// DEBUG!! -AC, Just here for testing...
|
|
if(forceCivPedCreation)
|
|
{
|
|
ambientShortage = 1;
|
|
copShortage = 0;
|
|
}
|
|
#if DEBUG_DRAW
|
|
if( CPedPopulation::ms_nNumOnFootCop < 30 &&
|
|
CPedDebugVisualiserMenu::GetForceAllCops())
|
|
{
|
|
ambientShortage = 0;
|
|
copShortage = 1;
|
|
}
|
|
#endif // !__FINAL
|
|
// END DEBUG!!
|
|
|
|
// Determine what ped type to create if any.
|
|
if((copShortage <= 0) && (ambientShortage <= 0))
|
|
{
|
|
// We have enough of everything. Don't create a new ped.
|
|
return false;
|
|
}
|
|
else if (copShortage >= ambientShortage)
|
|
{
|
|
// We need more cops.
|
|
*pNewPedModelIndex = fwModelId::MI_INVALID;
|
|
|
|
if(CModelInfo::GetStreamingModule()->HasObjectLoaded(strLocalIndex(MI_PED_COP)))
|
|
{
|
|
*pNewPedModelIndex = MI_PED_COP;
|
|
}
|
|
|
|
if( *pNewPedModelIndex == fwModelId::MI_INVALID)
|
|
{
|
|
// If modelIndex == fwModelId::MI_INVALID we haven't succeeded.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else // OtherShortage > copShortage
|
|
{
|
|
// We need more civilians.
|
|
// Work out which new ped model we'd like to load.
|
|
u32 popGroupCount = MaxPopCycleGroups;
|
|
u32 wantedGroup = ~0U;
|
|
// Check for a previously cached value
|
|
if(sm_CachedMostWantedPopGroup < popGroupCount && sm_PopulationRecalculateCountdown > 0)
|
|
{
|
|
// used cached pop group
|
|
wantedGroup = sm_CachedMostWantedPopGroup;
|
|
sm_PopulationRecalculateCountdown--;
|
|
}
|
|
|
|
if(wantedGroup >= popGroupCount)
|
|
{
|
|
// Recalculate the wanted group
|
|
float aRequestedPercentages[MaxPopCycleGroups] = {0};
|
|
float aPercentages[MaxPopCycleGroups] = {0};
|
|
u32 aTimesUsed[MaxPopCycleGroups] = {0};
|
|
|
|
if(HasValidCurrentPopAllocation())
|
|
{
|
|
Assertf(sm_popGroups.GetPedCount() <= MaxPopCycleGroups, "CPopCycle::FindNewPedModelIndex - popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetPedCount());
|
|
|
|
u32 popGroupIndex, totalModels = 0;
|
|
|
|
// init percentages
|
|
float probSum = 0.0f;
|
|
|
|
bool bSpawnMoreAquaticPeds = CWildlifeManager::GetInstance().ShouldSpawnMoreAquaticPeds();
|
|
|
|
// We want to try and spawn more aerial/ground peds if there are no cached ped gen points, because those kinds of peds do not require
|
|
// those points (they get their points from the wildlife manager).
|
|
bool bSpawnMoreAerialPeds = CWildlifeManager::GetInstance().ShouldSpawnMoreAerialPeds();
|
|
bool bSpawnMoreGroundWildlifePeds = CWildlifeManager::GetInstance().ShouldSpawnMoreGroundWildlifePeds();
|
|
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
float prob = GetCurrentPedGroupPercentage(popGroupIndex) * 0.01f;
|
|
|
|
if(requiredPopGroupFlags)
|
|
{
|
|
u32 iGroupFlags = sm_popGroups.GetPedGroup(popGroupIndex).GetFlags();
|
|
if(( iGroupFlags & requiredPopGroupFlags) != requiredPopGroupFlags)
|
|
{
|
|
// The flags don't match, set the probability of using this group to zero.
|
|
prob = 0.0f;
|
|
}
|
|
else if (bSpawnMoreAquaticPeds && iGroupFlags & POPGROUP_AQUATIC)
|
|
{
|
|
prob *= CWildlifeManager::GetInstance().GetIncreasedAquaticSpawningFactor();
|
|
}
|
|
else if (bSpawnMoreAerialPeds && iGroupFlags & POPGROUP_AERIAL)
|
|
{
|
|
prob *= CWildlifeManager::GetInstance().GetIncreasedAerialSpawningFactor();
|
|
}
|
|
else if (bSpawnMoreGroundWildlifePeds && iGroupFlags & POPGROUP_WILDLIFE)
|
|
{
|
|
prob *= CWildlifeManager::GetInstance().GetIncreasedGroundWildlifeSpawningFactor();
|
|
}
|
|
}
|
|
probSum += prob;
|
|
|
|
aRequestedPercentages[popGroupIndex] = prob;
|
|
}
|
|
|
|
if(requiredPopGroupFlags && probSum >= SMALL_FLOAT)
|
|
{
|
|
// Scale the probabilities so they sum up to 100% again, after we cleared out the
|
|
// ones with invalid flags.
|
|
float probScale = 1.0f/probSum;
|
|
for(popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
aRequestedPercentages[popGroupIndex] *= probScale;
|
|
}
|
|
}
|
|
|
|
// Go through all of the appropriate loaded peds and give each group that they belong to points based on the ped refs
|
|
s32 numPeds = gPopStreaming.GetAppropriateLoadedPeds().CountMembers();
|
|
|
|
for (s32 i = 0; i < numPeds; ++i)
|
|
{
|
|
strLocalIndex mi = strLocalIndex(gPopStreaming.GetAppropriateLoadedPeds().GetMember(i));
|
|
fwModelId modelId(mi);
|
|
if (!modelId.IsValid())
|
|
continue;
|
|
|
|
// Only consider models that aren't about to be streamed out.
|
|
if (!CModelInfo::GetAssetsAreDeletable(modelId))
|
|
{
|
|
CPedModelInfo* pedModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
|
|
if (!pedModelInfo)
|
|
continue;
|
|
|
|
if (CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(mi.Get(), pedModelInfo))
|
|
continue;
|
|
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if(aRequestedPercentages[popGroupIndex] > 0.0f)
|
|
{
|
|
if (GetPopGroups().IsPedIndexMember(popGroupIndex, mi.Get()))
|
|
{
|
|
u32 modelCount = pedModelInfo->GetNumPedModelRefs() - pedModelInfo->GetNumTimesInReusePool(); // don't count models in the reuse pool
|
|
aTimesUsed[popGroupIndex] += modelCount;
|
|
totalModels += modelCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// calculate current percentages and which group has the largest deviation from requested value
|
|
totalModels = MAX(1, totalModels);
|
|
float largestDeviation = -9999.f;
|
|
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
aPercentages[popGroupIndex] = (aTimesUsed[popGroupIndex] * 1.f) / totalModels;
|
|
|
|
if(requiredPopGroupFlags)
|
|
{
|
|
// Even though we have already cleared out aRequestedPercentages for groups that
|
|
// don't match the required flags, we have to explicitly reject them here as well,
|
|
// because otherwise they can still be considered the best group.
|
|
if((sm_popGroups.GetPedGroup(popGroupIndex).GetFlags() & requiredPopGroupFlags) != requiredPopGroupFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Potentially the population spawner could be in a situation where aquatic peds are the only group spawned so far,
|
|
// And as such will never be selected to spawn again regardless of how high their requested percentage would be.
|
|
// (If requested < 1 and aPercentages == 1 then the deviation is always negative)
|
|
// Here we correspondingly lower the amount of peds the spawner thinks are in the world so that this doesn't happen.
|
|
if (bSpawnMoreAquaticPeds && sm_popGroups.GetPedGroup(popGroupIndex).GetFlags() & POPGROUP_AQUATIC)
|
|
{
|
|
aPercentages[popGroupIndex] /= CWildlifeManager::GetInstance().GetIncreasedAquaticSpawningFactor();
|
|
}
|
|
else if (bSpawnMoreAerialPeds && sm_popGroups.GetPedGroup(popGroupIndex).GetFlags() & POPGROUP_AERIAL)
|
|
{
|
|
aPercentages[popGroupIndex] /= CWildlifeManager::GetInstance().GetIncreasedAerialSpawningFactor();
|
|
}
|
|
else if (bSpawnMoreGroundWildlifePeds && sm_popGroups.GetPedGroup(popGroupIndex).GetFlags() & POPGROUP_WILDLIFE)
|
|
{
|
|
aPercentages[popGroupIndex] /= CWildlifeManager::GetInstance().GetIncreasedGroundWildlifeSpawningFactor();
|
|
}
|
|
}
|
|
|
|
Assert(aPercentages[popGroupIndex] >= 0.0f && aPercentages[popGroupIndex] <= 1.0f);
|
|
Assert(aRequestedPercentages[popGroupIndex] >= 0.0f);
|
|
|
|
float deviation = aRequestedPercentages[popGroupIndex] - aPercentages[popGroupIndex];
|
|
if (deviation > largestDeviation)
|
|
{
|
|
largestDeviation = deviation;
|
|
wantedGroup = popGroupIndex;
|
|
}
|
|
}
|
|
|
|
// store the wanted group for next time
|
|
sm_CachedMostWantedPopGroup = wantedGroup;
|
|
// Reset the countdown
|
|
sm_PopulationRecalculateCountdown = POPULATION_RECALCULATE_COUNTDOWN;
|
|
}
|
|
}
|
|
|
|
// Check if we found any group we can use.
|
|
if(wantedGroup < (u32)sm_popGroups.GetPedCount())
|
|
{
|
|
CLoadedModelGroup loadedPeds = gPopStreaming.GetAppropriateLoadedPeds();
|
|
loadedPeds.RemoveModelsNotInGroup(wantedGroup, true);
|
|
u32 numAvailablePeds = loadedPeds.CountMembers();
|
|
|
|
if (numAvailablePeds > 0)
|
|
{
|
|
if (pos)
|
|
{
|
|
loadedPeds.SortPedsByDistance(*pos);
|
|
*pNewPedModelIndex = loadedPeds.GetMember(0);
|
|
}
|
|
else
|
|
*pNewPedModelIndex = loadedPeds.PickRandomPedModel();
|
|
|
|
if (*pNewPedModelIndex != fwModelId::MI_INVALID)
|
|
{
|
|
pVariationsOut = sm_popGroups.GetPedGroup(wantedGroup).FindVariations(*pNewPedModelIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*pNewPedModelIndex == fwModelId::MI_INVALID)
|
|
{
|
|
CPedPopulation::ChooseCivilianPedModelIndexArgs args;
|
|
args.m_RequiredPopGroupFlags = requiredPopGroupFlags;
|
|
args.m_AllowFlyingPeds = true;
|
|
args.m_AllowSwimmingPeds = true;
|
|
if(pos)
|
|
{
|
|
args.m_CreationCoors = pos;
|
|
args.m_SortByPosition = true; // care about which ped models are closest to the spawn position
|
|
}
|
|
|
|
// if we're in this situation we don't want to spawn a gang ped unless requested
|
|
if ((requiredPopGroupFlags & POPGROUP_IS_GANG) == 0)
|
|
{
|
|
args.m_RequiredGangMembership = NOT_GANG_MEMBER;
|
|
}
|
|
|
|
args.m_PedModelVariationsOut = &pVariationsOut;
|
|
*pNewPedModelIndex = CPedPopulation::ChooseCivilianPedModelIndex(args);
|
|
|
|
|
|
// Have the conditions for spawn location match the default for the ped.
|
|
if (*pNewPedModelIndex != fwModelId::MI_INVALID)
|
|
{
|
|
const CPedModelInfo* pModelInfo = static_cast<CPedModelInfo*>(CModelInfo::GetBaseModelInfo(fwModelId((strLocalIndex(*pNewPedModelIndex)))));
|
|
if (Verifyf(pModelInfo, "Valid ped model index but NULL model info!"))
|
|
{
|
|
conditions.m_bSpawnInAir = pModelInfo->ShouldSpawnInAirByDefault();
|
|
conditions.m_bSpawnInWater = pModelInfo->ShouldSpawnInWaterByDefault();
|
|
conditions.m_bSpawnAsWildlife = pModelInfo->ShouldSpawnAsWildlifeByDefault();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Have the conditions for spawn location selection match that of the group selected.
|
|
conditions.m_bSpawnInWater = (sm_popGroups.GetPedGroup(wantedGroup).GetFlags() & POPGROUP_AQUATIC) != 0;
|
|
conditions.m_bSpawnInAir = (sm_popGroups.GetPedGroup(wantedGroup).GetFlags() & POPGROUP_AERIAL) != 0;
|
|
conditions.m_bSpawnAsWildlife = (sm_popGroups.GetPedGroup(wantedGroup).GetFlags() & POPGROUP_WILDLIFE) != 0;
|
|
}
|
|
|
|
if (*pNewPedModelIndex == fwModelId::MI_INVALID)
|
|
{
|
|
// If modelIndex == fwModelId::MI_INVALID we haven't succeeded.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
#if __ASSERT
|
|
const CPedModelInfo* pModelInfo = static_cast<CPedModelInfo*>(CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(*pNewPedModelIndex))));
|
|
Assert(pModelInfo);
|
|
ePedType pedType = pModelInfo->GetDefaultPedType();
|
|
Assertf((pedType >= PEDTYPE_CIVMALE && pedType <= PEDTYPE_PROSTITUTE) || pedType == PEDTYPE_ANIMAL || pedType == PEDTYPE_ARMY, "Ped type is not set correctly (%d)", pedType);
|
|
#endif // __ASSERT
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 GroupPercentageCompare(const sGroupPercentage* group0, const sGroupPercentage* group1)
|
|
{
|
|
// if we have zero vehicles from a group, it ranks the
|
|
// highest, no matter what the actual deviation is, just to get at least one model in each group streamed in
|
|
if (group0->zeroInstances && group1->zeroInstances)
|
|
return group1->randNum - group0->randNum;
|
|
else if (group0->zeroInstances)
|
|
return -1;
|
|
else if (group1->zeroInstances)
|
|
return 1;
|
|
|
|
if (fabs(group0->deviation - group1->deviation) < 0.0001f)
|
|
return group1->randNum - group0->randNum;
|
|
|
|
if (group0->deviation < group1->deviation)
|
|
return 1;
|
|
else if (group0->deviation > group1->deviation)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 AvailableModelDistanceCompare(const sAvailableModel* model0, const sAvailableModel* model1)
|
|
{
|
|
if (model0->distanceSquared > model1->distanceSquared)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (model0->distanceSquared < model1->distanceSquared)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : FindNewCarModelIndex
|
|
// Purpose : This function will return the modelindex of the next car to create.
|
|
// Parameters : pNewCarModelIndex - the returned car model index
|
|
// : availableCars - list of cars to choose from
|
|
// Returns : true on success, false otherwise
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool CPopCycle::FindNewCarModelIndex(u32* pNewCarModelIndex, CLoadedModelGroup& availableCars, const Vector3* pos, bool cargens, bool bRandomizedSelection)
|
|
{
|
|
float aRequestedPercentages[MaxPopCycleGroups] = {0};
|
|
u32 aTimesUsed[MaxPopCycleGroups] = {0};
|
|
|
|
if (!HasValidCurrentPopAllocation())
|
|
return false;
|
|
|
|
Assertf(sm_popGroups.GetVehCount() <= MaxPopCycleGroups, "CPopCycle::FindNewCarModelIndex - popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
u32 popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetVehCount());
|
|
|
|
u32 popGroupIndex, totalModels = 0;
|
|
|
|
// init percentages
|
|
u32 numActiveGroups = 0;
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
aRequestedPercentages[popGroupIndex] = GetCurrentVehGroupPercentage(popGroupIndex) * 0.01f;
|
|
if (aRequestedPercentages[popGroupIndex] > 0.f)
|
|
numActiveGroups++;
|
|
}
|
|
|
|
sGroupPercentage *percentageStorage = Alloca(sGroupPercentage, numActiveGroups);
|
|
atUserArray<sGroupPercentage> percentages(percentageStorage, (u16) numActiveGroups);
|
|
|
|
// Go through all of the available vehicles and give each group that they belong to points based on the vehicle refs
|
|
CLoadedModelGroup appropriateVehs;
|
|
appropriateVehs.Merge(&gPopStreaming.GetAppropriateLoadedCars(), &gPopStreaming.GetInAppropriateLoadedCars(), &gPopStreaming.GetDiscardedCars(), &gPopStreaming.GetLoadedBoats());
|
|
appropriateVehs.RemoveSuppressedAndNonAmbientCars();
|
|
s32 numVehs = appropriateVehs.CountMembers();
|
|
|
|
|
|
sAvailableModel *availableCarsByDistanceStorage = Alloca(sAvailableModel, numVehs);
|
|
atUserArray<sAvailableModel> availableCarsByDistance(availableCarsByDistanceStorage, (u16) numVehs);
|
|
availableCarsByDistance.Resize(numVehs);
|
|
|
|
for (s32 i = 0; i < numVehs; ++i)
|
|
{
|
|
strLocalIndex mi = strLocalIndex(appropriateVehs.GetMember(i));
|
|
|
|
availableCarsByDistance[i].index = mi.Get();
|
|
availableCarsByDistance[i].distanceSquared = 0.0f;
|
|
|
|
fwModelId modelId(mi);
|
|
if (!modelId.IsValid())
|
|
continue;
|
|
|
|
CVehicleModelInfo* vehModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
|
|
if (!vehModelInfo)
|
|
continue;
|
|
|
|
if (pos)
|
|
{
|
|
availableCarsByDistance[i].distanceSquared = vehModelInfo->GetDistanceSqrToClosestInstance(*pos, cargens);
|
|
}
|
|
|
|
s32 numRefs = cargens ? vehModelInfo->GetNumVehicleModelRefsParked() : vehModelInfo->GetNumVehicleModelRefs() - vehModelInfo->GetNumVehicleModelRefsReusePool(); // don't count models in the vehicle reuse pool
|
|
|
|
bool counted = false;
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if(aRequestedPercentages[popGroupIndex] > 0.0f)
|
|
{
|
|
if (GetPopGroups().IsVehIndexMember(popGroupIndex, mi.Get()))
|
|
{
|
|
aTimesUsed[popGroupIndex] += numRefs;
|
|
if (!counted)
|
|
{
|
|
totalModels += numRefs;
|
|
counted = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
availableCarsByDistance.QSort(0, -1, AvailableModelDistanceCompare);
|
|
|
|
// calculate current percentages and which group has the largest deviation from requested value
|
|
totalModels = MAX(1, totalModels);
|
|
//float largestDeviation = -9999.f;
|
|
//u32 wantedGroup = 0;
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if(aRequestedPercentages[popGroupIndex] > 0.0f)
|
|
{
|
|
float percentage = (aTimesUsed[popGroupIndex] * 1.f) / totalModels;
|
|
Assert(percentage >= 0.0f && percentage <= 1.0f);
|
|
Assert(aRequestedPercentages[popGroupIndex] >= 0.0f);
|
|
|
|
sGroupPercentage& gp = percentages.Append();
|
|
gp.groupIndex = popGroupIndex;
|
|
gp.deviation = aRequestedPercentages[popGroupIndex] - percentage;
|
|
gp.requestedPercentage = aRequestedPercentages[popGroupIndex];
|
|
gp.randNum = fwRandom::GetRandomNumber();
|
|
gp.zeroInstances = gp.requestedPercentage > 0.0f && percentage < 0.0001f;
|
|
}
|
|
}
|
|
|
|
percentages.QSort(0, -1, GroupPercentageCompare);
|
|
|
|
if (!cargens)
|
|
{
|
|
sm_preferredSpawnGroupForVehs = -1;
|
|
|
|
for (s32 i = 0; i < percentages.GetCount(); ++i)
|
|
{
|
|
if (!GetPopGroups().GetVehGroup(percentages[i].groupIndex).IsFlagSet(POPGROUP_RARE))
|
|
{
|
|
sm_preferredSpawnGroupForVehs = percentages[i].groupIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 fallbackModel = fwModelId::MI_INVALID;
|
|
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
|
|
|
|
// see if we could use a bike - don't generate a bike if we don't have any peds that can ride it
|
|
s32 canChooseBike = -1;
|
|
|
|
if (bRandomizedSelection && pos)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// get a list of all candidates and randomly select one
|
|
//
|
|
// slower but better for populating car parks etc
|
|
//
|
|
atArray<u32> candidates;
|
|
|
|
for (u32 i=0; i<numVehs; ++i)
|
|
{
|
|
const u32 modelIndex = availableCarsByDistance[i].index;
|
|
|
|
// very nearby?
|
|
if (availableCarsByDistance[i].distanceSquared==0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// group checks
|
|
bool bPassedGroupChecks = false;
|
|
bool bInRequestedGroupWithDisparity = false;
|
|
bool bInPrevalentGroup = false;
|
|
for (s32 j=0; j<percentages.GetCount(); ++j)
|
|
{
|
|
const bool inGroup = CPopCycle::GetPopGroups().IsVehIndexMember(percentages[j].groupIndex, modelIndex);
|
|
const bool bGroupRequested = percentages[j].requestedPercentage > 0.0f;
|
|
const bool bGroupHasDisparity = percentages[j].deviation > 0.0f;
|
|
const bool bNotExceededRareness = percentages[j].requestedPercentage > 0.09f && !GetPopGroups().GetVehGroup(percentages[j].groupIndex).IsFlagSet(POPGROUP_RARE);
|
|
|
|
if ( inGroup && bGroupRequested && (bGroupHasDisparity || percentages[j].zeroInstances || !bNotExceededRareness) )
|
|
{
|
|
bPassedGroupChecks = true;
|
|
bInRequestedGroupWithDisparity |= bGroupHasDisparity;
|
|
bInPrevalentGroup |= ( percentages[j].requestedPercentage > 0.19f );
|
|
}
|
|
}
|
|
if (!bPassedGroupChecks)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// available?
|
|
if (!availableCars.IsMemberInGroup(modelIndex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Don't violate percentages with big or swanky vehicles
|
|
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
|
|
const s32 handlingIndex = vmi->GetHandlingId();
|
|
const CHandlingData* pHandling = CHandlingDataMgr::GetHandlingData(handlingIndex);
|
|
const bool superSwanky = vmi->GetVehicleSwankness() >= SWANKNESS_5;
|
|
if (((pHandling && (pHandling->mFlags & MF_IS_BIG)) || superSwanky) && !bInRequestedGroupWithDisparity)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Don't spawn the same super swanky vehicle less than 300m from a duplicate instance unless
|
|
// it's from a prevalent group
|
|
static const float minimumDistanceToSpawnIdenticalSuperSwankyCar = square(300.0f);
|
|
if (superSwanky && !bInPrevalentGroup && availableCarsByDistance[i].distanceSquared < minimumDistanceToSpawnIdenticalSuperSwankyCar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (availableCarsByDistance[i].distanceSquared >= (vmi->GetIdenticalModelSpawnDistance() * vmi->GetIdenticalModelSpawnDistance()))
|
|
{
|
|
candidates.Grow() = modelIndex;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// select
|
|
if (candidates.GetCount())
|
|
{
|
|
const u32 selected = fwRandom::GetRandomNumberInRange(0, candidates.GetCount()-1);
|
|
*pNewCarModelIndex = candidates[selected];
|
|
}
|
|
}
|
|
else if (pos)
|
|
{
|
|
bool foundUsableCarFromRequestedGroup = false;
|
|
bool foundNeededCarFromRequestedGroup = false;
|
|
|
|
for (u32 i = 0; i < numVehs; ++i)
|
|
{
|
|
if(availableCarsByDistance[i].distanceSquared == 0.0f)
|
|
{
|
|
break;
|
|
}
|
|
|
|
u32 modelIndex = availableCarsByDistance[i].index;
|
|
|
|
bool inRequestedGroup = false;
|
|
bool inPrevalentGroup = false;
|
|
bool inRequestedGroupWithDisparity = false;
|
|
bool inRequestedGroupWithZeroInstances = false;
|
|
|
|
// If we're in a requested group with zero instances, bail, we've gathered all of the information that we need
|
|
for (s32 j = 0; j < percentages.GetCount() && !inRequestedGroupWithZeroInstances; ++j)
|
|
{
|
|
const bool groupRequested = percentages[j].requestedPercentage > 0.0f;
|
|
const bool groupPrevalent = percentages[j].requestedPercentage > 0.19f;
|
|
const bool groupHasDisparity = percentages[j].deviation > 0.0f;
|
|
|
|
const bool inGroup = CPopCycle::GetPopGroups().IsVehIndexMember(percentages[j].groupIndex, modelIndex);
|
|
const bool inThisRequestedGroup = inGroup && groupRequested;
|
|
|
|
// Don't violate percentages for groups that are meant to be rare in this area
|
|
if ((percentages[j].requestedPercentage > 0.09f && !GetPopGroups().GetVehGroup(percentages[j].groupIndex).IsFlagSet(POPGROUP_RARE))
|
|
|| groupHasDisparity)
|
|
{
|
|
inRequestedGroup |= inThisRequestedGroup;
|
|
inPrevalentGroup |= inThisRequestedGroup && groupPrevalent;
|
|
inRequestedGroupWithDisparity |= inThisRequestedGroup && groupHasDisparity;
|
|
inRequestedGroupWithZeroInstances = inThisRequestedGroup && percentages[j].zeroInstances;
|
|
}
|
|
}
|
|
|
|
if(/**pNewCarModelIndex == fwModelId::MI_INVALID
|
|
|| */(inRequestedGroup && !foundUsableCarFromRequestedGroup)
|
|
|| (inRequestedGroupWithDisparity && !foundNeededCarFromRequestedGroup)
|
|
|| inRequestedGroupWithZeroInstances)
|
|
{
|
|
if (!availableCars.IsMemberInGroup(modelIndex))
|
|
continue;
|
|
|
|
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
|
|
|
|
const s32 handlingIndex = vmi->GetHandlingId();
|
|
const CHandlingData* pHandling = CHandlingDataMgr::GetHandlingData(handlingIndex);
|
|
|
|
const bool superSwanky = vmi->GetVehicleSwankness() >= SWANKNESS_5;
|
|
|
|
// Don't violate percentages with big or swanky vehicles
|
|
if (((pHandling && (pHandling->mFlags & MF_IS_BIG)) || superSwanky) && !inRequestedGroupWithDisparity)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (availableCarsByDistance[i].distanceSquared >= (vmi->GetIdenticalModelSpawnDistance() * vmi->GetIdenticalModelSpawnDistance()))
|
|
{
|
|
if(vmi->GetIsBike())
|
|
{
|
|
if(canChooseBike == -1)
|
|
{
|
|
canChooseBike = gPopStreaming.IsFallbackPedAvailable(true, false);
|
|
}
|
|
|
|
if(canChooseBike == 0 && !CPedPopulation::FindSpecificDriverModelForCarToUse(fwModelId(strLocalIndex(modelIndex))).IsValid())
|
|
{
|
|
// Skipping this bike because we don't have any peds that can ride it
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fallbackModel == fwModelId::MI_INVALID && playerVehicle && playerVehicle->GetModelIndex() == modelIndex)
|
|
{
|
|
fallbackModel = modelIndex;
|
|
continue;
|
|
}
|
|
|
|
static const float minimumDistanceToSpawnIdenticalSuperSwankyCar = square(300.0f);
|
|
|
|
// Don't spawn the same super swanky vehicle less than 300m from a duplicate instance unless
|
|
// it's from a prevalent group
|
|
if (superSwanky
|
|
&& !inPrevalentGroup
|
|
&& availableCarsByDistance[i].distanceSquared < minimumDistanceToSpawnIdenticalSuperSwankyCar)
|
|
{
|
|
// If the player's vehicle is the fallback model, replace it with this
|
|
if (fallbackModel == fwModelId::MI_INVALID || (playerVehicle && playerVehicle->GetModelIndex() == fallbackModel))
|
|
{
|
|
fallbackModel = modelIndex;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
static const float minimumDistanceToOverrideUsableCar = square(100.0f);
|
|
|
|
// Don't spawn the same vehicle less than 100 meters away from a duplicate instance unless we haven't found another usable model
|
|
// Often, we've fulfilled percentage requirements for all other groups while waiting for models to load --
|
|
// as soon as one model from the needy group loads, we use it for all subsequent creations until disparities level out
|
|
if(!foundUsableCarFromRequestedGroup
|
|
|| availableCarsByDistance[i].distanceSquared >= minimumDistanceToOverrideUsableCar
|
|
|| inRequestedGroupWithZeroInstances)
|
|
{
|
|
*pNewCarModelIndex = modelIndex;
|
|
|
|
foundUsableCarFromRequestedGroup |= inRequestedGroup;
|
|
foundNeededCarFromRequestedGroup |= inRequestedGroupWithDisparity;
|
|
}
|
|
|
|
// If we're in a requested group with zero instances, bail, we've found our top priority
|
|
if (inRequestedGroupWithZeroInstances)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (s32 i = 0; i < percentages.GetCount(); ++i)
|
|
{
|
|
CLoadedModelGroup wantedCars = availableCars;
|
|
wantedCars.RemoveModelsNotInGroup(percentages[i].groupIndex, false);
|
|
|
|
*pNewCarModelIndex = wantedCars.PickRandomCarModel(cargens).Get();
|
|
|
|
if (*pNewCarModelIndex != fwModelId::MI_INVALID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*pNewCarModelIndex == fwModelId::MI_INVALID)
|
|
*pNewCarModelIndex = fallbackModel;
|
|
|
|
return *pNewCarModelIndex != fwModelId::MI_INVALID;
|
|
}
|
|
|
|
#if __BANK
|
|
|
|
void CPopCycle::GetAmbientPedMultipliers(float * fWeatherMultiplier, float * fHighwayMultiplier, float * fInteriorMultiplier, float * fWantedMultiplier, float * fCombatMultiplier, float * fPedDensityMultiplier)
|
|
{
|
|
if(fWeatherMultiplier)
|
|
*fWeatherMultiplier = CalcWeatherMultiplier();
|
|
if(fHighwayMultiplier)
|
|
*fHighwayMultiplier = CalcHighWayPedMultiplier();
|
|
if(fInteriorMultiplier)
|
|
*fInteriorMultiplier = CPopCycle::GetInteriorPedMultiplier();
|
|
if(fWantedMultiplier)
|
|
*fWantedMultiplier = CPopCycle::GetWantedPedMultiplier();
|
|
if(fCombatMultiplier)
|
|
*fCombatMultiplier = CPopCycle::GetCombatPedMultiplier();
|
|
if(fPedDensityMultiplier)
|
|
*fPedDensityMultiplier = CPedPopulation::GetTotalAmbientPedDensityMultiplier();
|
|
}
|
|
|
|
void CPopCycle::GetScenarioPedMultipliers(float * fHighwayMultiplier, float * fInteriorMultiplier, float * fWantedMultiplier, float * fCombatMultiplier, float * fPedDensityMultiplier)
|
|
{
|
|
if(fHighwayMultiplier)
|
|
*fHighwayMultiplier = CalcHighWayPedMultiplier();
|
|
if(fInteriorMultiplier)
|
|
*fInteriorMultiplier = CPopCycle::GetInteriorPedMultiplier();
|
|
if(fWantedMultiplier)
|
|
*fWantedMultiplier = CPopCycle::GetWantedPedMultiplier();
|
|
if(fCombatMultiplier)
|
|
*fCombatMultiplier = CPopCycle::GetCombatPedMultiplier();
|
|
if(fPedDensityMultiplier)
|
|
*fPedDensityMultiplier = CPedPopulation::GetTotalAmbientPedDensityMultiplier();
|
|
}
|
|
|
|
#endif // __BANK
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : FindNumberOfEach
|
|
// Purpose : This function will calculate the number of dealers, gangs, cops and others we want in this zone.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::UpdatePercentages(void)
|
|
{
|
|
// Work out the percentage of cops on the beat.
|
|
// This now comes directly from popcycle.dat
|
|
float desiredPortionCopsPeds = GetCurrentDesiredPercentageOfCopsPeds() * 0.01f;
|
|
float desiredPortionOtherPeds = 1.0f - desiredPortionCopsPeds;
|
|
|
|
// Figure out the max number of peds.
|
|
int currentMaxNumAmbientPeds = 0;
|
|
int currentMaxNumScenarioPeds = 0;
|
|
if(HasValidCurrentPopAllocation())
|
|
{
|
|
const float scenarioPedsMultiplier = GET_POPULATION_VALUE(ScenarioPedsMultiplier) / 100.0f;
|
|
const float ambientPedsMultiplier = GET_POPULATION_VALUE(AmbientPedsMultiplier) / 100.0f;
|
|
|
|
currentMaxNumAmbientPeds = GetCurrentPopAllocation().GetMaxNumAmbientPeds();
|
|
currentMaxNumScenarioPeds = GetCurrentPopAllocation().GetMaxNumScenarioPeds();
|
|
currentMaxNumAmbientPeds = (int)((float)currentMaxNumAmbientPeds * CalcWeatherMultiplier() * CalcHighWayPedMultiplier() * CPopCycle::GetInteriorPedMultiplier() * CPopCycle::GetWantedPedMultiplier() * CPopCycle::GetCombatPedMultiplier() * CPedPopulation::GetTotalAmbientPedDensityMultiplier() * ambientPedsMultiplier);
|
|
currentMaxNumScenarioPeds = (int)((float)currentMaxNumScenarioPeds * CalcHighWayPedMultiplier() * CPopCycle::GetInteriorPedMultiplier() * CPopCycle::GetWantedPedMultiplier() * CPopCycle::GetCombatPedMultiplier() * CPedPopulation::GetTotalAmbientPedDensityMultiplier() * scenarioPedsMultiplier);
|
|
}
|
|
|
|
currentMaxNumAmbientPeds = static_cast<s32>(CPedPopulation::GetPopCycleMaxPopulationScaler() * currentMaxNumAmbientPeds);
|
|
currentMaxNumScenarioPeds = static_cast<s32>(CPedPopulation::GetPopCycleMaxPopulationScaler() * currentMaxNumScenarioPeds);
|
|
|
|
float maxTotalPeds = static_cast<float>(GET_POPULATION_VALUE(MaxTotalPeds));
|
|
float currentTotal = (float)(currentMaxNumAmbientPeds + currentMaxNumScenarioPeds);
|
|
|
|
if(currentTotal > maxTotalPeds)
|
|
{
|
|
currentMaxNumAmbientPeds = (int)((currentMaxNumAmbientPeds / currentTotal) * maxTotalPeds);
|
|
currentMaxNumScenarioPeds = (int)((currentMaxNumScenarioPeds / currentTotal) * maxTotalPeds);
|
|
}
|
|
|
|
|
|
|
|
#if __BANK
|
|
if (CPedPopulation::GetPopCycleOverrideNumAmbientPeds() >= 0)
|
|
{
|
|
currentMaxNumAmbientPeds = CPedPopulation::GetPopCycleOverrideNumAmbientPeds();
|
|
}
|
|
if (CPedPopulation::GetPopCycleOverrideNumScenarioPeds() >= 0)
|
|
{
|
|
currentMaxNumScenarioPeds = CPedPopulation::GetPopCycleOverrideNumScenarioPeds();
|
|
}
|
|
#endif
|
|
|
|
// Determine our total maxes for each type.
|
|
sm_currentMaxNumCopPeds = (s32)rage::Floorf(desiredPortionCopsPeds * currentMaxNumAmbientPeds);
|
|
sm_currentMaxNumAmbientPeds = (s32)(desiredPortionOtherPeds * currentMaxNumAmbientPeds);
|
|
sm_currentMaxNumScenarioPeds = (s32)(currentMaxNumScenarioPeds);
|
|
}
|
|
|
|
u32 CPopCycle::CountNumPedsInCombat()
|
|
{
|
|
if(fwTimer::GetTimeInMilliseconds() - sm_lastTimePedsInCombatCounted < 2000)
|
|
return sm_currentNumPedsInCombat;
|
|
|
|
sm_lastTimePedsInCombatCounted = fwTimer::GetTimeInMilliseconds();
|
|
sm_currentNumPedsInCombat = 0;
|
|
|
|
CPed::Pool* pedPool = CPed::GetPool();
|
|
s32 i = pedPool->GetSize();
|
|
while(i--)
|
|
{
|
|
CPed* pPed = pedPool->GetSlot(i);
|
|
if(pPed && pPed->GetPedResetFlag(CPED_RESET_FLAG_IsInCombat))
|
|
sm_currentNumPedsInCombat++;
|
|
}
|
|
|
|
return sm_currentNumPedsInCombat;
|
|
}
|
|
|
|
float CPopCycle::GetCurrentZoneVehDirtMin()
|
|
{
|
|
float dirtMin = -1.0f;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtMin = sm_pCurrZone->m_vehDirtMin;
|
|
}
|
|
|
|
#if __BANK
|
|
if( sm_enableOverrideVehDirtScale )
|
|
{
|
|
return sm_overridevehDirtScale;
|
|
}
|
|
#endif // __BANK
|
|
|
|
return dirtMin;
|
|
}
|
|
|
|
float CPopCycle::GetCurrentZoneVehDirtMax()
|
|
{
|
|
float dirtMax = -1.0f;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtMax = sm_pCurrZone->m_vehDirtMax;
|
|
}
|
|
|
|
#if __BANK
|
|
if( sm_enableOverrideVehDirtScale )
|
|
{
|
|
return sm_overridevehDirtScale;
|
|
}
|
|
#endif // __BANK
|
|
|
|
return dirtMax;
|
|
}
|
|
|
|
float CPopCycle::GetCurrentZonePedDirtMin()
|
|
{
|
|
float dirtMin = -1.0f;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtMin = sm_pCurrZone->m_pedDirtMin;
|
|
}
|
|
|
|
#if __BANK
|
|
if( sm_enableOverridePedDirtScale )
|
|
{
|
|
return sm_overridePedDirtScale;
|
|
}
|
|
#endif // __BANK
|
|
|
|
return dirtMin;
|
|
}
|
|
|
|
float CPopCycle::GetCurrentZonePedDirtMax()
|
|
{
|
|
float dirtMax = -1.0f;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtMax = sm_pCurrZone->m_pedDirtMax;
|
|
}
|
|
|
|
#if __BANK
|
|
if( sm_enableOverridePedDirtScale )
|
|
{
|
|
return sm_overridePedDirtScale;
|
|
}
|
|
#endif // __BANK
|
|
|
|
return dirtMax;
|
|
}
|
|
|
|
float CPopCycle::GetCurrentZoneDirtGrowScale()
|
|
{
|
|
float dirtGrowScale = 1.0f;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtGrowScale = sm_pCurrZone->m_dirtGrowScale;
|
|
}
|
|
|
|
return dirtGrowScale;
|
|
}
|
|
|
|
Color32 CPopCycle::GetCurrentZoneDirtCol()
|
|
{
|
|
Color32 dirtCol;
|
|
|
|
dirtCol.Set(0);
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
dirtCol = sm_pCurrZone->m_dirtCol;
|
|
}
|
|
|
|
#if __BANK
|
|
if( sm_enableOverrideDirtColor )
|
|
{
|
|
return sm_overrideDirtColor;
|
|
}
|
|
#endif // __BANK
|
|
|
|
return dirtCol;
|
|
}
|
|
|
|
bool CPopCycle::IsCurrentZoneAirport()
|
|
{
|
|
if (sm_pCurrZone)
|
|
{
|
|
return sm_pCurrZone->m_specialAttributeType == SPECIAL_AIRPORT;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
atHashWithStringNotFinal CPopCycle::GetCurrentZoneVfxRegionHashName()
|
|
{
|
|
atHashWithStringNotFinal vfxRegionHashName = VFXREGIONINFO_DEFAULT_HASHNAME;
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
vfxRegionHashName = sm_pCurrZone->m_vfxRegionHashName;
|
|
}
|
|
|
|
return vfxRegionHashName;
|
|
}
|
|
|
|
u8 CPopCycle::GetCurrentZonePlantsMgrTxdIdx()
|
|
{
|
|
u8 txdIdx = 0x07; // default
|
|
|
|
if(sm_pCurrZone)
|
|
{
|
|
txdIdx = sm_pCurrZone->m_plantsMgrTxdIdx;
|
|
}
|
|
|
|
return txdIdx;
|
|
}
|
|
|
|
bool CPopCycle::VehicleBikeGroupActive()
|
|
{
|
|
if (sm_vehicleBikeGroup == ~0U)
|
|
return false;
|
|
|
|
return GetCurrentVehGroupPercentage(sm_vehicleBikeGroup) > 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCurrentPortionOfMaxCars
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetCurrentPercentageOfMaxCars()
|
|
{
|
|
if (!HasValidCurrentPopAllocation())
|
|
return 0.0f;
|
|
|
|
const float fPercentage = (float) GetCurrentPopAllocation().GetPercentageOfMaxCars();
|
|
return fPercentage;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCurrentMaxNumParkedCars
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetCurrentMaxNumParkedCars()
|
|
{
|
|
if (!HasValidCurrentPopAllocation())
|
|
return 0;
|
|
|
|
s32 currentMaxParkCars = (s32)((GetCurrentPopAllocation().GetMaxNumParkedCars() * CVehiclePopulation::GetTotalParkedCarDensityMultiplier()) + (GetCurrentPopAllocation().GetMaxNumLowPrioParkedCars() * CVehiclePopulation::GetTotalLowPrioParkedCarDensityMultiplier()));
|
|
|
|
if(currentMaxParkCars > CVehiclePopulation::ms_iParkedVehiclesUpperLimit)
|
|
{
|
|
currentMaxParkCars = CVehiclePopulation::ms_iParkedVehiclesUpperLimit;
|
|
}
|
|
|
|
return currentMaxParkCars;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCurrentMaxNumLowPrioParkedCars
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetCurrentMaxNumLowPrioParkedCars()
|
|
{
|
|
if (!HasValidCurrentPopAllocation())
|
|
return 0;
|
|
|
|
return static_cast<s32>(GetCurrentPopAllocation().GetMaxNumLowPrioParkedCars() * CVehiclePopulation::GetTotalLowPrioParkedCarDensityMultiplier());
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCurrentDesiredPercentageOfCopsCars
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetCurrentDesiredPercentageOfCopsCars()
|
|
{
|
|
if (!HasValidCurrentPopAllocation())
|
|
return 0;
|
|
|
|
return GetCurrentPopAllocation().GetPercentageCopsCars();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCurrentDesiredPercentageOfCopsPeds
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetCurrentDesiredPercentageOfCopsPeds()
|
|
{
|
|
if (!HasValidCurrentPopAllocation())
|
|
return 0;
|
|
|
|
return GetCurrentPopAllocation().GetPercentageCopsPeds();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CalcWeatherMultiplier
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::CalcWeatherMultiplier()
|
|
{
|
|
// s32 Result = 100; //sm_nPercOther[sm_nCurrentTimeIndex][sm_nCurrentTimeOfWeek][sm_nCurrentZoneType];
|
|
|
|
//Don't make so many peds if it is raining.
|
|
const float fMinWeatherMultiplier=0.2f;
|
|
float fWeatherMultiplier = 1.0f + rage::Sqrtf(g_weather.GetRain())*(fMinWeatherMultiplier-1.0f);
|
|
|
|
//If the player is on a taxi mission then we need plenty of peds to pick up as fares even if it is wet.
|
|
if(CTheScripts::GetPlayerIsOnAMission())
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if(pPlayer)
|
|
{
|
|
if((pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))&&(pPlayer->GetMyVehicle()))
|
|
{
|
|
// const int iModelID=pPlayer->m_pMyVehicle->GetModelIndex();
|
|
// if( MODELID_CAR_TAXI==iModelID || MODELID_CAR_CABBIE==iModelID)
|
|
// {
|
|
// fWeatherMultiplier=1.0f;
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
|
|
return fWeatherMultiplier;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CalcHighWayPedMultiplier
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::CalcHighWayPedMultiplier()
|
|
{
|
|
/*
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if(pPlayer && pPlayer->GetPlayerInfo()->GetPlayerDataPlayerOnHighway())
|
|
{
|
|
return 0.4f;
|
|
}
|
|
*/
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : CalcHighWayCarMultiplier
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::CalcHighWayCarMultiplier()
|
|
{
|
|
CPed * pPlayer = CGameWorld::FindLocalPlayer();
|
|
if(pPlayer && pPlayer->GetPlayerInfo()->GetPlayerDataPlayerOnHighway())
|
|
{
|
|
return 1.2f;
|
|
}
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetInteriorPedMultiplier
|
|
// If we're in an interior and the flag is set for reduced population this function returns the appropriate multiplier
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetInteriorPedMultiplier()
|
|
{
|
|
CInteriorInst* pInterior = CPortalVisTracker::GetPrimaryInteriorInst();
|
|
s32 roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
|
|
|
|
if(pInterior && (roomIdx > 0))
|
|
{
|
|
CMloModelInfo* pMloModelInfo = (CMloModelInfo*)pInterior->GetBaseModelInfo();
|
|
|
|
Assert(pMloModelInfo->GetIsStreamType());
|
|
{
|
|
if (pMloModelInfo->GetRoomFlags(roomIdx) & ROOM_REDUCE_PEDS)
|
|
{
|
|
return 0.33f;
|
|
}
|
|
}
|
|
}
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetInteriorCarMultiplier
|
|
// If we're in an interior and the flag is set for reduced population this function returns the appropriate multiplier
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetInteriorCarMultiplier()
|
|
{
|
|
// Don't reduce vehicle population if the interior we are in is a road tunnel
|
|
// We can identify this by the interior proxy having an ID greater than 1.
|
|
// (index >= 0 indicates a tunnel, whilst index == 1 is reserved for the metro system)
|
|
CInteriorProxy * pProxy = CPortalVisTracker::GetPrimaryInteriorProxy();
|
|
if(pProxy && pProxy->GetGroupId() > 1)
|
|
return 1.0f;
|
|
|
|
CInteriorInst * pInterior = CPortalVisTracker::GetPrimaryInteriorInst();
|
|
s32 roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
|
|
|
|
if(pInterior && (roomIdx > 0))
|
|
{
|
|
CMloModelInfo* pMloModelInfo = (CMloModelInfo*)pInterior->GetBaseModelInfo();
|
|
|
|
Assert (pMloModelInfo->GetIsStreamType());
|
|
{
|
|
if (pMloModelInfo->GetRoomFlags(roomIdx) & ROOM_REDUCE_CARS)
|
|
{
|
|
return 0.33f;
|
|
}
|
|
}
|
|
}
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetWantedPedMultiplier
|
|
// If the wanted level goes up we want fewer peds around
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetWantedPedMultiplier()
|
|
{
|
|
float fMult = 1.0f;
|
|
if (CGameWorld::FindLocalPlayerWanted())
|
|
{
|
|
switch(CGameWorld::FindLocalPlayerWanted()->GetWantedLevel())
|
|
{
|
|
case WANTED_CLEAN:// Intentional fall though.
|
|
case WANTED_LEVEL1:
|
|
break;
|
|
case WANTED_LEVEL2:
|
|
fMult = 0.9f;
|
|
break;
|
|
case WANTED_LEVEL3:
|
|
fMult = 0.7f;
|
|
break;
|
|
case WANTED_LEVEL4:
|
|
fMult = 0.5f;
|
|
break;
|
|
case WANTED_LEVEL5:
|
|
fMult = 0.2f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
static const int nDesiredPedAmount = 36; // A sane value related to MAX_NUM_NETOBJPEDS
|
|
const int nAmbientPeds = CPedPopulation::ms_nNumOnFootCop + CPedPopulation::ms_nNumOnFootSwat + CPedPopulation::ms_nNumOnFootAmbient;
|
|
if (nAmbientPeds < nDesiredPedAmount) // Don't allow too many ambient peds globally, max 50 are allowed in MP atm.
|
|
{
|
|
fMult = Max(fMult, sm_fMinWantedMultiplierMP);
|
|
}
|
|
}
|
|
return fMult;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetWantedCarsMultiplier
|
|
// If the wanted level goes up we want fewer cars around
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetWantedCarMultiplier()
|
|
{
|
|
float fMult = 1.0f;
|
|
if (CGameWorld::FindLocalPlayerVehicle())
|
|
{
|
|
switch(CGameWorld::FindLocalPlayerWanted()->GetWantedLevel())
|
|
{
|
|
case WANTED_CLEAN:
|
|
case WANTED_LEVEL1:
|
|
break;
|
|
case WANTED_LEVEL2:
|
|
fMult = 0.8f;
|
|
break;
|
|
case WANTED_LEVEL3:
|
|
fMult = 0.6f;
|
|
break;
|
|
case WANTED_LEVEL4:
|
|
fMult = 0.6f;
|
|
break;
|
|
case WANTED_LEVEL5:
|
|
fMult = 0.6f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (CGameWorld::FindLocalPlayerWanted())
|
|
{
|
|
switch(CGameWorld::FindLocalPlayerWanted()->GetWantedLevel())
|
|
{
|
|
case WANTED_CLEAN:
|
|
case WANTED_LEVEL1:
|
|
break;
|
|
case WANTED_LEVEL2:
|
|
fMult = 0.9f;
|
|
break;
|
|
case WANTED_LEVEL3:
|
|
fMult = 0.6f;
|
|
break;
|
|
case WANTED_LEVEL4:
|
|
fMult = 0.4f;
|
|
break;
|
|
case WANTED_LEVEL5:
|
|
fMult = 0.2f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
{
|
|
fMult = ScaleMultiplier_SP_to_MP(fMult);
|
|
|
|
// Clamp
|
|
fMult = Clamp(fMult, sm_fMinWantedMultiplierMP, 1.0f);
|
|
}
|
|
return fMult;
|
|
}
|
|
|
|
// Modify a multiplier values to account for the ratio between vehicle upper limits in MP & SP
|
|
float CPopCycle::ScaleMultiplier_SP_to_MP(const float fMult)
|
|
{
|
|
Assert(NetworkInterface::IsGameInProgress());
|
|
|
|
const float fRatio = ((float)CVehiclePopulation::ms_iAmbientVehiclesUpperLimitMP) / ((float)CVehiclePopulation::ms_iAmbientVehiclesUpperLimit);
|
|
float fInvMult = 1.0f - fMult;
|
|
fInvMult *= fRatio;
|
|
return (1.0f - fInvMult);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCombatPedMultiplier
|
|
// As more peds enter combat, we want fewer peds around
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPopCycle::GetCombatPedMultiplier()
|
|
{
|
|
if(sm_currentNumPedsInCombat < sm_iMinPedsInCombatForPopMultiplier)
|
|
return 1.0f;
|
|
else if(sm_currentNumPedsInCombat >= sm_iMaxPedsInCombatForPopMultiplier)
|
|
return sm_fPedsInCombatMinPopMultiplier;
|
|
|
|
float S = (((float)sm_currentNumPedsInCombat) / ((float)sm_iMaxPedsInCombatForPopMultiplier-sm_iMinPedsInCombatForPopMultiplier));
|
|
|
|
S = Clamp(S, 0.0f, 1.0f);
|
|
S *= sm_fPedsInCombatMinPopMultiplier;
|
|
S = 1.0f - S;
|
|
|
|
return sm_bUseCombatMultiplier ? S : 1.0f;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetCombatCarMultiplier
|
|
// As more peds enter combat, we want fewer peds around
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
float CPopCycle::GetCombatCarMultiplier()
|
|
{
|
|
if(sm_currentNumPedsInCombat < sm_iMinPedsInCombatForPopMultiplier)
|
|
return 1.0f;
|
|
else if(sm_currentNumPedsInCombat >= sm_iMaxPedsInCombatForPopMultiplier)
|
|
return sm_fPedsInCombatMinPopMultiplier;
|
|
|
|
float S = (((float)sm_currentNumPedsInCombat) / ((float)sm_iMaxPedsInCombatForPopMultiplier-sm_iMinPedsInCombatForPopMultiplier));
|
|
|
|
S = Clamp(S, 0.0f, 1.0f);
|
|
S *= sm_fPedsInCombatMinPopMultiplier;
|
|
S = 1.0f - S;
|
|
|
|
if(NetworkInterface::IsGameInProgress())
|
|
S = ScaleMultiplier_SP_to_MP(S);
|
|
|
|
return sm_bUseCombatMultiplier ? S : 1.0f;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetTemporaryMaxNumScenarioPeds
|
|
// A cutscene can put a ceiling on the number of peds allowed
|
|
// TODO
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetTemporaryMaxNumScenarioPeds()
|
|
{
|
|
/*//TF **REPLACE WITH NEW CUTSCENE**
|
|
if ((JimmyCutSceneManager::GetInstance() && JimmyCutSceneManager::GetInstance()->IsRunning()) && CCutsceneManager::ms_iNumMaxPedsAllowed >= 0)
|
|
{
|
|
return CCutsceneManager::ms_iNumMaxPedsAllowed / 3;
|
|
}*/
|
|
return 9999;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetTemporaryMaxNumAmbientPeds
|
|
// A cutscene can put a ceiling on the number of peds allowed
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetTemporaryMaxNumAmbientPeds()
|
|
{
|
|
//TF **REPLACE WITH NEW CUTSCENE**
|
|
/*
|
|
if ((JimmyCutSceneManager::GetInstance() && JimmyCutSceneManager::GetInstance()->IsRunning()) && CCutsceneManager::ms_iNumMaxPedsAllowed >= 0)
|
|
{
|
|
return (CCutsceneManager::ms_iNumMaxPedsAllowed * 2) / 3;
|
|
}
|
|
*/
|
|
return 9999;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : GetTemporaryMaxNumCars
|
|
// A cutscene can put a ceiling on the number of cars allowed
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::GetTemporaryMaxNumCars()
|
|
{
|
|
//TF **REPLACE WITH NEW CUTSCENE**
|
|
/* if ((JimmyCutSceneManager::GetInstance() && JimmyCutSceneManager::GetInstance()->IsRunning()) && CCutsceneManager::ms_iNumMaxCarsAllowed >= 0)
|
|
{
|
|
return CCutsceneManager::ms_iNumMaxCarsAllowed;
|
|
}
|
|
*/
|
|
return 9999;
|
|
}
|
|
|
|
void CPopCycle::GetNetworkPedModelsForCurrentZone(CLoadedModelGroup &ambientModelsList, CLoadedModelGroup &gangModelsList)
|
|
{
|
|
Assertf(sm_popGroups.GetPedCount() <= MaxPopCycleGroups, "popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
u32 popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetPedCount());
|
|
|
|
CLoadedModelGroup *currentModelGroup = &ambientModelsList;
|
|
|
|
for (u32 popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if(GetCurrentPedGroupPercentage(popGroupIndex) > 0.0f)
|
|
{
|
|
if(GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_IS_GANG))
|
|
{
|
|
currentModelGroup = &gangModelsList;
|
|
}
|
|
else
|
|
{
|
|
currentModelGroup = &ambientModelsList;
|
|
}
|
|
|
|
const u32 numPedsInGroup = GetPopGroups().GetNumPeds(popGroupIndex);
|
|
|
|
for (u32 pedIndex = 0; pedIndex < numPedsInGroup; pedIndex++)
|
|
{
|
|
u32 modelIndex = GetPopGroups().GetPedIndex(popGroupIndex, pedIndex);
|
|
|
|
#if __ASSERT
|
|
fwModelId pedModelId((strLocalIndex(modelIndex)));
|
|
Assertf(pedModelId.IsValid(), "Model index %u is not valid (found in ped group %s).", modelIndex, GetPopGroups().GetPedGroup(popGroupIndex).GetName().GetCStr());
|
|
#endif
|
|
|
|
// Block certain models from being streamed in because their motion task is not set up to be synchronized over the network.
|
|
if (CWildlifeManager::GetInstance().CheckWildlifeMultiplayerSpawnConditions(modelIndex))
|
|
{
|
|
currentModelGroup->AddMember(modelIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPopCycle::GetNetworkVehicleModelsForCurrentZone(CLoadedModelGroup &commonModelsList, CLoadedModelGroup &zoneSpecificModelsList)
|
|
{
|
|
Assertf(sm_popGroups.GetPedCount() <= MaxPopCycleGroups, "popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
u32 popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetVehCount());
|
|
|
|
unsigned numZoneSpecificModels = 0;
|
|
bool overridingSpecificModel = false;
|
|
|
|
if(HasValidCurrentPopAllocation() && GetCurrentPopSchedule().GetOverrideVehicleModel() != fwModelId::MI_INVALID)
|
|
{
|
|
zoneSpecificModelsList.AddMember(GetCurrentPopSchedule().GetOverrideVehicleModel());
|
|
numZoneSpecificModels = 1;
|
|
overridingSpecificModel = true;
|
|
}
|
|
|
|
for (u32 popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if(GetCurrentVehGroupPercentage(popGroupIndex) > 0.0f)
|
|
{
|
|
const u32 numVehiclesInGroup = GetPopGroups().GetNumVehs(popGroupIndex);
|
|
|
|
for (u32 vehicleIndex = 0; vehicleIndex < numVehiclesInGroup; vehicleIndex++)
|
|
{
|
|
u32 modelIndex = GetPopGroups().GetVehIndex(popGroupIndex, vehicleIndex);
|
|
|
|
if(GetPopGroups().GetVehGroup(popGroupIndex).IsFlagSet(POPGROUP_NETWORK_COMMON))
|
|
{
|
|
commonModelsList.AddMember(modelIndex);
|
|
}
|
|
else
|
|
{
|
|
if(!overridingSpecificModel && numZoneSpecificModels < MAX_NUM_IN_LOADED_MODEL_GROUP)
|
|
{
|
|
zoneSpecificModelsList.AddMember(modelIndex);
|
|
numZoneSpecificModels++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : PickMostWantingGroupOfModels
|
|
// Go through the loaded models and analyzes the groups that they belong to. The group that needs the new model
|
|
// most gets it.
|
|
// TODO
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
s32 CPopCycle::PickMostWantingGroupOfModels(CLoadedModelGroup &ExistingGroup, bool bPeds, s32 **ppGroupList)
|
|
{
|
|
static float aPercentages[MaxPopCycleGroups];
|
|
static float aRequestedPercentages[MaxPopCycleGroups];
|
|
static float aNeed[MaxPopCycleGroups];
|
|
static s32 aNeedyGroup[MaxPopCycleGroups];
|
|
|
|
if(!HasValidCurrentPopAllocation())
|
|
return 0;
|
|
|
|
u32 popGroupCount=0;
|
|
if(bPeds)
|
|
{
|
|
Assertf(sm_popGroups.GetPedCount() <= MaxPopCycleGroups, "CPopCycle::PickMostWantingGroupOfModels - popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetPedCount());
|
|
}
|
|
else
|
|
{
|
|
Assertf(sm_popGroups.GetVehCount() <= MaxPopCycleGroups, "CPopCycle::PickMostWantingGroupOfModels - popcycle.dat has more than %d popgroup columns. MaxPopCycleGroups will need to be increased in code", MaxPopCycleGroups);
|
|
popGroupCount = Min(MaxPopCycleGroups, (u32)sm_popGroups.GetVehCount());
|
|
}
|
|
|
|
u32 popGroupIndex, totalModels = 0;
|
|
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
aPercentages[popGroupIndex] = 0.0f;
|
|
if(bPeds)
|
|
aRequestedPercentages[popGroupIndex] = GetCurrentPedGroupPercentage(popGroupIndex)*0.01f;//GetCurrentPopAllocation().GetPopGroupPercentage(popGroupIndex) * 0.01f;
|
|
else
|
|
aRequestedPercentages[popGroupIndex] = GetCurrentVehGroupPercentage(popGroupIndex)*0.01f;//GetCurrentPopAllocation().GetPopGroupPercentage(popGroupIndex) * 0.01f;
|
|
}
|
|
|
|
// Go through all of the loaded peds and give each group that they belong to 1 point.
|
|
s32 numMembers = ExistingGroup.CountMembers();
|
|
|
|
for (s32 m = 0; m < numMembers; m++)
|
|
{
|
|
u32 model = ExistingGroup.GetMember(m);
|
|
|
|
// Only consider models that aren't about to be streamed out.
|
|
fwModelId modelId((strLocalIndex(model)));
|
|
if ( !CModelInfo::GetAssetsAreDeletable(modelId))
|
|
{
|
|
totalModels++;
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
if ( (bPeds && GetPopGroups().IsPedIndexMember(popGroupIndex, model)) || ( (!bPeds) && GetPopGroups().IsVehIndexMember(popGroupIndex, model)) )
|
|
{
|
|
aPercentages[popGroupIndex] += 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bPeds)
|
|
{
|
|
// Apply special modifier for particularly needed peds.
|
|
for(popGroupIndex = 0; popGroupIndex < popGroupCount; ++popGroupIndex)
|
|
{
|
|
// If one of the gang groups needs peds and doesn't have any it gets priority.
|
|
if(GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_IS_GANG))
|
|
{
|
|
if (aRequestedPercentages[popGroupIndex] > 0.0f && aPercentages[popGroupIndex] == 0.0f)
|
|
{
|
|
aRequestedPercentages[popGroupIndex] *= 100.0f;
|
|
}
|
|
}
|
|
// If the player is in water aquatic peds get priority.
|
|
else if (GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_AQUATIC))
|
|
{
|
|
if (aRequestedPercentages[popGroupIndex] > 0.0f && CWildlifeManager::GetInstance().ShouldSpawnMoreAquaticPeds())
|
|
{
|
|
aRequestedPercentages[popGroupIndex] *= CWildlifeManager::GetInstance().GetIncreasedAquaticSpawningFactor();
|
|
}
|
|
}
|
|
else if (GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_AERIAL))
|
|
{
|
|
if (aRequestedPercentages[popGroupIndex] > 0.0f && CWildlifeManager::GetInstance().ShouldSpawnMoreAerialPeds())
|
|
{
|
|
aRequestedPercentages[popGroupIndex] *= CWildlifeManager::GetInstance().GetIncreasedGroundWildlifeSpawningFactor();
|
|
}
|
|
}
|
|
else if (GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_WILDLIFE))
|
|
{
|
|
if (aRequestedPercentages[popGroupIndex] > 0.0f && CWildlifeManager::GetInstance().ShouldSpawnMoreGroundWildlifePeds())
|
|
{
|
|
aRequestedPercentages[popGroupIndex] *= CWildlifeManager::GetInstance().GetIncreasedGroundWildlifeSpawningFactor();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 numWithNeed = 0;
|
|
for (popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
|
|
{
|
|
aPercentages[popGroupIndex] /= MAX(1, totalModels);
|
|
Assert(aPercentages[popGroupIndex] >= 0.0f && aPercentages[popGroupIndex] <= 1.0f);
|
|
Assert(aRequestedPercentages[popGroupIndex] >= 0.0f);
|
|
|
|
// Now calculate the number that determines how much this group needs a ped.
|
|
if (aRequestedPercentages[popGroupIndex] > 0.0f)
|
|
{
|
|
// if this is a rare group and we already have a model loaded from it don't add it to the list, we don't want more models
|
|
if (aPercentages[popGroupIndex] > 0.f)
|
|
{
|
|
if ((bPeds && GetPopGroups().GetPedGroup(popGroupIndex).IsFlagSet(POPGROUP_RARE)) || (!bPeds && GetPopGroups().GetVehGroup(popGroupIndex).IsFlagSet(POPGROUP_RARE)))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Now build a list of all the groups that have any need. Keep track of the number of them.
|
|
aNeed[numWithNeed] = aRequestedPercentages[popGroupIndex] - aPercentages[popGroupIndex];
|
|
aNeedyGroup[numWithNeed] = popGroupIndex;
|
|
numWithNeed++;
|
|
}
|
|
}
|
|
//Assert(numWithNeed > 0);
|
|
if(numWithNeed <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Sort the list by neediness.
|
|
bool bChange = true;
|
|
while (bChange)
|
|
{
|
|
bChange = false;
|
|
|
|
for (popGroupIndex = 0; popGroupIndex < numWithNeed-1; popGroupIndex++)
|
|
{
|
|
if (aNeed[popGroupIndex] < aNeed[popGroupIndex+1])
|
|
{
|
|
float temp1 = aNeed[popGroupIndex];
|
|
s32 temp2 = aNeedyGroup[popGroupIndex];
|
|
aNeed[popGroupIndex] = aNeed[popGroupIndex+1];
|
|
aNeedyGroup[popGroupIndex] = aNeedyGroup[popGroupIndex+1];
|
|
aNeed[popGroupIndex+1] = temp1;
|
|
aNeedyGroup[popGroupIndex+1] = temp2;
|
|
bChange = true;
|
|
}
|
|
}
|
|
}
|
|
*ppGroupList = aNeedyGroup;
|
|
return numWithNeed;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : AreTaxisRequired
|
|
// Purpose : If the taxis are required at all this function returns true.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool CPopCycle::AreTaxisRequired()
|
|
{
|
|
return true; // Taxis seem required everywhere on the map in IV.
|
|
|
|
//static const u32 taxiGroupNameHash = ATSTRINGHASH("TAXIS", 0x8DCB100);
|
|
//u32 popGroupIndexOfTaxis = 0;
|
|
//if(GetPopGroups().FindIndexFromNameHash(taxiGroupNameHash, popGroupIndexOfTaxis))
|
|
//{
|
|
// return (sm_zones[sm_pCurrZone->ZonePopulationType].m_timePopInfo[sm_nCurrentTimeIndex][sm_nCurrentTimeOfWeek].m_nPercTypeGroup.GetPercentage(popGroupIndexOfTaxis) > 0);
|
|
//}
|
|
//else
|
|
//{
|
|
// return false;
|
|
//}
|
|
}
|
|
|
|
CPopZone *CPopCycle::GetCurrentActiveZone(void)
|
|
{
|
|
return sm_pCurrZone;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : ClearPopCycleInfo
|
|
// Purpose : To clean the sm_popSchedules and sm_popGroups arrays out.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::ResetPopGroupsAndSchedules(void)
|
|
{
|
|
sm_popSchedules.Reset();
|
|
sm_popSchedulesMaxNumCars = 0;
|
|
sm_popGroups.Reset();
|
|
}
|
|
|
|
void CPopCycle::RefreshPopGroupData()
|
|
{
|
|
// Resize percentage arrays to be the same size as the popgroup array
|
|
int size = sm_popGroups.GetPedCount();
|
|
sm_currentPedPercentages.Reset();
|
|
sm_currentPedPercentages.Reserve(size);
|
|
sm_currentPedPercentages.Resize(size);
|
|
|
|
size = sm_popGroups.GetVehCount();
|
|
sm_currentVehPercentages.Reset();
|
|
sm_currentVehPercentages.Reserve(size);
|
|
sm_currentVehPercentages.Resize(size);
|
|
|
|
for(int i=0; i<sm_popGroups.GetPedCount(); i++)
|
|
sm_currentPedPercentages[i] = 0;
|
|
|
|
for(int i=0; i<sm_popGroups.GetVehCount(); i++)
|
|
sm_currentVehPercentages[i] = 0;
|
|
|
|
sm_commonVehicleGroup = ~0u;
|
|
sm_vehicleBikeGroup = ~0u;
|
|
|
|
atHashString vehMid("VEH_MID",0xB98B07D0);
|
|
sm_popGroups.FindVehGroupFromNameHash(vehMid, sm_commonVehicleGroup);
|
|
|
|
atHashString vehBikes("VEH_BIKES",0x14B612EF);
|
|
sm_popGroups.FindVehGroupFromNameHash(vehMid, sm_vehicleBikeGroup);
|
|
}
|
|
|
|
void CPopCycle::LoadPopGroupPatch(const char* file)
|
|
{
|
|
atHashString patchFileHash = atHashString(file);
|
|
|
|
if (sm_popGroupPatches.Find(patchFileHash) == -1)
|
|
sm_popGroupPatches.PushAndGrow(patchFileHash);
|
|
|
|
sm_popGroups.LoadPatch(file);
|
|
RefreshPopGroupData();
|
|
}
|
|
|
|
void CPopCycle::UnloadPopGroupPatch(const char* file)
|
|
{
|
|
sm_popGroupPatches.DeleteMatches(atHashString(file));
|
|
sm_popGroups.UnloadPatch(file);
|
|
RefreshPopGroupData();
|
|
}
|
|
|
|
void CPopCycle::LoadPopGroupsFile(const char* file)
|
|
{
|
|
sm_popGroups.Flush();
|
|
sm_popGroups.Reset();
|
|
|
|
// Try to load the new file and refresh the data
|
|
if (fwPsoStoreLoader::LoadDataIntoObject(file, META_FILE_EXT, sm_popGroups))
|
|
{
|
|
#if __BANK
|
|
const char* servicePath = "Population/popgroups";
|
|
fwPsoStoreLoadInstance inst(sm_popGroups.parser_GetPointer());
|
|
|
|
parRestUnregisterSingleton(servicePath, inst.GetInstance());
|
|
parRestRegisterSingleton(servicePath, sm_popGroups, file);
|
|
#endif
|
|
}
|
|
|
|
RefreshPopGroupData();
|
|
}
|
|
|
|
void CPopCycle::UnloadPopGroupsFile(const char* /*file*/)
|
|
{
|
|
// Just load via the original method
|
|
LoadPopGroups();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// name: LoadPopGroups
|
|
// description: Load the pedgrp.dat file and cargrp.dat files into the
|
|
// sm_popGroups array
|
|
// Parameters: None
|
|
// Returns: Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::LoadPopGroups(void)
|
|
{
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::POPGRP_FILE);
|
|
|
|
if (Verifyf(DATAFILEMGR.IsValid(pData), "Pop group file %s is not available", pData->m_filename))
|
|
{
|
|
// Load original data
|
|
LoadPopGroupsFile(pData->m_filename);
|
|
|
|
// Load any active patches on top
|
|
for (s32 i = 0; i < sm_popGroupPatches.GetCount(); i++)
|
|
{
|
|
CDataFileMgr::DataFile* pPopGroupPatch = DATAFILEMGR.FindDataFile(sm_popGroupPatches[i]);
|
|
|
|
LoadPopGroupPatch(pPopGroupPatch->m_filename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sm_popGroups.Reset();
|
|
RefreshPopGroupData();
|
|
}
|
|
}
|
|
|
|
#if __DEV
|
|
//#define WRITE_FILE_OUT // If this is defined the game will output a formatted version of Popcycle.datnew
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : LoadPopSchedules
|
|
// Purpose : Loads (or saves) the timecycle.dat files.
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::LoadPopSchedules(void)
|
|
{
|
|
// Load pop schedule files.
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::POPSCHED_FILE);
|
|
if(DATAFILEMGR.IsValid(pData))
|
|
{
|
|
// Read in this particular file
|
|
//char* fileName = "popcycle.dat";
|
|
sm_popSchedules.Load(pData->m_filename);
|
|
sm_popSchedules.PostLoad();
|
|
//NOTFINAL_ONLY(PARSER.SaveObject(pData->m_filename, "meta", &sm_popSchedules, parManager::XML));
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : LoadPopZoneToPopScheduleBindings
|
|
// Purpose : TODO
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CPopCycle::LoadPopZoneToPopScheduleBindings()
|
|
{
|
|
// Load pop schedule files.
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::ZONEBIND_FILE);
|
|
if (DATAFILEMGR.IsValid(pData))
|
|
{
|
|
// Read in this particular file
|
|
//char* fileName = "zonebind.dat";
|
|
LoadPopZoneToPopScheduleBindingsDataFile_PSO(pData->m_filename);
|
|
}
|
|
}
|
|
#if __BANK
|
|
void CPopCycle::ReloadPopZoneToPopScheduleBindings()
|
|
{
|
|
char filename[1024];
|
|
bool bOk = BANKMGR.OpenFile(filename, 1024, "*.pso.meta", false, "Browse for zonebind pso.meta file to load");
|
|
|
|
if(bOk)
|
|
{
|
|
// Read in this particular file
|
|
LoadPopZoneToPopScheduleBindingsDataFile_META(filename);
|
|
}
|
|
}
|
|
#endif
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Name : LoadPopZoneToPopScheduleBindingsDataFile
|
|
// Purpose : TODO
|
|
// Parameters : None
|
|
// Returns : Nothing
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
const u32 NoPopScheduleHash = ATSTRINGHASH("NO_POP_SCHEDULE", 0x07d143edb);
|
|
#if 0
|
|
const u32 ScumminessLevel0Hash = ATSTRINGHASH("SL_POSH", 0x0b876d280);
|
|
const u32 ScumminessLevel1Hash = ATSTRINGHASH("SL_NICE", 0x0b192cf1c);
|
|
const u32 ScumminessLevel2Hash = ATSTRINGHASH("SL_ABOVE_AV", 0x03f9c1cde);
|
|
const u32 ScumminessLevel3Hash = ATSTRINGHASH("SL_BELOW_AV", 0x0fe811e7b);
|
|
const u32 ScumminessLevel4Hash = ATSTRINGHASH("SL_CRAP", 0x0d0dd77d4);
|
|
const u32 ScumminessLevel5Hash = ATSTRINGHASH("SL_SCUM", 0x0b3a5ff70);
|
|
const u32 SpecialNoneHash = ATSTRINGHASH("SPECIAL_NONE", 0x067e4daf8);
|
|
const u32 SpecialAirportHash = ATSTRINGHASH("SPECIAL_AIRPORT", 0x0e9b99648);
|
|
#endif
|
|
|
|
void CPopZoneData::OnPostSetCallback(parTreeNode* UNUSED_PARAM(pTreeNode), parMember* UNUSED_PARAM(pMember), void* pInstance)
|
|
{
|
|
CPopZoneData* zoneData = static_cast<CPopZoneData*>(pInstance);
|
|
if (zoneData)
|
|
CPopCycle::ApplyZoneBindData(zoneData);
|
|
}
|
|
|
|
void CPopCycle::ApplyZoneBindData(const CPopZoneData* data)
|
|
{
|
|
Assert(data);
|
|
for (s32 i = 0; i < data->zones.GetCount(); ++i)
|
|
{
|
|
const CPopZoneData::sZone& zone = data->zones[i];
|
|
|
|
CPopZone* pZone = CPopZones::GetPopZone(zone.zoneName.c_str());
|
|
Assertf(pZone, "CPopCycle::LoadPopZoneToPopScheduleBindings - couldn't find a popzone called '%s'", zone.zoneName.c_str());
|
|
int popZoneId = CPopZones::GetPopZoneId(pZone);
|
|
|
|
if (popZoneId == CPopZones::INVALID_ZONEID)
|
|
continue;
|
|
|
|
s32 popScheduleIndexForSP = -1;
|
|
if (zone.spName.GetHash() != NoPopScheduleHash)
|
|
{
|
|
#if __ASSERT
|
|
bool scheduleFoundForSP =
|
|
#endif // __ASSERT
|
|
sm_popSchedules.GetIndexFromNameHash(zone.spName.GetHash(), popScheduleIndexForSP);
|
|
Assertf(scheduleFoundForSP, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Unrecognized pop schedule name '%s'", zone.spName.TryGetCStr());
|
|
}
|
|
|
|
s32 popScheduleIndexForMP = -1;
|
|
if (zone.mpName.GetHash() != NoPopScheduleHash)
|
|
{
|
|
#if __ASSERT
|
|
bool scheduleFoundForMP =
|
|
#endif // __ASSERT
|
|
sm_popSchedules.GetIndexFromNameHash(zone.mpName.GetHash(), popScheduleIndexForMP);
|
|
Assertf(scheduleFoundForMP, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Unrecognized pop schedule name '%s'", zone.mpName.TryGetCStr());
|
|
}
|
|
|
|
Assertf(popScheduleIndexForSP != -1 || popScheduleIndexForMP != -1, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Zone with no SP or MP pop schedule specified!");
|
|
CPopZones::SetPopScheduleIndices(popZoneId, popScheduleIndexForSP, popScheduleIndexForMP);
|
|
|
|
CPopZones::SetScumminess(popZoneId, zone.scumminessLevel);
|
|
|
|
// set zone dirt info
|
|
Color32 dirtCol(zone.dirtRed, zone.dirtGreen, zone.dirtBlue);
|
|
CPopZones::SetVehDirtRange(popZoneId, zone.vehDirtMin, zone.vehDirtMax);
|
|
CPopZones::SetPedDirtRange(popZoneId, zone.pedDirtMin, zone.pedDirtMax);
|
|
CPopZones::SetDirtGrowScale(popZoneId, zone.vehDirtGrowScale);
|
|
CPopZones::SetDirtColor(popZoneId, dirtCol);
|
|
|
|
CPopZones::SetVfxRegionHashName(popZoneId, zone.vfxRegion);
|
|
CPopZones::SetPlantsMgrTxdIdx(popZoneId, zone.plantsMgrTxdIdx);
|
|
|
|
CPopZones::SetLawResponseDelayType(popZoneId, zone.lawResponseTime);
|
|
CPopZones::SetVehicleResponseType(popZoneId, zone.lawResponseType);
|
|
|
|
CPopZones::SetMPGangTerritoryIndex(popZoneId, zone.mpGangTerritoryIndex);
|
|
|
|
CPopZones::SetSpecialAttribute(popZoneId, zone.specialZoneAttribute);
|
|
|
|
CPopZones::SetPopulationRangeScaleParameters(popZoneId, zone.popBaseRangeScale, zone.popRangeScaleStart, zone.popRangeScaleEnd, zone.popRangeMultiplier);
|
|
|
|
CPopZones::SetAllowScenarioWeatherExits(popZoneId, zone.allowScenarioWeatherExits);
|
|
CPopZones::SetAllowHurryAwayToLeavePavement(popZoneId, zone.allowHurryAwayToLeavePavement);
|
|
}
|
|
}
|
|
|
|
void CPopCycle::LoadPopZoneToPopScheduleBindingsDataFile_META(const char* fileName)
|
|
{
|
|
fwPsoStoreLoader loader = fwPsoStoreLoader::MakeSimpleFileLoader<CPopZoneData>();
|
|
fwPsoStoreLoadInstance inst;
|
|
|
|
loader.Load(fileName, inst);
|
|
|
|
CPopZoneData* zoneData = reinterpret_cast<CPopZoneData*>(inst.GetInstance());
|
|
|
|
parRestRegisterSingleton("popzone/zonebind", *zoneData, fileName);
|
|
|
|
ApplyZoneBindData(zoneData);
|
|
|
|
loader.Unload(inst);
|
|
}
|
|
|
|
void CPopCycle::LoadPopZoneToPopScheduleBindingsDataFile_PSO(const char* fileName)
|
|
{
|
|
// Try to load into a temporary CPopZoneData object.
|
|
CPopZoneData zoneData;
|
|
if (Verifyf(fwPsoStoreLoader::LoadDataIntoObject(fileName, META_FILE_EXT, zoneData), "Failed to load zonebind metadata file: %s", fileName))
|
|
{
|
|
ApplyZoneBindData(&zoneData);
|
|
}
|
|
}
|
|
|
|
void CPopCycle::LoadZoneBindFile(const char* fileName)
|
|
{
|
|
LoadPopZoneToPopScheduleBindingsDataFile_PSO(fileName);
|
|
}
|
|
|
|
void CPopCycle::UnloadZoneBindFile(const char* UNUSED_PARAM(fileName))
|
|
{
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::ZONEBIND_FILE);
|
|
LoadPopZoneToPopScheduleBindingsDataFile_PSO(pData->m_filename);
|
|
}
|
|
|
|
void CPopCycle::LoadPopSchedFile(const char* fileName)
|
|
{
|
|
CPopCycle::GetPopSchedules().Reset();
|
|
CPopCycle::GetPopSchedules().Load(fileName);
|
|
CPopCycle::GetPopSchedules().PostLoad();
|
|
}
|
|
|
|
void CPopCycle::UnloadPopSchedFile(const char* UNUSED_PARAM(fileName))
|
|
{
|
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::POPSCHED_FILE);
|
|
CPopCycle::LoadPopSchedFile(pData->m_filename);
|
|
}
|
|
|
|
|
|
#if 0 // use to convert from zonebind.dat to .meta
|
|
void CPopCycle::LoadPopZoneToPopScheduleBindingsDataFile2(const char* fileName)
|
|
{
|
|
CPopZoneData zoneData;
|
|
|
|
// Try to open the file.
|
|
//CFileMgr::SetDir("common:/data/");
|
|
FileHandle fd = CFileMgr::OpenFile(fileName, "rb");
|
|
Assertf(CFileMgr::IsValidFileHandle(fd), "Problem opening zone binding file.");
|
|
//CFileMgr::SetDir("");
|
|
|
|
char line[2048];
|
|
while(CFileMgr::ReadLine(fd, &line[0], 1024))
|
|
{
|
|
// Check for empty or commented out lines...
|
|
if(line[0] == '/' || line[0] == '\0' || line[1] == '\0' || line[2] == '\0') // Note: ps3 returns empty lines as space,0
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CPopZoneData::sZone zone;
|
|
|
|
// Read the part of the line with the name of the zone.
|
|
char popZoneNameBuff[256];
|
|
const char* seps = " ,\t";
|
|
char* pToken = NULL;
|
|
pToken = strtok(line, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Badly formed line encountered.");
|
|
strncpy(popZoneNameBuff, pToken, 256 );
|
|
zone.zoneName = popZoneNameBuff;
|
|
|
|
// Read the part of the line with the name of the SP pop schedule to use for that zone.
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line.");
|
|
|
|
// Get the index of the SP schedule we're interested in.
|
|
u32 popScheduleNameHashForSP = atStringHash(pToken);
|
|
s32 popScheduleIndexForSP = -1;
|
|
zone.spName = pToken;
|
|
|
|
if(popScheduleNameHashForSP != NoPopScheduleHash)
|
|
{
|
|
#if __ASSERT
|
|
bool scheduleFoundForSP =
|
|
#endif // __ASSERT
|
|
sm_popSchedules.GetIndexFromNameHash(popScheduleNameHashForSP, popScheduleIndexForSP);
|
|
Assertf(scheduleFoundForSP, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Unrecognized pop schedule name %s.", pToken);
|
|
}
|
|
|
|
// Read the part of the line with the name of the MP pop schedule to use for that zone.
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line.");
|
|
|
|
// Get the index of the MP schedule we're interested in.
|
|
u32 popScheduleNameHashForMP = atStringHash(pToken);
|
|
s32 popScheduleIndexForMP = -1;
|
|
zone.mpName = pToken;
|
|
|
|
if(popScheduleNameHashForMP != NoPopScheduleHash)
|
|
{
|
|
#if __ASSERT
|
|
bool scheduleFoundForMP =
|
|
#endif // __ASSERT
|
|
sm_popSchedules.GetIndexFromNameHash(popScheduleNameHashForMP, popScheduleIndexForMP);
|
|
Assertf(scheduleFoundForMP, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Unrecognized pop schedule name %s.", pToken);
|
|
}
|
|
|
|
Assertf(popScheduleIndexForSP != -1 || popScheduleIndexForMP != -1, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Zone with no SP or MP pop schedule specified!");
|
|
|
|
CPopZone* pZone = CPopZones::GetPopZone(popZoneNameBuff);
|
|
Assertf(pZone, "CPopCycle::LoadPopZoneToPopScheduleBindings - couldn't find a zone called %s in POPZONE.IPL", popZoneNameBuff);
|
|
int popZoneId = CPopZones::GetPopZoneId(pZone);
|
|
|
|
// Read the part of the line with scumminess level to use for that zone.
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line.");
|
|
if (popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
int iScumminessLevel = CPopZones::GetZoneScumminessFromString(pToken);
|
|
CPopZones::SetScumminess(popZoneId, iScumminessLevel);
|
|
zone.scumminessLevel = (eZoneScumminess)iScumminessLevel;
|
|
}
|
|
|
|
// read vehicle dirt info
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
float dirtMin = (float)atof(pToken);
|
|
zone.vehDirtMin = dirtMin;
|
|
zone.pedDirtMin = dirtMin;
|
|
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line, missing dirtMax");
|
|
float dirtMax = pToken ? (float)atof(pToken) : -1.f;
|
|
zone.vehDirtMax = dirtMax;
|
|
zone.pedDirtMax = dirtMax;
|
|
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line, missing dirtGrowScale");
|
|
float dirtGrowScale = pToken ? (float)atof(pToken) : 1.0f;
|
|
zone.vehDirtGrowScale = dirtGrowScale;
|
|
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line, missing dirtRed");
|
|
float dirtR = pToken ? (float)atof(pToken) : 0.f;
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line, missing dirtGreen");
|
|
float dirtG = pToken ? (float)atof(pToken) : 0.f;
|
|
pToken = strtok(NULL, seps);
|
|
Assertf(pToken, "CPopCycle::LoadPopZoneToPopScheduleBindings(), Not enough items on the line, missing dirtBlue");
|
|
float dirtB = pToken ? (float)atof(pToken) : 0.f;
|
|
|
|
Color32 dirtCol(dirtR, dirtG, dirtB, 1.f);
|
|
zone.dirtRed = dirtCol.GetRed();
|
|
zone.dirtGreen = dirtCol.GetGreen();
|
|
zone.dirtBlue = dirtCol.GetBlue();
|
|
|
|
if(popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
// set zone dirt info
|
|
CPopZones::SetVehDirtRange(popZoneId, dirtMin, dirtMax);
|
|
CPopZones::SetPedDirtRange(popZoneId, dirtMin, dirtMax);
|
|
CPopZones::SetDirtGrowScale(popZoneId, dirtGrowScale);
|
|
CPopZones::SetDirtColor(popZoneId, dirtCol);
|
|
}
|
|
}
|
|
|
|
// read in the vfx region data
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
if(popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
CPopZones::SetVfxRegionHashName(popZoneId, atHashWithStringNotFinal(pToken));
|
|
}
|
|
zone.vfxRegion = pToken;
|
|
}
|
|
|
|
// read in the law response time
|
|
int iLawResponseTime = LAW_RESPONSE_DELAY_MEDIUM;
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
iLawResponseTime = CDispatchData::GetLawResponseDelayTypeFromString(pToken);
|
|
}
|
|
zone.lawResponseTime = (eLawResponseTime)iLawResponseTime;
|
|
|
|
int iLawZoneType = VEHICLE_RESPONSE_DEFAULT;
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
iLawZoneType = CDispatchData::GetVehicleResponseTypeFromString(pToken);
|
|
}
|
|
zone.lawResponseType = (eZoneVehicleResponseType)iLawZoneType;
|
|
|
|
// read in the multiplayer gang territory index (-1 means zone is not part of a territory)
|
|
pToken = strtok(NULL, seps);
|
|
s32 territoryIndex = -1;
|
|
if (pToken)
|
|
{
|
|
territoryIndex = atoi(pToken);
|
|
}
|
|
zone.mpGangTerritoryIndex = territoryIndex;
|
|
|
|
// Read in the Special Zone attributes
|
|
int iSpecialZoneAttribute = SPECIAL_NONE;
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
iSpecialZoneAttribute = CPopZones::GetSpecialZoneAttributeFromString(pToken);
|
|
}
|
|
zone.specialZoneAttribute = (eZoneSpecialAttributeType)iSpecialZoneAttribute;
|
|
|
|
if(popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
// Set the zones schedule.
|
|
CPopZones::SetPopScheduleIndices(popZoneId, popScheduleIndexForSP, popScheduleIndexForMP);
|
|
|
|
// Set the law response delay type
|
|
CPopZones::SetLawResponseDelayType(popZoneId, iLawResponseTime);
|
|
|
|
// Set the law response delay type
|
|
CPopZones::SetVehicleResponseType(popZoneId, iLawZoneType);
|
|
|
|
// Set the MP Gang Territory index
|
|
CPopZones::SetMPGangTerritoryIndex(popZoneId, territoryIndex);
|
|
|
|
// Set the special attributes
|
|
CPopZones::SetSpecialAttribute(popZoneId, iSpecialZoneAttribute);
|
|
}
|
|
|
|
|
|
float fPopMultStartZ = 0.0f;
|
|
float fPopMultEndZ = 0.0f;
|
|
float fPopHeightMultiplier = 1.0f;
|
|
|
|
float fPopBaseRangeScale = 1.0f;
|
|
|
|
// Read in the low (start) Z used for multiplying population range based on height
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
fPopMultStartZ = (float)atof(pToken);
|
|
}
|
|
|
|
// Read in the high (end) Z used for multiplying population range based on height
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
fPopMultEndZ = (float)atof(pToken);
|
|
}
|
|
|
|
// Read in the total height-based multiplier on population range when height >= fPopMultEndZ
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
fPopHeightMultiplier = (float)atof(pToken);
|
|
}
|
|
|
|
// Read in the total base range multiplier for this zone
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
fPopBaseRangeScale = (float)atof(pToken);
|
|
}
|
|
|
|
if(popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
CPopZones::SetPopulationRangeScaleParameters(popZoneId, fPopBaseRangeScale, fPopMultStartZ, fPopMultEndZ, fPopHeightMultiplier);
|
|
}
|
|
|
|
zone.popRangeScaleStart = fPopMultStartZ;
|
|
zone.popRangeScaleEnd = fPopMultEndZ;
|
|
zone.popBaseRangeScale = fPopBaseRangeScale;
|
|
zone.popRangeMultiplier = fPopHeightMultiplier;
|
|
|
|
int iAllowScenarioWeatherExits = 1; // Default to allowing scenario weather exits
|
|
// Read in the AllowScenarioWeatherExits flag
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
iAllowScenarioWeatherExits = (int)atoi(pToken);
|
|
}
|
|
if (popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
CPopZones::SetAllowScenarioWeatherExits(popZoneId, iAllowScenarioWeatherExits);
|
|
}
|
|
zone.allowScenarioWeatherExits = iAllowScenarioWeatherExits != 0;
|
|
|
|
int iAllowHurryAwayToLeavePavements = 0; // Default to not allow this.
|
|
// Read in the CareAboutPavementsForShockingEvents flag
|
|
pToken = strtok(NULL, seps);
|
|
if (pToken)
|
|
{
|
|
iAllowHurryAwayToLeavePavements = (int)atoi(pToken);
|
|
}
|
|
zone.allowHurryAwayToLeavePavement = iAllowHurryAwayToLeavePavements != 0;
|
|
if (popZoneId != CPopZones::INVALID_ZONEID)
|
|
{
|
|
CPopZones::SetAllowHurryAwayToLeavePavement(popZoneId, iAllowHurryAwayToLeavePavements);
|
|
|
|
zoneData.zones.PushAndGrow(zone);
|
|
}
|
|
}
|
|
|
|
PARSER.SaveObject("X:/zonebind", "meta", &zoneData, parManager::XML);
|
|
// Close the zone binding file.
|
|
CFileMgr::CloseFile(fd);
|
|
}
|
|
#endif
|