#include "common.h"

#include "CarAI.h"

#include "Accident.h"
#include "AutoPilot.h"
#include "CarCtrl.h"
#include "General.h"
#include "HandlingMgr.h"
#include "ModelIndices.h"
#include "PlayerPed.h"
#include "Wanted.h"
#include "DMAudio.h"
#include "Fire.h"
#include "Pools.h"
#include "Timer.h"
#include "TrafficLights.h"
#include "Vehicle.h"
#include "World.h"
#include "ZoneCull.h"

#define DISTANCE_TO_SWITCH_DISTANCE_GOTO 20.0f

float CCarAI::FindSwitchDistanceClose(CVehicle* pVehicle)
{
	return 30.0f;
}

float CCarAI::FindSwitchDistanceFarNormalVehicle(CVehicle* pVehicle)
{
	return FindSwitchDistanceClose(pVehicle) + 5.0f;
}

float CCarAI::FindSwitchDistanceFar(CVehicle* pVehicle)
{
	if (pVehicle->bIsLawEnforcer)
		return 50.0f;
	return FindSwitchDistanceFarNormalVehicle(pVehicle);
}

void CCarAI::UpdateCarAI(CVehicle* pVehicle)
{
	if (pVehicle->bIsLawEnforcer){
		if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_FARAWAY ||
			pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY ||
			pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE ||
			pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE)
			pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle);
	}
	switch (pVehicle->GetStatus()){
	case STATUS_PLAYER:
	case STATUS_PLAYER_PLAYBACKFROMBUFFER:
	case STATUS_TRAIN_MOVING:
	case STATUS_TRAIN_NOT_MOVING:
	case STATUS_HELI:
	case STATUS_PLANE:
	case STATUS_PLAYER_REMOTE:
	case STATUS_PLAYER_DISABLED:
		break;
	case STATUS_SIMPLE:
	case STATUS_PHYSICS:
		switch (pVehicle->AutoPilot.m_nCarMission) {
		case MISSION_RAMPLAYER_FARAWAY:
			if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
				pVehicle->AutoPilot.m_bIgnorePathfinding) {
				pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_CLOSE;
				if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
					pVehicle->m_bSirenOrAlarm = true;
			}
			if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
				(FindPlayerPed()->m_pWanted->m_nWantedLevel == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
				CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
				pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
				pVehicle->m_bSirenOrAlarm = false;
				if (CCullZones::NoPolice())
					pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		case MISSION_RAMPLAYER_CLOSE:
			if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
				pVehicle->AutoPilot.m_bIgnorePathfinding) {
				if (FindPlayerVehicle()) {
					if (pVehicle->GetHasCollidedWith(FindPlayerVehicle())) {
						if (pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNLEFT && pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNRIGHT) {
							if (FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) {
								pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
								pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800;
							}
							else {
								pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
								pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 50;
							}
						}
					}
				}
				if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f)
#ifdef FIX_BUGS
					pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds();
#else
					pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep();
#endif
				else
					pVehicle->m_nTimeBlocked = 0;
				if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() ||
					FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) {
					if (pVehicle->bIsLawEnforcer &&
						(pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) &&
						(FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) {
						TellOccupantsToLeaveCar(pVehicle);
						pVehicle->AutoPilot.m_nCruiseSpeed = 0;
						pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
						if (FindPlayerPed()->m_pWanted->m_nWantedLevel <= 1)
							pVehicle->m_bSirenOrAlarm = false;
					}
				}
			}
			else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)){
				pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY;
				pVehicle->m_bSirenOrAlarm = false;
				pVehicle->m_nCarHornTimer = 0;
			}
			if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
				(FindPlayerPed()->m_pWanted->m_nWantedLevel == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())){
				CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
				pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
				pVehicle->m_bSirenOrAlarm = false;
				if (CCullZones::NoPolice())
					pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}

			else if (pVehicle->bIsLawEnforcer)
				MellowOutChaseSpeed(pVehicle);
			break;
		case MISSION_BLOCKPLAYER_FARAWAY:
			if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
				pVehicle->AutoPilot.m_bIgnorePathfinding) {
				pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE;
				if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
					pVehicle->m_bSirenOrAlarm = true;
			}
			if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
				(FindPlayerPed()->m_pWanted->m_nWantedLevel == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
				CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
				pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
				pVehicle->m_bSirenOrAlarm = false;
				if (CCullZones::NoPolice())
					pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		case MISSION_BLOCKPLAYER_CLOSE:
			if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
				pVehicle->AutoPilot.m_bIgnorePathfinding) {
				if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f)
