From ff92b822b673e77c9fa9af6ecbab5745a6ff01d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?eray=20or=C3=A7unus?= <erayorcunus@gmail.com>
Date: Sun, 16 Feb 2020 05:15:58 +0300
Subject: [PATCH] CPopulation 1

---
 src/control/Population.cpp         |  91 -------
 src/control/Replay.cpp             |   4 +-
 src/control/Script.cpp             |   8 +-
 src/core/World.cpp                 |   2 +-
 src/core/config.h                  |   2 +
 src/modelinfo/PedModelInfo.h       |   3 +-
 src/peds/CivilianPed.cpp           |   4 +-
 src/peds/CivilianPed.h             |   2 +-
 src/peds/Ped.cpp                   |   4 +-
 src/peds/Ped.h                     |   2 +-
 src/peds/PedType.cpp               |   2 +-
 src/peds/PedType.h                 |   4 +-
 src/peds/Population.cpp            | 393 +++++++++++++++++++++++++++++
 src/{control => peds}/Population.h |  33 ++-
 14 files changed, 442 insertions(+), 112 deletions(-)
 delete mode 100644 src/control/Population.cpp
 create mode 100644 src/peds/Population.cpp
 rename src/{control => peds}/Population.h (59%)

diff --git a/src/control/Population.cpp b/src/control/Population.cpp
deleted file mode 100644
index d7e32dbf..00000000
--- a/src/control/Population.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "common.h"
-#include "patcher.h"
-#include "Game.h"
-#include "General.h"
-#include "World.h"
-#include "Entity.h"
-#include "Population.h"
-
-PedGroup *CPopulation::ms_pPedGroups = (PedGroup*)0x6E9248;
-bool &CPopulation::ms_bGivePedsWeapons = *(bool*)0x95CCF6;
-int32 &CPopulation::m_AllRandomPedsThisType = *(int32*)0x5FA570;
-float &CPopulation::PedDensityMultiplier = *(float*)0x5FA56C;
-uint32 &CPopulation::ms_nTotalMissionPeds = *(uint32*)0x8F5F70;
-int32 &CPopulation::MaxNumberOfPedsInUse = *(int32*)0x5FA574;
-uint32& CPopulation::ms_nNumCivMale = *(uint32*)0x8F2548;
-uint32& CPopulation::ms_nNumCivFemale = *(uint32*)0x8F5F44;
-uint32& CPopulation::ms_nNumCop = *(uint32*)0x885AFC;
-bool& CPopulation::bZoneChangeHasHappened = *(bool*)0x95CD79;
-uint32& CPopulation::ms_nNumEmergency = *(uint32*)0x94071C;
-uint32& CPopulation::m_CountDownToPedsAtStart = *(uint32*)0x95CD4F;
-uint32& CPopulation::ms_nNumGang1 = *(uint32*)0x8F1B1C;
-uint32& CPopulation::ms_nNumGang2 = *(uint32*)0x8F1B14;
-uint32& CPopulation::ms_nTotalPeds = *(uint32*)0x95CB50;
-uint32& CPopulation::ms_nNumGang3 = *(uint32*)0x8F2548;
-uint32& CPopulation::ms_nTotalGangPeds = *(uint32*)0x885AF0;
-uint32& CPopulation::ms_nNumGang4 = *(uint32*)0x8F1B2C;
-uint32& CPopulation::ms_nTotalCivPeds = *(uint32*)0x8F2C3C;
-uint32& CPopulation::ms_nNumGang5 = *(uint32*)0x8F1B30;
-uint32& CPopulation::ms_nNumDummy = *(uint32*)0x8F1A98;
-uint32& CPopulation::ms_nNumGang6 = *(uint32*)0x8F1B20;
-uint32& CPopulation::ms_nNumGang9 = *(uint32*)0x8F1B10;
-uint32& CPopulation::ms_nNumGang7 = *(uint32*)0x8F1B28;
-uint32& CPopulation::ms_nNumGang8 = *(uint32*)0x8F1B0C;
-
-WRAPPER void CPopulation::Update(void) { EAXJMP(0x4F39A0); }
-WRAPPER void CPopulation::LoadPedGroups() { EAXJMP(0x4F3870); }
-WRAPPER void CPopulation::UpdatePedCount(uint32, bool) { EAXJMP(0x4F5A60); }
-WRAPPER void CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool) { EAXJMP(0x4F6200); }
-WRAPPER CPed *CPopulation::AddPedInCar(CVehicle *vehicle) { EAXJMP(0x4F5800); }
-WRAPPER bool CPopulation::IsPointInSafeZone(CVector *coors) { EAXJMP(0x4F60C0); }
-
-void
-CPopulation::Initialise()
-{
-	debug("Initialising CPopulation...\n");
-
-	ms_nNumCivMale = 0;
-	m_AllRandomPedsThisType = -1;
-	ms_nNumCivFemale = 0;
-	PedDensityMultiplier = 1.0;
-	ms_nNumCop = 0;
-	bZoneChangeHasHappened = 0;
-	ms_nNumEmergency = 0;
-	m_CountDownToPedsAtStart = 2;
-	ms_nNumGang1 = 0;
-	ms_nTotalMissionPeds = 0;
-	ms_nNumGang2 = 0;
-	ms_nTotalPeds = 0;
-	ms_nNumGang3 = 0;
-	ms_nTotalGangPeds = 0;
-	ms_nNumGang4 = 0;
-	ms_nTotalCivPeds = 0;
-	ms_nNumGang5 = 0;
-	ms_nNumDummy = 0;
-	ms_nNumGang6 = 0;
-	ms_nNumGang9 = 0;
-	ms_nNumGang7 = 0;
-	ms_nNumGang8 = 0;
-
-	LoadPedGroups();
-	DealWithZoneChange(LEVEL_COMMERCIAL, LEVEL_INDUSTRIAL, true);
-
-	debug("CPopulation ready\n");
-}
-
-void
-CPopulation::RemovePed(CEntity* ent)
-{
-	CWorld::Remove(ent);
-	delete ent;
-}
-
-int32
-CPopulation::ChooseCivilianOccupation(int32 group)
-{
-	return ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, 8)];
-}
-
-STARTPATCHES
-InjectHook(0x4F3770, CPopulation::Initialise, PATCH_JUMP);
-ENDPATCHES
\ No newline at end of file
diff --git a/src/control/Replay.cpp b/src/control/Replay.cpp
index f36ef932..495c3840 100644
--- a/src/control/Replay.cpp
+++ b/src/control/Replay.cpp
@@ -819,7 +819,7 @@ bool CReplay::PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, flo
 					CStreaming::RequestModel(ph->mi, 0);
 				}
 				else {
-					CPed* new_p = new(ph->index << 8) CCivilianPed(ph->pedtype, ph->mi);
+					CPed* new_p = new(ph->index << 8) CCivilianPed((ePedType)ph->pedtype, ph->mi);
 					new_p->m_status = STATUS_PLAYER_PLAYBACKFROMBUFFER;
 					new_p->GetMatrix().SetUnity();
 					CWorld::Add(new_p);
@@ -1169,7 +1169,7 @@ void CReplay::RestoreStuffFromMem(void)
 		ped->m_pVehicleAnim = 0;
 		ped->m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, ped);
 		DMAudio.SetEntityStatus(ped->m_audioEntityId, true);
