From 2f7d2fa3ad697b6fa1896ac75f97c23f52ed843e Mon Sep 17 00:00:00 2001
From: Nikolay Korolev <nickvnuk@gmail.com>
Date: Sun, 10 May 2020 12:25:30 +0300
Subject: [PATCH] more car control

---
 src/control/CarCtrl.cpp      | 355 ++++++++++++++++++++++-------------
 src/control/CarCtrl.h        |   7 +-
 src/control/Script.cpp       |   8 +
 src/core/Streaming.cpp       |  36 +++-
 src/modelinfo/ModelIndices.h |   7 +
 src/peds/CopPed.cpp          |  20 +-
 src/peds/CopPed.h            |   3 +-
 src/peds/Population.cpp      |  15 +-
 src/peds/Population.h        |   4 +-
 src/vehicles/Boat.cpp        |  11 +-
 src/vehicles/Vehicle.cpp     |  89 +++++----
 src/vehicles/Vehicle.h       |   2 +
 12 files changed, 370 insertions(+), 187 deletions(-)

diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index 88fc92f3..d29b52ab 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -35,46 +35,50 @@
 #include "World.h"
 #include "Zones.h"
 
-#define DISTANCE_TO_SPAWN_ROADBLOCK_PEDS 51.0f
-#define DISTANCE_TO_SCAN_FOR_DANGER 11.0f
-#define SAFE_DISTANCE_TO_PED 3.0f
-#define INFINITE_Z 1000000000.0f
+#define DISTANCE_TO_SPAWN_ROADBLOCK_PEDS (51.0f)
+#define DISTANCE_TO_SCAN_FOR_DANGER (11.0f)
+#define SAFE_DISTANCE_TO_PED (3.0f)
+#define INFINITE_Z (1000000000.0f)
 
-#define VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING 4.0f
-#define PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING 4.0f
-#define OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING 8.0f
-#define WIDTH_COEF_TO_WEAVE_SAFELY 1.2f
-#define OBJECT_WIDTH_TO_WEAVE 0.3f
-#define PED_WIDTH_TO_WEAVE 0.8f
+#define VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f)
+#define PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f)
+#define OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING (8.0f)
+#define WIDTH_COEF_TO_WEAVE_SAFELY (1.2f)
+#define OBJECT_WIDTH_TO_WEAVE (0.3f)
+#define PED_WIDTH_TO_WEAVE (0.8f)
 
-#define PATH_DIRECTION_NONE 0
-#define PATH_DIRECTION_STRAIGHT 1
-#define PATH_DIRECTION_RIGHT 2
-#define PATH_DIRECTION_LEFT 4
+#define PATH_DIRECTION_NONE (0)
+#define PATH_DIRECTION_STRAIGHT (1)
+#define PATH_DIRECTION_RIGHT (2)
+#define PATH_DIRECTION_LEFT (4)
 
-#define ATTEMPTS_TO_FIND_NEXT_NODE 15
+#define ATTEMPTS_TO_FIND_NEXT_NODE (15)
 
-#define DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP 5.0f
-#define DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK 10.0f
-#define MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING 0.13f
-#define DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN 40.0f
-#define MAX_ANGLE_TO_STEER_AT_HIGH_SPEED 0.2f
-#define MIN_SPEED_TO_START_LIMITING_STEER 0.45f
-#define DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW 5.0f
-#define DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW 8.0f
-#define DEFAULT_MAX_STEER_ANGLE 0.5f
-#define MIN_LOWERING_SPEED_COEFFICIENT 0.4f
-#define MAX_ANGLE_FOR_SPEED_LIMITING 1.2f
-#define MIN_ANGLE_FOR_SPEED_LIMITING 0.4f
-#define MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES 0.1f
-#define MIN_ANGLE_TO_APPLY_HANDBRAKE 0.7f
-#define MIN_SPEED_TO_APPLY_HANDBRAKE 0.3f
+#define DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP (5.0f)
+#define DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK (10.0f)
+#define MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING (0.13f)
+#define DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN (40.0f)
+#define MAX_ANGLE_TO_STEER_AT_HIGH_SPEED (0.2f)
+#define MIN_SPEED_TO_START_LIMITING_STEER (0.45f)
+#define DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW (5.0f)
+#define DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW (8.0f)
+#define DEFAULT_MAX_STEER_ANGLE (0.5f)
+#define MIN_LOWERING_SPEED_COEFFICIENT (0.4f)
+#define MAX_ANGLE_FOR_SPEED_LIMITING (1.2f)
+#define MIN_ANGLE_FOR_SPEED_LIMITING (0.4f)
+#define MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES (0.1f)
+#define MIN_ANGLE_TO_APPLY_HANDBRAKE (0.7f)
+#define MIN_SPEED_TO_APPLY_HANDBRAKE (0.3f)
 
-#define PROBABILITY_OF_DEAD_PED_ACCIDENT 0.005f
-#define DISTANCE_BETWEEN_CAR_AND_DEAD_PED 6.0f
-#define PROBABILITY_OF_PASSENGER_IN_VEHICLE 0.125f
+#define PROBABILITY_OF_DEAD_PED_ACCIDENT (0.005f)
+#define DISTANCE_BETWEEN_CAR_AND_DEAD_PED (6.0f)
+#define PROBABILITY_OF_PASSENGER_IN_VEHICLE (0.125f)
 
-#define FARAWAY_DISTANCE_REMOVAL 120.0f
+#define ONSCREEN_DESPAWN_RANGE (120.0f)
+#define MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN (100.0f)
+#define REQUEST_ONSCREEN_DISTANCE ((ONSCREEN_DESPAWN_RANGE + MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) / 2)
+#define OFFSCREEN_DESPAWN_RANGE (40.0f)
+#define EXTENDED_RANGE_DESPAWN_MULTIPLIER (1.5f)
 
 int CCarCtrl::NumLawEnforcerCars;
 int CCarCtrl::NumAmbulancesOnDuty;
@@ -177,7 +181,7 @@ CCarCtrl::GenerateOneRandomCar()
 		angleLimit = -1.0f;
 		bTopDownCamera = true;
 		invertAngleLimitTest = true;