#ifdef FIX_BUGS
					pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds();
#else
					pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep();
#endif
				else
					pVehicle->m_nTimeBlocked = 0;
				if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() ||
					FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) {
					if (pVehicle->bIsLawEnforcer &&
						(pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) &&
						(FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) {
						TellOccupantsToLeaveCar(pVehicle);
						pVehicle->AutoPilot.m_nCruiseSpeed = 0;
						pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
						if (FindPlayerPed()->m_pWanted->m_nWantedLevel <= 1)
							pVehicle->m_bSirenOrAlarm = false;
					}
				}
			}else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)) {
				pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY;
				pVehicle->m_bSirenOrAlarm = false;
				pVehicle->m_nCarHornTimer = 0;
			}
			if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
				(FindPlayerPed()->m_pWanted->m_nWantedLevel == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
				CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
				pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
				pVehicle->m_bSirenOrAlarm = false;
				if (CCullZones::NoPolice())
					pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			if (pVehicle->bIsLawEnforcer)
				MellowOutChaseSpeed(pVehicle);
			break;
		case MISSION_GOTOCOORDS:
			if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < DISTANCE_TO_SWITCH_DISTANCE_GOTO ||
			  pVehicle->AutoPilot.m_bIgnorePathfinding)
				pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT;
			break;
		case MISSION_GOTOCOORDS_STRAIGHT:
		{
			float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D();
			if ((pVehicle->bIsAmbulanceOnDuty || pVehicle->bIsFireTruckOnDuty) && distance < 20.0f)
				pVehicle->AutoPilot.m_nCarMission = MISSION_EMERGENCYVEHICLE_STOP;
			if (distance < 5.0f){
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
				pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
			}
			else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0){
				pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
				pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ?
					MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS;
			}
			break;
		}
		case MISSION_EMERGENCYVEHICLE_STOP:
			if (pVehicle->GetMoveSpeed().Magnitude2D() < 0.01f){
				if (pVehicle->bIsAmbulanceOnDuty){
					float distance = 30.0f;
					if (gAccidentManager.FindNearestAccident(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)){
						TellOccupantsToLeaveCar(pVehicle);
						pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER;
					}else{
						CCarCtrl::JoinCarWithRoadSystem(pVehicle);
						pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
						pVehicle->m_bSirenOrAlarm = false;
						pVehicle->AutoPilot.m_nCruiseSpeed = 17;
						if (pVehicle->bIsAmbulanceOnDuty){
							pVehicle->bIsAmbulanceOnDuty = false;
							--CCarCtrl::NumAmbulancesOnDuty;
						}
					}
				}
				if (pVehicle->bIsFireTruckOnDuty) {
					float distance = 30.0f;
					if (gFireManager.FindNearestFire(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)) {
						TellOccupantsToLeaveCar(pVehicle);
						pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER;
					}
					else {
						CCarCtrl::JoinCarWithRoadSystem(pVehicle);
						pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
						pVehicle->m_bSirenOrAlarm = false;
						pVehicle->AutoPilot.m_nCruiseSpeed = 17;
						if (pVehicle->bIsFireTruckOnDuty) {
							pVehicle->bIsFireTruckOnDuty = false;
							--CCarCtrl::NumFiretrucksOnDuty;
						}
					}
				}
			}
			break;
		case MISSION_GOTOCOORDS_ACCURATE:
			if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < 20.0f ||
			  pVehicle->AutoPilot.m_bIgnorePathfinding)
				pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE;
			break;
		case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE:
		{
			float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D();
			if (distance < 1.0f) {
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
				pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
			}
			else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0) {
				pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
				pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ?
					MISSION_GOTO_COORDS_STRAIGHT_ACCURATE : MISSION_GOTOCOORDS_ACCURATE;
			}
			break;
		}
		case MISSION_RAMCAR_FARAWAY:
			if (pVehicle->AutoPilot.m_pTargetCar){
				if ((pVehicle->GetPosition() - pVehicle->AutoPilot.m_pTargetCar->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) ||
				  pVehicle->AutoPilot.m_bIgnorePathfinding)
					pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE;
			}else{
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		case MISSION_RAMCAR_CLOSE:
			if (pVehicle->AutoPilot.m_pTargetCar){
				if
#ifdef FIX_BUGS
				    (FindPlayerVehicle() == pVehicle->AutoPilot.m_pTargetCar &&
#endif
					(FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
				  (FindPlayerPed()->m_pWanted->m_nWantedLevel == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice()))
#ifdef FIX_BUGS
					)
#endif
				{
					CCarCtrl::JoinCarWithRoadSystem(pVehicle);
					pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
					pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
					pVehicle->m_bSirenOrAlarm = false;
					if (CCullZones::NoPolice())
						pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
				}
				if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() <= FindSwitchDistanceFar(pVehicle) ||
				  pVehicle->AutoPilot.m_bIgnorePathfinding){
					if (pVehicle->GetHasCollidedWith(pVehicle->AutoPilot.m_pTargetCar)){
						if (pVehicle->GetMoveSpeed().Magnitude() < 0.04f){
							pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
							pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800;
						}
					}
				}else{
					pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY;
					CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				}
			}else{
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		case MISSION_BLOCKCAR_FARAWAY:
			if (pVehicle->AutoPilot.m_pTargetCar){
				if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) ||
				  pVehicle->AutoPilot.m_bIgnorePathfinding){
					pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_CLOSE;
					if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
						pVehicle->m_bSirenOrAlarm = true;
				}
			}else{
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		case MISSION_BLOCKCAR_CLOSE:
			if (pVehicle->AutoPilot.m_pTargetCar){
				if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() > FindSwitchDistanceFar(pVehicle) &&
				  !pVehicle->AutoPilot.m_bIgnorePathfinding){
					pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY;
					pVehicle->m_bSirenOrAlarm = false;
					pVehicle->m_nCarHornTimer = 0;
					CCarCtrl::JoinCarWithRoadSystem(pVehicle);
				}
			}else{
				pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
			}
			break;
		default:
			if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->m_nWantedLevel > 0 && !CCullZones::NoPolice()){
				if (ABS(FindPlayerCoors().x - pVehicle->GetPosition().x) > 10.0f ||
				  ABS(FindPlayerCoors().y - pVehicle->GetPosition().y) > 10.0f){
					pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle);
					pVehicle->SetStatus(STATUS_PHYSICS);
					pVehicle->AutoPilot.m_nCarMission = 
						FindPoliceCarMissionForWantedLevel();
					pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
					pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
				}else if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE){
					pVehicle->SetStatus(STATUS_PHYSICS);
					TellOccupantsToLeaveCar(pVehicle);
					pVehicle->AutoPilot.m_nCruiseSpeed = 0;
					pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
					if (FindPlayerPed()->m_pWanted->m_nWantedLevel <= 1)
						pVehicle->m_bSirenOrAlarm = false;
				}
			}
			break;
		}
		break;
	case STATUS_ABANDONED:
	case STATUS_WRECKED:
		pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
		pVehicle->AutoPilot.m_nCruiseSpeed = 0;
		break;
	}
	float flatSpeed = pVehicle->GetMoveSpeed().MagnitudeSqr2D();
	if (flatSpeed > SQR(0.018f)){
		pVehicle->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds();
		pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
	}
	if (pVehicle->GetStatus() == STATUS_PHYSICS && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){
		if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE){
			if (pVehicle->AutoPilot.m_nCarMission != MISSION_STOP_FOREVER &&
			  pVehicle->AutoPilot.m_nCruiseSpeed != 0 &&
			  (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE || pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE)){
				if (pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS
					) {
					if (CTimer::GetTimeInMilliseconds() - pVehicle->m_nLastTimeCollided > 500)
						pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
					if (flatSpeed < SQR(0.018f) && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nAntiReverseTimer > 2000){
						pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
						if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE &&
						  pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE || pVehicle->VehicleCreatedBy == MISSION_VEHICLE)
							pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1500;
						else
							pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 750;
						pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
						if (pVehicle->VehicleCreatedBy == RANDOM_VEHICLE)
							pVehicle->AutoPilot.m_nDrivingStyle = Max(DRIVINGSTYLE_AVOID_CARS, pVehicle->AutoPilot.m_nDrivingStyle);
						pVehicle->PlayCarHorn();
					}
				}
			}
		}
	}
	if ((pVehicle->m_randomSeed & 7) == 0){
		if (CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 30000 &&
		  CTimer::GetPreviousTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission <= 30000 &&
		  pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE &&
		  !CTrafficLights::ShouldCarStopForBridge(pVehicle)){
			pVehicle->SetStatus(STATUS_PHYSICS);
			CCarCtrl::SwitchVehicleToRealPhysics(pVehicle);
			pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
			pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
			pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 400;
		}
	}
	if (pVehicle->GetUp().z < -0.7f){
		pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
		pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000;
	}
	if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){
		switch (pVehicle->AutoPilot.m_nCarMission){
		case MISSION_RAMPLAYER_FARAWAY:
		case MISSION_RAMPLAYER_CLOSE:
		case MISSION_BLOCKPLAYER_FARAWAY:
		case MISSION_BLOCKPLAYER_CLOSE:
			if (FindPlayerVehicle() && FindPlayerSpeed().Magnitude() > pVehicle->GetMoveSpeed().Magnitude()){
				if (FindPlayerSpeed().Magnitude() > 0.1f){
					if (DotProduct2D(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.0f){
						CVector2D dist = pVehicle->GetPosition() - FindPlayerCoors();
						CVector2D speed = FindPlayerSpeed();
						if (0.5f * dist.Magnitude() * speed.Magnitude() < DotProduct2D(dist, speed)){
							if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() > 12.0f){
								pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
								pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 500;
							}
						}
					}
				}
			}
			break;
		default: break;
		}
	}
	if (pVehicle->pDriver && pVehicle->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS){
		if ((pVehicle->GetPosition() - FindPlayerCoors()).Magnitude() < 15.0f){
			if (!FindPlayerVehicle() || pVehicle->GetHasCollidedWith(FindPlayerVehicle())){
				pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
				pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000;
			}
		}
	}
	if (pVehicle->m_bSirenOrAlarm){
		if ((uint8)(pVehicle->m_randomSeed ^ CGeneral::GetRandomNumber()) == 0xAD)
			pVehicle->m_nCarHornTimer = 45;
	}
}