-		CPopulation::UpdatePedCount(ped->m_nPedType, false);
+		CPopulation::UpdatePedCount((ePedType)ped->m_nPedType, false);
 		if (ped->m_wepModelID >= 0)
 			ped->AddWeaponModel(ped->m_wepModelID);
 	}
diff --git a/src/control/Script.cpp b/src/control/Script.cpp
index 84d4f179..ab22cc7b 100644
--- a/src/control/Script.cpp
+++ b/src/control/Script.cpp
@@ -1729,7 +1729,7 @@ int8 CRunningScript::ProcessCommands100To199(int32 command)
 		else if (ScriptParams[0] == PEDTYPE_EMERGENCY || ScriptParams[0] == PEDTYPE_FIREMAN)
 			ped = new CEmergencyPed(ScriptParams[1]);
 		else
-			ped = new CCivilianPed(ScriptParams[0], ScriptParams[1]);
+			ped = new CCivilianPed((ePedType)ScriptParams[0], ScriptParams[1]);
 		ped->CharCreatedBy = MISSION_CHAR;
 		ped->bRespondsToThreats = false;
 		ped->bAllowMedicsToReviveMe = false;
@@ -2768,7 +2768,7 @@ int8 CRunningScript::ProcessCommands200To299(int32 command)
 		else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN)
 			pPed = new CEmergencyPed(ScriptParams[2]);
 		else