-		preferredDistance = 55.0f;
+		preferredDistance = OFFSCREEN_DESPAWN_RANGE + 15.0f;
 		/* BUG: testForCollision not initialized in original game. */
 		testForCollision = false;
 	}else if (!pPlayerVehicle){
@@ -191,14 +195,14 @@ CCarCtrl::GenerateOneRandomCar()
 			/* Forward to his current direction (camera direction). */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 1:
 			/* Spawn a vehicle close to player to his side. */
 			/* Kinda not within camera angle. */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = false;
-			preferredDistance = 40.0f;
+			preferredDistance = OFFSCREEN_DESPAWN_RANGE;
 			break;
 		}
 	}else if (fPlayerVehicleSpeed > 0.4f){ /* 72 km/h */
@@ -213,21 +217,21 @@ CCarCtrl::GenerateOneRandomCar()
 			/* Spawn a vehicle in a very narrow gap in front of a player */
 			angleLimit = 0.85f; /* approx 30 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 2:
 			/* Spawn a vehicle relatively far away from player. */
 			/* Forward to his current direction (camera direction). */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 3:
 			/* Spawn a vehicle close to player to his side. */
 			/* Kinda not within camera angle. */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = false;
-			preferredDistance = 40.0f;
+			preferredDistance = OFFSCREEN_DESPAWN_RANGE;
 			break;
 		}
 	}else if (fPlayerVehicleSpeed > 0.1f){ /* 18 km/h */
@@ -241,14 +245,14 @@ CCarCtrl::GenerateOneRandomCar()
 			/* Spawn a vehicle in a very narrow gap in front of a player */
 			angleLimit = 0.85f; /* approx 30 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 1:
 			/* Spawn a vehicle relatively far away from player. */
 			/* Forward to his current direction (camera direction). */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 2:
 		case 3:
@@ -256,7 +260,7 @@ CCarCtrl::GenerateOneRandomCar()
 			/* Kinda not within camera angle. */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = false;
-			preferredDistance = 40.0f;
+			preferredDistance = OFFSCREEN_DESPAWN_RANGE;
 			break;
 		}
 	}else{
@@ -271,14 +275,14 @@ CCarCtrl::GenerateOneRandomCar()
 			/* Forward to his current direction (camera direction). */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = true;
-			preferredDistance = 110.0f * TheCamera.GenerationDistMultiplier;
+			preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier;
 			break;
 		case 1:
 			/* Spawn a vehicle close to player to his side. */
 			/* Kinda not within camera angle. */
 			angleLimit = 0.707f; /* 45 degrees */
 			invertAngleLimitTest = false;
-			preferredDistance = 40.0f;
+			preferredDistance = OFFSCREEN_DESPAWN_RANGE;
 			break;
 		}
 	}
@@ -586,20 +590,21 @@ CCarCtrl::GenerateOneRandomCar()
 	case COPS_BOAT:
 		pVehicle->ChangeLawEnforcerState(1);
 		pVehicle->SetStatus(STATUS_PHYSICS);
+		break;
 	default:
-		pVehicle->SetStatus(STATUS_SIMPLE);
+		bBoatGenerated ? pVehicle->SetStatus(STATUS_PHYSICS) : pVehicle->SetStatus(STATUS_SIMPLE);
 		break;
 	}
 	CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0);
 	if (!pVehicle->GetIsOnScreen()){
-		if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > 40.0f * (pVehicle->bExtendedRange ? 1.5f : 1.0f)) {
+		if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > OFFSCREEN_DESPAWN_RANGE * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f)) {
 			/* Too far away cars that are not visible aren't needed. */
 			delete pVehicle;
 			return;
 		}
 	}else{
-		if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > TheCamera.GenerationDistMultiplier * (pVehicle->bExtendedRange ? 1.5f : 1.0f) * FARAWAY_DISTANCE_REMOVAL ||
-			(vecTargetPos - pVehicle->GetPosition()).Magnitude2D() < TheCamera.GenerationDistMultiplier * 100.0f) {
+		if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > TheCamera.GenerationDistMultiplier * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f) * ONSCREEN_DESPAWN_RANGE ||
+			(vecTargetPos - pVehicle->GetPosition()).Magnitude2D() < TheCamera.GenerationDistMultiplier * MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) {
 			delete pVehicle;
 			return;
 		}
@@ -840,24 +845,27 @@ int32
 CCarCtrl::ChoosePoliceCarModel(void)
 {
 	if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired() &&
+#ifdef FIX_BUGS
+		(CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 || LastTimeMiamiViceGenerated == 0) &&
+#else
 		CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 &&
+#endif
 		CStreaming::HasModelLoaded(MI_VICECHEE)) {
-		// TODO(MIAMI): setup correct models!
 		switch (MiamiViceCycle) {
 		case 0:
-			if (CStreaming::HasModelLoaded(MI_COP) && CStreaming::HasModelLoaded(MI_COP))
+			if (CStreaming::HasModelLoaded(MI_VICE1) && CStreaming::HasModelLoaded(MI_VICE2))
 				return MI_VICECHEE;
 			break;
 		case 1:
-			if (CStreaming::HasModelLoaded(MI_COP) && CStreaming::HasModelLoaded(MI_COP))
+			if (CStreaming::HasModelLoaded(MI_VICE3) && CStreaming::HasModelLoaded(MI_VICE4))
 				return MI_VICECHEE;
 			break;
 		case 2:
-			if (CStreaming::HasModelLoaded(MI_COP) && CStreaming::HasModelLoaded(MI_COP))
+			if (CStreaming::HasModelLoaded(MI_VICE5) && CStreaming::HasModelLoaded(MI_VICE6))
 				return MI_VICECHEE;
 			break;
 		case 3:
-			if (CStreaming::HasModelLoaded(MI_COP) && CStreaming::HasModelLoaded(MI_COP))
+			if (CStreaming::HasModelLoaded(MI_VICE7) && CStreaming::HasModelLoaded(MI_VICE8))
 				return MI_VICECHEE;
 			break;
 		default:
@@ -956,7 +964,7 @@ CCarCtrl::PossiblyRemoveVehicle(CVehicle* pVehicle)
 			return;
 		}
 		float distanceToPlayer = (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D();
-		float threshold = 40.0f;
+		float threshold = OFFSCREEN_DESPAWN_RANGE;
 		if (pVehicle->GetIsOnScreen() ||
 			TheCamera.Cams[TheCamera.ActiveCam].LookingLeft ||
 			TheCamera.Cams[TheCamera.ActiveCam].LookingRight ||
@@ -968,12 +976,12 @@ CCarCtrl::PossiblyRemoveVehicle(CVehicle* pVehicle)
 			pVehicle->bIsLawEnforcer ||
 			pVehicle->bIsCarParkVehicle
 			){
-			threshold = FARAWAY_DISTANCE_REMOVAL * TheCamera.GenerationDistMultiplier;
+			threshold = ONSCREEN_DESPAWN_RANGE * TheCamera.GenerationDistMultiplier;
 		}
 		if (TheCamera.GetForward().z < -0.9f)
 			threshold = 70.0f;
 		if (pVehicle->bExtendedRange)
-			threshold *= 1.5f;
+			threshold *= EXTENDED_RANGE_DESPAWN_MULTIPLIER;
 		if (distanceToPlayer > threshold && !CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){
 			if (pVehicle->GetIsOnScreen()){
 				pVehicle->bFadeOut = true;
@@ -1553,12 +1561,13 @@ void CCarCtrl::WeaveThroughCarsSectorList(CPtrList& lst, CVehicle* pVehicle, CPh
 
 void CCarCtrl::WeaveForOtherCar(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight)
 {
-	// TODO(MIAMI):
+	CVehicle* pOtherCar = (CVehicle*)pOtherEntity;
+	if (pVehicle->bPartOfConvoy && pOtherCar->bPartOfConvoy)
+		return;
 	if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE && pOtherEntity == FindPlayerVehicle())
 		return;
 	if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMCAR_CLOSE && pOtherEntity == pVehicle->AutoPilot.m_pTargetCar)
 		return;
-	CVehicle* pOtherCar = (CVehicle*)pOtherEntity;
 	CVector2D vecDiff = pOtherCar->GetPosition() - pVehicle->GetPosition();
 	float angleBetweenVehicles = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y);
 	float distance = vecDiff.Magnitude();
@@ -1958,11 +1967,13 @@ void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float t
 void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float targetY, CVehicle* pTarget)
 #endif
 {
+	if (pVehicle->m_nRouteSeed)
+		CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed);
 	int prevNode = pVehicle->AutoPilot.m_nCurrentRouteNode;
 	int curNode = pVehicle->AutoPilot.m_nNextRouteNode;
 	CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNode];
 	CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode];
