From f71953077d2412c4542e616cc9748217dfe046c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Tue, 29 Sep 2020 22:53:12 +0300 Subject: Population and many small classes done, mouse AUX buttons, Frontend, Hud, fixes --- src/peds/Population.cpp | 1024 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 842 insertions(+), 182 deletions(-) (limited to 'src/peds/Population.cpp') diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp index 35a09a4f..94533f4a 100644 --- a/src/peds/Population.cpp +++ b/src/peds/Population.cpp @@ -23,6 +23,13 @@ #include "Script.h" #include "Shadows.h" #include "SurfaceTable.h" +#include "Weather.h" +#include "Darkel.h" +#include "Streaming.h" +#include "Clock.h" +#include "WaterLevel.h" + +// --MIAMI: File done #define MIN_CREATION_DIST 40.0f // not for start of the game (look at the GeneratePedsAtStartOfGame) #define CREATION_RANGE 10.0f // added over the MIN_CREATION_DIST. @@ -30,37 +37,13 @@ #define PED_REMOVE_DIST (MIN_CREATION_DIST + CREATION_RANGE + 1.0f) #define PED_REMOVE_DIST_SPECIAL (MIN_CREATION_DIST + CREATION_RANGE + 15.0f) // for peds with bCullExtraFarAway flag -// Transition areas between zones -const RegenerationPoint aSafeZones[] = { -// TODO(MIAMI): this is totally bogus - { LEVEL_BEACH, LEVEL_MAINLAND, 400.0f, 814.0f, -954.0f, -903.0f, 30.0f, 100.0f, - CVector(790.0f, -917.0f, 39.0f), CVector(775.0f, -921.0f, 39.0f), CVector(424.0f, -942.0f, 38.0f), CVector(439.0f, -938.0f, 38.0f) }, -#ifndef MIAMI - { LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 400.0f, 814.0f, -954.0f, -903.0f, 30.0f, 100.0f, - CVector(790.0f, -917.0f, 39.0f), CVector(775.0f, -921.0f, 39.0f), CVector(424.0f, -942.0f, 38.0f), CVector(439.0f, -938.0f, 38.0f) }, - { LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 555.0f, 711.0f, 118.0f, 186.0f, -30.0f, -10.0f, - CVector(698.0f, 182.0f, -20.0f), CVector(681.0f, 178.0f, -20.0f), CVector(586.0f, 144.0f, -20.0f), CVector(577.0f, 135.0f, -20.0f) }, - { LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 626.0f, 744.0f, -124.0f, -87.0f, -20.0f, -6.0f, - CVector(736.0f, -117.0f, -13.0f), CVector(730.0f, -115.0f, -13.0f), CVector(635.0f, -93.0f, -12.5f), CVector(650.0f, -89.0f, -12.5f) }, - { LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 645.0f, 734.0f, -780.0f, -750.0f, -25.0f, -6.0f, - CVector(729.0f, -764.0f, -18.0f), CVector(720.0f, -769.0f, -17.0f), CVector(652.0f, -774.0f, -10.5f), CVector(659.0f, -770.0f, -10.5f) }, - { LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -532.0f, -136.0f, -668.0f, -599.0f, 34.0f, 60.0f, - CVector(-172.0f, -619.0f, 44.0f), CVector(-183.0f, -623.0f, 44.0f), CVector(-511.0f, -645.0f, 41.0f), CVector(-493.0f, -639.0f, 41.5f) }, - { LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -325.0f, -175.0f, 27.0f, 75.0f, -30.0f, -10.0f, - CVector(-185.0f, 40.8f, -20.5f), CVector(-202.0f, 37.0f, -20.5f), CVector(-315.0f, 65.5f, -20.5f), CVector(-306.0f, 62.4f, -20.5f) }, - { LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -410.0f, -310.0f, -1055.0f, -1030.0f, -20.0f, -6.0f, - CVector(-321.0f, -1043.0f, -13.2f), CVector(-328.0f, -1045.0f, -13.2f), CVector(-398.0f, -1044.0f, -13.5f), CVector(-390.0f, -1040.5f, -13.5f) }, - { LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -425.0f, -280.0f, -471.0f, -447.0f, -20.0f, -5.0f, - CVector(-292.0f, -457.0f, -11.6f), CVector(-310.0f, -461.0f, -11.6f), CVector(-413.0f, -461.0f, -11.5f), CVector(-399.0f, -457.0f, -11.3f) } -#endif -}; - PedGroup CPopulation::ms_pPedGroups[NUMPEDGROUPS]; bool CPopulation::ms_bGivePedsWeapons; int32 CPopulation::m_AllRandomPedsThisType = -1; float CPopulation::PedDensityMultiplier = 1.0f; uint32 CPopulation::ms_nTotalMissionPeds; int32 CPopulation::MaxNumberOfPedsInUse = 25; +int32 CPopulation::MaxNumberOfPedsInUseInterior = 40; uint32 CPopulation::ms_nNumCivMale; uint32 CPopulation::ms_nNumCivFemale; uint32 CPopulation::ms_nNumCop; @@ -80,13 +63,14 @@ uint32 CPopulation::ms_nNumGang6; uint32 CPopulation::ms_nNumGang9; uint32 CPopulation::ms_nNumGang7; uint32 CPopulation::ms_nNumGang8; -CVector CPopulation::RegenerationPoint_a; -CVector CPopulation::RegenerationPoint_b; -CVector CPopulation::RegenerationForward; uint32 CPopulation::ms_nTotalCarPassengerPeds; uint32 CPopulation::NumMiamiViceCops; +uint32 gLastSelectedCivilianIndex; +CEntity *gSunbatheObstacles[2]; +CEntity *gCoupleObstacles[3]; + void CPopulation::Initialise() { @@ -108,16 +92,16 @@ CPopulation::Initialise() ms_nNumDummy = 0; ms_nTotalCarPassengerPeds = 0; + ms_nTotalCivPeds = 0; + ms_nTotalGangPeds = 0; + ms_nTotalPeds = 0; + ms_nTotalMissionPeds = 0; + m_CountDownToPedsAtStart = 2; + bZoneChangeHasHappened = false; // III leftover m_AllRandomPedsThisType = -1; PedDensityMultiplier = 1.0f; - bZoneChangeHasHappened = false; - m_CountDownToPedsAtStart = 2; - ms_nTotalMissionPeds = 0; - ms_nTotalPeds = 0; - ms_nTotalGangPeds = 0; - ms_nTotalCivPeds = 0; LoadPedGroups(); @@ -134,7 +118,45 @@ CPopulation::RemovePed(CPed *ent) int32 CPopulation::ChooseCivilianOccupation(int32 group) { - return ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)]; + if (CWeather::Rain > 0.1f) { + int32 lastModel; + for (int i = 0; i < 8; i++) { + gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + + if (!CPopulation::IsSunbather(lastModel)) + break; + } + return lastModel; + + } else { + gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + } +} + +int32 +CPopulation::ChooseNextCivilianOccupation(int32 group) +{ + if (CWeather::Rain > 0.1f) { + int32 lastModel; + for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { + ++gLastSelectedCivilianIndex; + if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) + gLastSelectedCivilianIndex = 0; + lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + + if (!CPopulation::IsSunbather(ms_pPedGroups[group].models[gLastSelectedCivilianIndex])) + break; + } + return lastModel; + + } else { + ++gLastSelectedCivilianIndex; + if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) + gLastSelectedCivilianIndex = 0; + return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + } } // returns eCopType @@ -332,54 +354,17 @@ CPopulation::ChooseGangOccupation(int gangId) return CGangs::ChooseGangPedModel(gangId); } -//--MIAMI: done void CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool forceIndustrialZone) { } -void -CPopulation::FindCollisionZoneForCoors(CVector *coors, int *safeZoneOut, eLevelName *levelOut) -{ - *safeZoneOut = -1; - for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) { - if (coors->x > aSafeZones[i].x1 && coors->x < aSafeZones[i].x2) { - if (coors->y > aSafeZones[i].y1 && coors->y < aSafeZones[i].y2) { - if (coors->z > aSafeZones[i].z1 && coors->z < aSafeZones[i].z2) - *safeZoneOut = i; - } - } - } - // Then it's transition area - if (*safeZoneOut >= 0) - *levelOut = LEVEL_GENERIC; - else - *levelOut = CTheZones::GetLevelFromPosition(coors); -} - -void -CPopulation::FindClosestZoneForCoors(CVector *coors, int *safeZoneOut, eLevelName level1, eLevelName level2) -{ - float minDist = 10000000.0f; - int closestSafeZone = -1; - for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) { - if ((level1 == aSafeZones[i].srcLevel || level1 == aSafeZones[i].destLevel) && (level2 == aSafeZones[i].srcLevel || level2 == aSafeZones[i].destLevel)) { - CVector2D safeZoneDistVec(coors->x - (aSafeZones[i].x1 + aSafeZones[i].x2) * 0.5f, coors->y - (aSafeZones[i].y1 + aSafeZones[i].y2) * 0.5f); - float safeZoneDist = safeZoneDistVec.Magnitude(); - if (safeZoneDist < minDist) { - minDist = safeZoneDist; - closestSafeZone = i; - } - } - } - *safeZoneOut = closestSafeZone; -} - void CPopulation::Update(bool addPeds) { if (!CReplay::IsPlayingBack()) { ManagePopulation(); + RemovePedsIfThePoolGetsFull(); MoveCarsAndPedsOutOfAbandonedZones(); if (m_CountDownToPedsAtStart != 0) { if (--m_CountDownToPedsAtStart == 0) @@ -406,7 +391,7 @@ CPopulation::Update(bool addPeds) void CPopulation::GeneratePedsAtStartOfGame() { - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 100; i++) { ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale; ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7 + ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 @@ -421,20 +406,6 @@ CPopulation::GeneratePedsAtStartOfGame() } } -bool -CPopulation::IsPointInSafeZone(CVector *coors) -{ - for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) { - if (coors->x > aSafeZones[i].x1 && coors->x < aSafeZones[i].x2) { - if (coors->y > aSafeZones[i].y1 && coors->y < aSafeZones[i].y2) { - if (coors->z > aSafeZones[i].z1 && coors->z < aSafeZones[i].z2) - return true; - } - } - } - return false; -} - // More speed = wider area to spawn peds float CPopulation::PedCreationDistMultiplier() @@ -556,8 +527,10 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree int pedAmount; CZoneInfo zoneInfo; + int32 man = -1, woman = -1; CPed *gangLeader = nil; bool addCop = false; + bool isSecurityGuard = false; bool forceAddingCop = false; CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; CVector playerCentreOfWorld = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); @@ -580,9 +553,25 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE); } } + float missionAndWeatherMult = -0.8f * Sqrt(CWeather::Rain) + 1.0f; + + // Taxi side mission + if (CTheScripts::IsPlayerOnAMission()) { + CPed *player = FindPlayerPed(); + if (player && player->InVehicle()) { + int32 model = player->m_pMyVehicle->GetModelIndex(); + if (model == MI_TAXI || model == MI_CABBIE || model == MI_ZEBRA || model == MI_KAUFMAN) + missionAndWeatherMult = 1.0f; + } + } + if (CDarkel::FrenzyOnGoing()) + missionAndWeatherMult = 1.0f; + int selectedMaxPeds = CGame::IsInInterior() ? CPopulation::MaxNumberOfPedsInUseInterior : CPopulation::MaxNumberOfPedsInUse; + // Yeah, float - float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier * CIniFile::PedNumberMultiplier; - maxPossiblePedsForArea = Min(maxPossiblePedsForArea, MaxNumberOfPedsInUse); + float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier + * (CDarkel::FrenzyOnGoing() ? 1.f : CIniFile::PedNumberMultiplier) * missionAndWeatherMult; + maxPossiblePedsForArea = Min(maxPossiblePedsForArea, selectedMaxPeds); if (ms_nTotalPeds < maxPossiblePedsForArea || addCop) { int decisionThreshold = CGeneral::GetRandomNumberInRange(0, 1000); @@ -590,39 +579,48 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree pedTypeToAdd = PEDTYPE_COP; modelToAdd = ChoosePolicePedOccupation(); } else { - for (int i = 0; i < NUM_GANGS; i++) { + int i = 0; + for (i = 0; i < NUM_GANGS; i++) { if (decisionThreshold < zoneInfo.gangPedThreshold[i]) { - pedTypeToAdd = PEDTYPE_GANG1 + i; break; } + } - if (i == NUM_GANGS - 1) { + if (i == NUM_GANGS) { + if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) <= 0.95f) { modelToAdd = ChooseCivilianOccupation(zoneInfo.pedGroup); + if (modelToAdd == -1) return; pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; + + } else { + ChooseCivilianCoupleOccupations(zoneInfo.pedGroup, man, woman); + if (man == -1 || woman == -1) + return; + pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(woman))->m_pedType; } + } else { + pedTypeToAdd = PEDTYPE_GANG1 + i; + if (IsSecurityGuard((ePedType)pedTypeToAdd)) { + isSecurityGuard = true; + modelToAdd = ChooseGangOccupation(pedTypeToAdd - PEDTYPE_GANG1); + + if (modelToAdd == -1) + return; + pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; + + } } } if (!addCop && m_AllRandomPedsThisType > PEDTYPE_PLAYER1) pedTypeToAdd = m_AllRandomPedsThisType; - if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9) { - int randVal = CGeneral::GetRandomNumber() % 100; - if (randVal < 50) - return; - - if (randVal < 57) { - pedAmount = 1; - } else if (randVal >= 74) { - if (randVal >= 85) - pedAmount = 4; - else - pedAmount = 3; - } else { - pedAmount = 2; - } + if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { + minDist += 30.0f; + maxDist += 30.0f; + pedAmount = ComputeRandomisedGangSize(); } else pedAmount = 1; @@ -635,35 +633,59 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree if (!foundCoors) return; + uint8 nodeSpawnRate = Min(ThePaths.m_pathNodes[node1].spawnRate, ThePaths.m_pathNodes[node2].spawnRate); + int randomRate = CGeneral::GetRandomNumber() & 0xF; + if (randomRate > nodeSpawnRate) + return; + + CPathFind::TakeWidthIntoAccountForCoors(&ThePaths.m_pathNodes[node1], &ThePaths.m_pathNodes[node2], CGeneral::GetRandomNumber(), &generatedCoors.x, &generatedCoors.y); + if (CGame::currArea == AREA_MALL && (pedTypeToAdd == PEDTYPE_CIVMALE || pedTypeToAdd == PEDTYPE_CIVFEMALE || pedTypeToAdd == PEDTYPE_CRIMINAL) && + CGeneral::GetRandomNumberInRange(0.f, 1.f) > 0.5f) { + + PlaceMallPedsAsStationaryGroup(generatedCoors, zoneInfo.pedGroup); + return; + } + + if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { + PlaceGangMembers((ePedType)pedTypeToAdd, pedAmount, generatedCoors); + return; + } + + if (man > -1 && woman > -1) { + PlaceCouple(PEDTYPE_CIVMALE, man, PEDTYPE_CIVFEMALE, woman, generatedCoors); + return; + } + for (int i = 0; i < pedAmount; ++i) { - if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9) - modelToAdd = ChooseGangOccupation(pedTypeToAdd - PEDTYPE_GANG1); if (pedTypeToAdd == PEDTYPE_COP) { // Unused code, ChoosePolicePedOccupation returns COP_STREET. Spawning FBI/SWAT/Army done in somewhere else. if (modelToAdd == COP_STREET) { - if (!CModelInfo::GetModelInfo(MI_COP)->GetRwObject()) + if (!CStreaming::HasModelLoaded(MI_COP)) return; } else if (modelToAdd == COP_FBI) { - if (!CModelInfo::GetModelInfo(MI_FBI)->GetRwObject()) + if (!CStreaming::HasModelLoaded(MI_COP) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId)) return; } else if (modelToAdd == COP_SWAT) { - if (!CModelInfo::GetModelInfo(MI_SWAT)->GetRwObject()) + if (!CStreaming::HasModelLoaded(MI_SWAT) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_UZI)->m_nModelId)) return; - } else if (modelToAdd == COP_ARMY && !CModelInfo::GetModelInfo(MI_ARMY)->GetRwObject()) { - return; + } else if (modelToAdd == COP_ARMY) { + if (!CStreaming::HasModelLoaded(MI_ARMY) || + !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_GRENADE)->m_nModelId)) + return; } - } else if (!CModelInfo::GetModelInfo(modelToAdd)->GetRwObject()) { + } else if (!CStreaming::HasModelLoaded(modelToAdd)) { return; } generatedCoors.z += 0.7f; // What? How can this not be met? if (i < pedAmount) { - //rand() + // rand() + // III leftover, unused if (gangLeader) { // Align gang members in formation. (btw i can't be 0 in here) float offsetMin = i * 0.75f; @@ -690,32 +712,81 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree generatedCoors.z = Max(generatedCoors.z, groundZ); } - bool farEnoughToAdd = true; - CMatrix mat(TheCamera.GetCameraMatrix()); - if (TheCamera.IsSphereVisible(generatedCoors, 2.0f, &mat)) { + bool surfaceAndDistIsOk = true; + if (TheCamera.IsSphereVisible(generatedCoors, 2.0f)) { if (PedCreationDistMultiplier() * MIN_CREATION_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D()) - farEnoughToAdd = false; + surfaceAndDistIsOk = false; + } + + // Place skaters if only they're on tarmac. + if (((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedStatType == PEDSTAT_SKATER) { + CEntity* foundEnt = nil; + CColPoint foundCol; + CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); + if (foundEnt) { + if (foundCol.surfaceB == SURFACE_TARMAC || foundCol.surfaceB == SURFACE_PAVEMENT) + surfaceAndDistIsOk = true; + else + surfaceAndDistIsOk = false; + + } else { + surfaceAndDistIsOk = false; + } } - if (!farEnoughToAdd) + if (!surfaceAndDistIsOk) break; CPed *newPed = AddPed((ePedType)pedTypeToAdd, modelToAdd, generatedCoors); if (forceAddingCop && newPed->m_nPedType == PEDTYPE_COP) ((CCopPed*)newPed)->m_bThrowsSpikeTrap = true; - newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); - + bool gonnaSunbathe = false; + if (CPopulation::IsSunbather(modelToAdd)) { + CEntity* foundEnt = nil; + CColPoint foundCol; + CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); + if (foundEnt) { + if ((foundCol.surfaceB == SURFACE_CONCRETE_BEACH || foundCol.surfaceB == SURFACE_SAND) + && CClock::GetHours() >= 10 && CClock::GetHours() <= 18 && 0.0f == CWeather::Rain) { + gonnaSunbathe = true; + if (CPedPlacement::IsPositionClearForPed(generatedCoors, 3.0f, ARRAY_SIZE(gSunbatheObstacles), gSunbatheObstacles)) { + for (int j = 0; j < ARRAY_SIZE(gSunbatheObstacles); j++) { + if (gSunbatheObstacles[j] && gSunbatheObstacles[j] != newPed) + gonnaSunbathe = false; + } + } + } + } + } + if (gonnaSunbathe) { + float heading = CGeneral::GetRandomNumberInRange(0.f, 1.f) * TWOPI; + newPed->m_fRotationDest = heading; + newPed->m_fRotationCur = heading; + // unused + // v61 = CGeneral::GetRandomTrueFalse(); + newPed->SetWaitState(WAITSTATE_SUN_BATHE_IDLE, nil); + CVector toyPos(newPed->GetPosition()); + float waterLevel; + if (CWaterLevel::GetGroundLevel(toyPos, &waterLevel, nil, 30.0f)) { + toyPos.z = 0.004f + waterLevel; + CEntity *toy = CWaterLevel::CreateBeachToy(toyPos, BEACHTOY_11); + if (toy) + toy->SetHeading(heading); + + if (!(CGeneral::GetRandomNumber() & 3)) { + CWaterLevel::CreateBeachToy(toyPos + CVector(CGeneral::GetRandomNumberInRange(-2.f, 2.f), CGeneral::GetRandomNumberInRange(-2.f, 2.f), 0.f), BEACHTOY_6); + } + } + } else { + newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + } + if (i != 0) { // Gang member newPed->SetLeader(gangLeader); -#ifndef FIX_BUGS - // seems to be a miami leftover (this code is not on PS2) but gang peds end up just being frozen - newPed->m_nPedState = PED_UNKNOWN; - gangLeader->m_nPedState = PED_UNKNOWN; - newPed->m_fRotationCur = CGeneral::GetRadianAngleBetweenPoints( - gangLeader->GetPosition().x, gangLeader->GetPosition().y, - newPed->GetPosition().x, newPed->GetPosition().y); - newPed->m_fRotationDest = newPed->m_fRotationCur; -#endif + + newPed->SetPedState(PED_UNKNOWN); + gangLeader->SetPedState(PED_UNKNOWN); + } else { gangLeader = newPed; } @@ -730,7 +801,6 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree } } -// TODO(Miami) CPed* CPopulation::AddPedInCar(CVehicle* car, bool isDriver) { @@ -779,11 +849,15 @@ CPopulation::AddPedInCar(CVehicle* car, bool isDriver) preferredModel = MI_TAXI_D; break; } - } else if (car->GetModelIndex() == MI_VICECHEE && car->bIsLawEnforcer) { - preferredModel = COP_MIAMIVICE; - pedType = PEDTYPE_COP; - miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1); - break; + // fall through if not + } else if (car->GetModelIndex() == MI_VICECHEE) { + if (car->bIsLawEnforcer) { + preferredModel = COP_MIAMIVICE; + pedType = PEDTYPE_COP; + miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1); + break; + } + // fall through if not } int gangOfPed = 0; @@ -796,20 +870,20 @@ CPopulation::AddPedInCar(CVehicle* car, bool isDriver) pedType = gangOfPed + PEDTYPE_GANG1; preferredModel = ChooseGangOccupation(gangOfPed); } else if (gangOfPed == NUM_GANGS) { - CVehicleModelInfo *carModelInfo = ((CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex())); + CVehicleModelInfo *carModel = ((CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex())); + preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup); int i = 15; for(; i >= 0; i--) { - // Should return random model each time - preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup); - if (preferredModel == -1) - preferredModel = defaultModel; + CPedModelInfo* pedModel = (CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel); - if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) { + if (pedModel->GetRwObject()) { if (!car->IsPassenger(preferredModel) && !car->IsDriver(preferredModel)) { - if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModelInfo->m_vehicleClass)) + if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModel->m_vehicleClass)) break; } } + + preferredModel = ChooseNextCivilianOccupation(zoneInfo.pedGroup); } if (i == -1) preferredModel = defaultModel; @@ -900,7 +974,7 @@ bool CPopulation::TestRoomForDummyObject(CObject *obj) { int16 collidingObjs; - CWorld::FindObjectsKindaColliding(obj->m_objectMatrix.GetPosition(), CModelInfo::GetModelInfo(obj->GetModelIndex())->GetColModel()->boundingSphere.radius, + CWorld::FindObjectsKindaColliding(obj->m_objectMatrix.GetPosition(), obj->GetBoundRadius(), false, &collidingObjs, 2, nil, false, true, true, false, false); return collidingObjs == 0; @@ -975,17 +1049,20 @@ CPopulation::ManagePopulation(void) for (int i = objectPoolSize * frameMod32 / 32; i < objectPoolSize * (frameMod32 + 1) / 32; i++) { CObject *obj = CPools::GetObjectPool()->GetSlot(i); if (obj && obj->CanBeDeleted()) { - if ((obj->GetPosition() - playerPos).Magnitude() <= 80.0f || - (obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() <= 80.0f) { - if (obj->ObjectCreatedBy == TEMP_OBJECT && CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) { + float objPlayerDist = (obj->GetPosition() - playerPos).Magnitude(); + if (obj->ObjectCreatedBy == TEMP_OBJECT) { + if (obj->GetModelIndex() != MI_ROADWORKBARRIER1 && obj->GetModelIndex() != MI_BEACHBALL) { + if (objPlayerDist > 51.0f || objPlayerDist > 25.0f && !obj->GetIsOnScreen() || CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) { + CWorld::Remove(obj); + delete obj; + } + } else if (objPlayerDist > 120.0f) { CWorld::Remove(obj); delete obj; } - } else { - if (obj->ObjectCreatedBy == TEMP_OBJECT) { - CWorld::Remove(obj); - delete obj; - } else if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) { + + } else if (objPlayerDist > 80.0f && (obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() > 80.0f) { + if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) { ConvertToDummyObject(obj); } } @@ -996,7 +1073,7 @@ CPopulation::ManagePopulation(void) int dummyPoolSize = CPools::GetDummyPool()->GetSize(); for (int i = dummyPoolSize * frameMod32 / 32; i < dummyPoolSize * (frameMod32 + 1) / 32; i++) { CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); - if (dummy) { + if (dummy && (dummy->m_area == CGame::currArea || dummy->m_area == AREA_EVERYWHERE)) { if ((dummy->GetPosition() - playerPos).Magnitude() < 80.0f) ConvertToRealObject((CDummyObject*)dummy); } @@ -1011,7 +1088,8 @@ CPopulation::ManagePopulation(void) CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); if (ped && !ped->IsPlayer() && ped->CanBeDeleted() && !ped->bInVehicle) { - if (ped->m_nPedState == PED_DEAD && CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime > 60000) + uint32 timeSinceDeath = CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime; + if (ped->m_nPedState == PED_DEAD && (timeSinceDeath > 30000 || CDarkel::FrenzyOnGoing() && timeSinceDeath > 15000)) ped->bFadeOut = true; if (ped->bFadeOut && CVisibilityPlugins::GetClumpAlpha(ped->GetClump()) == 0) { @@ -1020,22 +1098,33 @@ CPopulation::ManagePopulation(void) } float dist = (ped->GetPosition() - playerPos).Magnitude2D(); + bool pedIsFarAway = false; - if (ped->IsGangMember() || (ped->bDeadPedInFrontOfCar && ped->m_vehicleInAccident)) + if (ped->IsGangMember()) dist -= 30.0f; + else if (ped->bDeadPedInFrontOfCar && ped->m_vehicleInAccident) + dist = 0.0f; - bool pedIsFarAway = false; - if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist - || (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist) - || (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist - && !ped->GetIsOnScreen() - && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER - && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT - && !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft - && !TheCamera.Cams[TheCamera.ActiveCam].LookingRight - && !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind)) + if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist || + (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist)) { pedIsFarAway = true; + } else if (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist) { + if (CTimer::GetTimeInMilliseconds() > ped->m_nExtendedRangeTimer && !ped->GetIsOnScreen()) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_CAMERA + && !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft + && !TheCamera.Cams[TheCamera.ActiveCam].LookingRight + && !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) { + pedIsFarAway = true; + } + } + + } else { + ped->m_nExtendedRangeTimer = ped->m_nPedType == PEDTYPE_COP ? CTimer::GetTimeInMilliseconds() + 10000 : CTimer::GetTimeInMilliseconds() + 4000; + } + if (!pedIsFarAway) continue; @@ -1074,24 +1163,18 @@ CPopulation::ManagePopulation(void) CPed* CPopulation::AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit) { - if (TheCamera.IsSphereVisible(pos, 2.0f)) { - float fDistanceToPlayer = (pos - FindPlayerPed()->GetPosition()).Magnitude2D(); - float fDistanceMultiplier; - if (FindPlayerVehicle()) - fDistanceMultiplier = clamp(FindPlayerVehicle()->GetMoveSpeed().Magnitude2D() - 0.1f + 1.0f, 1.0f, 1.5f); - else - fDistanceMultiplier = 1.0f; - if (40.0f * fDistanceMultiplier > fDistanceToPlayer) + if (TheCamera.IsSphereVisible(pos, 2.0f) && MIN_CREATION_DIST * PedCreationDistMultiplier() > (pos - FindPlayerPed()->GetPosition()).Magnitude2D()) { return nil; } + bool found; float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found) + 1.0f; if (!found) return nil; z = Max(z, pos.z); - if (!CModelInfo::GetModelInfo(MI_MALE01)->GetRwObject()) // strange way to check it + if (!CModelInfo::GetModelInfo(MI_MALE01)->GetRwObject()) return nil; - CPed* pPed = CPopulation::AddPed(PEDTYPE_CIVMALE, MI_MALE01, pos); // TODO(MIAMI): 4th parameter + CPed* pPed = CPopulation::AddPed(PEDTYPE_CIVMALE, MI_MALE01, pos); pPed->SetDie(); pPed->m_nPedMoney = 0; pPed->bDeadPedInFrontOfCar = true; @@ -1101,16 +1184,14 @@ CPopulation::AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit) if (!CPedPlacement::IsPositionClearForPed(pos, 2.0f, 3, pEntities)) { for (int i = 0; i < 3; i++) { if (pEntities[i] && pEntities[i] != pCulprit && pEntities[i] != pPed) { - CWorld::Remove(pPed); - delete pPed; + RemovePed(pPed); return nil; } } } CColPoint colpts[MAX_COLLISION_POINTS]; if (CCollision::ProcessColModels(pCulprit->GetMatrix(), *pCulprit->GetColModel(), pPed->GetMatrix(), *pPed->GetColModel(), colpts, nil, nil)) { - CWorld::Remove(pPed); - delete pPed; + RemovePed(pPed); return nil; } CVisibilityPlugins::SetClumpAlpha(pPed->GetClump(), 0); @@ -1133,4 +1214,583 @@ bool CPopulation::CanJeerAtStripper(int32 model) { return model == MI_WMOBE || model == MI_WMYBE || model == MI_WMOST || model == MI_BMYBB; +} + +void +CPopulation::RemovePedsIfThePoolGetsFull(void) +{ + if ((CTimer::GetFrameCounter() & 7) == 5) { + if (CPools::GetPedPool()->GetNoOfFreeSpaces() < 8) { + CPed *closestPed = nil; + float closestDist = 10000000.0; + int poolSize = CPools::GetPedPool()->GetSize(); + for (int i = poolSize - 1; i >= 0; i--) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (ped && ped->CanBeDeleted()) { + float dist = (TheCamera.GetPosition() - ped->GetPosition()).Magnitude(); + if (dist < closestDist) { + closestDist = dist; + closestPed = ped; + } + } + } + if (closestPed) { + RemovePed(closestPed); + } + } + } +} + +bool +CPopulation::IsMale(int32 model) +{ + switch (model) { + case MI_HMYST: + case MI_HMOST: + case MI_HMYRI: + case MI_HMORI: + case MI_HMYBE: + case MI_HMOBE: + case MI_HMOTR: + case MI_HMYAP: + case MI_HMOCA: + case MI_BMODK: + case MI_BMYKR: + case MI_BMYST: + case MI_BMOST: + case MI_BMYRI: + case MI_BMYBE: + case MI_BMOBE: + case MI_BMYBU: + case MI_BMOTR: + case MI_BMYPI: + case MI_BMYBB: + case MI_WMYCR: + case MI_WMYST: + case MI_WMOST: + case MI_WMYRI: + case MI_WMORI: + case MI_WMYBE: + case MI_WMOBE: + case MI_WMYCW: + case MI_WMYGO: + case MI_WMOGO: + case MI_WMYLG: + case MI_WMYBU: + case MI_WMOBU: + case MI_WMOTR: + case MI_WMYPI: + case MI_WMOCA: + case MI_WMYJG: + case MI_WMYSK: + + // BUG? Why no JMOTO? + return true; + default: + return false; + } +} + +bool +CPopulation::IsFemale(int32 model) +{ + switch (model) { + case MI_HFYST: + case MI_HFOST: + case MI_HFYRI: + case MI_HFORI: + case MI_HFYBE: + case MI_HFOBE: + case MI_HFYBU: + case MI_HFYMD: + case MI_HFYCG: + case MI_HFYPR: + case MI_HFOTR: + case MI_BFYST: + case MI_BFOST: + case MI_BFYRI: + case MI_BFORI: + case MI_BFYBE: + case MI_BFOBE: + case MI_BFYPR: + case MI_BFOTR: + case MI_WFYST: + case MI_WFOST: + case MI_WFYRI: + case MI_WFORI: + case MI_WFYBE: + case MI_WFOBE: + case MI_WFOGO: + case MI_WFYLG: + case MI_WFYBU: + case MI_WFYPR: + case MI_WFOTR: + case MI_WFYJG: + case MI_WFYSK: + case MI_WFYSH: + case MI_WFOSH: + case MI_JFOTO: + return true; + default: + return false; + } +} + +bool +CPopulation::IsSunbather(int32 model) +{ + switch (model) { + case MI_HFYBE: + case MI_HFOBE: + case MI_HMYBE: + case MI_HMOBE: + case MI_BFYBE: + case MI_BMYBE: + case MI_BFOBE: + case MI_BMOBE: + case MI_WFYBE: + case MI_WMYBE: + case MI_WFOBE: + case MI_WMOBE: + return true; + default: + return false; + } +} + +int32 +CPopulation::ComputeRandomisedGangSize(void) +{ + return CGeneral::GetRandomNumberInRange(3, 6); +} + +bool +CPopulation::CanSolicitPlayerInCar(int32 model) +{ + return model == MI_HFYPR || model == MI_BFYPR || model == MI_WFYPR; +} + +bool +CPopulation::CanSolicitPlayerOnFoot(int32 model) +{ + return model == MI_HFYMD || model == MI_HFYCG || model == MI_BFOTR || model == MI_BMOTR || model == MI_WFOTR || model == MI_WMOTR; +} + +bool +CPopulation::IsSecurityGuard(ePedType pedType) +{ + return pedType == PEDTYPE_GANG5; +} + +void +CPopulation::ChooseCivilianCoupleOccupations(int32 group, int32& man, int32& woman) +{ + man = -1; + woman = -1; + + for (int i = 0; i < 8; i++) { + if (man > -1) + break; + + int32 model = ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)]; + if (man == -1 && IsMale(model) && ((CPedModelInfo*)CModelInfo::GetModelInfo(model))->m_pedType == PEDTYPE_CIVMALE) { + man = model; + } + } + + if (man != -1) { + int32 model; + for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { + model = ms_pPedGroups[group].models[i]; + if (IsFemale(model)) { + CPedModelInfo* womanModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(model); + if (womanModelInfo->m_pedType == PEDTYPE_CIVFEMALE) { + CPedModelInfo* manModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(man); + + // If both are skater or not, finalize the decision + if (manModelInfo && womanModelInfo) { + if (manModelInfo->m_animGroup == womanModelInfo->m_animGroup) { + if (manModelInfo->m_pedStatType != PEDSTAT_SKATER && womanModelInfo->m_pedStatType != PEDSTAT_SKATER) + break; + + if (manModelInfo->m_pedStatType == PEDSTAT_SKATER && womanModelInfo->m_pedStatType == PEDSTAT_SKATER) + break; + } + } + } + } + } + woman = model; + } +} + +void +CPopulation::PlaceGangMembers(ePedType pedType, int pedAmount, CVector const& coors) +{ + if (CGeneral::GetRandomNumberInRange(0.f, 1.f) < 0.333f) { + PlaceGangMembersInFormation(pedType, pedAmount, coors); + } else { + PlaceGangMembersInCircle(pedType, pedAmount, coors); + } +} + +void +CPopulation::PlaceGangMembersInFormation(ePedType pedType, int pedAmount, CVector const& coors) +{ + CPed *createdPeds[5]; + + if (!TheCamera.IsSphereVisible(coors, 3.0f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + if (CPedPlacement::IsPositionClearForPed(coors, 3.0f, -1, 0)) { + bool leaderFoundGround; + float leaderGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &leaderFoundGround) + 1.0f; + if (leaderFoundGround) { + float finalZ = coors.z > leaderGroundZ ? coors.z : leaderGroundZ; + int leaderModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(leaderModel))->GetRwObject()) { + CPed *leader = AddPed(pedType, leaderModel, CVector(coors.x, coors.y, finalZ)); + if (leader) { + leader->SetObjective(OBJECTIVE_NONE); + leader->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + leader->bIsLeader = true; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + leader->bCanAttackPlayerWithCops = true; + + int pedIdx = 1; + createdPeds[0] = leader; + for (int i = 1; i < pedAmount; ++i) { + int memberModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (!((CPedModelInfo*)CModelInfo::GetModelInfo(memberModel))->GetRwObject()) + continue; + + CPed* memberPed = AddPed(pedType, memberModel, CVector(coors.x, coors.y, finalZ)); + if (!memberPed) + continue; + + memberPed->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, leader); + memberPed->SetFormation((eFormation)i); + CVector formationPos = memberPed->GetFormationPosition(); + CVector finalFormationPos = formationPos; + bool formationFoundGround; + float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &formationFoundGround) + 1.0f; + + finalFormationPos.z = Max(finalFormationPos.z, formationGroundZ); + if (formationFoundGround) { + if (Abs(finalFormationPos.z - leader->GetPosition().z) <= 1.0f) { + if (CWorld::GetIsLineOfSightClear(finalFormationPos, leader->GetPosition(), true, false, false, false, false, false, false)) { + memberPed->SetPosition(finalFormationPos); + createdPeds[pedIdx++] = memberPed; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + leader->bCanAttackPlayerWithCops = true; + + CVisibilityPlugins::SetClumpAlpha(memberPed->GetClump(), 0); + continue; + } + } + } + RemovePed(memberPed); + } + if (pedIdx >= 3) { + for (int j = 1; j < pedIdx; ++j) + createdPeds[j]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } + } + } + } +} + +void +CPopulation::PlaceGangMembersInCircle(ePedType pedType, int pedAmount, CVector const& coors) +{ + CPed *createdPeds[5]; + + if (pedAmount < 2) + return; + + float circleSector = TWOPI / pedAmount; + + float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); + + if (!TheCamera.IsSphereVisible(coors, circleR) || + MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + + if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, 0)) { + int pedIdx = 0; + CVector leaderPos; + + for (int i = 0; i < pedAmount; i++) { + float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; + float xOffset = randomR * Cos(angleMult * circleSector); + float yOffset = randomR * Sin(angleMult * circleSector); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; + if (foundGround) { + CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); + + if (i == 0) + leaderPos = finalPos; + + int gangModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(gangModel))->GetRwObject()) { + CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; + CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetModelInfo(gangModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + bool foundObstacle = false; + for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { + CEntity* obstacle = obstacles[m]; + if (obstacle) { + int n = 0; + bool obstacleIsHarmless = false; + for (int n = 0; n < pedIdx; n++) { + if (obstacle == createdPeds[n]) + obstacleIsHarmless = true; + } + if (!obstacleIsHarmless) { + foundObstacle = true; + break; + } + } + } + bool memberCanSeeLeader = i == 0 ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); + + bool notTooCloseToLeader = i == 0 ? true : !(Abs(finalPos.z - leaderPos.z) < 1.0f); + + if (!foundObstacle && memberCanSeeLeader && notTooCloseToLeader) { + CPed* newPed = AddPed(pedType, gangModel, finalPos); + if (newPed) { + createdPeds[pedIdx++] = newPed; + float angle = CGeneral::GetRadianAngleBetweenPoints( + coors.x, coors.y, + finalPos.x, finalPos.y); + newPed->m_fRotationDest = angle; + newPed->m_fRotationCur = angle; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + newPed->bCanAttackPlayerWithCops = true; + + CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); + } + // No. +#ifndef FIX_BUGS + else + CWorld::Remove(nil); +#endif + } + } + } + } + if (pedIdx >= 3) { + for (int j = 0; j < pedIdx / 2; ++j) { + createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); + createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); + } + + // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: + if (pedIdx % 2 != 0) { + CPed *tmim = createdPeds[(pedIdx - 1) / 2]; + float angle = CGeneral::GetRadianAngleBetweenPoints( + tmim->GetPosition().x, tmim->GetPosition().y, + createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); + tmim->SetHeading(angle); + tmim->SetPedState(PED_UNKNOWN); + } + createdPeds[0]->bIsLeader = true; + + for (int l = 1; l < pedIdx; ++l) + createdPeds[l]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } +} + +void +CPopulation::PlaceCouple(ePedType manType, int32 manModel, ePedType womanType, int32 womanModel, CVector coors) +{ + // Homosexuality filter!!!! Homophobic R* >>>:( + if (manType != PEDTYPE_CIVMALE || womanType != PEDTYPE_CIVFEMALE) + return; + + if (!TheCamera.IsSphereVisible(coors, 1.5f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + if (CPedPlacement::IsPositionClearForPed(coors, CModelInfo::GetModelInfo(manModel)->GetColModel()->boundingSphere.radius, -1, 0)) { + bool manFoundGround; + float manGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &manFoundGround) + 1.0f; + if (manFoundGround) { + CVector correctedManPos = coors; + correctedManPos.z = Max(coors.z, manGroundZ); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(manModel))->GetRwObject()) { + CPed *man = AddPed(PEDTYPE_CIVMALE, manModel, correctedManPos); + if (man) { + man->SetObjective(OBJECTIVE_NONE); + man->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + man->bIsLeader = true; + CVisibilityPlugins::SetClumpAlpha(man->GetClump(), 0); + + if (((CPedModelInfo*)CModelInfo::GetModelInfo(womanModel))->GetRwObject()) { + CPed* woman = AddPed(PEDTYPE_CIVFEMALE, womanModel, correctedManPos); // will set the correct position later + if (woman) { + woman->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, man); + woman->SetFormation(FORMATION_RIGHT); + + CVector formationPos = woman->GetFormationPosition(); + CVector womanPos = formationPos; + bool womanFoundGround; + float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &womanFoundGround) + 1.0f; + + if (womanFoundGround) { + CVector correctedWomanPos = womanPos; + correctedWomanPos.z = Max(womanPos.z, formationGroundZ); + woman->SetPosition(correctedWomanPos); + + // What's the point of this?? + CEntity* obstacles[3]; + memcpy(obstacles, gCoupleObstacles, sizeof(gCoupleObstacles)); + + CPedPlacement::IsPositionClearForPed(womanPos, CModelInfo::GetModelInfo(womanModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + for (int i = 0; i < ARRAY_SIZE(obstacles); i++) { + CEntity *obstacle = obstacles[i]; + if (obstacle) { + + // We found a real obstacle, so let's break and we can delete them... + if (obstacle != man && obstacle != woman) + break; + } + if (i == ARRAY_SIZE(obstacles) - 1) { + CVisibilityPlugins::SetClumpAlpha(woman->GetClump(), 0); + return; + } + } + } + RemovePed(woman); + RemovePed(man); + } + } + } + } + } + } + } +} + +// Mostly copy paste of PlaceGangMembersInFormation. +void +CPopulation::PlaceMallPedsAsStationaryGroup(CVector const& coors, int32 group) +{ +#ifdef FIX_BUGS + CPed *createdPeds[6]; +#else + CPed *createdPeds[5]; +#endif + + if (CGame::currArea != AREA_MALL) + return; + + int pedAmount = CGeneral::GetRandomNumberInRange(0, 4) + 3; + + float circleSector = TWOPI / pedAmount; + + float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); + + if (!TheCamera.IsSphereVisible(coors, circleR) || + MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + + if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, 0)) { + int pedIdx = 0; + CVector leaderPos; + + for (int i = 0; i < pedAmount; i++) { + float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; + float xOffset = randomR * Cos(angleMult * circleSector); + float yOffset = randomR * Sin(angleMult * circleSector); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; + if (foundGround) { + CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); + + if (i == 0) + leaderPos = finalPos; + + int pedModel = ChooseCivilianOccupation(group); + CPedModelInfo *pedModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(pedModel); + if (pedModelInfo->GetRwObject()) { + CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; + CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetModelInfo(pedModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + bool foundObstacle = false; + for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { + CEntity* obstacle = obstacles[m]; + if (obstacle) { + int n = 0; + bool obstacleIsHarmless = false; + for (int n = 0; n < pedIdx; n++) { + if (obstacle == createdPeds[n]) + obstacleIsHarmless = true; + } + if (!obstacleIsHarmless) { + foundObstacle = true; + break; + } + } + } + bool memberCanSeeLeader = i == 0 ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); + + bool notTooCloseToLeader = i == 0 ? true : !(Abs(finalPos.z - leaderPos.z) < 1.0f); + + if (!foundObstacle && memberCanSeeLeader && notTooCloseToLeader) { + CPed *newPed = AddPed(pedModelInfo->m_pedType, pedModel, finalPos); + if (newPed) { + createdPeds[pedIdx++] = newPed; + float angle = CGeneral::GetRadianAngleBetweenPoints( + coors.x, coors.y, + finalPos.x, finalPos.y); + newPed->m_fRotationDest = angle; + newPed->m_fRotationCur = angle; + newPed->m_fearFlags = 0; + CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); + } + // No. +#ifndef FIX_BUGS + else + CWorld::Remove(nil); +#endif + } + } + } + } + if (pedIdx >= 3) { + for (int j = 0; j < pedIdx / 2; ++j) { + createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); + createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); + } + + // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: + if (pedIdx % 2 != 0) { + CPed *tmim = createdPeds[(pedIdx - 1) / 2]; + float angle = CGeneral::GetRadianAngleBetweenPoints( + tmim->GetPosition().x, tmim->GetPosition().y, + createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); + tmim->SetHeading(angle); + tmim->SetPedState(PED_UNKNOWN); + } + createdPeds[0]->bIsLeader = true; + + for (int l = 1; l < pedIdx; ++l) + createdPeds[l]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } } \ No newline at end of file -- cgit v1.2.3