summaryrefslogtreecommitdiffstats
path: root/src/peds/EmergencyPed.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/peds/EmergencyPed.cpp435
1 files changed, 413 insertions, 22 deletions
diff --git a/src/peds/EmergencyPed.cpp b/src/peds/EmergencyPed.cpp
index cbcfb403..16468270 100644
--- a/src/peds/EmergencyPed.cpp
+++ b/src/peds/EmergencyPed.cpp
@@ -2,37 +2,428 @@
#include "patcher.h"
#include "EmergencyPed.h"
#include "ModelIndices.h"
-
-class CEmergencyPed_ : public CEmergencyPed
-{
-public:
- CEmergencyPed *ctor(int pedtype) { return ::new (this) CEmergencyPed(pedtype); };
- void dtor(void) { CEmergencyPed::~CEmergencyPed(); }
-};
-
-WRAPPER void CEmergencyPed::ProcessControl(void) { EAXJMP(0x4C2F10); }
+#include "Vehicle.h"
+#include "Fire.h"
+#include "General.h"
+#include "CarCtrl.h"
+#include "AccidentManager.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;
+ 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 = 0;
+ m_nEmergencyPedState = EMERGENCY_PED_READY;
m_pAttendedAccident = nil;
- field_1356 = 0;
+ 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)
+{
+ if (m_nZoneLevel > LEVEL_NONE && m_nZoneLevel != CCollision::ms_collisionInMemory)
+ return;
+
+ CPed::ProcessControl();
+ if (bWasPostponed)
+ return;
+
+ if(!DyingOrDead()) {
+ GetWeapon()->Update(m_audioEntityId);
+
+ 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;
+ }
+}
+
+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_bloodyFootprintCount = 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_STD, 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->m_ped_flagH1 = 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;
+ }
+ }
+}
+
+class CEmergencyPed_ : public CEmergencyPed
+{
+public:
+ CEmergencyPed* ctor(int pedtype) { return ::new (this) CEmergencyPed(pedtype); };
+ void dtor(void) { CEmergencyPed::~CEmergencyPed(); }
+ void ProcessControl_(void) { CEmergencyPed::ProcessControl(); }
+};
+
STARTPATCHES
InjectHook(0x4C2E40, &CEmergencyPed_::ctor, PATCH_JUMP);
InjectHook(0x4C2EF0, &CEmergencyPed_::dtor, PATCH_JUMP);
+ InjectHook(0x4C2F10, &CEmergencyPed_::ProcessControl_, PATCH_JUMP);
+ InjectHook(0x4C3EC0, &CEmergencyPed::InRange, PATCH_JUMP);
ENDPATCHES