-	CPathNode* pTargetNode;
+	CPathNode* pTargetNode[2];
 	int16 numNodes;
 	float distanceToTargetNode;
 	ThePaths.DoPathSearch(0, pCurNode->GetPosition(), curNode,
@@ -1971,33 +1982,42 @@ void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float t
 #else
 		CVector(targetX, targetY, 0.0f),
 #endif
-		&pTargetNode, &numNodes, 1, pVehicle, &distanceToTargetNode, 999999.9f, -1);
+		pTargetNode, &numNodes, 2, pVehicle, &distanceToTargetNode, 999999.9f, -1);
 
 	int newNextNode;
 	int nextLink;
-	if (numNodes != 1 || pTargetNode == pCurNode){
-		float currentAngle = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y);
-		nextLink = 0;
-		float lowestAngleChange = 10.0f;
-		int numLinks = pCurNode->numLinks;
-		newNextNode = 0;
-		for (int i = 0; i < numLinks; i++){
-			int conNode = ThePaths.ConnectedNode(i + pCurNode->firstLink);
-			if (conNode == prevNode && i > 1)
-				continue;
-			CPathNode* pTestNode = &ThePaths.m_pathNodes[conNode];
-			float angle = CGeneral::GetATanOfXY(pTestNode->GetX() - pCurNode->GetX(), pTestNode->GetY() - pCurNode->GetY());
-			angle = LimitRadianAngle(angle - currentAngle);
-			angle = ABS(angle);
-			if (angle < lowestAngleChange){
-				lowestAngleChange = angle;
-				newNextNode = conNode;
-				nextLink = i;
+	if (numNodes != 1 && numNodes != 2 || pTargetNode[0] == pCurNode){
+		if (numNodes != 2 || pTargetNode[1] == pCurNode) {
+			float currentAngle = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y);
+			nextLink = 0;
+			float lowestAngleChange = 10.0f;
+			int numLinks = pCurNode->numLinks;
+			newNextNode = 0;
+			for (int i = 0; i < numLinks; i++) {
+				int conNode = ThePaths.ConnectedNode(i + pCurNode->firstLink);
+				if (conNode == prevNode && i > 1)
+					continue;
+				CPathNode* pTestNode = &ThePaths.m_pathNodes[conNode];
+				float angle = CGeneral::GetATanOfXY(pTestNode->GetX() - pCurNode->GetX(), pTestNode->GetY() - pCurNode->GetY());
+				angle = LimitRadianAngle(angle - currentAngle);
+				angle = ABS(angle);
+				if (angle < lowestAngleChange) {
+					lowestAngleChange = angle;
+					newNextNode = conNode;
+					nextLink = i;
+				}
 			}
 		}
-	}else{
+		else {
+			nextLink = 0;
+			newNextNode = pTargetNode[1] - ThePaths.m_pathNodes;
+			for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++)
+				;
+		}
+	}
+	else {
 		nextLink = 0;
-		newNextNode = pTargetNode - ThePaths.m_pathNodes;
+		newNextNode = pTargetNode[0] - ThePaths.m_pathNodes;
 		for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++)
 			;
 	}
@@ -2017,11 +2037,11 @@ void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float t
 	int8 lanesOnNextNode;
 	if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode) {
 		pVehicle->AutoPilot.m_nNextDirection = 1;
-		lanesOnNextNode = pNextLink->numLeftLanes;
+		lanesOnNextNode = pNextLink->numRightLanes;
 	}
 	else {
 		pVehicle->AutoPilot.m_nNextDirection = -1;
-		lanesOnNextNode = pNextLink->numRightLanes;
+		lanesOnNextNode = pNextLink->numLeftLanes;
 	}
 	float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX();
 	float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY();