-			pPed = new CCivilianPed(ScriptParams[1], ScriptParams[2]);
+			pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]);
 		pPed->CharCreatedBy = MISSION_CHAR;
 		pPed->bRespondsToThreats = false;
 		pPed->bAllowMedicsToReviveMe = false;
@@ -3963,7 +3963,7 @@ int8 CRunningScript::ProcessCommands400To499(int32 command)
 		else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN)
 			pPed = new CEmergencyPed(ScriptParams[2]);
 		else
-			pPed = new CCivilianPed(ScriptParams[1], ScriptParams[2]);
+			pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]);
 		pPed->CharCreatedBy = MISSION_CHAR;
 		pPed->bRespondsToThreats = false;
 		pPed->bAllowMedicsToReviveMe = false;
@@ -7360,7 +7360,7 @@ int8 CRunningScript::ProcessCommands800To899(int32 command)
 		CZoneInfo zoneinfo;
 		CTheZones::GetZoneInfoForTimeOfDay(&CWorld::Players[CWorld::PlayerInFocus].GetPos(), &zoneinfo);
 		int mi;
-		int pedtype = PEDTYPE_COP;
+		ePedType pedtype = PEDTYPE_COP;
 		int attempt = 0;
 		while (pedtype != PEDTYPE_CIVMALE && pedtype != PEDTYPE_CIVFEMALE && attempt < 5) {
 			mi = CPopulation::ChooseCivilianOccupation(zoneinfo.pedGroup);
diff --git a/src/core/World.cpp b/src/core/World.cpp
index c914b647..9c1678e1 100644
--- a/src/core/World.cpp
+++ b/src/core/World.cpp
@@ -949,7 +949,7 @@ CWorld::RemoveEntityInsteadOfProcessingIt(CEntity* ent)
 		if (FindPlayerPed() == ent)
 			Remove(ent);
 		else
-			CPopulation::RemovePed(ent);
+			CPopulation::RemovePed((CPed*)ent);
 	} else {
 		Remove(ent);
 		delete ent;
diff --git a/src/core/config.h b/src/core/config.h
index de96ad1b..0ff1809c 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -93,6 +93,8 @@ enum Config {
 
 	NUMPEDROUTES = 200,
 	NUMPHONES = 50,
+	NUMPEDGROUPS = 31,
+	NUMMODELSPERPEDGROUP = 8,
 
 	NUMVISIBLEENTITIES = 2000,
 	NUMINVISIBLEENTITIES = 150,
diff --git a/src/modelinfo/PedModelInfo.h b/src/modelinfo/PedModelInfo.h
index a2d58d6e..e8260fa2 100644
--- a/src/modelinfo/PedModelInfo.h
+++ b/src/modelinfo/PedModelInfo.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "ClumpModelInfo.h"
+#include "PedType.h"
 
 enum PedNode {
 	PED_TORSO,
@@ -22,7 +23,7 @@ class CPedModelInfo : public CClumpModelInfo
 {
 public:
 	uint32 m_animGroup;
-	uint32 m_pedType;
+	ePedType m_pedType;
 	uint32 m_pedStatType;
 	uint32 m_carsCanDrive;
 	CColModel *m_hitColModel;
diff --git a/src/peds/CivilianPed.cpp b/src/peds/CivilianPed.cpp
index 9eeeeccd..e0950688 100644
--- a/src/peds/CivilianPed.cpp
+++ b/src/peds/CivilianPed.cpp
@@ -8,7 +8,7 @@
 #include "Vehicle.h"
 #include "SurfaceTable.h"
 
-CCivilianPed::CCivilianPed(int pedtype, int mi) : CPed(pedtype)
+CCivilianPed::CCivilianPed(ePedType pedtype, uint32 mi) : CPed(pedtype)
 {
 	SetModelIndex(mi);
 	for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) {
@@ -380,7 +380,7 @@ CCivilianPed::ProcessControl(void)
 class CCivilianPed_ : public CCivilianPed
 {
 public:
-	CCivilianPed *ctor(int pedtype, int mi) { return ::new (this) CCivilianPed(pedtype, mi); };
+	CCivilianPed *ctor(ePedType pedtype, uint32 mi) { return ::new (this) CCivilianPed(pedtype, mi); };
 	void dtor(void) { CCivilianPed::~CCivilianPed(); }
 	void ProcessControl_(void) { CCivilianPed::ProcessControl(); }
 };
diff --git a/src/peds/CivilianPed.h b/src/peds/CivilianPed.h
index 80845e62..6082c6ab 100644
--- a/src/peds/CivilianPed.h
+++ b/src/peds/CivilianPed.h
@@ -5,7 +5,7 @@
 class CCivilianPed : public CPed
 {
 public:
-	CCivilianPed(int, int);
+	CCivilianPed(ePedType, uint32);
 	~CCivilianPed(void) { }
 
 	void CivilianAI(void);
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index 44aaae5b..1ea13cc2 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -353,7 +353,7 @@ CPed::~CPed(void)
 	}
 	if (m_pFire)
 		m_pFire->Extinguish();
-	CPopulation::UpdatePedCount(m_nPedType, true);
+	CPopulation::UpdatePedCount((ePedType)m_nPedType, true);
 	DMAudio.DestroyEntity(m_audioEntityId);
 }
 
@@ -594,7 +594,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
 	m_collPoly.valid = false;
 	m_fCollisionSpeed = 0.0f;
 	m_wepModelID = -1;
-	CPopulation::UpdatePedCount(m_nPedType, false);
+	CPopulation::UpdatePedCount((ePedType)m_nPedType, false);
 }
 
 uint32
diff --git a/src/peds/Ped.h b/src/peds/Ped.h
index b18b23fc..0c974330 100644
--- a/src/peds/Ped.h
+++ b/src/peds/Ped.h
@@ -450,7 +450,7 @@ public:
 	eCrimeType m_crimeToReportOnPhone;
 	uint32 m_phoneTalkTimer;
 	CAccident *m_lastAccident;
-	int32 m_nPedType;
+	uint32 m_nPedType;
 	CPedStats *m_pedStats;
 	float m_fleeFromPosX;
 	float m_fleeFromPosY;
diff --git a/src/peds/PedType.cpp b/src/peds/PedType.cpp
index a8e052c7..3b95109d 100644
--- a/src/peds/PedType.cpp
+++ b/src/peds/PedType.cpp
@@ -120,7 +120,7 @@ CPedType::LoadPedData(void)
 	delete[] buf;
 }
 
-int32
+ePedType
 CPedType::FindPedType(char *type)
 {
 	if(strcmp(type, "PLAYER1") == 0) return PEDTYPE_PLAYER1;
diff --git a/src/peds/PedType.h b/src/peds/PedType.h
index 9d284318..4c962644 100644
--- a/src/peds/PedType.h
+++ b/src/peds/PedType.h
@@ -1,7 +1,7 @@
 #pragma once
 
 // Index into the PedType array
-enum
+enum ePedType
 {
 	PEDTYPE_PLAYER1,
 	PEDTYPE_PLAYER2,
@@ -77,7 +77,7 @@ public:
 	static void Initialise(void);
 	static void Shutdown(void);
 	static void LoadPedData(void);
-	static int32 FindPedType(char *type);
+	static ePedType FindPedType(char *type);
 	static uint32 FindPedFlag(char *type);
 	static void Save(uint8 *buf, uint32 *size);
 	static void Load(uint8 *buf, uint32 size);
diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp
new file mode 100644
index 00000000..e9fbbde1
--- /dev/null
+++ b/src/peds/Population.cpp
@@ -0,0 +1,393 @@
+#include "common.h"
+#include "patcher.h"
+#include "Game.h"
+#include "General.h"
+#include "World.h"
+#include "Population.h"
+#include "FileMgr.h"
+#include "Gangs.h"
+#include "ModelIndices.h"
+#include "Zones.h"
+#include "Ped.h"
+
+// TO-DO: These are hard-coded, reverse them.
+// More clearly they're transition areas between zones.
+RegenerationPoint (&aSafeZones)[8] = *(RegenerationPoint(*)[8]) * (uintptr*)0x5FA578;
+
+PedGroup (&CPopulation::ms_pPedGroups)[NUMPEDGROUPS] = *(PedGroup(*)[NUMPEDGROUPS]) * (uintptr*)0x6E9248;
+bool &CPopulation::ms_bGivePedsWeapons = *(bool*)0x95CCF6;
+int32 &CPopulation::m_AllRandomPedsThisType = *(int32*)0x5FA570;
+float &CPopulation::PedDensityMultiplier = *(float*)0x5FA56C;
+uint32 &CPopulation::ms_nTotalMissionPeds = *(uint32*)0x8F5F70;
+int32 &CPopulation::MaxNumberOfPedsInUse = *(int32*)0x5FA574;
+uint32& CPopulation::ms_nNumCivMale = *(uint32*)0x8F2548;
+uint32& CPopulation::ms_nNumCivFemale = *(uint32*)0x8F5F44;
+uint32& CPopulation::ms_nNumCop = *(uint32*)0x885AFC;
+bool& CPopulation::bZoneChangeHasHappened = *(bool*)0x95CD79;
+uint32& CPopulation::ms_nNumEmergency = *(uint32*)0x94071C;
+uint32& CPopulation::m_CountDownToPedsAtStart = *(uint32*)0x95CD4F;
+uint32& CPopulation::ms_nNumGang1 = *(uint32*)0x8F1B1C;
+uint32& CPopulation::ms_nNumGang2 = *(uint32*)0x8F1B14;
+uint32& CPopulation::ms_nTotalPeds = *(uint32*)0x95CB50;
+uint32& CPopulation::ms_nNumGang3 = *(uint32*)0x8F2548;
+uint32& CPopulation::ms_nTotalGangPeds = *(uint32*)0x885AF0;
+uint32& CPopulation::ms_nNumGang4 = *(uint32*)0x8F1B2C;
+uint32& CPopulation::ms_nTotalCivPeds = *(uint32*)0x8F2C3C;
+uint32& CPopulation::ms_nNumGang5 = *(uint32*)0x8F1B30;
+uint32& CPopulation::ms_nNumDummy = *(uint32*)0x8F1A98;
+uint32& CPopulation::ms_nNumGang6 = *(uint32*)0x8F1B20;
+uint32& CPopulation::ms_nNumGang9 = *(uint32*)0x8F1B10;
+uint32& CPopulation::ms_nNumGang7 = *(uint32*)0x8F1B28;
+uint32& CPopulation::ms_nNumGang8 = *(uint32*)0x8F1B0C;
+CVector &CPopulation::RegenerationPoint_a = *(CVector*)0x8E2AA4;
+CVector &CPopulation::RegenerationPoint_b = *(CVector*)0x8E2A98;
+CVector &CPopulation::RegenerationForward = *(CVector*)0x8F1AD4;
+
+WRAPPER void CPopulation::Update(void) { EAXJMP(0x4F39A0); }
+WRAPPER CPed *CPopulation::AddPedInCar(CVehicle *vehicle) { EAXJMP(0x4F5800); }
+WRAPPER bool CPopulation::IsPointInSafeZone(CVector *coors) { EAXJMP(0x4F60C0); }
+
+void
+CPopulation::Initialise()
+{
+	debug("Initialising CPopulation...\n");
+
+	ms_nNumCivMale = 0;
+	ms_nNumCivFemale = 0;
+	ms_nNumCop = 0;
+	ms_nNumEmergency = 0;
+	ms_nNumGang1 = 0;
+	ms_nNumGang2 = 0;
+	ms_nNumGang3 = 0;
+	ms_nNumGang4 = 0;
+	ms_nNumGang5 = 0;
+	ms_nNumGang6 = 0;
+	ms_nNumGang7 = 0;
+	ms_nNumGang8 = 0;
+	ms_nNumGang9 = 0;
+	ms_nNumDummy = 0;
+
+	m_AllRandomPedsThisType = -1;
+	PedDensityMultiplier = 1.0;
+	bZoneChangeHasHappened = false;
+	m_CountDownToPedsAtStart = 2;
+	
+	ms_nTotalMissionPeds = 0;
+	ms_nTotalPeds = 0;
+	ms_nTotalGangPeds = 0;
+	ms_nTotalCivPeds = 0;
+
+	LoadPedGroups();
+	DealWithZoneChange(LEVEL_COMMERCIAL, LEVEL_INDUSTRIAL, true);
+
+	debug("CPopulation ready\n");
+}
+
+void
+CPopulation::RemovePed(CPed *ent)
+{
+	// CPed dtor already does that
+	// CWorld::Remove((CEntity*)ent);
+	delete ent;
+}
+
+int32
+CPopulation::ChooseCivilianOccupation(int32 group)
+{
+	return ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)];
+}
+
+int32
+CPopulation::ChoosePolicePedOccupation()
+{
+	CGeneral::GetRandomNumber();
+	return 0;
+}
+
+void
+CPopulation::LoadPedGroups()
+{
+	int fd;
+	char line[1024];
+	int nextPedGroup = 0;
+	// char unused[16]; // non-existence of that in mobile kinda verifies that
+	char modelName[256];
+
+	CFileMgr::ChangeDir("\\DATA\\");
+	fd = CFileMgr::OpenFile("PEDGRP.DAT", "r");
+	CFileMgr::ChangeDir("\\");
+	while (CFileMgr::ReadLine(fd, line, sizeof(line))) {
+		int end;
+		// find end of line
+		for (end = 0; ; end++) {
+			if (line[end] == '\n')
+				break;
+			if (line[end] == ',' || line[end] == '\r')
+				line[end] = ' ';
+		}
+		line[end] = '\0';
+		int cursor = 0;
+		int i;
+		for (i = 0; i < NUMMODELSPERPEDGROUP; i++) {
+			while (line[cursor] <= ' ' && line[cursor] != '\0')
+				++cursor;
+
+			if (line[cursor] == '#')
+				break;
+
+			// find next whitespace
+			int nextWhitespace;
+			for (nextWhitespace = cursor; line[nextWhitespace] > ' '; ++nextWhitespace)
+				;
+
+			if (cursor == nextWhitespace)
+				break;
+			
+			// read until next whitespace
+			strncpy(modelName, &line[cursor], nextWhitespace - cursor);
+			modelName[nextWhitespace - cursor] = '\0';
+			CModelInfo::GetModelInfo(modelName, &ms_pPedGroups[nextPedGroup].models[i]);
+			cursor = nextWhitespace;
+		}
+		if (i == NUMMODELSPERPEDGROUP)
+			nextPedGroup++;
+	}
+	CFileMgr::CloseFile(fd);
+}
+
+void
+CPopulation::UpdatePedCount(ePedType pedType, bool decrease)
+{
+	if (decrease) {
+		switch (pedType) {
+			case PEDTYPE_PLAYER1:
+			case PEDTYPE_PLAYER2:
+			case PEDTYPE_PLAYER3:
+			case PEDTYPE_PLAYER4:
+			case PEDTYPE_UNUSED1:
+			case PEDTYPE_SPECIAL:
+				return;
+			case PEDTYPE_CIVMALE:
+				--ms_nNumCivMale;
+				break;
+			case PEDTYPE_CIVFEMALE:
+				--ms_nNumCivFemale;
+				break;
+			case PEDTYPE_COP:
+				--ms_nNumCop;
+				break;
+			case PEDTYPE_GANG1:
+				--ms_nNumGang1;
+				break;
+			case PEDTYPE_GANG2:
+				--ms_nNumGang2;
+				break;
+			case PEDTYPE_GANG3:
+				--ms_nNumGang3;
+				break;
+			case PEDTYPE_GANG4:
+				--ms_nNumGang4;
+				break;
+			case PEDTYPE_GANG5:
+				--ms_nNumGang5;
+				break;
+			case PEDTYPE_GANG6:
+				--ms_nNumGang6;
+				break;
+			case PEDTYPE_GANG7:
+				--ms_nNumGang7;
+				break;
+			case PEDTYPE_GANG8:
+				--ms_nNumGang8;
+				break;
+			case PEDTYPE_GANG9:
+				--ms_nNumGang9;
+				break;
+			case PEDTYPE_EMERGENCY:
+			case PEDTYPE_FIREMAN:
+				--ms_nNumEmergency;
+				break;
+			case PEDTYPE_CRIMINAL:
+				--ms_nNumCivMale;
+				break;
+			case PEDTYPE_PROSTITUTE:
+				--ms_nNumCivFemale;
+				break;
+			case PEDTYPE_UNUSED2:
+				--ms_nNumDummy;
+				break;
+			default:
+				Error("Unknown ped type, UpdatePedCount, Population.cpp");
+				break;
+		}
+	} else {
+		switch (pedType) {
+			case PEDTYPE_PLAYER1:
+			case PEDTYPE_PLAYER2:
+			case PEDTYPE_PLAYER3:
+			case PEDTYPE_PLAYER4:
+			case PEDTYPE_UNUSED1:
+			case PEDTYPE_SPECIAL:
+				return;
+			case PEDTYPE_CIVMALE:
+				++ms_nNumCivMale;
+				break;
+			case PEDTYPE_CIVFEMALE:
+				++ms_nNumCivFemale;
+				break;
+			case PEDTYPE_COP:
+				++ms_nNumCop;
+				break;
+			case PEDTYPE_GANG1:
+				++ms_nNumGang1;
+				break;
+			case PEDTYPE_GANG2:
+				++ms_nNumGang2;
+				break;
+			case PEDTYPE_GANG3:
+				++ms_nNumGang3;
+				break;
+			case PEDTYPE_GANG4:
+				++ms_nNumGang4;
+				break;
+			case PEDTYPE_GANG5:
+				++ms_nNumGang5;
+				break;
+			case PEDTYPE_GANG6:
+				++ms_nNumGang6;
+				break;
+			case PEDTYPE_GANG7:
+				++ms_nNumGang7;
+				break;
+			case PEDTYPE_GANG8:
+				++ms_nNumGang8;
+				break;
+			case PEDTYPE_GANG9:
+				++ms_nNumGang9;
+				break;
+			case PEDTYPE_EMERGENCY:
+			case PEDTYPE_FIREMAN:
+				++ms_nNumEmergency;
+				break;
+			case PEDTYPE_CRIMINAL:
+				++ms_nNumCivMale;
+				break;
+			case PEDTYPE_PROSTITUTE:
+				++ms_nNumCivFemale;
+				break;
+			case PEDTYPE_UNUSED2:
+				++ms_nNumDummy;
+				break;
+			default:
+				Error("Unknown ped type, UpdatePedCount, Population.cpp");
+				break;
+		}
+	}
+}
+
+int
+CPopulation::ChooseGangOccupation(int gangId)
+{
+	int8 modelOverride = CGangs::GetGangPedModelOverride(gangId);
+
+	// All gangs have 2 models
+	int firstGangModel = 2 * gangId + MI_GANG01;
+
+	// GetRandomNumberInRange never returns max. value
+	if (modelOverride == -1)
+		return CGeneral::GetRandomNumberInRange(firstGangModel, firstGangModel + 2);
+
+	if (modelOverride != 0)
+		return firstGangModel + 1;
+	else
+		return firstGangModel;
+}
+
+void
+CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool forceIndustrialZone)
+{
+	bZoneChangeHasHappened = true;
+
+	CVector findSafeZoneAround;
+	int safeZone;
+
+	if (forceIndustrialZone) {
+		// Commercial to industrial transition area on Callahan Bridge
+		findSafeZoneAround.x = 690.0f;
+		findSafeZoneAround.y = -920.0f;
+		findSafeZoneAround.z = 42.0f;
+	} else {
+		findSafeZoneAround = FindPlayerCoors();
+	}
+	eLevelName level;
+	FindCollisionZoneForCoors(&findSafeZoneAround, &safeZone, &level);
+
+	// We aren't in a "safe zone", find closest one
+	if (safeZone < 0)
+		FindClosestZoneForCoors(&findSafeZoneAround, &safeZone, oldLevel, newLevel);
+
+	// No, there should be one!
+	if (safeZone < 0) {
+		if (newLevel == LEVEL_INDUSTRIAL) {
+			safeZone = 0;
+		} else if (newLevel == LEVEL_SUBURBAN) {
+			safeZone = 4;
+		}
+	}
+
+	if (aSafeZones[safeZone].srcLevel == newLevel) {
+		CPopulation::RegenerationPoint_a = aSafeZones[safeZone].srcPosA;
+		CPopulation::RegenerationPoint_b = aSafeZones[safeZone].srcPosB;
+		CPopulation::RegenerationForward = aSafeZones[safeZone].destPosA - aSafeZones[safeZone].srcPosA;
+		RegenerationForward.Normalise();
+	} else if (aSafeZones[safeZone].destLevel == newLevel) {
+		CPopulation::RegenerationPoint_a = aSafeZones[safeZone].destPosA;
+		CPopulation::RegenerationPoint_b = aSafeZones[safeZone].destPosB;
+		CPopulation::RegenerationForward = aSafeZones[safeZone].srcPosA - aSafeZones[safeZone].destPosA;
+		RegenerationForward.Normalise();
+	}
+}
+
+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_NONE;
+	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;
+}
+
+STARTPATCHES
+	InjectHook(0x4F3770, &CPopulation::Initialise, PATCH_JUMP);
+	InjectHook(0x4F5780, &CPopulation::ChooseGangOccupation, PATCH_JUMP);
+	InjectHook(0x4F6200, &CPopulation::DealWithZoneChange, PATCH_JUMP);
+	InjectHook(0x4F6010, &CPopulation::FindCollisionZoneForCoors, PATCH_JUMP);
+ENDPATCHES
\ No newline at end of file
diff --git a/src/control/Population.h b/src/peds/Population.h
similarity index 59%
rename from src/control/Population.h
rename to src/peds/Population.h
index 572d6f58..c8f6f985 100644
--- a/src/control/Population.h
+++ b/src/peds/Population.h
@@ -1,19 +1,37 @@
 #pragma once
 
 #include "Game.h"
