diff --git a/SilentPatchSA/PedSA.cpp b/SilentPatchSA/PedSA.cpp index 9b0eb4b..e1c6239 100644 --- a/SilentPatchSA/PedSA.cpp +++ b/SilentPatchSA/PedSA.cpp @@ -125,4 +125,16 @@ uint8_t CPed::GetWeaponSkillForRenderWeaponPedsForPC_SAMP() uint8_t (CPed::*funcCall)(); Memory::ReadCall( 0x7330A2, funcCall ); return std::invoke( funcCall, this ); -} \ No newline at end of file +} + +bool CTaskComplexSequence::Contains(int taskID) const +{ + for (CTask* task : m_taskSequence) + { + if (task->GetTaskType() == taskID) + { + return true; + } + } + return false; +} diff --git a/SilentPatchSA/PedSA.h b/SilentPatchSA/PedSA.h index 4928988..f406ea7 100644 --- a/SilentPatchSA/PedSA.h +++ b/SilentPatchSA/PedSA.h @@ -4,16 +4,77 @@ #include "GeneralSA.h" class CEntryExit; +class CEvent; +class CPed; + +// This structure is very incomplete, but it is good enough for now +class __declspec(novtable) CTask +{ +public: + virtual ~CTask(); + virtual CTask* Clone() const = 0; + virtual CTask* GetSubTask() const = 0; + virtual bool IsSimpleTask() const = 0; + virtual int GetTaskType() const = 0; + virtual void StopTimer(const CEvent*) { } + virtual bool MakeAbortable(CPed*, int, const CEvent*) = 0; + +public: + CTask* m_parentTask; +}; +static_assert(sizeof(CTask) == 0x8, "Wrong size: CTask"); + +class __declspec(novtable) CTaskSimple : public CTask +{ +public: + virtual bool IsSimpleTask() const override { return true; } + virtual bool SetPedPosition(CPed*) { return false; } +}; + +class __declspec(novtable) CTaskComplex : public CTask +{ +public: + virtual bool IsSimpleTask() const override { return false; } + virtual void SetSubTask(CTask*); + virtual CTask* CreateNextSubTask(CPed*) = 0; + virtual CTask* CreateFirstSubTask(CPed*) = 0; + virtual CTask* ControlSubTask(CPed*) = 0; +}; // Stub -class CTaskSimpleJetPack +class __declspec(novtable) CTaskSimpleJetPack : public CTaskSimple { public: void RenderJetPack(class CPed* pPed); }; +class __declspec(novtable) CTaskComplexSequence : public CTaskComplex +{ +public: + bool Contains(int taskID) const; + +public: + std::byte __pad1[8]; + CTask* m_taskSequence[8]; + std::byte __pad2[16]; +}; +static_assert(sizeof(CTaskComplexSequence) == 0x40, "Wrong size: CTaskComplexSequence"); + +class CTaskManager +{ +public: + CTask* m_primaryTasks[5]; + CTask* m_secondaryTasks[6]; + CPed* m_pPed; +}; +static_assert(sizeof(CTaskManager) == 0x30, "Wrong size: CTaskManager"); + class CPedIntelligence { +public: + CPed* m_pPed; + CTaskManager m_taskManager; + public: class CTaskSimpleJetPack* GetTaskJetPack() const; }; diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index 0f3872e..77cb783 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -1860,6 +1860,38 @@ namespace CameraCrosshairFix } } + +// ============= Cancel the Drive By task of biker cops when losing the wanted level ============= +namespace BikerCopsDriveByFix +{ + static void (*orgJoinCarWithRoadSystem)(CVehicle* vehicle); + void JoinCarWithRoadSystem_AbortDriveByTask(CVehicle* vehicle) + { + orgJoinCarWithRoadSystem(vehicle); + + CPed* driver = vehicle->GetDriver(); + if (driver != nullptr) + { + CPedIntelligence* driverIntelligence = driver->GetPedIntelligencePtr(); + if (driverIntelligence != nullptr) + { + // If the driver has a sequence, it's the fourth one + CTask* primaryTask = driverIntelligence->m_taskManager.m_primaryTasks[3]; + if (primaryTask != nullptr && primaryTask->GetTaskType() == 244) // TASK_COMPLEX_SEQUENCE + { + // If the sequence contains a TASK_SIMPLE_GANG_DRIVEBY, abort it + CTaskComplexSequence* taskSequence = reinterpret_cast(primaryTask); + if (taskSequence->Contains(1022)) + { + taskSequence->MakeAbortable(driver, 1, nullptr); + } + } + } + } + } +} + + // ============= LS-RP Mode stuff ============= namespace LSRPMode { @@ -4488,6 +4520,14 @@ void Patch_SA_10() } + // Cancel the Drive By task of biker cops when losing the wanted level + { + using namespace BikerCopsDriveByFix; + + InterceptCall(0x41C00E, orgJoinCarWithRoadSystem, JoinCarWithRoadSystem_AbortDriveByTask); + } + + #if FULL_PRECISION_D3D // Test - full precision D3D device Patch( 0x7F672B+1, *(uint8_t*)(0x7F672B+1) | D3DCREATE_FPU_PRESERVE ); @@ -6047,6 +6087,15 @@ void Patch_SA_NewBinaries_Common() auto getWeaponInfo = get_pattern("E8 ? ? ? ? 8B 40 0C 83 C4 08 85 C0"); InterceptCall(getWeaponInfo, orgGetWeaponInfo, GetWeaponInfo_OrCamera); } + + + // Cancel the Drive By task of biker cops when losing the wanted level + { + using namespace BikerCopsDriveByFix; + + auto backToCruisingIfNoWantedLevel = get_pattern("56 E8 ? ? ? ? 80 A6 ? ? ? ? ? 83 C4 04", 1); + InterceptCall(backToCruisingIfNoWantedLevel, orgJoinCarWithRoadSystem, JoinCarWithRoadSystem_AbortDriveByTask); + } }