@@ -2076,6 +2096,8 @@ void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float t
 
 bool CCarCtrl::PickNextNodeToFollowPath(CVehicle* pVehicle)
 {
+	if (pVehicle->m_nRouteSeed)
+		CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed);
 	int curNode = pVehicle->AutoPilot.m_nNextRouteNode;
 	CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode];
 	if (pVehicle->AutoPilot.m_nPathFindNodesCount == 0){
@@ -2168,6 +2190,7 @@ void CCarCtrl::Init(void)
 	LastTimeAmbulanceCreated = 0;
 #ifdef FIX_BUGS
 	LastTimeLawEnforcerCreated = 0;
+	LastTimeMiamiViceGenerated = 0;
 #endif
 	bCarsGeneratedAroundCamera = false;
 	CountDownToCarsAtStart = 2;
@@ -2197,6 +2220,7 @@ void CCarCtrl::ReInit(void)
 	LastTimeFireTruckCreated = 0;
 	LastTimeAmbulanceCreated = 0;
 	LastTimeLawEnforcerCreated = 0;
+	LastTimeMiamiViceGenerated = 0;
 #endif
 	CountDownToCarsAtStart = 2;
 	CarDensityMultiplier = 1.0f;
@@ -2313,7 +2337,7 @@ void CCarCtrl::SteerAICarWithPhysics(CVehicle* pVehicle)
 			pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
 		break;
 	case TEMPACT_HANDBRAKETURNLEFT:
-		swerve = -1.0f; // It seems like this should be swerve = 1.0f (fixed in VC)
+		swerve = 1.0f;
 		accel = 0.0f;
 		brake = 0.0f;
 		handbrake = true;
@@ -2321,7 +2345,7 @@ void CCarCtrl::SteerAICarWithPhysics(CVehicle* pVehicle)
 			pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
 		break;
 	case TEMPACT_HANDBRAKETURNRIGHT:
-		swerve = 1.0f; // It seems like this should be swerve = -1.0f (fixed in VC)
+		swerve = -1.0f;
 		accel = 0.0f;
 		brake = 0.0f;
 		handbrake = true;
@@ -2432,6 +2456,9 @@ void CCarCtrl::SteerAICarWithPhysics_OnlyMission(CVehicle* pVehicle, float* pSwe
 		SteerAICarWithPhysicsTryingToBlockTarget_Stop(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y,
 			FindPlayerSpeed().x, FindPlayerSpeed().y, pSwerve, pAccel, pBrake, pHandbrake);
 		return;
+	case MISSION_WAITFORDELETION:
+	case MISSION_HELI_LAND:
+		return;
 	case MISSION_GOTOCOORDS_STRAIGHT:
 	case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE:
 		SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil,
@@ -2445,6 +2472,12 @@ void CCarCtrl::SteerAICarWithPhysics_OnlyMission(CVehicle* pVehicle, float* pSwe
 		*pHandbrake = true;
 		*pBrake = 0.5f;
 		return;
+	case MISSION_GOTOCOORDS_ASTHECROWSWIMS:
+		SteerAIBoatWithPhysicsHeadingForTarget(pVehicle,
+			pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y,
+			pSwerve, pAccel, pBrake);
+		*pHandbrake = false;
+		return;
 	case MISSION_RAMCAR_CLOSE:
 		SteerAICarWithPhysicsHeadingForTarget(pVehicle, pVehicle->AutoPilot.m_pTargetCar,
 			pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y,
@@ -2466,26 +2499,93 @@ void CCarCtrl::SteerAICarWithPhysics_OnlyMission(CVehicle* pVehicle, float* pSwe
 			pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().y,
 			pSwerve, pAccel, pBrake, pHandbrake);
 		return;
+	case MISSION_HELI_FLYTOCOORS:
+		//SteerAIHeliTowardsTargetCoors((CAutomobile*)pVehicle);
+		return;
+	case MISSION_ATTACKPLAYER:
+		SteerAIBoatWithPhysicsAttackingPlayer(pVehicle, pSwerve, pAccel, pBrake, pHandbrake);
+		return;
+	case MISSION_PLANE_FLYTOCOORS:
+		//SteerAIPlaneTowardsTargetCoors((CAutomobile*)pVehicle);
+		return;
+	case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1:
+		SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil,
+			pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y,
+			pSwerve, pAccel, pBrake, pHandbrake);
+		return;
+	case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2:
+		SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, FindPlayerCoors().x, FindPlayerCoors().y,
+			pSwerve, pAccel, pBrake, pHandbrake);
+		return;
+	case MISSION_BLOCKPLAYER_FORWARDANDBACK:
+		//SteerAICarBlockingPlayerForwardAndBack(pVehicle, pSwerve, pAccel, pBrake, pHandbrake);
+		return;
 	default:
+		assert(0);
 		return;
 	}
 }
 
-void CCarCtrl::SteerAIBoatWithPhysics(CBoat* pBoat)
+void CCarCtrl::SteerAIBoatWithPhysicsHeadingForTarget(CVehicle* pVehicle, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake)
 {
-	if (pBoat->AutoPilot.m_nCarMission == MISSION_GOTOCOORDS_ASTHECROWSWIMS){
-		SteerAIBoatWithPhysicsHeadingForTarget(pBoat,
-			pBoat->AutoPilot.m_vecDestinationCoors.x, pBoat->AutoPilot.m_vecDestinationCoors.y,
-			&pBoat->m_fSteeringLeftRight, &pBoat->m_fAccelerate, &pBoat->m_fBrake);
-	}else if (pBoat->AutoPilot.m_nCarMission == MISSION_NONE){
-		pBoat->m_fSteeringLeftRight = 0.0f;
-		pBoat->m_fAccelerate = 0.0f;
-		pBoat->m_fBrake = 0.0f;
+	CVector2D forward = pVehicle->GetForward();
+	forward.Normalise();
+	float angleToTarget = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y);
+	float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y);
+	float steerAngle = LimitRadianAngle(angleToTarget - angleForward);
+	steerAngle = clamp(steerAngle, -DEFAULT_MAX_STEER_ANGLE, DEFAULT_MAX_STEER_ANGLE);
+#ifdef FIX_BUGS
+	float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed();
+#else
+	float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed;
+#endif
+	float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED;
+	float speedDiff = speedTarget - currentSpeed;
+	if (speedDiff <= 0.0f) {
+		speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f;
+		steerAngle *= -1;
 	}
-	pBoat->m_fSteerAngle = pBoat->m_fSteeringLeftRight;
-	pBoat->m_fGasPedal = pBoat->m_fAccelerate;
-	pBoat->m_fBrakePedal = pBoat->m_fBrake;
-	pBoat->bIsHandbrakeOn = false;
+	else if (speedDiff / currentSpeed > 0.25f) {
+		*pAccel = 1.0f;
+	}
+	else {
+		*pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f;
+	}
+	*pBrake = 0.0f;
+	*pSwerve = steerAngle;
+}
+
+void CCarCtrl::SteerAIBoatWithPhysicsAttackingPlayer(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake)
+{
+	float distanceToPlayer = (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude();
+	float projection = Min(distanceToPlayer * 0.05f, 2.0f);
+	CVector2D forward = pVehicle->GetForward();
+	forward.Normalise();
+	CVector2D vecToProjection = FindPlayerCoors() + FindPlayerSpeed() * projection * GAME_SPEED_TO_CARAI_SPEED;
+	float angleToTarget = CGeneral::GetATanOfXY(vecToProjection.x - pVehicle->GetPosition().x, vecToProjection.y - pVehicle->GetPosition().y);
+	float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y);
+	float steerAngle = LimitRadianAngle(angleToTarget - angleForward);
+#ifdef FIX_BUGS
+	float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed();
+#else
+	float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed;
+#endif
+	float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED;
+	float speedDiff = speedTarget - currentSpeed;
+	if (speedDiff <= 0.0f) {
+		speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f;
+	}
+	else if (speedDiff / currentSpeed > 0.25f) {
+		*pAccel = 1.0f;
+	}
+	else {
+		*pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f;
+	}
+	*pBrake = 0.0f;
+	*pSwerve = steerAngle;
+	*pHandbrake = false;
+	if (pVehicle->GetModelIndex() == MI_PREDATOR && distanceToPlayer < 40.0f && steerAngle < 0.15f)
+		pVehicle->FireFixedMachineGuns();
 }
 
 float CCarCtrl::FindMaxSteerAngle(CVehicle* pVehicle)