void CCarAI::CarHasReasonToStop(CVehicle* pVehicle)
{
	pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
}

float CCarAI::GetCarToGoToCoors(CVehicle* pVehicle, CVector* pTarget)
{
	if (pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT){
		pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
		pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
		pVehicle->AutoPilot.m_nCruiseSpeed = 20;
		pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
		pVehicle->SetStatus(STATUS_PHYSICS);
		pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, *pTarget, false)) ?
			MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS;
	}else if (Abs(pTarget->x - pVehicle->AutoPilot.m_vecDestinationCoors.x) > 2.0f ||
	  Abs(pTarget->y - pVehicle->AutoPilot.m_vecDestinationCoors.y) > 2.0f){
		pVehicle->AutoPilot.m_vecDestinationCoors = *pTarget;
	}
	return (pVehicle->GetPosition() - *pTarget).Magnitude2D();
}

void CCarAI::AddPoliceCarOccupants(CVehicle* pVehicle)
{
	if (pVehicle->bOccupantsHaveBeenGenerated)
		return;
	pVehicle->bOccupantsHaveBeenGenerated = true;
	switch (pVehicle->GetModelIndex()){
	case MI_FBICAR:
	case MI_ENFORCER:
		pVehicle->SetUpDriver();
		for (int i = 0; i < 3; i++)
			pVehicle->SetupPassenger(i);
		return;
	case MI_POLICE:
	case MI_RHINO:
	case MI_BARRACKS:
		pVehicle->SetUpDriver();
		if (FindPlayerPed()->m_pWanted->m_nWantedLevel > 1)
			pVehicle->SetupPassenger(0);
		return;
	default:
		return;
	}
}

