#include "common.h"
#include "EmergencyPed.h"
#include "DMAudio.h"
#include "ModelIndices.h"
#include "Vehicle.h"
#include "Fire.h"
#include "General.h"
#include "CarCtrl.h"
#include "Accident.h"
CEmergencyPed::CEmergencyPed(uint32 type) : CPed(type)
{
switch (type){
case PEDTYPE_EMERGENCY:
SetModelIndex(MI_MEDIC);
m_pRevivedPed = nil;
field_1360 = 0;
break;
case PEDTYPE_FIREMAN:
SetModelIndex(MI_FIREMAN);
m_pRevivedPed = nil;
break;
default:
break;
}
m_nEmergencyPedState = EMERGENCY_PED_READY;
m_pAttendedAccident = nil;
m_bStartedToCPR = false;
}
bool
CEmergencyPed::InRange(CPed *victim)
{
if (!m_pMyVehicle)
return true;
if ((m_pMyVehicle->GetPosition() - victim->GetPosition()).Magnitude() > 30.0f)
return false;
return true;
}
void
CEmergencyPed::ProcessControl(void)
{
CPed::ProcessControl();
if (bWasPostponed)
return;
if(!DyingOrDead()) {
GetWeapon()->Update(m_audioEntityId, nil);
if (IsPedInControl() && m_moved.Magnitude() > 0.0f)
Avoid();
switch (m_nPedState) {
case PED_SEEK_POS:
Seek();
break;
case PED_SEEK_ENTITY:
if (m_pSeekTarget) {
m_vecSeekPos = m_pSeekTarget->GetPosition();
Seek();
} else {
ClearSeek();
}
break;
default:
break;
}
switch (m_nPedType) {
case PEDTYPE_EMERGENCY:
if (IsPedInControl() || m_nPedState == PED_DRIVING)
MedicAI();
break;
case PEDTYPE_FIREMAN:
if (IsPedInControl())
FiremanAI();
break;
default:
return;
}
}
}
// This function was buggy and incomplete in both III and VC, firemen had to be in 5.0m range of fire etc. etc.
// Copied some code from MedicAI to make it work.
void
CEmergencyPed::FiremanAI(void)
{
float fireDist;
CFire *nearestFire;
switch (m_nEmergencyPedState) {
case EMERGENCY_PED_READY:
nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist);
if (nearestFire) {
m_nPedState = PED_NONE;
SetSeek(nearestFire->m_vecPos, 1.0f);
SetMoveState(PEDMOVE_RUN);
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
m_pAttendedFire = nearestFire;
#ifdef FIX_BUGS
bIsRunning = true;
++nearestFire->m_nFiremenPuttingOut;
#endif
}
break;
case EMERGENCY_PED_DETERMINE_NEXT_STATE:
nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist);
if (nearestFire && nearestFire != m_pAttendedFire) {
m_nPedState = PED_NONE;
SetSeek(nearestFire->m_vecPos, 1.0f);
SetMoveState(PEDMOVE_RUN);
#ifdef FIX_BUGS
bIsRunning = true;
if (m_pAttendedFire) {
--m_pAttendedFire->m_nFiremenPuttingOut;
}
++nearestFire->m_nFiremenPuttingOut;
m_pAttendedFire = nearestFire;
} else if (!nearestFire) {
#else
m_pAttendedFire = nearestFire;
} else {
#endif
m_nEmergencyPedState = EMERGENCY_PED_STOP;
}
// "Extinguish" the fire (Will overwrite the stop decision above if the attended and nearest fires are same)
if (fireDist < 5.0f) {
SetIdle();
m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL;
}
break;
case EMERGENCY_PED_STAND_STILL:
if (!m_pAttendedFire->m_bIsOngoing)
m_nEmergencyPedState = EMERGENCY_PED_STOP;
// Leftover
// fireDist = 30.0f;
nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist);
if (nearestFire) {
#ifdef FIX_BUGS
if(nearestFire != m_pAttendedFire && (nearestFire->m_vecPos - GetPosition()).Magnitude() < 30.0f)
#endif
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
}
Say(SOUND_PED_EXTINGUISHING_FIRE);
break;
case EMERGENCY_PED_STOP:
#ifdef FIX_BUGS
bIsRunning = false;
if (m_pAttendedFire)
#endif
--m_pAttendedFire->m_nFiremenPuttingOut;
m_nPedState = PED_NONE;
SetWanderPath(CGeneral::GetRandomNumber() & 7);
m_pAttendedFire = nil;
m_nEmergencyPedState = EMERGENCY_PED_READY;
SetMoveState(PEDMOVE_WALK);
break;
default: break;
}
}
void
CEmergencyPed::MedicAI(void)
{
float distToEmergency;
if (!bInVehicle && IsPedInControl()) {
ScanForThreats();
if (m_threatEntity && m_threatEntity->IsPed() && ((CPed*)m_threatEntity)->IsPlayer()) {
if (((CPed*)m_threatEntity)->GetWeapon()->IsTypeMelee()) {
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity);
} else {
SetFlee(m_threatEntity, 6000);
Say(SOUND_PED_FLEE_SPRINT);
}
return;
}
}
if (InVehicle()) {
if (m_pMyVehicle->IsCar() && m_objective != OBJECTIVE_LEAVE_VEHICLE) {
if (gAccidentManager.FindNearestAccident(m_pMyVehicle->GetPosition(), &distToEmergency)
&& distToEmergency < 25.0f && m_pMyVehicle->m_vecMoveSpeed.Magnitude() < 0.01f) {
m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
SetObjective(OBJECTIVE_LEAVE_VEHICLE, m_pMyVehicle);
Say(SOUND_PED_LEAVE_VEHICLE);
} else if (m_pMyVehicle->pDriver == this && m_nPedState == PED_DRIVING
&& m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE && !(CGeneral::GetRandomNumber() & 31)) {
bool waitUntilMedicEntersCar = false;
for (int i = 0; i < m_numNearPeds; ++i) {
CPed *nearPed = m_nearPeds[i];
if (nearPed->m_nPedType == PEDTYPE_EMERGENCY) {
if ((nearPed->m_nPedState == PED_SEEK_CAR || nearPed->m_nPedState == PED_ENTER_CAR)
&& nearPed->m_pMyVehicle == m_pMyVehicle) {
waitUntilMedicEntersCar = true;
break;
}
}
}
if (!waitUntilMedicEntersCar) {
CCarCtrl::JoinCarWithRoadSystem(m_pMyVehicle);
m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
m_pMyVehicle->m_bSirenOrAlarm = 0;
m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 12;
m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS;
if (m_pMyVehicle->bIsAmbulanceOnDuty) {
m_pMyVehicle->bIsAmbulanceOnDuty = false;
--CCarCtrl::NumAmbulancesOnDuty;
}
}
}
}
}
CVector headPos, midPos;
CAccident *nearestAccident;
if (IsPedInControl()) {
switch (m_nEmergencyPedState) {
case EMERGENCY_PED_READY:
nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency);
field_1360 = 0;
if (nearestAccident) {
m_pRevivedPed = nearestAccident->m_pVictim;
m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed);
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&midPos, PED_MID);
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&headPos, PED_HEAD);
SetSeek((headPos + midPos) * 0.5f, 1.0f);
SetObjective(OBJECTIVE_NONE);
bIsRunning = true;
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
m_pAttendedAccident = nearestAccident;
++m_pAttendedAccident->m_nMedicsAttending;
} else {
if (m_pMyVehicle) {
if (!bInVehicle) {
if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_pMyVehicle->pDriver || m_pMyVehicle->m_nGettingInFlags) {
CPed* driver = m_pMyVehicle->pDriver;
if (driver && driver->m_nPedType != PEDTYPE_EMERGENCY && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, driver);
} else if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER
&& m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER
&& m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_pMyVehicle);
}
} else {
SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle);
}
}
} else if (m_nPedState != PED_WANDER_PATH) {
SetWanderPath(CGeneral::GetRandomNumber() & 7);
}
}
break;
case EMERGENCY_PED_DETERMINE_NEXT_STATE:
nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency);
if (nearestAccident) {
if (nearestAccident != m_pAttendedAccident || m_nPedState != PED_SEEK_POS) {
m_pRevivedPed = nearestAccident->m_pVictim;
m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed);
if (!InRange(m_pRevivedPed)) {
m_nEmergencyPedState = EMERGENCY_PED_STOP;
break;
}
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&midPos, PED_MID);
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&headPos, PED_HEAD);
SetSeek((headPos + midPos) * 0.5f, nearestAccident->m_nMedicsPerformingCPR * 0.5f + 1.0f);
SetObjective(OBJECTIVE_NONE);
bIsRunning = true;
--m_pAttendedAccident->m_nMedicsAttending;
++nearestAccident->m_nMedicsAttending;
m_pAttendedAccident = nearestAccident;
}
} else {
m_nEmergencyPedState = EMERGENCY_PED_STOP;
bIsRunning = false;
}
if (distToEmergency < 5.0f) {
if (m_pRevivedPed->m_pFire) {
bIsRunning = false;
SetMoveState(PEDMOVE_STILL);
} else if (distToEmergency < 4.5f) {
bIsRunning = false;
SetMoveState(PEDMOVE_WALK);
if (distToEmergency < 1.0f
|| distToEmergency < 4.5f && m_pAttendedAccident->m_nMedicsPerformingCPR) {
m_nEmergencyPedState = EMERGENCY_PED_START_CPR;
}
}
}
break;
case EMERGENCY_PED_START_CPR:
if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f || m_pRevivedPed->bFadeOut) {
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
} else {
m_pRevivedPed->m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds();
SetMoveState(PEDMOVE_STILL);
m_nPedState = PED_CPR;
m_nLastPedState = PED_CPR;
SetLookFlag(m_pRevivedPed, 0);
SetLookTimer(500);
Say(SOUND_PED_HEALING);
if (m_pAttendedAccident->m_nMedicsPerformingCPR) {
SetIdle();
m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL;
} else {
m_nEmergencyPedState = EMERGENCY_PED_FACE_TO_PATIENT;
m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_MEDIC, ANIM_CPR, 4.0f);
bIsDucking = true;
}
SetLookTimer(2000);
++m_pAttendedAccident->m_nMedicsPerformingCPR;
m_bStartedToCPR = true;
}
break;
case EMERGENCY_PED_FACE_TO_PATIENT:
if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f)
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
else {
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&midPos, PED_MID);
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&headPos, PED_HEAD);
midPos = (headPos + midPos) * 0.5f;
m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(
midPos.x, midPos.y,
GetPosition().x, GetPosition().y);
m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest);
m_pLookTarget = m_pRevivedPed;
m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget);
TurnBody();
if (Abs(m_fRotationCur - m_fRotationDest) < DEGTORAD(45.0f))
m_nEmergencyPedState = EMERGENCY_PED_PERFORM_CPR;
else
m_fRotationCur = (m_fRotationCur + m_fRotationDest) * 0.5f;
}
break;
case EMERGENCY_PED_PERFORM_CPR:
if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) {
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
break;
}
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&midPos, PED_MID);
m_pRevivedPed->m_pedIK.GetComponentPosition(*(RwV3d *)&headPos, PED_HEAD);
midPos = (headPos + midPos) * 0.5f;
m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(
midPos.x, midPos.y,
GetPosition().x, GetPosition().y);
m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest);
m_pLookTarget = m_pRevivedPed;
m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget);
TurnBody();
if (CTimer::GetTimeInMilliseconds() <= m_lookTimer) {
SetMoveState(PEDMOVE_STILL);
break;
}
m_nEmergencyPedState = EMERGENCY_PED_STOP_CPR;
m_nPedState = PED_NONE;
SetMoveState(PEDMOVE_WALK);
m_pVehicleAnim = nil;
if (!m_pRevivedPed->bBodyPartJustCameOff) {
m_pRevivedPed->m_fHealth = 100.0f;
m_pRevivedPed->m_nPedState = PED_NONE;
m_pRevivedPed->m_nLastPedState = PED_WANDER_PATH;
m_pRevivedPed->SetGetUp();
m_pRevivedPed->bUsesCollision = true;
m_pRevivedPed->SetMoveState(PEDMOVE_WALK);
m_pRevivedPed->RestartNonPartialAnims();
m_pRevivedPed->bIsPedDieAnimPlaying = false;
m_pRevivedPed->bKnockedUpIntoAir = false;
m_pRevivedPed->m_pCollidingEntity = nil;
}
break;
case EMERGENCY_PED_STOP_CPR:
m_nEmergencyPedState = EMERGENCY_PED_STOP;
bIsDucking = true;
break;
case EMERGENCY_PED_STAND_STILL:
if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f)
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
else {
if (!m_pAttendedAccident->m_pVictim)
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
if (!m_pAttendedAccident->m_nMedicsPerformingCPR)
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
if (gAccidentManager.UnattendedAccidents())
m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE;
}
break;
case EMERGENCY_PED_STOP:
m_bStartedToCPR = false;
m_nPedState = PED_NONE;
if (m_pAttendedAccident) {
m_pAttendedAccident->m_pVictim = nil;
--m_pAttendedAccident->m_nMedicsAttending;
m_pAttendedAccident = nil;
}
SetWanderPath(CGeneral::GetRandomNumber() & 7);
m_pRevivedPed = nil;
m_nEmergencyPedState = EMERGENCY_PED_READY;
SetMoveState(PEDMOVE_WALK);
break;
default: break;
}
}
}