@@ -2672,6 +2772,7 @@ void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget(CVehicle* pVehicle, floa
 		pVehicle->AutoPilot.m_nCarMission = (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_CLOSE) ?
 			MISSION_BLOCKCAR_HANDBRAKESTOP : MISSION_BLOCKPLAYER_HANDBRAKESTOP;
 }
+
 void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle* pVehicle, float targetX, float targetY, float targetSpeedX, float targetSpeedY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake)
 {
 	*pSwerve = 0.0f;
@@ -2713,26 +2814,6 @@ void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle* pVehicle,
 	}
 }
 
-void CCarCtrl::SteerAIBoatWithPhysicsHeadingForTarget(CBoat* pBoat, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake)
-{
-	CVector2D forward(pBoat->GetForward());
-	forward.Normalise();
-	CVector2D distanceToTarget = CVector2D(targetX, targetY) - pBoat->GetPosition();
-	float angleToTarget = CGeneral::GetATanOfXY(distanceToTarget.x, distanceToTarget.y);
-	float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y);
-	float angleDiff = LimitRadianAngle(angleToTarget - angleForward);
-	angleDiff = Min(DEFAULT_MAX_STEER_ANGLE, Max(-DEFAULT_MAX_STEER_ANGLE, angleDiff));
-	float currentSpeed = pBoat->GetMoveSpeed().Magnitude2D(); // +0.0f for some reason
-	float speedDiff = pBoat->AutoPilot.m_nCruiseSpeed - currentSpeed * 60.0f;
-	if (speedDiff > 0.0f){
-		float accRemaining = speedDiff / pBoat->AutoPilot.m_nCruiseSpeed;
-		*pAccel = (accRemaining > 0.25f) ? 1.0f : 1.0f - (0.25f - accRemaining) * 4.0f;
-	}else
-		*pAccel = (speedDiff < -5.0f) ? -0.2f : -0.1f;
-	*pBrake = 0.0f;
-	*pSwerve = angleDiff;
-}
-
 void
 CCarCtrl::RegisterVehicleOfInterest(CVehicle* pVehicle)
 {
@@ -2852,8 +2933,12 @@ bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle* pVehicle, CVector vecTar
 	return false;
 }
 
+
+//TODO
 void CCarCtrl::FindLinksToGoWithTheseNodes(CVehicle* pVehicle)
 {
+	if (pVehicle->m_nRouteSeed)
+		CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed);
 	int nextLink;
 	CPathNode* pCurNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nCurrentRouteNode];
 	for (nextLink = 0; nextLink < 12; nextLink++)
@@ -2881,6 +2966,8 @@ void CCarCtrl::GenerateEmergencyServicesCar(void)
 {
 	if (FindPlayerPed()->m_pWanted->m_nWantedLevel > 3)
 		return;
+	if (CGame::IsInInterior())
+		return;
 	if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars +
 		NumLawEnforcerCars + NumRandomCars > MaxNumberOfCarsInUse)
 		return;
@@ -2936,9 +3023,11 @@ bool CCarCtrl::GenerateOneEmergencyServicesCar(uint32 mi, CVector vecPos)
 		if (ThePaths.NewGenerateCarCreationCoors(pPlayerPos.x, pPlayerPos.y, 0.707f, 0.707f,
 		  120.0f, -1.0f, true, &spawnPos, &curNode, &nextNode, &posBetweenNodes, false)){
 			int16 colliding[2];
-			CWorld::FindObjectsKindaColliding(spawnPos, 10.0f, true, colliding, 2, nil, false, true, true, false, false);
-			if (colliding[0] == 0)
-				created = true;
+			if (!ThePaths.GetNode(curNode)->bWaterPath) {
+				CWorld::FindObjectsKindaColliding(spawnPos, 10.0f, true, colliding, 2, nil, false, true, true, false, false);
+				if (colliding[0] == 0)
+					created = true;
+			}
 		}
 		attempts += 1;
 	}
@@ -2997,18 +3086,24 @@ void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove)
 	if (remove){
 		switch (pVehicle->VehicleCreatedBy){
 		case RANDOM_VEHICLE:
-			if (pVehicle->bIsLawEnforcer)
-				--NumLawEnforcerCars;
-			--NumRandomCars;
+			if (pVehicle->bIsLawEnforcer) {
+				if (--NumLawEnforcerCars < 0)
+					NumLawEnforcerCars = 0;
+			}
+			if (--NumRandomCars < 0)
+				NumRandomCars = 0;
 			return;
 		case MISSION_VEHICLE:
-			--NumMissionCars;
+			if (--NumMissionCars < 0)
+				NumMissionCars = 0;
 			return;
 		case PARKED_VEHICLE:
-			--NumParkedCars;
+			if (--NumParkedCars < 0)
+				NumParkedCars = 0;
 			return;
 		case PERMANENT_VEHICLE:
-			--NumPermanentCars;;
+			if (--NumPermanentCars < 0)
+				NumPermanentCars = 0;
 			return;
 		}
 	}
@@ -3026,7 +3121,7 @@ void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove)
 			++NumParkedCars;
 			return;
 		case PERMANENT_VEHICLE:
-			++NumPermanentCars;;
+			++NumPermanentCars;
 			return;
 		}
 	}
@@ -3034,7 +3129,11 @@ void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove)
 
 bool CCarCtrl::ThisRoadObjectCouldMove(int16 mi)
 {
+#ifdef GTA_BRIDGE
 	return mi == MI_BRIDGELIFT || mi == MI_BRIDGEROADSEGMENT;
+#else
+	return false;
+#endif
 }
 
 bool CCarCtrl::MapCouldMoveInThisArea(float x, float y)
diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h
index f30d0720..8138266f 100644
--- a/src/control/CarCtrl.h
+++ b/src/control/CarCtrl.h
@@ -107,13 +107,11 @@ public:
 	static float FindSpeedMultiplier(float, float, float, float);
 	static void SteerAICarWithPhysics(CVehicle*);
 	static void SteerAICarWithPhysics_OnlyMission(CVehicle*, float*, float*, float*, bool*);
-	static void SteerAIBoatWithPhysics(CBoat*);
 	static float FindMaxSteerAngle(CVehicle*);
 	static void SteerAICarWithPhysicsFollowPath(CVehicle*, float*, float*, float*, bool*);
 	static void SteerAICarWithPhysicsHeadingForTarget(CVehicle*, CPhysical*, float, float, float*, float*, float*, bool*);
 	static void SteerAICarWithPhysicsTryingToBlockTarget(CVehicle*, float, float, float, float, float*, float*, float*, bool*);
 	static void SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle*, float, float, float, float, float*, float*, float*, bool*);
-	static void SteerAIBoatWithPhysicsHeadingForTarget(CBoat*, float, float, float*, float*, float*);
 	static bool ThisRoadObjectCouldMove(int16);
 	static void ClearInterestingVehicleList();
 	static void FindLinksToGoWithTheseNodes(CVehicle*);
@@ -127,6 +125,11 @@ public:
 	static int32 ChooseCarModelToLoad(int32 rating);
 	static bool BoatWithTallMast(int32 mi);
 	static void RemoveCarsIfThePoolGetsFull(void);
+	static void SteerAIBoatWithPhysicsHeadingForTarget(CVehicle*, float, float, float*, float*, float*);
+	static void SteerAIHeliTowardsTargetCoors(CAutomobile*);
+	static void SteerAIPlaneTowardsTargetCoors(CAutomobile*);
+	static void SteerAIBoatWithPhysicsAttackingPlayer(CVehicle*, float*, float*, float*, bool*);
+	static void SteerAICarBlockingPlayerForwardAndBack(CVehicle*, float*, float*, float*, bool*);
 
 	static float GetPositionAlongCurrentCurve(CVehicle* pVehicle)
 	{
diff --git a/src/control/Script.cpp b/src/control/Script.cpp
index 307cb536..90bde151 100644
--- a/src/control/Script.cpp
+++ b/src/control/Script.cpp
@@ -9784,7 +9784,15 @@ int8 CRunningScript::ProcessCommands1200To1299(int32 command)
 	case COMMAND_SET_CAR_FORWARD_SPEED:
 	case COMMAND_SET_AREA_VISIBLE:
 	case COMMAND_SET_CUTSCENE_ANIM_TO_LOOP:
+		assert(0);
 	case COMMAND_MARK_CAR_AS_CONVOY_CAR:
+	{
+		CollectParameters(&m_nIp, 2);
+		CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]);
+		assert(pVehicle);
+		pVehicle->bPartOfConvoy = ScriptParams[1];
+		return 0;
+	}
 	case COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER:
 	case COMMAND_GET_HAVOC_CAUSED_BY_PLAYER:
 	case COMMAND_CREATE_SCRIPT_ROADBLOCK:
diff --git a/src/core/Streaming.cpp b/src/core/Streaming.cpp
index eb7fc8f5..cc746553 100644
--- a/src/core/Streaming.cpp
+++ b/src/core/Streaming.cpp
@@ -1397,12 +1397,44 @@ CStreaming::StreamVehiclesAndPeds(void)
 		SetModelIsDeletable(MI_CHOPPER);
 
 	if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired()) {
-		//TODO(MIAMI): miami vice peds
+		SetModelIsDeletable(MI_VICE1);
+		SetModelIsDeletable(MI_VICE2);
+		SetModelIsDeletable(MI_VICE3);
+		SetModelIsDeletable(MI_VICE4);
+		SetModelIsDeletable(MI_VICE5);
+		SetModelIsDeletable(MI_VICE6);
+		SetModelIsDeletable(MI_VICE7);
+		SetModelIsDeletable(MI_VICE8);
+		switch (CCarCtrl::MiamiViceCycle) {
+		case 0:
+			RequestModel(MI_VICE1, STREAMFLAGS_DONT_REMOVE);
+			RequestModel(MI_VICE2, STREAMFLAGS_DONT_REMOVE);
+			break;
+		case 1:
+			RequestModel(MI_VICE3, STREAMFLAGS_DONT_REMOVE);
+			RequestModel(MI_VICE4, STREAMFLAGS_DONT_REMOVE);
+			break;
+		case 2:
+			RequestModel(MI_VICE5, STREAMFLAGS_DONT_REMOVE);
+			RequestModel(MI_VICE6, STREAMFLAGS_DONT_REMOVE);
+			break;
+		case 3:
+			RequestModel(MI_VICE7, STREAMFLAGS_DONT_REMOVE);
+			RequestModel(MI_VICE8, STREAMFLAGS_DONT_REMOVE);
+			break;
+		}
 		RequestModel(MI_VICECHEE, STREAMFLAGS_DONT_REMOVE);
 	}
 	else {
 		SetModelIsDeletable(MI_VICECHEE);
-		//TODO(MIAMI): miami vice peds
+		SetModelIsDeletable(MI_VICE1);
+		SetModelIsDeletable(MI_VICE2);
+		SetModelIsDeletable(MI_VICE3);
+		SetModelIsDeletable(MI_VICE4);
+		SetModelIsDeletable(MI_VICE5);
+		SetModelIsDeletable(MI_VICE6);
+		SetModelIsDeletable(MI_VICE7);
+		SetModelIsDeletable(MI_VICE8);
 	}
 
 	if(timeBeforeNextLoad >= 0)
diff --git a/src/modelinfo/ModelIndices.h b/src/modelinfo/ModelIndices.h
index d3d568b2..f0db054d 100644
--- a/src/modelinfo/ModelIndices.h
+++ b/src/modelinfo/ModelIndices.h
@@ -180,6 +180,13 @@ enum
 	MI_TAXI_D = 28,	// HMOCA
 	MI_GANG01 = 83,	// CBa
 	MI_VICE1 = 97,
+	MI_VICE2,
+	MI_VICE3,
+	MI_VICE4,
+	MI_VICE5,
+	MI_VICE6,
+	MI_VICE7,
+	MI_VICE8,
 	MI_WFYG2 = 106,	// last regular ped
 	MI_SPECIAL01 = 109,
 	MI_SPECIAL21 = 129,