void CCarAI::AddAmbulanceOccupants(CVehicle* pVehicle)
{
	pVehicle->SetUpDriver();
	pVehicle->SetupPassenger(1);
}

void CCarAI::AddFiretruckOccupants(CVehicle* pVehicle)
{
	pVehicle->SetUpDriver();
	pVehicle->SetupPassenger(0);
}

void CCarAI::TellOccupantsToLeaveCar(CVehicle* pVehicle)
{
	if (pVehicle->pDriver){
		pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle);
		if (pVehicle->GetModelIndex() == MI_AMBULAN)
			pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE);
	}
	int timer = 100;
	for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++){
		if (pVehicle->pPassengers[i]) {
			pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle);
		}
	}
}

void CCarAI::TellCarToRamOtherCar(CVehicle* pVehicle, CVehicle* pTarget)
{
	pVehicle->AutoPilot.m_pTargetCar = pTarget;
	pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar);
	pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY;
	pVehicle->bEngineOn = true;
	pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed);
}

void CCarAI::TellCarToBlockOtherCar(CVehicle* pVehicle, CVehicle* pTarget)
{
	pVehicle->AutoPilot.m_pTargetCar = pTarget;
	pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar);
	pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY;
	pVehicle->bEngineOn = true;
	pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed);
}