+#include "PedType.h"
 
 class CPed;
 class CVehicle;
 
 struct PedGroup
 {
-	int32 models[8];
+	int32 models[NUMMODELSPERPEDGROUP];
+};
+
+// Don't know the original name
+struct RegenerationPoint
+{
+	eLevelName srcLevel; // this and below one may need to be exchanged
+	eLevelName destLevel;
+	float x1;
+	float x2;
+	float y1;
+	float y2;
+	float z1;
+	float z2;
+	CVector destPosA;
+	CVector destPosB;
+	CVector srcPosA;
+	CVector srcPosB;
 };
 
 class CPopulation
 {
 public:
-	static PedGroup *ms_pPedGroups;	//[31]
+	static PedGroup (&ms_pPedGroups)[NUMPEDGROUPS];
 	static bool &ms_bGivePedsWeapons;
 	static int32 &m_AllRandomPedsThisType;
 	static float &PedDensityMultiplier;
@@ -38,14 +56,21 @@ public:
 	static uint32& ms_nNumGang9;
 	static uint32& ms_nNumGang7;
 	static uint32& ms_nNumGang8;
+	static CVector& RegenerationPoint_a;
+	static CVector& RegenerationPoint_b;
+	static CVector& RegenerationForward;
 
 	static void Initialise();
 	static void Update(void);
 	static void LoadPedGroups();
-	static void UpdatePedCount(uint32, bool);
+	static void UpdatePedCount(ePedType, bool);
 	static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool);
 	static CPed *AddPedInCar(CVehicle *vehicle);
 	static bool IsPointInSafeZone(CVector *coors);
-	static void RemovePed(CEntity* ent);
+	static void RemovePed(CPed *ent);
 	static int32 ChooseCivilianOccupation(int32);
+	static int32 ChoosePolicePedOccupation();
+	static int32 ChooseGangOccupation(int);
+	static void FindCollisionZoneForCoors(CVector*, int*, eLevelName*);
+	static void FindClosestZoneForCoors(CVector*, int*, eLevelName, eLevelName);
 };