diff --git a/src/peds/CopPed.cpp b/src/peds/CopPed.cpp
index 0c78279e..9e77ec72 100644
--- a/src/peds/CopPed.cpp
+++ b/src/peds/CopPed.cpp
@@ -17,7 +17,7 @@
 #include "Renderer.h"
 #include "Camera.h"
 
-CCopPed::CCopPed(eCopType copType) : CPed(PEDTYPE_COP)
+CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP)
 {
 	m_nCopType = copType;
 	switch (copType) {
@@ -57,7 +57,23 @@ CCopPed::CCopPed(eCopType copType) : CPed(PEDTYPE_COP)
 		m_wepSkills = 32; /* TODO: what is this? seems unused */
 		m_wepAccuracy = 84;
 		break;
-	default:
+	case COP_MIAMIVICE:
+		switch (modifier) {
+		case 0: SetModelIndex(MI_VICE1); break;
+		case 1: SetModelIndex(MI_VICE2); break;
+		case 2: SetModelIndex(MI_VICE3); break;
+		case 3: SetModelIndex(MI_VICE4); break;
+		case 4: SetModelIndex(MI_VICE5); break;
+		case 5: SetModelIndex(MI_VICE6); break;
+		case 6: SetModelIndex(MI_VICE7); break;
+		case 7: SetModelIndex(MI_VICE8); break;
+		default: assert(0); break;
+		}
+		GiveWeapon(WEAPONTYPE_UZI, 1000);
+		SetCurrentWeapon(WEAPONTYPE_UZI);
+		m_fArmour = 100.0f;
+		m_wepSkills = 176;
+		m_wepAccuracy = 76;
 		break;
 	}
 	m_bIsInPursuit = false;
diff --git a/src/peds/CopPed.h b/src/peds/CopPed.h
index d63aef06..c88dffcd 100644
--- a/src/peds/CopPed.h
+++ b/src/peds/CopPed.h
@@ -7,6 +7,7 @@ enum eCopType
 	COP_FBI = 1,
 	COP_SWAT = 2,
 	COP_ARMY = 3,
+	COP_MIAMIVICE = 5
 };
 
 class CCopPed : public CPed
@@ -24,7 +25,7 @@ public:
 	eCopType m_nCopType;
 	int8 field_1364;
 
-	CCopPed(eCopType);
+	CCopPed(eCopType, int32 modifier = 0);
 	~CCopPed();
 
 	void ClearPursuit(void);
diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp
index 1d8c7f5d..2dc0bbab 100644
--- a/src/peds/Population.cpp
+++ b/src/peds/Population.cpp
@@ -460,7 +460,7 @@ CPopulation::PedCreationDistMultiplier()
 }
 
 CPed*
-CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors)
+CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors, int32 modifier)
 {
 	switch (pedType) {
 		case PEDTYPE_CIVMALE:
@@ -480,7 +480,7 @@ CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors)
 		}
 		case PEDTYPE_COP:
 		{
-			CCopPed *ped = new CCopPed((eCopType)miOrCopType);
+			CCopPed *ped = new CCopPed((eCopType)miOrCopType, modifier);
 			ped->SetPosition(coors);
 			ped->SetOrientation(0.0f, 0.0f, 0.0f);
 			CWorld::Add(ped);
@@ -713,9 +713,10 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree
 }
 
 CPed*
-CPopulation::AddPedInCar(CVehicle* car)
+CPopulation::AddPedInCar(CVehicle* car, bool isPassenger)
 {
 	int defaultModel = MI_MALE01;
+	int miamiViceIndex = 0;
 	bool imSureThatModelIsLoaded = true;
 	CVector coors = FindPlayerCoors();
 	CZoneInfo zoneInfo;
@@ -740,7 +741,6 @@ CPopulation::AddPedInCar(CVehicle* car)
 			break;
 		case MI_POLICE:
 		case MI_PREDATOR:
-		case MI_VICECHEE: // TODO(MIAMI): proper model
 			preferredModel = COP_STREET;
 			pedType = PEDTYPE_COP;
 			break;
@@ -753,6 +753,11 @@ CPopulation::AddPedInCar(CVehicle* car)
 			preferredModel = COP_ARMY;
 			pedType = PEDTYPE_COP;
 			break;
+		case MI_VICECHEE: // TODO(MIAMI): figure out new structure of the function
+			preferredModel = COP_MIAMIVICE;
+			pedType = PEDTYPE_COP;
+			miamiViceIndex = (isPassenger ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1);
+			break;
 		case MI_TAXI:
 		case MI_CABBIE:
 		case MI_ZEBRA:
@@ -799,7 +804,7 @@ CPopulation::AddPedInCar(CVehicle* car)
 		pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(defaultModel))->m_pedType;
 	}
 
-	CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition());
+	CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition(), miamiViceIndex);
 	newPed->bUsesCollision = false;
 
 	// what??
diff --git a/src/peds/Population.h b/src/peds/Population.h
index 181c5809..20d6e567 100644
--- a/src/peds/Population.h
+++ b/src/peds/Population.h
@@ -70,7 +70,7 @@ public:
 	static void LoadPedGroups();
 	static void UpdatePedCount(ePedType, bool);
 	static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool);
-	static CPed *AddPedInCar(CVehicle *car);
+	static CPed *AddPedInCar(CVehicle *car, bool isPassenger);
 	static bool IsPointInSafeZone(CVector *coors);
 	static void RemovePed(CPed *ent);
 	static int32 ChooseCivilianOccupation(int32);
@@ -80,7 +80,7 @@ public:
 	static void FindClosestZoneForCoors(CVector*, int*, eLevelName, eLevelName);
 	static void GeneratePedsAtStartOfGame();
 	static float PedCreationDistMultiplier();
-	static CPed *AddPed(ePedType pedType, uint32 mi, CVector const &coors);
+	static CPed *AddPed(ePedType pedType, uint32 mi, CVector const &coors, int32 modifier = 0);
 	static void AddToPopulation(float, float, float, float);
 	static void ManagePopulation(void);
 	static void MoveCarsAndPedsOutOfAbandonedZones(void);
diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp
index 5879717a..261a8426 100644
--- a/src/vehicles/Boat.cpp
+++ b/src/vehicles/Boat.cpp
@@ -3,6 +3,7 @@
 #include "General.h"
 #include "Timecycle.h"
 #include "HandlingMgr.h"