eCarMission CCarAI::FindPoliceCarMissionForWantedLevel()
{
	switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->m_nWantedLevel){
	case 0:
	case 1: return MISSION_BLOCKPLAYER_FARAWAY;
	case 2: return (CGeneral::GetRandomNumber() & 3) >= 3 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
	case 3: return (CGeneral::GetRandomNumber() & 3) >= 2 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
	case 4: 
	case 5:
	case 6: return (CGeneral::GetRandomNumber() & 3) >= 1 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
	default: return MISSION_BLOCKPLAYER_FARAWAY;
	}
}

int32 CCarAI::FindPoliceCarSpeedForWantedLevel(CVehicle* pVehicle)
{
	switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->m_nWantedLevel) {
	case 0: return CGeneral::GetRandomNumberInRange(12, 16);
	case 1: return 25;
	case 2: return 34;
	case 3: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 0.9f;
	case 4: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.2f;
	case 5: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.25f;
	case 6: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.3f;
	default: return 0;
	}
}

void CCarAI::MellowOutChaseSpeed(CVehicle* pVehicle)
{
	if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->m_nWantedLevel == 1){
		float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude();
		if (FindPlayerVehicle()){
			if (distanceToPlayer < 10.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 15;
			else if (distanceToPlayer < 20.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 22;
			else
				pVehicle->AutoPilot.m_nCruiseSpeed = 25;
		}else{
			if (distanceToPlayer < 20.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 5;
			else if (distanceToPlayer < 40.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 13;
			else
				pVehicle->AutoPilot.m_nCruiseSpeed = 25;
		}
	}else if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->m_nWantedLevel == 2){
		float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude();
		if (FindPlayerVehicle()) {
			if (distanceToPlayer < 10.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 27;
			else if (distanceToPlayer < 20.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 30;
			else
				pVehicle->AutoPilot.m_nCruiseSpeed = 34;
		}
		else {
			if (distanceToPlayer < 20.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 5;
			else if (distanceToPlayer < 40.0f)
				pVehicle->AutoPilot.m_nCruiseSpeed = 18;
			else
				pVehicle->AutoPilot.m_nCruiseSpeed = 34;
		}
	}
}

void CCarAI::MakeWayForCarWithSiren(CVehicle *pVehicle)
{
	float flatSpeed = pVehicle->GetMoveSpeed().Magnitude2D();
	if (flatSpeed < 0.1f)
		return;
	CVector2D forward = pVehicle->GetMoveSpeed() / flatSpeed;
	float projection = flatSpeed * 45 + 20;
	int i = CPools::GetVehiclePool()->GetSize();
	while (--i >= 0) {
		CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i);
		if (!vehicle)
			continue;
		if (!vehicle->IsCar() && !vehicle->IsBike())
			continue;
		if (vehicle->GetStatus() != STATUS_SIMPLE && vehicle->GetStatus() != STATUS_PHYSICS)
			continue;
		if (vehicle->VehicleCreatedBy != RANDOM_VEHICLE)
			continue;
		if (vehicle->bIsLawEnforcer || vehicle->bIsAmbulanceOnDuty || vehicle->bIsFireTruckOnDuty)
			continue;
		if (vehicle == pVehicle)
			continue;
		if (Abs(pVehicle->GetPosition().z - vehicle->GetPosition().z) >= 5.0f)
			continue;
		CVector2D distance = vehicle->GetPosition() - pVehicle->GetPosition();
		if (distance.Magnitude() >= projection)
			continue;
		if (vehicle->GetMoveSpeed().Magnitude2D() <= 0.05f)
			continue;
		float correlation = DotProduct2D(forward, distance) / distance.Magnitude();
		if (correlation <= 0.0f)
			continue;
		if (correlation > 0.8f && DotProduct2D(forward, vehicle->GetForward()) > 0.7f){
			if (vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVELEFT && vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVERIGHT){
				vehicle->AutoPilot.m_nTempAction = (distance.x * forward.y - distance.y * forward.x > 0.0f) ?
					TEMPACT_SWERVELEFT : TEMPACT_SWERVERIGHT;
				vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000;
			}
			vehicle->SetStatus(STATUS_PHYSICS);
		}else{
			if (DotProduct2D(vehicle->GetMoveSpeed(), distance) < 0.0f && vehicle->AutoPilot.m_nTempAction != TEMPACT_WAIT){
				vehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
				vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000;
			}
		}
	}
}

src/control/CarAI.cpp