+#include "CarAI.h"
 #include "CarCtrl.h"
 #include "RwHelper.h"
 #include "ModelIndices.h"
@@ -152,6 +153,7 @@ CBoat::ProcessControl(void)
 	case STATUS_SIMPLE:
 		m_bIsAnchored = false;
 		m_fOrientation = INVALID_ORIENTATION;
+		CCarAI::UpdateCarAI(this);
 		CPhysical::ProcessControl();
 		bBoatInWater = true;
 		bPropellerInWater = true;
@@ -160,7 +162,8 @@ CBoat::ProcessControl(void)
 	case STATUS_PHYSICS:
 		m_bIsAnchored = false;
 		m_fOrientation = INVALID_ORIENTATION;
-		CCarCtrl::SteerAIBoatWithPhysics(this);
+		CCarAI::UpdateCarAI(this);
+		CCarCtrl::SteerAICarWithPhysics(this);
 		break;
 	case STATUS_ABANDONED:
 	case STATUS_WRECKED:
@@ -398,9 +401,9 @@ CBoat::ProcessControl(void)
 		}
 
 		// Slow down or push down boat as it approaches the world limits
-		m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - 1900.0f)*0.01f);	// east
-		m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - -1515.0f)*0.01f);	// west
-		m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - 600.0f)*0.01f);	// north
+		m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - 1500.0f)*0.01f);	// east
+		m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - -2300.0f)*0.01f);	// west
+		m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - 1900.0f)*0.01f);	// north
 		m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - -1900.0f)*0.01f);	// south
 
 		if(!onLand && bBoatInWater)
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 496a72de..d0341058 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -100,6 +100,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
 	m_bSirenOrAlarm = 0;
 	m_nCarHornTimer = 0;
 	m_nCarHornPattern = 0;
+	bPartOfConvoy = false;
 	bCreatedAsPoliceVehicle = false;
 	bParking = false;
 	m_nAlarmState = 0;
@@ -697,51 +698,57 @@ void
 CVehicle::DoFixedMachineGuns(void)
 {
 	if(CPad::GetPad(0)->GetCarGunFired() && !bGunSwitchedOff){
-		if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 150){
-			CVector source, target;
-			float dx, dy, len;
-
-			dx = GetForward().x;
-			dy = GetForward().y;
-			len = Sqrt(SQR(dx) + SQR(dy));
-			if(len < 0.1f) len = 0.1f;
-			dx /= len;
-			dy /= len;
-
-			m_nGunFiringTime = CTimer::GetTimeInMilliseconds();
-
-			source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f);
-			target = source + CVector(dx, dy, 0.0f)*60.0f;
-			target += CVector(
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f,
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f,
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.02f);
-			CWeapon::DoTankDoomAiming(this, pDriver, &source, &target);
-			FireOneInstantHitRound(&source, &target, 15);
-
-			source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f);
-			target = source + CVector(dx, dy, 0.0f)*60.0f;
-			target += CVector(
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f,
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.015f,
-				((CGeneral::GetRandomNumber()&0xFF)-128) * 0.02f);
-			CWeapon::DoTankDoomAiming(this, pDriver, &source, &target);
-			FireOneInstantHitRound(&source, &target, 15);
-
-			DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
-
-			m_nAmmoInClip--;
-			if(m_nAmmoInClip == 0){
-				m_nAmmoInClip = 20;
-				m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400;
-			}
-		}
+		FireFixedMachineGuns();
 	}else{
 		if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400)
 			m_nAmmoInClip = 20;
 	}
 }
 
+void
+CVehicle::FireFixedMachineGuns(void)
+{
+	if (CTimer::GetTimeInMilliseconds() <= m_nGunFiringTime + 150)
+		return;
+	CVector source, target;
+	float dx, dy, len;
+
+	dx = GetForward().x;
+	dy = GetForward().y;
+	len = Sqrt(SQR(dx) + SQR(dy));
+	if (len < 0.1f) len = 0.1f;
+	dx /= len;
+	dy /= len;
+
+	m_nGunFiringTime = CTimer::GetTimeInMilliseconds();
+
+	source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f);
+	target = source + CVector(dx, dy, 0.0f) * 60.0f;
+	target += CVector(
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f,
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f,
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f);
+	CWeapon::DoTankDoomAiming(this, pDriver, &source, &target);
+	FireOneInstantHitRound(&source, &target, 15);
+
+	source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f);
+	target = source + CVector(dx, dy, 0.0f) * 60.0f;
+	target += CVector(
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f,
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f,
+		((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f);
+	CWeapon::DoTankDoomAiming(this, pDriver, &source, &target);
+	FireOneInstantHitRound(&source, &target, 15);
+
+	DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
+
+	m_nAmmoInClip--;
+	if (m_nAmmoInClip == 0) {
+		m_nAmmoInClip = 20;
+		m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400;
+	}
+}
+
 void
 CVehicle::ExtinguishCarFire(void)
 {
@@ -1034,7 +1041,7 @@ CVehicle::SetUpDriver(void)
 	if(VehicleCreatedBy != RANDOM_VEHICLE)
 		return nil;
 
-	pDriver = CPopulation::AddPedInCar(this);
+	pDriver = CPopulation::AddPedInCar(this, false);
 	pDriver->m_pMyVehicle = this;
 	pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle);
 	pDriver->bInVehicle = true;
@@ -1050,7 +1057,7 @@ CVehicle::SetupPassenger(int n)
 	if(pPassengers[n])
 		return pPassengers[n];
 
-	pPassengers[n] = CPopulation::AddPedInCar(this);
+	pPassengers[n] = CPopulation::AddPedInCar(this, true);
 	pPassengers[n]->m_pMyVehicle = this;
 	pPassengers[n]->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle);
 	pPassengers[n]->bInVehicle = true;
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index c03e9209..3b6d778e 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -182,6 +182,7 @@ public:
 	uint8 bIsCarParkVehicle : 1; // Car has been created using the special CAR_PARK script command
 	uint8 bHasAlreadyBeenRecorded : 1; // Used for replays
 
+	uint8 bPartOfConvoy : 1;
 	uint8 bCreatedAsPoliceVehicle : 1;// True if this guy was created as a police vehicle (enforcer, policecar, miamivice car etc)
 	uint8 bParking : 1;
 
@@ -287,6 +288,7 @@ public:
 	bool ShufflePassengersToMakeSpace(void);
 	void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage);
 	void DoFixedMachineGuns(void);
+	void FireFixedMachineGuns(void);
 
 
 	bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; }