From 53023eb65bdcde43e341c1ecb7cf0c7f8ee524fb Mon Sep 17 00:00:00 2001 From: aap Date: Sun, 7 Jul 2019 13:09:11 +0200 Subject: the great reorganization --- src/peds/CivilianPed.cpp | 19 + src/peds/CivilianPed.h | 16 + src/peds/CopPed.cpp | 14 + src/peds/CopPed.h | 73 ++ src/peds/DummyPed.h | 11 + src/peds/EmergencyPed.cpp | 7 + src/peds/EmergencyPed.h | 13 + src/peds/Ped.cpp | 2949 +++++++++++++++++++++++++++++++++++++++++++++ src/peds/Ped.h | 577 +++++++++ src/peds/PedIK.cpp | 109 ++ src/peds/PedIK.h | 40 + src/peds/PedPlacement.cpp | 40 + src/peds/PedPlacement.h | 8 + src/peds/PedStats.cpp | 125 ++ src/peds/PedStats.h | 79 ++ src/peds/PedType.cpp | 243 ++++ src/peds/PedType.h | 90 ++ src/peds/PlayerPed.cpp | 14 + src/peds/PlayerPed.h | 48 + 19 files changed, 4475 insertions(+) create mode 100644 src/peds/CivilianPed.cpp create mode 100644 src/peds/CivilianPed.h create mode 100644 src/peds/CopPed.cpp create mode 100644 src/peds/CopPed.h create mode 100644 src/peds/DummyPed.h create mode 100644 src/peds/EmergencyPed.cpp create mode 100644 src/peds/EmergencyPed.h create mode 100644 src/peds/Ped.cpp create mode 100644 src/peds/Ped.h create mode 100644 src/peds/PedIK.cpp create mode 100644 src/peds/PedIK.h create mode 100644 src/peds/PedPlacement.cpp create mode 100644 src/peds/PedPlacement.h create mode 100644 src/peds/PedStats.cpp create mode 100644 src/peds/PedStats.h create mode 100644 src/peds/PedType.cpp create mode 100644 src/peds/PedType.h create mode 100644 src/peds/PlayerPed.cpp create mode 100644 src/peds/PlayerPed.h (limited to 'src/peds') diff --git a/src/peds/CivilianPed.cpp b/src/peds/CivilianPed.cpp new file mode 100644 index 00000000..a4881e71 --- /dev/null +++ b/src/peds/CivilianPed.cpp @@ -0,0 +1,19 @@ +#include "common.h" +#include "patcher.h" +#include "CivilianPed.h" + +WRAPPER void CCivilianPed::ProcessControl(void) { EAXJMP(0x4BFFE0); } + +CCivilianPed::CCivilianPed(int pedtype, int mi) : CPed(pedtype) +{ + CPed::SetModelIndex(mi); + for (int i = 0; i < 10; i++) + { + m_nearPeds[i] = nil; + } +} + +STARTPATCHES + InjectHook(0x4BFF30, &CCivilianPed::ctor, PATCH_JUMP); + InjectHook(0x4BFFC0, &CCivilianPed::dtor, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/peds/CivilianPed.h b/src/peds/CivilianPed.h new file mode 100644 index 00000000..8d004ad7 --- /dev/null +++ b/src/peds/CivilianPed.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Ped.h" + +class CCivilianPed : public CPed +{ +public: + CCivilianPed(int, int); + virtual ~CCivilianPed(void) { } + + virtual void ProcessControl(void); + + CCivilianPed *ctor(int pedtype, int mi) { return ::new (this) CCivilianPed(pedtype, mi); }; + void dtor(void) { this->CCivilianPed::~CCivilianPed(); } +}; +static_assert(sizeof(CCivilianPed) == 0x53C, "CCivilianPed: error"); diff --git a/src/peds/CopPed.cpp b/src/peds/CopPed.cpp new file mode 100644 index 00000000..041185ee --- /dev/null +++ b/src/peds/CopPed.cpp @@ -0,0 +1,14 @@ +#include "common.h" +#include "patcher.h" +#include "CopPed.h" + +CCopPed::~CCopPed() +{ + ClearPursuit(); +} + +WRAPPER void CCopPed::ClearPursuit(void) { EAXJMP(0x4C28C0); } + +STARTPATCHES + InjectHook(0x4C13E0, &CCopPed::dtor, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/peds/CopPed.h b/src/peds/CopPed.h new file mode 100644 index 00000000..b938dfc2 --- /dev/null +++ b/src/peds/CopPed.h @@ -0,0 +1,73 @@ +#pragma once +#include "Ped.h" + +enum eCrimeType +{ + CRIME_NONE, + CRIME_POSSESSION_GUN, + CRIME_HIT_PED, + CRIME_HIT_COP, + CRIME_SHOOT_PED, + CRIME_SHOOT_COP, + CRIME_STEAL_CAR, + CRIME_RUN_REDLIGHT, + CRIME_RECKLESS_DRIVING, + CRIME_SPEEDING, + CRIME_RUNOVER_PED, + CRIME_RUNOVER_COP, + CRIME_SHOOT_HELI, + CRIME_PED_BURNED, + CRIME_COP_BURNED, + CRIME_VEHICLE_BURNED, + CRIME_DESTROYED_CESSNA, +}; + +enum eCopType +{ + COP_STREET = 0, + COP_FBI = 1, + COP_SWAT = 2, + COP_ARMY = 3, +}; + +class CCrime +{ +public: + eCrimeType m_eCrimeType; + CEntity *m_pVictim; + int32 m_nCrimeTime; + CVector m_vecCrimePos; + int8 m_bReported; + int8 m_bMultiplier; + int8 pad_20[2]; +}; + +class CCopPed : public CPed +{ +public: + int16 m_wRoadblockNode; + int8 field_1342; + int8 field_1343; + float m_fDistanceToTarget; + int8 m_bIsInPursuit; + int8 m_bIsDisabledCop; + int8 field_1350; + int8 field_1351; + int8 m_bZoneDisabledButClose; + int8 m_bZoneDisabled; + int8 field_1354; + int8 field_1355; + int32 field_1356; + eCopType m_nCopType; + int8 field_1364; + int8 field_1365; + int8 field_1366; + int8 field_1367; + + ~CCopPed(); + void dtor(void) { this->CCopPed::~CCopPed(); } + + void ClearPursuit(void); +}; + +static_assert(sizeof(CCopPed) == 0x558, "CCopPed: error"); diff --git a/src/peds/DummyPed.h b/src/peds/DummyPed.h new file mode 100644 index 00000000..af633dc4 --- /dev/null +++ b/src/peds/DummyPed.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Dummy.h" + +// actually unused +class CDummyPed : CDummy +{ + int32 pedType; + int32 unknown; +}; +static_assert(sizeof(CDummyPed) == 0x70, "CDummyPed: error"); diff --git a/src/peds/EmergencyPed.cpp b/src/peds/EmergencyPed.cpp new file mode 100644 index 00000000..7b847896 --- /dev/null +++ b/src/peds/EmergencyPed.cpp @@ -0,0 +1,7 @@ +#include "common.h" +#include "patcher.h" +#include "EmergencyPed.h" + +STARTPATCHES +InjectHook(0x4C2EF0, &CEmergencyPed::dtor, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/peds/EmergencyPed.h b/src/peds/EmergencyPed.h new file mode 100644 index 00000000..fa07f3ee --- /dev/null +++ b/src/peds/EmergencyPed.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Ped.h" + +class CEmergencyPed : public CPed +{ +public: + // 0x53C + uint8 stuff[24]; + + void dtor(void) { this->CEmergencyPed::~CEmergencyPed(); } +}; +static_assert(sizeof(CEmergencyPed) == 0x554, "CEmergencyPed: error"); diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp new file mode 100644 index 00000000..80ef53ee --- /dev/null +++ b/src/peds/Ped.cpp @@ -0,0 +1,2949 @@ +#include "common.h" +#include "patcher.h" +#include "main.h" +#include "Pools.h" +#include "Particle.h" +#include "Stats.h" +#include "World.h" +#include "DMAudio.h" +#include "RpAnimBlend.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "General.h" +#include "VisibilityPlugins.h" +#include "AudioManager.h" +#include "HandlingMgr.h" +#include "Replay.h" +#include "PedPlacement.h" +#include "Shadows.h" +#include "Weather.h" +#include "ZoneCull.h" +#include "Population.h" +#include "Renderer.h" +#include "Lights.h" +#include "PointLights.h" +#include "Pad.h" + +WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC430); } +WRAPPER void CPed::Say(uint16 audio) { EAXJMP(0x4E5A10); } +WRAPPER void CPed::SetDie(AnimationId anim, float arg1, float arg2) { EAXJMP(0x4D37D0); } +WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } +WRAPPER void CPed::RestorePreviousState(void) { EAXJMP(0x4C5E30); } +WRAPPER void CPed::ClearAttack(void) { EAXJMP(0x4E6790); } +WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg) { EAXJMP(0x4E2480); } +WRAPPER void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg) { EAXJMP(0x4E2920); } +WRAPPER void CPed::SetPedPositionInCar(void) { EAXJMP(0x4D4970); } +WRAPPER void CPed::ProcessControl(void) { EAXJMP(0x4C8910); } +WRAPPER void CPed::PreRender(void) { EAXJMP(0x4CFDD0); } +WRAPPER void CPed::Render(void) { EAXJMP(0x4D03F0); } +WRAPPER int32 CPed::ProcessEntityCollision(CEntity*, CColPoint*) { EAXJMP(0x4CBB30); } +WRAPPER void CPed::SetMoveAnim(void) { EAXJMP(0x4C5A40); } +WRAPPER void CPed::SetFollowRoute(int16, int16) { EAXJMP(0x4DD690); } +WRAPPER void CPed::SetDuck(uint32) { EAXJMP(0x4E4920); } +WRAPPER void CPed::RegisterThreatWithGangPeds(CEntity*) { EAXJMP(0x4E3870); } + +bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; +bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; +bool &CPed::bPedCheat3 = *(bool*)0x95CD59; + +uint16 &CPed::distanceMultToCountPedNear = *(uint16*)0x5F8C98; + +CVector &CPed::offsetToOpenRegularCarDoor = *(CVector*)0x62E030; +CVector &CPed::offsetToOpenLowCarDoor = *(CVector*)0x62E03C; +CVector &CPed::offsetToOpenVanDoor = *(CVector*)0x62E048; + +void *CPed::operator new(size_t sz) { return CPools::GetPedPool()->New(); } +void *CPed::operator new(size_t sz, int handle) { return CPools::GetPedPool()->New(handle); } +void CPed::operator delete(void *p, size_t sz) { CPools::GetPedPool()->Delete((CPed*)p); } +void CPed::operator delete(void *p, int handle) { CPools::GetPedPool()->Delete((CPed*)p); } + +CPed::~CPed(void) +{ + CWorld::Remove(this); + CRadar::ClearBlipForEntity(BLIP_CHAR, CPools::GetPedPool()->GetIndex(this)); + if (bInVehicle && m_pMyVehicle){ + uint8 door_flag = GetVehEnterExitFlag(m_vehEnterType); + if (m_pMyVehicle->pDriver == this) + m_pMyVehicle->pDriver = nil; + else { + for (int i = 0; i < m_pMyVehicle->m_nNumMaxPassengers; i++) { + if (m_pMyVehicle->pPassengers[i] == this) + m_pMyVehicle->pPassengers[i] = nil; + } + } + if (m_nPedState == PED_EXIT_CAR || m_nPedState == PED_DRAG_FROM_CAR) + m_pMyVehicle->m_nGettingOutFlags &= ~door_flag; + bInVehicle = false; + m_pMyVehicle = nil; + }else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK){ + QuitEnteringCar(); + } + if (m_pFire) + m_pFire->Extinguish(); + CPopulation::UpdatePedCount(m_nPedType, true); + DMAudio.DestroyEntity(m_audioEntityId); +} + +void +CPed::FlagToDestroyWhenNextProcessed(void) +{ + bRemoveFromWorld = true; + if (!bInVehicle || !m_pMyVehicle) + return; + if (m_pMyVehicle->pDriver == this){ + m_pMyVehicle->pDriver = nil; + if (IsPlayer() && m_pMyVehicle->m_status != STATUS_WRECKED) + m_pMyVehicle->m_status = STATUS_ABANDONED; + }else{ + m_pMyVehicle->RemovePassenger(this); + } + bInVehicle = false; + m_pMyVehicle = nil; + if (CharCreatedBy == MISSION_CHAR) + m_nPedState = PED_DEAD; + else + m_nPedState = PED_NONE; + m_pVehicleAnim = nil; +} + +static char ObjectiveText[34][28] = { + "No Obj", + "Wait on Foot", + "Flee on Foot Till Safe", + "Guard Spot", + "Guard Area", + "Wait in Car", + "Wait in Car then Getout", + "Kill Char on Foot", + "Kill Char Any Means", + "Flee Char on Foot Till Safe", + "Flee Char on Foot Always", + "GoTo Char on Foot", + "Follow Char in Formation", + "Leave Car", + "Enter Car as Passenger", + "Enter Car as Driver", + "Follow Car in Car", + "Fire at Obj from Vehicle", + "Destroy Obj", + "Destroy Car", + "GoTo Area Any Means", + "GoTo Area on Foot", + "Run to Area", + "GoTo Area in Car", + "Follow Car on Foot Woffset", + "Guard Attack", + "Set Leader", + "Follow Route", + "Solicit", + "Take Taxi", + "Catch Train", + "Buy IceCream", + "Steal Any Car", + "Mug Char", +}; + +static char StateText[56][18] = { + "None", // 1 + "Idle", + "Look Entity", + "Look Heading", + "Wander Range", + "Wander Path", + "Seek Pos", + "Seek Entity", + "Flee Pos", + "Flee Entity", + "Pursue", + "Follow Path", + "Sniper Mode", + "Rocket Mode", + "Dummy", + "Pause", + "Attack", + "Fight", + "Face Phone", + "Make Call", + "Chat", + "Mug", + "AimGun", + "AI Control", + "Seek Car", + "Seek InBoat", + "Follow Route", + "C.P.R.", + "Solicit", + "Buy IceCream", + "Investigate", + "Step away", + "STATES_NO_AI", + "On Fire", + "Jump", + "Fall", + "GetUp", + "Stagger", + "Dive away", + "STATES_NO_ST", + "Enter Train", + "Exit Train", + "Arrest Plyr", + "Driving", + "Passenger", + "Taxi Passngr", + "Open Door", + "Die", + "Dead", + "CarJack", + "Drag fm Car", + "Enter Car", + "Steal Car", + "Exit Car", + "Hands Up", + "Arrested", +}; + +static char PersonalityTypeText[32][18] = { + "Player", + "Cop", + "Medic", + "Fireman", + "Gang 1", + "Gang 2", + "Gang 3", + "Gang 4", + "Gang 5", + "Gang 6", + "Gang 7", + "Street Guy", + "Suit Guy", + "Sensible Guy", + "Geek Guy", + "Old Guy", + "Tough Guy", + "Street Girl", + "Suit Girl", + "Sensible Girl", + "Geek Girl", + "Old Girl", + "Tough Girl", + "Tramp", + "Tourist", + "Prostitute", + "Criminal", + "Busker", + "Taxi Driver", + "Psycho", + "Steward", + "Sports Fan", +}; + +static char WaitStateText[21][16] = { + "No Wait", + "Traffic Lights", + "Pause CrossRoad", + "Look CrossRoad", + "Look Ped", + "Look Shop", + "Look Accident", + "FaceOff Gang", + "Double Back", + "Hit Wall", + "Turn 180deg", + "Surprised", + "Ped Stuck", + "Look About", + "Play Duck", + "Play Cower", + "Play Taxi", + "Play HandsUp", + "Play HandsCower", + "Play Chat", + "Finish Flee", +}; + +CPed::CPed(uint32 pedType) : m_pedIK(this) +{ + m_type = ENTITY_TYPE_PED; + bPedPhysics = true; + bUseCollisionRecords = true; + + m_vecAnimMoveDelta.x = 0.0; + m_vecAnimMoveDelta.y = 0.0; + m_fHealth = 100.0f; + m_fArmour = 0.0f; + m_nPedType = pedType; + field_520 = 0; + m_talkTimer = 0; + m_talkTypeLast = 167; + m_talkType = 167; + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + CharCreatedBy = RANDOM_CHAR; + m_leader = nil; + m_pedInObjective = nil; + m_carInObjective = nil; + bInVehicle = 0; + m_pMyVehicle = nil; + m_pVehicleAnim = nil; + m_vecOffsetSeek.x = 0.0; + m_vecOffsetSeek.y = 0.0; + m_vecOffsetSeek.z = 0.0; + m_pedFormation = 0; + m_lastThreatTimer = 0; + m_nPedStateTimer = 0; + m_actionX = 0; + m_actionY = 0; + m_phoneTalkTimer = 0; + m_stateUnused = 0; + m_leaveCarTimer = 0; + m_getUpTimer = 0; + m_attackTimer = 0; + m_timerUnused = 0; + m_lookTimer = 0; + m_standardTimer = 0; + m_lastHitTime = 0; + m_hitRecoverTimer = 0; + field_4E8 = 0; + m_moved = CVector2D(0.0f, 0.0f); + m_fRotationCur = 0.0f; + m_headingRate = 15.0f; + m_fRotationDest = 0.0f; + m_vehEnterType = VEHICLE_ENTER_FRONT_LEFT; + m_walkAroundType = 0; + m_pCurrentPhysSurface = nil; + m_vecOffsetFromPhysSurface = CVector(0.0f, 0.0f, 0.0f); + m_pSeekTarget = nil; + m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); + m_wepSkills = 0; + field_318 = 1.0f; + field_31C = 0; + m_phoneId = -1; + m_lastAccident = 0; + m_fleeFrom = nil; + m_fleeFromPosX = 0; + m_fleeFromPosY = 0; + m_fleeTimer = 0; + m_vecSeekPosEx = CVector(0.0f, 0.0f, 0.0f); + m_seekExAngle = 0.0f; + m_nWaitState = WAITSTATE_FALSE; + m_nWaitTimer = 0; + m_pCollidingEntity = nil; + m_nPedState = PED_IDLE; + m_nLastPedState = PED_NONE; + m_nMoveState = PEDMOVE_STILL; + m_nStoredActionState = 0; + m_pFire = nil; + m_pPointGunAt = nil; + m_pLookTarget = nil; + m_fLookDirection = 0.0f; + m_pCurSurface = nil; + m_targetUnused = nil; + m_nPathNodes = 0; + m_nCurPathNode = 0; + m_nPathState = 0; + m_pNextPathNode = nil; + m_pLastPathNode = nil; + m_routeLastPoint = -1; + m_routePoints = 0; + m_routePos = 0; + m_routeType = 0; + m_bodyPartBleeding = -1; + + m_fMass = 70.0f; + m_fTurnMass = 100.0f; + m_fAirResistance = 0.4f / m_fMass; + m_fElasticity = 0.05f; + + bIsStanding = false; + m_ped_flagA2 = false; + m_ped_flagA4 = false; + bIsPointingGunAt = false; + bIsLooking = false; + m_ped_flagA20 = false; + bIsRestoringLook = false; + bIsAimingGun = false; + + bIsRestoringGun = false; + bCanPointGunAtTarget = false; + bIsTalking = false; + bIsInTheAir = false; + bIsLanding = false; + m_ped_flagB20 = false; + m_ped_flagB40 = false; + m_ped_flagB80 = false; + + m_ped_flagC1 = false; + bRespondsToThreats = true; + m_ped_flagC4 = true; + m_ped_flagC8 = false; + m_ped_flagC10 = false; + m_ped_flagC20 = false; + m_ped_flagC40 = false; + m_ped_flagC80 = false; + + m_ped_flagD1 = false; + m_ped_flagD2 = false; + m_ped_flagD4 = false; + m_ped_flagD8 = false; + m_ped_flagD10 = false; + m_ped_flagD20 = false; + m_ped_flagD40 = false; + m_ped_flagD80 = false; + + m_ped_flagE1 = false; + m_ped_flagE2 = false; + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + bIsDucking = false; + m_ped_flagE20 = false; + bDoBloodyFootprints = false; + m_ped_flagE80 = false; + + m_ped_flagF1 = false; + m_ped_flagF2 = false; + m_ped_flagF4 = false; + m_ped_flagF8 = false; + m_ped_flagF10 = false; + m_ped_flagF20 = false; + m_ped_flagF40 = false; + m_ped_flagF80 = false; + + m_ped_flagG1 = false; + m_ped_flagG2 = true; + m_ped_flagG4 = false; + m_ped_flagG8 = false; + m_ped_flagG10 = false; + m_ped_flagG20 = false; + m_ped_flagG40 = false; + m_ped_flagG80 = false; + + m_ped_flagH1 = false; + m_ped_flagH2 = false; + m_ped_flagH4 = false; + m_ped_flagH8 = false; + m_ped_flagH10 = false; + m_ped_flagH20 = false; + m_ped_flagH40 = false; + m_ped_flagH80 = false; + + m_ped_flagI1 = false; + m_ped_flagI2 = false; + m_ped_flagI4 = false; + bRecordedForReplay = false; + m_ped_flagI10 = false; +#ifdef KANGAROO_CHEAT + m_ped_flagI80 = false; +#endif + + if ((CGeneral::GetRandomNumber() & 3) == 0) + m_ped_flagD1 = true; + + m_audioEntityId = DMAudio.CreateEntity(0, this); + DMAudio.SetEntityStatus(m_audioEntityId, 1); + m_fearFlags = CPedType::GetThreats(m_nPedType); + m_threatEntity = nil; + m_eventOrThread = CVector2D(0.0f, 0.0f); + m_pEventEntity = nil; + m_fAngleToEvent = 0.0f; + m_numNearPeds = 0; + + for (int i = 0; i < 10; i++) + { + m_nearPeds[i] = nil; + if (i < 8) { + m_pPathNodesStates[i] = nil; + } + } + m_maxWeaponTypeAllowed = 0; + m_currentWeapon = 0; + m_storedWeapon = NO_STORED_WEAPON; + + for(int i = 0; i < NUM_PED_WEAPONTYPES; i++) + { + CWeapon &weapon = GetWeapon(i); + weapon.m_eWeaponType = WEAPONTYPE_UNARMED; + weapon.m_eWeaponState = WEAPONSTATE_READY; + weapon.m_nAmmoInClip = 0; + weapon.m_nAmmoTotal = 0; + weapon.m_nTimer = 0; + } + + m_lastHitState = 0; + GiveWeapon(WEAPONTYPE_UNARMED, 0); + m_wepAccuracy = 60; + m_lastWepDam = -1; + m_collPoly.valid = false; + m_fCollisionSpeed = 0.0f; + m_wepModelID = -1; + CPopulation::UpdatePedCount(m_nPedType, false); +} + +uint32 +CPed::GiveWeapon(eWeaponType weaponType, uint32 ammo) +{ + CWeapon &weapon = GetWeapon(weaponType); + + if (HasWeapon(weaponType)) { + if (weapon.m_nAmmoTotal + ammo > 99999) + weapon.m_nAmmoTotal = 99999; + else + weapon.m_nAmmoTotal += ammo; + + weapon.Reload(); + } else { + weapon.Initialise(weaponType, ammo); + // TODO: It seems game uses this as both weapon count and max WeaponType we have, which is ofcourse erroneous. + m_maxWeaponTypeAllowed++; + } + if (weapon.m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO) + weapon.m_eWeaponState = WEAPONSTATE_READY; + + return weaponType; +} + +static RwObject* +RemoveAllModelCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) { + RpClumpRemoveAtomic(atomic->clump, atomic); + RpAtomicDestroy(atomic); + } + return object; +} + +static PedOnGroundState +CheckForPedsOnGroundToAttack(CPlayerPed *player, CPed **pedOnGround) +{ + PedOnGroundState stateToReturn; + float angleToFace; + CPed *currentPed = nil; + PedState currentPedState; + CPed *pedOnTheFloor = nil; + CPed *deadPed = nil; + CPed *pedBelow = nil; + bool foundDead = false; + bool foundOnTheFloor = false; + bool foundBelow = false; + float angleDiff; + float distance; + + if (!CGame::nastyGame) + return NO_PED; + + for (int currentPedId = 0; currentPedId < player->m_numNearPeds; currentPedId++) { + + currentPed = player->m_nearPeds[currentPedId]; + + CVector posDifference = currentPed->GetPosition() - player->GetPosition(); + distance = posDifference.Magnitude(); + + if (distance < 2.0f) { + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + currentPed->GetPosition().x, currentPed->GetPosition().y, + player->GetPosition().x, player->GetPosition().y); + + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + player->m_fRotationCur = CGeneral::LimitRadianAngle(player->m_fRotationCur); + + angleDiff = fabs(angleToFace - player->m_fRotationCur); + + if (angleDiff > PI) + angleDiff = 2 * PI - angleDiff; + + currentPedState = currentPed->m_nPedState; + + if (currentPedState == PED_FALL || currentPedState == PED_GETUP || currentPedState == PED_DIE || currentPedState == PED_DEAD) { + if (distance < 2.0f && angleDiff < DEGTORAD(65.0f)) { + if (currentPedState == PED_DEAD) { + foundDead = 1; + if (!deadPed) + deadPed = currentPed; + } else if (!currentPed->IsPedHeadAbovePos(-0.6f)) { + foundOnTheFloor = 1; + if (!pedOnTheFloor) + pedOnTheFloor = currentPed; + } + } + } else if ((distance >= 0.8f || angleDiff >= DEGTORAD(75.0f)) + && (distance >= 1.3f || angleDiff >= DEGTORAD(55.0f)) + && (distance >= 1.7f || angleDiff >= DEGTORAD(35.0f)) + && (distance >= 2.0f || angleDiff >= DEGTORAD(30.0f))) { + + if (angleDiff < DEGTORAD(75.0f)) { + foundBelow = 1; + if (!pedBelow) + pedBelow = currentPed; + } + } else { + foundBelow = 1; + pedBelow = currentPed; + break; + } + } + } + + if (foundOnTheFloor) { + currentPed = pedOnTheFloor; + stateToReturn = PED_ON_THE_FLOOR; + } else if (foundDead) { + currentPed = deadPed; + stateToReturn = PED_DEAD_ON_THE_FLOOR; + } else if (foundBelow) { + currentPed = pedBelow; + stateToReturn = PED_BELOW_PLAYER; + } else { + currentPed = nil; + stateToReturn = NO_PED; + } + + if (pedOnGround) + * pedOnGround = currentPed; + + return stateToReturn; +} + +bool +CPed::IsPlayer(void) +{ + return m_nPedType == PEDTYPE_PLAYER1 || m_nPedType== PEDTYPE_PLAYER2 || + m_nPedType == PEDTYPE_PLAYER3 || m_nPedType == PEDTYPE_PLAYER4; +} + +bool +CPed::UseGroundColModel(void) +{ + return m_nPedState == PED_FALL || + m_nPedState == PED_DIVE_AWAY || + m_nPedState == PED_DIE || + m_nPedState == PED_DEAD; +} + +bool +CPed::CanSetPedState(void) +{ + return m_nPedState != PED_DIE && m_nPedState != PED_ARRESTED && + m_nPedState != PED_ENTER_CAR && m_nPedState != PED_DEAD && m_nPedState != PED_CARJACK && m_nPedState != PED_STEAL_CAR; +} + +bool +CPed::IsPedInControl(void) +{ + return m_nPedState <= PED_STATES_NO_AI + && !bIsInTheAir && !bIsLanding + && m_fHealth > 0.0f; +} + +bool +CPed::CanStrafeOrMouseControl(void) +{ + return m_nPedState == PED_NONE || m_nPedState == PED_IDLE || m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY || + m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT || m_nPedState == PED_AIM_GUN || m_nPedState == PED_JUMP; +} + +void +CPed::AddWeaponModel(int id) +{ + RpAtomic *atm; + + if (id != -1) { + atm = (RpAtomic*)CModelInfo::GetModelInfo(id)->CreateInstance(); + RwFrameDestroy(RpAtomicGetFrame(atm)); + RpAtomicSetFrame(atm, GetNodeFrame(PED_HANDR)); + RpClumpAddAtomic((RpClump*)m_rwObject, atm); + m_wepModelID = id; + } +} + +void +CPed::AimGun(void) +{ + RwV3d pos; + CVector vector; + + if (m_pSeekTarget) { + if (m_pSeekTarget->IsPed()) { + ((CPed*)m_pSeekTarget)->m_pedIK.GetComponentPosition(&pos, PED_TORSO); + vector.x = pos.x; + vector.y = pos.y; + vector.z = pos.z; + } else { + vector = *(m_pSeekTarget->GetPosition()); + } + Say(SOUND_PED_ATTACK); + + bCanPointGunAtTarget = m_pedIK.PointGunAtPosition(&vector); + if (m_pLookTarget != m_pSeekTarget) { + SetLookFlag(m_pSeekTarget, 1); + } + + } else { + if (IsPlayer()) { + bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, ((CPlayerPed*)this)->m_fFPSMoveHeading); + } else { + bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, 0.0f); + } + } +} + +void +CPed::ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer) +{ + CVector pos2 = CVector( + pos.x, + pos.y, + pos.z + 0.1f + ); + + if (!CPed::IsPlayer() || evenOnPlayer) { + ++CStats::HeadShots; + + // BUG: This condition will always return true. + if (m_nPedState != PED_PASSENGER || m_nPedState != PED_TAXI_PASSENGER) { + CPed::SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); + } + + m_ped_flagC20 = true; + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150; + + CParticle::AddParticle(PARTICLE_TEST, pos2, + CVector(0.0f, 0.0f, 0.0f), nil, 0.2f, 0, 0, 0, 0); + + if (CEntity::GetIsOnScreen()) { + for(int i=0; i < 32; i++) { + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, + pos2, CVector(0.0f, 0.0f, 0.03f), + nil, 0.0f, 0, 0, 0, 0); + } + + for (int i = 0; i < 16; i++) { + CParticle::AddParticle(PARTICLE_DEBRIS2, + pos2, + CVector(0.0f, 0.0f, 0.01f), + nil, 0.0f, 0, 0, 0, 0); + } + } + } +} + +void +CPed::RemoveBodyPart(PedNode nodeId, int8 unk) +{ + RwFrame *frame; + RwV3d pos; + + frame = GetNodeFrame(nodeId); + if (frame) { + if (CGame::nastyGame) { + if (nodeId != PED_HEAD) + CPed::SpawnFlyingComponent(nodeId, unk); + + RecurseFrameChildrenVisibilityCB(frame, nil); + pos.x = 0.0f; + pos.y = 0.0f; + pos.z = 0.0f; + + for (frame = RwFrameGetParent(frame); frame; frame = RwFrameGetParent(frame)) + RwV3dTransformPoints(&pos, &pos, 1, RwFrameGetMatrix(frame)); + + if (CEntity::GetIsOnScreen()) { + CParticle::AddParticle(PARTICLE_TEST, pos, + CVector(0.0f, 0.0f, 0.0f), + nil, 0.2f, 0, 0, 0, 0); + + for (int i = 0; i < 16; i++) { + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, + pos, + CVector(0.0f, 0.0f, 0.03f), + nil, 0.0f, 0, 0, 0, 0); + } + } + m_ped_flagC20 = true; + m_bodyPartBleeding = nodeId; + } + } else { + printf("Trying to remove ped component"); + } +} + +RwObject* +CPed::SetPedAtomicVisibilityCB(RwObject *object, void *data) +{ + if (data == nil) + RpAtomicSetFlags(object, 0); + return object; +} + +RwFrame* +CPed::RecurseFrameChildrenVisibilityCB(RwFrame *frame, void *data) +{ + RwFrameForAllObjects(frame, SetPedAtomicVisibilityCB, data); + RwFrameForAllChildren(frame, RecurseFrameChildrenVisibilityCB, nil); + return frame; +} + +void +CPed::SetLookFlag(CEntity *target, bool unknown) +{ + if (m_lookTimer < CTimer::GetTimeInMilliseconds()) { + bIsLooking = true; + bIsRestoringLook = false; + m_pLookTarget = target; + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + m_fLookDirection = 999999.0f; + m_lookTimer = 0; + m_ped_flagA20 = unknown; + if (m_nPedState != PED_DRIVING) { + m_pedIK.m_flags &= ~CPedIK::FLAG_2; + } + } +} + +void +CPed::SetLookFlag(float direction, bool unknown) +{ + if (m_lookTimer < CTimer::GetTimeInMilliseconds()) { + bIsLooking = true; + bIsRestoringLook = false; + m_pLookTarget = nil; + m_fLookDirection = direction; + m_lookTimer = 0; + m_ped_flagA20 = unknown; + if (m_nPedState != PED_DRIVING) { + m_pedIK.m_flags &= ~CPedIK::FLAG_2; + } + } +} + +void +CPed::SetLookTimer(int time) +{ + if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + m_lookTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +bool +CPed::OurPedCanSeeThisOne(CEntity *target) +{ + CColPoint colpoint; + CEntity *ent; + + CVector2D dist = CVector2D(target->GetPosition()) - CVector2D(this->GetPosition()); + + // Check if target is behind ped + if (DotProduct2D(dist, CVector2D(this->GetForward())) < 0.0f) + return false; + + // Check if target is too far away + if (dist.Magnitude() >= 40.0f) + return false; + + // Check line of sight from head + CVector headPos = this->GetPosition(); + headPos.z += 1.0f; + return !CWorld::ProcessLineOfSight(headPos, target->GetPosition(), colpoint, ent, true, false, false, false, false, false); +} + +void +CPed::Avoid(void) +{ + CPed *nearestPed; + + if(m_pedStats->m_temper > m_pedStats->m_fear && m_pedStats->m_temper > 50) + return; + + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { + nearestPed = m_nearPeds[0]; + + if (nearestPed && nearestPed->m_nPedState != PED_DEAD && nearestPed != m_pSeekTarget && nearestPed != m_pedInObjective) { + + // Check if this ped wants to avoid the nearest one + if (CPedType::GetAvoid(this->m_nPedType) & CPedType::GetFlag(nearestPed->m_nPedType)) { + + // Further codes checks whether the distance between us and ped will be equal or below 1.0, if we walk up to him by 1.25 meters. + // If so, we want to avoid it, so we turn our body 45 degree and look to somewhere else. + + // Game converts from radians to degress and back again here, doesn't make much sense + CVector2D forward(-sin(m_fRotationCur), cos(m_fRotationCur)); + forward.Normalise(); // this is kinda pointless + + // Move forward 1.25 meters + CVector2D testPosition = CVector2D(GetPosition()) + forward*1.25f; + + // Get distance to ped we want to avoid + CVector2D distToPed = CVector2D(nearestPed->GetPosition()) - testPosition; + + if (distToPed.Magnitude() <= 1.0f && CPed::OurPedCanSeeThisOne((CEntity*)nearestPed)) { + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + + 500 + (m_randomSeed + 3 * CTimer::GetFrameCounter()) + % 1000 / 5; + + m_fRotationDest += DEGTORAD(45.0f); + if (!bIsLooking) { + CPed::SetLookFlag(nearestPed, 0); + CPed::SetLookTimer(CGeneral::GetRandomNumberInRange(500, 800)); + } + } + } + } + } + } +} + +void +CPed::ClearAimFlag(void) +{ + if (bIsAimingGun) { + bIsAimingGun = false; + bIsRestoringGun = true; + m_pedIK.m_flags &= ~CPedIK:: FLAG_4; + } + + if (CPed::IsPlayer()) + ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0; +} + +void +CPed::ClearLookFlag(void) { + if (bIsLooking) { + bIsLooking = false; + bIsRestoringLook = true; + m_ped_flagI1 = false; + + m_pedIK.m_flags &= ~CPedIK::FLAG_2; + if (CPed::IsPlayer()) + m_lookTimer = CTimer::GetTimeInMilliseconds() + 2000; + else + m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; + + if (m_nPedState == PED_LOOK_HEADING || m_nPedState == PED_LOOK_ENTITY) { + CPed::RestorePreviousState(); + CPed::ClearLookFlag(); + } + } +} + +bool +CPed::IsPedHeadAbovePos(float zOffset) +{ + RwMatrix mat; + + CPedIK::GetWorldMatrix(GetNodeFrame(PED_HEAD), &mat); + return zOffset + GetPosition().z < RwMatrixGetPos(&mat)->z; +} + +void +CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) +{ + CWeaponInfo *currentWeapon; + CAnimBlendAssociation *newAnim; + CPed *ped = (CPed*)arg; + + if (attackAssoc) { + switch (attackAssoc->animId) { + case ANIM_WEAPON_START_THROW: + if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1380) && ped->IsPlayer()) { + attackAssoc->blendDelta = -1000.0f; + newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROWU); + } else { + attackAssoc->blendDelta = -1000.0f; + newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROW); + } + + newAnim->SetFinishCallback(FinishedAttackCB, ped); + return; + + case ANIM_FIGHT_PPUNCH: + attackAssoc->blendDelta = -8.0f; + attackAssoc->flags |= ASSOC_DELETEFADEDOUT; + ped->ClearAttack(); + return; + + case ANIM_WEAPON_THROW: + case ANIM_WEAPON_THROWU: + if (ped->GetWeapon()->m_nAmmoTotal > 0) { + currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); + ped->AddWeaponModel(currentWeapon->m_nModelId); + } + break; + default: + break; + } + } + + if (!ped->m_ped_flagA4) + ped->ClearAttack(); +} + +void +CPed::Attack(void) +{ + CAnimBlendAssociation *weaponAnimAssoc; + int32 weaponAnim; + float animStart; + RwFrame *frame; + eWeaponType ourWeaponType; + float weaponAnimTime; + eWeaponFire ourWeaponFire; + float animLoopEnd; + CWeaponInfo *ourWeapon; + bool lastReloadWasInFuture; + AnimationId reloadAnim; + CAnimBlendAssociation *reloadAnimAssoc; + float delayBetweenAnimAndFire; + CVector firePos; + + ourWeaponType = GetWeapon()->m_eWeaponType; + ourWeapon = CWeaponInfo::GetWeaponInfo(ourWeaponType); + ourWeaponFire = ourWeapon->m_eWeaponFire; + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_AnimToPlay); + lastReloadWasInFuture = m_ped_flagA4; + reloadAnimAssoc = nil; + reloadAnim = NUM_ANIMS; + delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; + weaponAnim = ourWeapon->m_AnimToPlay; + + if (weaponAnim == ANIM_WEAPON_HGUN_BODY) + reloadAnim = ANIM_HGUN_RELOAD; + else if (weaponAnim == ANIM_WEAPON_AK_BODY) + reloadAnim = ANIM_AK_RELOAD; + + if (reloadAnim != NUM_ANIMS) + reloadAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, reloadAnim); + + if (bIsDucking) + return; + + if (reloadAnimAssoc) { + if (!CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) + ClearAttack(); + + return; + } + + // BUG: We currently don't know any situation this cond. could be true. + if (CTimer::GetTimeInMilliseconds() < m_lastHitTime) + lastReloadWasInFuture = true; + + if (!weaponAnimAssoc) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_Anim2ToPlay); + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + + // Long throw granade, molotov + if (!weaponAnimAssoc && ourWeapon->m_bThrow) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_WEAPON_THROWU); + delayBetweenAnimAndFire = 0.2f; + } + } + if (weaponAnimAssoc) { + animStart = ourWeapon->m_fAnimLoopStart; + weaponAnimTime = weaponAnimAssoc->currentTime; + if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) { + if (ourWeapon->m_bCanAimWithArm) + m_pedIK.m_flags |= CPedIK::FLAG_4; + else + m_pedIK.m_flags &= ~CPedIK::FLAG_4; + } + + if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) { + if (weaponAnimAssoc->speed < 1.0f) + weaponAnimAssoc->speed = 1.0f; + + } else { + firePos = ourWeapon->m_vecFireOffset; + if (ourWeaponType == WEAPONTYPE_BASEBALLBAT) { + if (weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) + firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; + + firePos = GetMatrix() * firePos; + } else if (ourWeaponType != WEAPONTYPE_UNARMED) { + if (weaponAnimAssoc->animId == ANIM_KICK_FLOOR) + frame = GetNodeFrame(PED_FOOTR); + else + frame = GetNodeFrame(PED_HANDR); + + for (; frame; frame = RwFrameGetParent(frame)) + RwV3dTransformPoints((RwV3d*)firePos, (RwV3d*)firePos, 1, RwFrameGetMatrix(frame)); + } else { + firePos = GetMatrix() * firePos; + } + + GetWeapon()->Fire(this, &firePos); + + if (ourWeaponType == WEAPONTYPE_MOLOTOV || ourWeaponType == WEAPONTYPE_GRENADE) { + RemoveWeaponModel(ourWeapon->m_nModelId); + } + if (!GetWeapon()->m_nAmmoTotal && ourWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) { + SelectGunIfArmed(); + } + + if (GetWeapon()->m_eWeaponState != WEAPONSTATE_MELEE_MADECONTACT) { + // If reloading just began, start the animation + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING && reloadAnim != NUM_ANIMS && !reloadAnimAssoc) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, reloadAnim, 8.0f); + ClearLookFlag(); + ClearAimFlag(); + m_ped_flagA4 = false; + bIsPointingGunAt = false; + m_lastHitTime = CTimer::GetTimeInMilliseconds(); + return; + } + } else { + if (weaponAnimAssoc->animId == ANIM_WEAPON_BAT_V || weaponAnimAssoc->animId == ANIM_WEAPON_BAT_H) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, 1.0f); + } else if (weaponAnimAssoc->animId == ANIM_FIGHT_PPUNCH) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_PUNCH_ATTACK, 0.0f); + } + + weaponAnimAssoc->speed = 0.5f; + + // BUG: We currently don't know any situation this cond. could be true. + if (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime) { + weaponAnimAssoc->callbackType = 0; + } + } + + lastReloadWasInFuture = false; + } + + if (ourWeaponType == WEAPONTYPE_SHOTGUN) { + weaponAnimTime = weaponAnimAssoc->currentTime; + firePos = ourWeapon->m_vecFireOffset; + + if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) { + for (frame = GetNodeFrame(PED_HANDR); frame; frame = RwFrameGetParent(frame)) + RwV3dTransformPoints((RwV3d*)firePos, (RwV3d*)firePos, 1, RwFrameGetMatrix(frame)); + + CVector gunshellPos( + firePos.x - 0.6f * GetForward().x, + firePos.y - 0.6f * GetForward().y, + firePos.z - 0.15f * GetUp().z + ); + + CVector2D gunshellRot( + GetRight().x, + GetRight().y + ); + + gunshellRot.Normalise(); + GetWeapon()->AddGunshell(this, gunshellPos, gunshellRot, 0.025f); + } + } + animLoopEnd = ourWeapon->m_fAnimLoopEnd; + if (ourWeaponFire == WEAPON_FIRE_MELEE && weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) + animLoopEnd = 3.4f/6.0f; + + weaponAnimTime = weaponAnimAssoc->currentTime; + + // Anim loop end, either start the loop again or finish the attack + if (weaponAnimTime > animLoopEnd || !weaponAnimAssoc->IsRunning() && ourWeaponFire != WEAPON_FIRE_PROJECTILE) { + + if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd + && (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime) + && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { + + weaponAnim = weaponAnimAssoc->animId; + if (ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), nil) < PED_ON_THE_FLOOR) { + if (weaponAnim != ourWeapon->m_Anim2ToPlay || weaponAnim == ANIM_RBLOCK_CSHOOT) { + weaponAnimAssoc->Start(ourWeapon->m_fAnimLoopStart); + } else { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + } + } else { + if (weaponAnim == ourWeapon->m_Anim2ToPlay) + weaponAnimAssoc->SetCurrentTime(0.1f); + else + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + } + } else { + ClearAimFlag(); + + // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) + if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= ourWeapon->m_fAnimLoopEnd) { + switch (ourWeaponType) { + case WEAPONTYPE_UZI: + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_UZI_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_AK47: + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_M16: + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_M16_BULLET_ECHO, 0.0f); + break; + default: + break; + } + } + + // Fun fact: removing this part leds to reloading flamethrower + if (ourWeaponType == WEAPONTYPE_FLAMETHROWER && weaponAnimAssoc->IsRunning()) { + weaponAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; + weaponAnimAssoc->flags &= ~ASSOC_RUNNING; + weaponAnimAssoc->blendDelta = -4.0f; + } + } + } + if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire) + lastReloadWasInFuture = false; + + m_ped_flagA4 = lastReloadWasInFuture; + return; + } + + if (lastReloadWasInFuture) { + if (ourWeaponFire != WEAPON_FIRE_PROJECTILE || !CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) { + if (!CGame::nastyGame || ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), nil) < PED_ON_THE_FLOOR) { + weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + } else { + weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + } + + weaponAnimAssoc->SetFinishCallback(CPed::FinishedAttackCB, this); + weaponAnimAssoc->flags |= ASSOC_RUNNING; + + if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) + weaponAnimAssoc->SetCurrentTime(0.0f); + + if (CPed::IsPlayer()) { + ((CPlayerPed*)this)->field_1376 = 0.0f; + ((CPlayerPed*)this)->field_1380 = false; + } + } + } + else + CPed::FinishedAttackCB(nil, this); +} + +void +CPed::RemoveWeaponModel(int modelId) +{ + // modelId is not used!! This function just removes the current weapon. + RwFrameForAllObjects(GetNodeFrame(PED_HANDR),RemoveAllModelCB,nil); + m_wepModelID = -1; +} + +void +CPed::SetCurrentWeapon(uint32 weaponType) +{ + CWeaponInfo *weaponInfo; + if (HasWeapon(weaponType)) { + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + RemoveWeaponModel(weaponInfo->m_nModelId); + + m_currentWeapon = weaponType; + + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + AddWeaponModel(weaponInfo->m_nModelId); + } +} + +// Only used while deciding which gun ped should switch to, if no ammo left. +bool +CPed::SelectGunIfArmed(void) +{ + for (int i = 0; i < m_maxWeaponTypeAllowed; i++) { + if (GetWeapon(i).m_nAmmoTotal > 0) { + eWeaponType weaponType = GetWeapon(i).m_eWeaponType; + if (weaponType >= WEAPONTYPE_COLT45 && weaponType != WEAPONTYPE_M16 && weaponType <= WEAPONTYPE_FLAMETHROWER) { + SetCurrentWeapon(i); + return true; + } + } + } + SetCurrentWeapon(WEAPONTYPE_UNARMED); + return false; +} + +void +CPed::Duck(void) +{ + if (CTimer::GetTimeInMilliseconds() > m_duckTimer) + ClearDuck(); +} + +void +CPed::ClearDuck(void) +{ + CAnimBlendAssociation *animAssoc; + + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_DUCK_DOWN); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_DUCK_LOW); + + if (animAssoc) { + + if (bCrouchWhenShooting) { + + if (m_nPedState == PED_ATTACK || m_nPedState == PED_AIM_GUN) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_RBLOCK_CSHOOT); + if (!animAssoc || animAssoc->blendDelta < 0.0f) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_RBLOCK_CSHOOT, 4.0f); + } + } + } + } else + bIsDucking = false; +} + +void +CPed::ClearPointGunAt(void) +{ + CAnimBlendAssociation *animAssoc; + CWeaponInfo *weaponInfo; + + ClearLookFlag(); + ClearAimFlag(); + bIsPointingGunAt = false; + if (m_nPedState == PED_AIM_GUN) { + RestorePreviousState(); + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weaponInfo->m_AnimToPlay); + if (!animAssoc || animAssoc->blendDelta < 0.0f) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weaponInfo->m_Anim2ToPlay); + } + if (animAssoc) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->blendDelta = -4.0; + } + } +} + +void +CPed::BeingDraggedFromCar(void) +{ + CAnimBlendAssociation *animAssoc; + AnimationId enterAnim; + bool dontRunAnim = false; + PedLineUpPhase lineUpType; + + if (!m_pVehicleAnim) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE, 100.0f); + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SIT); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_LSIT); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITP); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITPLO); + } + } + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (m_vehEnterType == VEHICLE_ENTER_FRONT_LEFT || m_vehEnterType == VEHICLE_ENTER_REAR_LEFT) { + if (m_ped_flagF10) { + enterAnim = ANIM_CAR_QJACKED; + } else if (m_pMyVehicle->bLowVehicle) { + enterAnim = ANIM_CAR_LJACKED_LHS; + } else { + enterAnim = ANIM_CAR_JACKED_LHS; + } + } else if (m_vehEnterType == VEHICLE_ENTER_FRONT_RIGHT || m_vehEnterType == VEHICLE_ENTER_REAR_RIGHT) { + if (m_pMyVehicle->bLowVehicle) + enterAnim = ANIM_CAR_LJACKED_RHS; + else + enterAnim = ANIM_CAR_JACKED_RHS; + } else + dontRunAnim = true; + + + if (!dontRunAnim) + m_pVehicleAnim = CAnimManager::AddAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, enterAnim); + + m_pVehicleAnim->SetFinishCallback(PedSetDraggedOutCarCB, this); + lineUpType = LINE_UP_TO_CAR_START; + } else if (m_pVehicleAnim->currentTime <= 1.4f) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + lineUpType = LINE_UP_TO_CAR_START; + } else { + lineUpType = LINE_UP_TO_CAR_2; + } + + LineUpPedWithCar(lineUpType); +} + +void +CPed::RestartNonPartialAnims(void) +{ + CAnimBlendAssociation *assoc; + + for (assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (!assoc->IsPartial()) + assoc->flags |= ASSOC_RUNNING; + } +} + +void +CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) +{ + CAnimBlendAssociation *quickJackedAssoc; + CVehicle *vehicle; + CPed *ped = (CPed*)arg; + eWeaponType weaponType = ped->GetWeapon()->m_eWeaponType; + + quickJackedAssoc = RpAnimBlendClumpGetAssociation((RpClump*) ped->m_rwObject, ANIM_CAR_QJACKED); + if (ped->m_nPedState != PED_ARRESTED) { + ped->m_nLastPedState = PED_NONE; + if (dragAssoc) + dragAssoc->blendDelta = -1000.0; + } + ped->RestartNonPartialAnims(); + ped->m_pVehicleAnim = nil; + ped->m_pSeekTarget = nil; + vehicle = ped->m_pMyVehicle; + + vehicle->m_nGettingOutFlags &= ~GetVehEnterExitFlag(ped->m_vehEnterType); + + if (vehicle->pDriver == ped) { + vehicle->RemoveDriver(); + if (vehicle->m_nDoorLock == CARLOCK_COP_CAR) + vehicle->m_nDoorLock = CARLOCK_UNLOCKED; + + if (ped->m_nPedType == PEDTYPE_COP && vehicle->IsLawEnforcementVehicle()) + vehicle->ChangeLawEnforcerState(false); + } else { + for (int i = 0; i < vehicle->m_nNumMaxPassengers; i++) { + if (vehicle->pPassengers[i] == ped) { + vehicle->pPassengers[i] = nil; + vehicle->m_nNumPassengers--; + } + } + } + + ped->bInVehicle = false; + if (ped->IsPlayer()) + AudioManager.PlayerJustLeftCar(); + + if (quickJackedAssoc) { + dragAssoc->SetDeleteCallback(PedSetQuickDraggedOutCarPositionCB, ped); + } else { + dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); + if (ped->CanSetPedState()) + CAnimManager::BlendAnimation((RpClump*) ped->m_rwObject, ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); + } + + // Only uzi can be used on cars, so previous weapon was stored + if (ped->IsPlayer() && weaponType == WEAPONTYPE_UZI) { + if (ped->m_storedWeapon != NO_STORED_WEAPON) { + ped->SetCurrentWeapon(ped->m_storedWeapon); + ped->m_storedWeapon = NO_STORED_WEAPON; + } + } else { + ped->AddWeaponModel(CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId); + } + ped->m_nStoredActionState = 0; + ped->m_ped_flagI4 = false; +} + +void +CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float seatPosMult) +{ + CVehicleModelInfo *vehModel; + CVector vehDoorPos; + CVector vehDoorOffset; + float seatOffset; + + vehModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(veh->m_modelIndex); + if (veh->bIsVan && (enterType == VEHICLE_ENTER_REAR_LEFT || enterType == VEHICLE_ENTER_REAR_RIGHT)) { + seatOffset = 0.0f; + vehDoorOffset = offsetToOpenVanDoor; + } else { + seatOffset = veh->m_handling->fSeatOffsetDistance * seatPosMult; + if (veh->bLowVehicle) { + vehDoorOffset = offsetToOpenLowCarDoor; + } else { + vehDoorOffset = offsetToOpenRegularCarDoor; + } + } + + switch (enterType) { + case VEHICLE_ENTER_FRONT_RIGHT: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case VEHICLE_ENTER_REAR_RIGHT: + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case VEHICLE_ENTER_FRONT_LEFT: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + case VEHICLE_ENTER_REAR_LEFT: + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + default: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); + } + *output = vehDoorPos - vehDoorOffset; +} + +// This function was mostly duplicate of GetLocalPositionToOpenCarDoor, so I've used it. +void +CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType) +{ + CVector localPos; + CVector vehDoorPos; + + GetLocalPositionToOpenCarDoor(&localPos, veh, enterType, 1.0f); + vehDoorPos = Multiply3x3(veh->GetMatrix(), localPos) + veh->GetPosition(); + +/* + // Not used. + CVector localVehDoorOffset; + + if (veh->bIsVan && (enterType == VEHICLE_ENTER_REAR_LEFT || enterType == VEHICLE_ENTER_REAR_RIGHT)) { + localVehDoorOffset = offsetToOpenVanDoor; + } else { + if (veh->bIsLow) { + localVehDoorOffset = offsetToOpenLowCarDoor; + } else { + localVehDoorOffset = offsetToOpenRegularCarDoor; + } + } + + vehDoorPosWithoutOffset = Multiply3x3(veh->GetMatrix(), localPos + localVehDoorOffset) + veh->GetPosition(); +*/ + *output = vehDoorPos; +} + +void +CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float offset) +{ + CVector doorPos; + CMatrix vehMat(veh->GetMatrix()); + + GetLocalPositionToOpenCarDoor(output, veh, enterType, offset); + doorPos = Multiply3x3(vehMat, *output); + + *output = *veh->GetPosition() + doorPos; +} + +void +CPed::LineUpPedWithCar(PedLineUpPhase phase) +{ + bool vehIsUpsideDown = false; + int vehAnim; + float seatPosMult = 0.0f; + float currentZ; + float adjustedTimeStep; + + if (CReplay::IsPlayingBack()) + return; + + if (!m_ped_flagC8 && phase != LINE_UP_TO_CAR_2) { + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SIT)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_LSIT)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITP)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITPLO)) { + SetPedPositionInCar(); + return; + } + m_ped_flagC8 = 1; + } + if (phase == LINE_UP_TO_CAR_START) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + CVehicle *veh = m_pMyVehicle; + + // Not quite right, IsUpsideDown func. checks for <= -0.9f. + // Since that function is also used in this file, doesn't this variable indicate upsidedownness?! + if (veh->GetUp().z <= -0.8f) + vehIsUpsideDown = true; + + if (m_vehEnterType == VEHICLE_ENTER_FRONT_RIGHT || m_vehEnterType == VEHICLE_ENTER_REAR_RIGHT) { + if (vehIsUpsideDown) { + m_fRotationDest = -PI + veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5 * PI + veh->GetForward().Heading(); + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } else if (m_vehEnterType == VEHICLE_ENTER_FRONT_LEFT || m_vehEnterType == VEHICLE_ENTER_REAR_LEFT) { + if (vehIsUpsideDown) { + m_fRotationDest = veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = -0.5 * PI + veh->GetForward().Heading(); + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } + + if (!bInVehicle) + seatPosMult = 1.0f; + + if (m_pVehicleAnim) { + vehAnim = m_pVehicleAnim->animId; + + switch (vehAnim) { + case ANIM_CAR_JACKED_RHS: + case ANIM_CAR_LJACKED_RHS: + case ANIM_CAR_JACKED_LHS: + case ANIM_CAR_LJACKED_LHS: + case ANIM_CAR_QJACKED: + case ANIM_CAR_GETOUT_LHS: + case ANIM_CAR_GETOUT_LOW_LHS: + case ANIM_CAR_GETOUT_RHS: + case ANIM_CAR_GETOUT_LOW_RHS: + case ANIM_CAR_CRAWLOUT_RHS: + case ANIM_CAR_CRAWLOUT_RHS2: + case ANIM_VAN_GETIN_L: + case ANIM_VAN_GETOUT_L: + case ANIM_VAN_GETIN: + case ANIM_VAN_GETOUT: + seatPosMult = m_pVehicleAnim->currentTime / m_pVehicleAnim->hierarchy->totalLength; + break; + case ANIM_CAR_QJACK: + case ANIM_CAR_GETIN_LHS: + case ANIM_CAR_GETIN_LOW_LHS: + case ANIM_CAR_GETIN_RHS: + case ANIM_CAR_GETIN_LOW_RHS: + case ANIM_DRIVE_BOAT: + seatPosMult = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; + break; + case ANIM_CAR_CLOSEDOOR_LHS: + case ANIM_CAR_CLOSEDOOR_LOW_LHS: + case ANIM_CAR_CLOSEDOOR_RHS: + case ANIM_CAR_CLOSEDOOR_LOW_RHS: + case ANIM_CAR_SHUFFLE_RHS: + case ANIM_CAR_LSHUFFLE_RHS: + seatPosMult = 0.0f; + break; + case ANIM_CAR_CLOSE_LHS: + case ANIM_CAR_CLOSE_RHS: + case ANIM_COACH_OPEN_L: + case ANIM_COACH_OPEN_R: + case ANIM_COACH_IN_L: + case ANIM_COACH_IN_R: + case ANIM_COACH_OUT_L: + seatPosMult = 1.0f; + break; + default: + break; + } + } + + CVector neededPos; + + if (phase == LINE_UP_TO_CAR_2) { + neededPos = *GetPosition(); + } else { + GetPositionToOpenCarDoor(&neededPos, veh, m_vehEnterType, seatPosMult); + } + + CVector autoZPos = neededPos; + + if (veh->bIsInWater) { + if (veh->m_vehType == VEHICLE_TYPE_BOAT && veh->IsUpsideDown()) + autoZPos.z += 1.0f; + } else { + CPedPlacement::FindZCoorForPed(&autoZPos); + } + + if (phase == LINE_UP_TO_CAR_END || phase == LINE_UP_TO_CAR_2) { + neededPos.z = GetPosition().z; + + // Getting out + if (!veh->bIsBus || (veh->bIsBus && vehIsUpsideDown)) { + float pedZSpeedOnExit = m_vecMoveSpeed.z - 0.008f * CTimer::GetTimeStep(); + + // If we're not in ground at next step, apply animation + if (neededPos.z + pedZSpeedOnExit >= autoZPos.z) { + m_vecMoveSpeed.z = pedZSpeedOnExit; + ApplyMoveSpeed(); + // Removing below line breaks the animation + neededPos.z = GetPosition().z; + } else { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + } + } + + if (autoZPos.z > neededPos.z) { + currentZ = GetPosition().z; + if (m_pVehicleAnim && vehAnim != ANIM_VAN_GETIN_L && vehAnim != ANIM_VAN_CLOSE_L && vehAnim != ANIM_VAN_CLOSE && vehAnim != ANIM_VAN_GETIN) { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } else if (neededPos.z <= currentZ && m_pVehicleAnim && vehAnim != ANIM_VAN_CLOSE_L && vehAnim != ANIM_VAN_CLOSE) { + adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = currentZ - (currentZ - neededPos.z) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep); + } + } else { + // We may need to raise up the ped + if (phase == LINE_UP_TO_CAR_START) { + currentZ = GetPosition().z; + + if (neededPos.z > currentZ) { + + if (m_pVehicleAnim && + (vehAnim == ANIM_CAR_GETIN_RHS || vehAnim == ANIM_CAR_GETIN_LOW_RHS || vehAnim == ANIM_CAR_GETIN_LHS || vehAnim == ANIM_CAR_GETIN_LOW_LHS + || vehAnim == ANIM_CAR_QJACK || vehAnim == ANIM_VAN_GETIN_L || vehAnim == ANIM_VAN_GETIN)) { + adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; + } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK) { + neededPos.z = max(currentZ, autoZPos.z); + } + } + } + } + + bool stillGettingInOut = false; + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) + stillGettingInOut = veh->m_vehType != VEHICLE_TYPE_BOAT || m_ped_flagG10; + + if (!stillGettingInOut) { + m_fRotationCur = m_fRotationDest; + } else { + float limitedAngle = CGeneral::LimitRadianAngle(m_fRotationDest); + float timeUntilStateChange = (m_nPedStateTimer - CTimer::GetTimeInMilliseconds())/600.0f; + + m_vecOffsetSeek.z = 0.0; + if (timeUntilStateChange <= 0.0f) { + m_vecOffsetSeek.x = 0.0; + m_vecOffsetSeek.y = 0.0; + } else { + neededPos -= timeUntilStateChange * m_vecOffsetSeek; + } + + if (limitedAngle > PI + m_fRotationCur) { + limitedAngle -= 2 * PI; + } else if (limitedAngle < m_fRotationCur - PI) { + limitedAngle += 2 * PI; + } + m_fRotationCur -= (m_fRotationCur - limitedAngle) * (1.0f - timeUntilStateChange); + } + + if (seatPosMult > 0.2f || vehIsUpsideDown) { + GetPosition() = neededPos; + + GetMatrix().SetRotate(0.0f, 0.0f, m_fRotationCur); + + // It will be all 0 after rotate. + GetPosition() = neededPos; + } else { + CVector output; + CMatrix vehDoorMat(veh->GetMatrix()); + + GetLocalPositionToOpenCarDoor(&output, veh, m_vehEnterType, 0.0f); + *vehDoorMat.GetPosition() += Multiply3x3(vehDoorMat, output); + GetMatrix() = vehDoorMat; + } + +} + +static void +particleProduceFootDust(CPed *ped, CVector *pos, float size, int times) +{ + switch (ped->m_nLastCollType) + { + case 1: // somewhere hard + case 3: // soft dirt + case 5: // pavement + case 18:// sand + for (int i = 0; i < times; ++i) { + CVector adjustedPos = *pos; + adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + CParticle::AddParticle(PARTICLE_PEDFOOT_DUST, adjustedPos, CVector(0.0f, 0.0f, 0.0f), nil, size, CRGBA(0, 0, 0, 0), 0, 0, 0, 0); + } + break; + default: + break; + } +} + +static void +particleProduceFootSplash(CPed *ped, CVector *pos, float size, int times) +{ + for (int i = 0; i < times; i++) { + CVector adjustedPos = *pos; + adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + + CVector direction = ped->GetForward() * -0.05f; + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, adjustedPos, direction, nil, size, CRGBA(32, 32, 32, 32), 0, 0, CGeneral::GetRandomNumberInRange(0, 1), 200); + } +} + +void +CPed::PlayFootSteps(void) +{ + if (bDoBloodyFootprints) { + if (m_bloodyFootprintCount > 0 && m_bloodyFootprintCount < 300) { + m_bloodyFootprintCount--; + + if (m_bloodyFootprintCount == 0) + bDoBloodyFootprints = false; + } + } + + if (!bIsStanding) + return; + + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); + CAnimBlendAssociation *walkRunAssoc = nil; + float walkRunAssocBlend = 0.0f, idleAssocBlend = 0.0f; + + for (; assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (assoc->flags & ASSOC_FLAG80) { + walkRunAssoc = assoc; + walkRunAssocBlend += assoc->blendAmount; + } else if ((assoc->flags & ASSOC_FLAG200) == 0) { + idleAssocBlend += assoc->blendAmount; + } + } + + if (walkRunAssoc && walkRunAssocBlend > 0.5f && idleAssocBlend < 1.0f) { + float stepStart = 1 / 15.0f; + float stepEnd = walkRunAssoc->hierarchy->totalLength / 2.0f + stepStart; + float currentTime = walkRunAssoc->currentTime; + int stepPart = 0; + + if (currentTime >= stepStart && currentTime - walkRunAssoc->timeStep < stepStart) + stepPart = 1; + else if (currentTime >= stepEnd && currentTime - walkRunAssoc->timeStep < stepEnd) + stepPart = 2; + + if (stepPart != 0) { + DMAudio.PlayOneShot(m_audioEntityId, stepPart == 1 ? SOUND_STEP_START : SOUND_STEP_END, 1.0f); + CVector footPos(0.0f, 0.0f, 0.0f); + + for (RwFrame *frame = GetNodeFrame(stepPart == 1 ? PED_FOOTL : PED_FOOTR); frame; frame = RwFrameGetParent(frame)) + RwV3dTransformPoints(footPos, footPos, 1, RwFrameGetMatrix(frame)); + + CVector forward = GetForward(); + + footPos.z -= 0.1f; + footPos += 0.2f * forward; + + if (bDoBloodyFootprints) { + CVector2D top(forward * 0.26f); + CVector2D right(GetRight() * 0.14f); + + CShadows::AddPermanentShadow(1, gpBloodPoolTex, &footPos, + top.x, top.y, + right.x, right.y, + 255, 255, 0, 0, 4.0f, 3000.0f, 1.0f); + + if (m_bloodyFootprintCount <= 20) { + m_bloodyFootprintCount = 0; + bDoBloodyFootprints = false; + } else { + m_bloodyFootprintCount -= 20; + } + } + if (CWeather::Rain <= 0.1f || CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) { + if(IsPlayer()) + particleProduceFootDust(this, &footPos, 0.0f, 4); + } else if(stepPart == 2) { + particleProduceFootSplash(this, &footPos, 0.15f, 4); + } + } + } + + if (m_nLastCollType == 19) { // Water + float pedSpeed = CVector2D(m_vecMoveSpeed).Magnitude(); + if (pedSpeed > 0.03f && CTimer::GetFrameCounter() % 2 == 0 && pedSpeed > 0.13f) { + float particleSize = pedSpeed * 2.0f; + + if (particleSize < 0.25f) + particleSize = 0.25f; + + if (particleSize > 0.75f) + particleSize = 0.75f; + + CVector particlePos = GetPosition() + GetForward() * 0.3f; + particlePos.z -= 1.2f; + + CVector particleDir = m_vecMoveSpeed * 0.75f; + + particleDir.z = CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + CParticle::AddParticle(PARTICLE_PED_SPLASH, particlePos, particleDir, nil, 0.8f * particleSize, CRGBA(155,155,185,128), 0, 0, 0, 0); + + particleDir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.05f); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, particlePos, particleDir, nil, particleSize, CRGBA(255,255,255,255), 0, 0, 0, 0); + } + } +} + +bool +CPed::IsPointerValid(void) +{ + int8 pedIndex = CPools::GetPedPool()->GetIndex(this) >> 8; + if (pedIndex < 0 || pedIndex >= NUMPEDS) + return false; + + if (m_entryInfoList.first || FindPlayerPed() == this) + return true; + + return false; +} + +// Some kind of binary sort +void +CPed::SortPeds(CPed **list, int min, int max) +{ + if (min >= max) + return; + + CVector leftDiff, rightDiff; + CVector middleDiff = GetPosition() - list[(max + min) / 2]->GetPosition(); + float middleDist = middleDiff.Magnitude(); + + int left = max; + int right; + for(right = min; right <= left; ){ + // Those 1.0s are to make sure loop always run for first time. + for (float rightDist = middleDist-1.0f; middleDist > rightDist; right++) { + rightDiff = GetPosition() - list[right]->GetPosition(); + rightDist = rightDiff.Magnitude(); + } + right--; + + for (float leftDist = middleDist+1.0f; middleDist < leftDist; left--) { + leftDiff = GetPosition() - list[left]->GetPosition(); + leftDist = leftDiff.Magnitude(); + } + left++; + + if (right <= left) { + CPed *ped = list[right]; + list[right] = list[left]; + list[left] = ped; + right++; + left--; + } + } + SortPeds(list, min, left); + SortPeds(list, right, max); +} + +void +CPed::BuildPedLists(void) +{ + static CPed *unsortedNearPeds[10]; + uint16 nextNearPedSlot = 0; + + if ((CTimer::GetFrameCounter() + m_randomSeed) & 15) { + + for(int i = 0; i < 10; ) { + if (m_nearPeds[i]) { + if (m_nearPeds[i]->IsPointerValid()) { + float distSqr = (GetPosition() - m_nearPeds[i]->GetPosition()).MagnitudeSqr2D(); + if (distSqr < 900.0f) { + i++; + continue; + } + } + + // If we arrive here, the ped we're checking isn't "near", so we should remove it. + for (int j = i; j < 9; j++) { + m_nearPeds[j] = m_nearPeds[j + 1]; + m_nearPeds[j + 1] = nil; + } + // Above loop won't work when it's 9, so we need to empty slot 9. + m_nearPeds[9] = nil; + m_numNearPeds--; + } else + i++; + } + } else { + CVector centre = CEntity::GetBoundCentre(); + CRect rect( + (centre.x - 20.0f) * 0.025f + 50.0f, + (centre.y - 20.0f) * 0.025f + 50.0f, + (centre.x + 20.0f) * 0.025f + 50.0f, + (centre.y + 20.0f) * 0.025f + 50.0f); + + for(int y = rect.top; y <= rect.bottom; y++) { + for(int x = rect.left; x <= rect.right; x++) { + for (CPtrNode *pedPtrNode = CWorld::GetSector(x,y)->m_lists[ENTITYLIST_PEDS].first; pedPtrNode; pedPtrNode = pedPtrNode->next) { + CPed *ped = (CPed*)pedPtrNode->item; + if (ped != this && !ped->bInVehicle) { + float dist = (ped->GetPosition() - GetPosition()).Magnitude2D(); + if (distanceMultToCountPedNear * 30.0f > dist) + { + unsortedNearPeds[nextNearPedSlot] = ped; + nextNearPedSlot++; + } + } + } + } + } + unsortedNearPeds[nextNearPedSlot] = nil; + SortPeds(unsortedNearPeds, 0, nextNearPedSlot - 1); + for (m_numNearPeds = 0; m_numNearPeds < 10; m_numNearPeds++) { + CPed *ped = unsortedNearPeds[m_numNearPeds]; + if (!ped) + break; + + m_nearPeds[m_numNearPeds] = ped; + } + for (int pedToClear = m_numNearPeds; pedToClear < 10; pedToClear++) + m_nearPeds[pedToClear] = nil; + } +} + +void +CPed::SetPedStats(ePedStats pedStat) +{ + m_pedStats = CPedStats::ms_apPedStats[pedStat]; +} + +void +CPed::SetModelIndex(uint32 mi) +{ + CEntity::SetModelIndex(mi); + RpAnimBlendClumpInit((RpClump*) m_rwObject); + RpAnimBlendClumpFillFrameArray((RpClump*) m_rwObject, m_pFrames); + CPedModelInfo *modelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex); + SetPedStats((ePedStats) modelInfo->m_pedStatType); + m_headingRate = m_pedStats->m_headingChangeRate; + m_animGroup = (AssocGroupId) modelInfo->m_animGroup; + CAnimManager::AddAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE); + + // This is a mistake by R*, velocity is CVector, whereas m_vecAnimMoveDelta is CVector2D. + (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity = (CVector*) &m_vecAnimMoveDelta; +} + +void +CPed::RemoveLighting(bool reset) +{ + CRenderer::RemoveVehiclePedLights(this, reset); +} + +bool +CPed::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + if (bRenderScorched) { + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + } else { + // Note that this lightMult is only affected by LIGHT_DARKEN. If there's no LIGHT_DARKEN, it will be 1.0. + float lightMult = CPointLights::GenerateLightsAffectingObject(&GetPosition()); + if (!bHasBlip && lightMult != 1.0f) { + SetAmbientAndDirectionalColours(lightMult); + return true; + } + } + return false; +} + +void +CPed::Teleport(CVector pos) +{ + CWorld::Remove(this); + GetPosition() = pos; + bIsStanding = false; + m_nPedStateTimer = 0; + m_actionX = 0.0f; + m_actionY = 0.0f; + m_pDamageEntity = nil; + CWorld::Add(this); +} + +void +CPed::CalculateNewOrientation(void) +{ + if (CReplay::IsPlayingBack() || !IsPedInControl()) + return; + + CVector pos = *GetPosition(); + + GetMatrix().SetRotate(0.0f, 0.0f, m_fRotationCur); + + // Because SetRotate makes pos. all 0 + GetPosition() = pos; +} + +float +CPed::WorkOutHeadingForMovingFirstPerson(float offset) +{ + if (!IsPlayer()) + return 0.0f; + + CPad *pad0 = CPad::GetPad(0); + float leftRight = pad0->GetPedWalkLeftRight(); + float upDown = pad0->GetPedWalkUpDown(); + float &angle = ((CPlayerPed*)this)->m_fWalkAngle; + + if (upDown != 0.0f) { + angle = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); + } else { + if (leftRight < 0.0f) + angle = 0.5 * PI; + else if (leftRight > 0.0f) + angle = -0.5 * PI; + } + + return CGeneral::LimitRadianAngle(offset + angle); +} + +void +CPed::CalculateNewVelocity(void) +{ + if (IsPedInControl()) { + float headAmount = DEGTORAD(m_headingRate) * CTimer::GetTimeStep(); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float limitedRotDest = CGeneral::LimitRadianAngle(m_fRotationDest); + + if (m_fRotationCur - PI > limitedRotDest) { + limitedRotDest += 2 * PI; + } else if(PI + m_fRotationCur < limitedRotDest) { + limitedRotDest -= 2 * PI; + } + + if (IsPlayer() && m_nPedState == PED_ATTACK) + headAmount /= 4.0f; + + float neededTurn = limitedRotDest - m_fRotationCur; + if (neededTurn <= headAmount) { + if (neededTurn > (-headAmount)) + m_fRotationCur += neededTurn; + else + m_fRotationCur -= headAmount; + } else { + m_fRotationCur += headAmount; + } + } + + CVector2D forward(sin(m_fRotationCur), cos(m_fRotationCur)); + + m_moved.x = CrossProduct2D(m_vecAnimMoveDelta, forward); // (m_vecAnimMoveDelta.x * cos(m_fRotationCur)) + -sin(m_fRotationCur) * m_vecAnimMoveDelta.y; + m_moved.y = DotProduct2D(m_vecAnimMoveDelta, forward); // m_vecAnimMoveDelta.y* cos(m_fRotationCur) + (m_vecAnimMoveDelta.x * sin(m_fRotationCur)); + + if (CTimer::GetTimeStep() >= 0.01f) { + m_moved = m_moved * (1 / CTimer::GetTimeStep()); + } else { + m_moved = m_moved * (1 / 100.0f); + } + + if ((!TheCamera.Cams[TheCamera.ActiveCam].GetWeaponFirstPersonOn() && !TheCamera.Cams[0].Using3rdPersonMouseCam()) + || FindPlayerPed() != this || !CanStrafeOrMouseControl()) + return; + + float walkAngle = WorkOutHeadingForMovingFirstPerson(m_fRotationCur); + float pedSpeed = m_moved.Magnitude(); + float localWalkAngle = CGeneral::LimitRadianAngle(walkAngle - m_fRotationCur); + + if (localWalkAngle < -0.5 * PI) { + localWalkAngle += PI; + } else if (localWalkAngle > 0.5 * PI) { + localWalkAngle -= PI; + } + + // Interestingly this part is responsible for diagonal walking. + if (localWalkAngle > -DEGTORAD(50.0f) && localWalkAngle < DEGTORAD(50.0f)) { + TheCamera.Cams[TheCamera.ActiveCam].m_fPlayerVelocity = pedSpeed; + m_moved = CVector2D(-sin(walkAngle), cos(walkAngle)) * pedSpeed; + } + + CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_STANCE); + CAnimBlendAssociation* fightAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_FIGHT_IDLE); + + if ((!idleAssoc || idleAssoc->blendAmount < 0.5f) && !fightAssoc) { + LimbOrientation newUpperLegs; + newUpperLegs.phi = localWalkAngle; + + if (newUpperLegs.phi < -DEGTORAD(100.0f)) { + newUpperLegs.phi += PI; + } else if (newUpperLegs.phi > DEGTORAD(100.0f)) { + newUpperLegs.phi -= PI; + } + + if (newUpperLegs.phi > -DEGTORAD(50.0f) && newUpperLegs.phi < DEGTORAD(50.0f)) { + newUpperLegs.theta = 0.0f; + m_pedIK.RotateTorso(m_pFrames[PED_UPPERLEGL], &newUpperLegs, false); + m_pedIK.RotateTorso(m_pFrames[PED_UPPERLEGR], &newUpperLegs, false); + } + } +} + +bool +CPed::CanBeDeleted(void) +{ + if (this->bInVehicle) + return false; + + switch (CharCreatedBy) { + case RANDOM_CHAR: + return true; + case MISSION_CHAR: + return false; + default: + return true; + } +} + +bool +CPed::CanPedDriveOff(void) +{ + if (m_nPedState != PED_DRIVING || m_lookTimer > CTimer::GetTimeInMilliseconds()) + return false; + + for (int i = 0; i < m_numNearPeds; i++) { + CPed *ped = m_nearPeds[i]; + if (ped->m_nPedType == m_nPedType && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && ped->m_carInObjective == m_carInObjective) { + m_lookTimer = CTimer::GetTimeInMilliseconds() + 1000; + return false; + } + } + return true; +} + +// I couldn't find where it is used. +bool +CPed::CanPedJumpThis(int32 unused) +{ + CVector2D forward(-sin(m_fRotationCur), cos(m_fRotationCur)); + CVector pos = GetPosition(); + // wat? + CVector forwardPos( + forward.x + pos.x, + forward.y + pos.y, + pos.z); + return CWorld::GetIsLineOfSightClear(pos, forwardPos, true, false, false, true, false, false, false); +} + +bool +CPed::CanPedReturnToState(void) +{ + return m_nPedState <= PED_STATES_NO_AI && m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK && + m_nPedState != PED_FIGHT && m_nPedState != PED_STEP_AWAY && m_nPedState != PED_SNIPER_MODE && m_nPedState != PED_LOOK_ENTITY; +} + +bool +CPed::CanSeeEntity(CEntity *entity, float threshold) +{ + float neededAngle = CGeneral::GetRadianAngleBetweenPoints( + entity->GetPosition().x, + entity->GetPosition().x, + GetPosition().x, + GetPosition().y); + + if (neededAngle < 0.0f) + neededAngle += 2 * PI; + else if (neededAngle > 2 * PI) + neededAngle -= 2 * PI; + + float ourAngle = m_fRotationCur; + if (ourAngle < 0.0f) + ourAngle += 2 * PI; + else if (ourAngle > 2 * PI) + ourAngle -= 2 * PI; + + float neededTurn = fabs(neededAngle - ourAngle); + + return neededTurn < threshold || 2 * PI - threshold < neededTurn; +} + +bool +CPed::IsTemporaryObjective(eObjective objective) +{ + return objective == OBJECTIVE_LEAVE_VEHICLE || objective == OBJECTIVE_SET_LEADER || + objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER; +} + +void +CPed::SetMoveState(eMoveState state) +{ + m_nMoveState = state; +} + +void +CPed::SetObjectiveTimer(int time) +{ + if (time == 0) { + m_objectiveTimer = 0; + } else if (CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +void +CPed::ForceStoredObjective(eObjective objective) +{ + if (objective != OBJECTIVE_ENTER_CAR_AS_DRIVER && objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + m_prevObjective = m_objective; + return; + } + + switch (m_objective) + { + case OBJECTIVE_FLEE_TILL_SAFE: + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + return; + default: + m_prevObjective = m_objective; + } +} + +void +CPed::SetStoredObjective(void) +{ + if (m_objective == m_prevObjective) + return; + + switch (m_objective) + { + case OBJECTIVE_FLEE_TILL_SAFE: + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_FOLLOW_PED_IN_FORMATION: + case OBJECTIVE_LEAVE_VEHICLE: + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + return; + default: + m_prevObjective = m_objective; + } +} + +void +CPed::RestorePreviousObjective(void) +{ + if (m_objective == OBJECTIVE_NONE) + return; + + if (m_objective != OBJECTIVE_LEAVE_VEHICLE && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) + m_pedInObjective = nil; + + if (m_objective == OBJECTIVE_WAIT_IN_CAR_THEN_GETOUT) { + m_objective = OBJECTIVE_NONE; + if (m_pMyVehicle) + SetObjective(OBJECTIVE_LEAVE_VEHICLE, m_pMyVehicle); + + } else { + m_objective = m_prevObjective; + m_prevObjective = OBJECTIVE_NONE; + } + m_ped_flagD40 = false; +} + +void +CPed::SetLeader(CEntity *leader) +{ + m_leader = (CPed*)leader; + + if(m_leader) + m_leader->RegisterReference((CEntity **)&m_leader); +} + +void +CPed::SetObjective(eObjective newObj, void *entity) +{ + if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) + return; + + if (m_prevObjective == newObj) { + // Why? + if (m_prevObjective != OBJECTIVE_NONE) + return; + } + + if (entity == this) + return; + + SetObjectiveTimer(0); + if (m_objective == newObj) { + switch (newObj) { + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_FOLLOW_PED_IN_FORMATION: + case OBJECTIVE_GOTO_AREA_ANY_MEANS: + case OBJECTIVE_FIGHT_CHAR: + if (m_pedInObjective == entity) + return; + + break; + case OBJECTIVE_LEAVE_VEHICLE: + case OBJECTIVE_FLEE_CAR: + return; + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_DESTROY_CAR: + case OBJECTIVE_SOLICIT: + case OBJECTIVE_BUY_ICE_CREAM: + if (m_carInObjective == entity) + return; + + break; + case OBJECTIVE_SET_LEADER: + if (m_leader == entity) + return; + + break; + default: + break; + } + } else { + if (newObj == OBJECTIVE_LEAVE_VEHICLE && !bInVehicle) + return; + } + + m_ped_flagD40 = false; + if (!IsTemporaryObjective(m_objective) || IsTemporaryObjective(newObj)) { + if (m_objective != newObj) { + if (IsTemporaryObjective(newObj)) + ForceStoredObjective(newObj); + else + SetStoredObjective(); + } + m_objective = newObj; + } else { + m_prevObjective = newObj; + } + + switch (newObj) { + case OBJECTIVE_WAIT_IN_CAR_THEN_GETOUT: + + // In this special case, entity parameter isn't CEntity, but int. + SetObjectiveTimer((int)entity); + return; + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_MUG_CHAR: + m_pLastPathNode = nil; + m_ped_flagD20 = false; + m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + m_pLookTarget = (CEntity*)entity; + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + return; + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_FIGHT_CHAR: + m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + return; + case OBJECTIVE_FOLLOW_PED_IN_FORMATION: + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + m_pedFormation = 1; + return; + case OBJECTIVE_LEAVE_VEHICLE: + case OBJECTIVE_FLEE_CAR: + m_carInObjective = (CVehicle*)entity; + m_carInObjective->RegisterReference((CEntity **)&m_carInObjective); + if (!m_carInObjective->bIsBus || m_leaveCarTimer) + return; + break; + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + if (m_nMoveState == PEDMOVE_STILL) + SetMoveState(PEDMOVE_RUN); + + if (((CVehicle*)entity)->m_vehType == VEHICLE_TYPE_BOAT && !IsPlayer()) { + RestorePreviousObjective(); + return; + } + // fall through + case OBJECTIVE_DESTROY_CAR: + case OBJECTIVE_SOLICIT: + case OBJECTIVE_BUY_ICE_CREAM: + m_carInObjective = (CVehicle*)entity; + m_carInObjective->RegisterReference((CEntity**)&m_carInObjective); + m_pSeekTarget = m_carInObjective; + m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); + m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); + if (newObj == OBJECTIVE_SOLICIT) { + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; + } else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == MISSION_CHAR && + (m_carInObjective->m_status == STATUS_PLAYER_DISABLED || CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls)) { + SetObjectiveTimer(14000); + } else { + m_objectiveTimer = 0; + } + return; + case OBJECTIVE_SET_LEADER: + SetLeader((CEntity*)entity); + RestorePreviousObjective(); + return; + default: + return; + } + for (int i=0; i < m_carInObjective->m_nNumMaxPassengers; i++) { + if (m_carInObjective->pPassengers[i] == this) { + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 1200 * i; + return; + } + } +} + +void +CPed::SetIdle(void) +{ + if (m_nPedState != PED_IDLE && m_nPedState != PED_MUG && m_nPedState != PED_FLEE_ENTITY) { + m_nPedState = PED_IDLE; + SetMoveState(PEDMOVE_STILL); + } + if (m_nWaitState == WAITSTATE_FALSE) { + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000, 4000); + } +} + +void +CPed::SetObjective(eObjective newObj) +{ + if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) + return; + + if (newObj == OBJECTIVE_NONE) { + if ((m_objective == OBJECTIVE_LEAVE_VEHICLE || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) + && IsPedInControl()) { + + m_ped_flagG8 = true; + return; + } + // Unused code from assembly... + /* + else if(m_objective == OBJECTIVE_FLEE_CAR) { + + } else { + + } + */ + m_objective = newObj; + m_prevObjective = OBJECTIVE_NONE; + } else if (m_prevObjective != newObj || m_prevObjective == OBJECTIVE_NONE) { + SetObjectiveTimer(0); + + if (m_objective == newObj) + return; + + if (IsTemporaryObjective(m_objective)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) + SetStoredObjective(); + + m_objective = newObj; + } + m_ped_flagD40 = false; + + switch (newObj) { + case OBJECTIVE_NONE: + m_prevObjective = OBJECTIVE_NONE; + break; + case OBJECTIVE_HAIL_TAXI: + m_nWaitTimer = 0; + SetIdle(); + SetMoveState(PEDMOVE_STILL); + break; + default: + break; + } + } +} + +// Only used in 01E1: SET_CHAR_OBJ_FOLLOW_ROUTE opcode +// IDA fails very badly in here, puts a fake loop and ignores SetFollowRoute call... +void +CPed::SetObjective(eObjective newObj, int16 routePoint, int16 routeType) +{ + if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) + return; + + if (m_prevObjective == newObj && m_prevObjective != OBJECTIVE_NONE) + return; + + SetObjectiveTimer(0); + + if (m_objective == newObj && newObj == OBJECTIVE_FOLLOW_ROUTE && m_routeLastPoint == routePoint && m_routeType == routeType) + return; + + m_ped_flagD40 = false; + if (IsTemporaryObjective(m_objective)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) + SetStoredObjective(); + + m_objective = newObj; + } + + if (newObj == OBJECTIVE_FOLLOW_ROUTE) { + SetFollowRoute(routePoint, routeType); + } +} + +void +CPed::ClearChat(void) +{ + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_CHAT); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + bIsTalking = false; + ClearLookFlag(); + RestorePreviousState(); +} + +bool +CPed::IsGangMember(void) +{ + return m_nPedType >= PEDTYPE_GANG1 && m_nPedType <= PEDTYPE_GANG9; +} + +void +CPed::InformMyGangOfAttack(CEntity *attacker) +{ + CPed *attackerPed; + + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) + return; + + if (attacker->IsPed()) { + attackerPed = (CPed*)attacker; + } else { + if (!attacker->IsVehicle()) + return; + + attackerPed = ((CVehicle*)attacker)->pDriver; + if (!attackerPed) + return; + } + + if (attackerPed->m_nPedType == PEDTYPE_COP) + return; + + for (int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed && nearPed != this) { + CPed *leader = nearPed->m_leader; + if (leader && leader == this && nearPed->m_pedStats->m_fear < nearPed->m_pedStats->m_temper) + { + nearPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attackerPed); + nearPed->SetObjectiveTimer(30000); + } + } + } +} + +void +CPed::QuitEnteringCar(void) +{ + CAnimBlendAssociation *animAssoc = m_pVehicleAnim; + CVehicle *veh = m_pMyVehicle; + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + RestartNonPartialAnims(); + + if (!RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_STANCE)) + CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE, 100.0f); + + if (veh) { + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_nPedState == PED_CARJACK) + veh->m_veh_flagC10 = false; + + if (veh->m_nNumGettingIn != 0) + veh->m_nNumGettingIn--; + + veh->m_nGettingInFlags = GetVehEnterExitFlag(m_vehEnterType); + } + + bUsesCollision = true; + + if (IsPlayer() && GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) { + if (IsPlayer() && m_storedWeapon != NO_STORED_WEAPON) { + SetCurrentWeapon(m_storedWeapon); + m_storedWeapon = NO_STORED_WEAPON; + } + } else { + CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + AddWeaponModel(curWeapon->m_nModelId); + } + if (m_nPedState == PED_DIE || m_nPedState == PED_DEAD) { + animAssoc = m_pVehicleAnim; + if (animAssoc) { + animAssoc->blendDelta = -4.0; + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + animAssoc = m_pVehicleAnim; + animAssoc->flags &= ~ASSOC_RUNNING; + } + } else + SetIdle(); + + m_pVehicleAnim = nil; + + if (veh) { + if (veh->m_autoPilot.m_nCruiseSpeed == 0) + veh->m_autoPilot.m_nCruiseSpeed = 17; + } +} + +void +CPed::ReactToAttack(CEntity *attacker) +{ + if (IsPlayer() && attacker->IsPed()) { + InformMyGangOfAttack(attacker); + SetLookFlag(attacker, 1); + SetLookTimer(700); + return; + } + + if (IsPedInControl() && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats)) { + CPed *ourLeader = m_leader; + if (ourLeader != attacker && (!ourLeader || FindPlayerPed() != ourLeader) + && attacker->IsPed()) { + + CPed *attackerPed = (CPed*)attacker; + if (bNotAllowedToDuck) { + if (!attackerPed->GetWeapon()->IsTypeMelee()) { + field_4E8 = CTimer::GetTimeInMilliseconds(); + return; + } + } else if (bCrouchWhenShooting || m_ped_flagE1) { + SetDuck(CGeneral::GetRandomNumberInRange(1000,3000)); + return; + } + + if (m_pedStats->m_fear <= 100 - attackerPed->m_pedStats->m_temper) { + if (m_pedStats != attackerPed->m_pedStats) { + if (IsGangMember() || m_nPedType == PEDTYPE_EMERGENCY || m_nPedType == PEDTYPE_FIREMAN) { + RegisterThreatWithGangPeds(attackerPed); + } + if (!attackerPed->GetWeapon()->IsTypeMelee() && GetWeapon()->IsTypeMelee()) { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attacker); + SetMoveState(PEDMOVE_RUN); + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attacker); + SetObjectiveTimer(20000); + } + } + } else { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attackerPed); + SetMoveState(PEDMOVE_RUN); + if (attackerPed->GetWeapon()->IsTypeMelee()) + Say(SOUND_PED_FLEE_RUN); + } + } + } +} + +bool +CPed::TurnBody(void) +{ + float lookDir; + bool doneSmoothly = true; + + if (m_pLookTarget) { + CVector &lookPos = m_pLookTarget->GetPosition(); + + lookDir = CGeneral::GetRadianAngleBetweenPoints( + lookPos.x, + lookPos.y, + GetPosition().x, + GetPosition().y); + } else + lookDir = m_fLookDirection; + + float limitedLookDir = CGeneral::LimitRadianAngle(lookDir); + float currentRot = m_fRotationCur; + + if (currentRot - PI > limitedLookDir) + limitedLookDir += 2 * PI; + else if (PI + currentRot < limitedLookDir) + limitedLookDir -= 2 * PI; + + float neededTurn = currentRot - limitedLookDir; + m_fRotationDest = limitedLookDir; + + if (fabs(neededTurn) > 0.05f) { + doneSmoothly = false; + currentRot -= neededTurn * 0.2f; + } + + m_fRotationCur = currentRot; + m_fLookDirection = limitedLookDir; + return doneSmoothly; +} + +void +CPed::Chat(void) +{ + if (bIsLooking && TurnBody()) + ClearLookFlag(); + + if (!m_pLookTarget || !m_pLookTarget->IsPed()) { + ClearChat(); + return; + } + + CPed *partner = (CPed*) m_pLookTarget; + + if (partner->m_nPedState != PED_CHAT) { + ClearChat(); + if (partner->m_pedInObjective) { + if (partner->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || + partner->m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE) + ReactToAttack(partner->m_pedInObjective); + } + return; + } + if (bIsTalking) { + if (CGeneral::GetRandomNumber() < 512) { + CAnimBlendAssociation *chatAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_CHAT); + if (chatAssoc) { + chatAssoc->blendDelta = -4.0f; + chatAssoc->flags |= ASSOC_FADEOUTWHENDONE; + } + bIsTalking = false; + } else + Say(SOUND_PED_CHAT); + + } else if (!RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject, ASSOC_FLAG100)) { + + if (CGeneral::GetRandomNumber() < 20) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f); + } + if (!bIsTalking) { + CAnimBlendAssociation *chatAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_IDLE_CHAT, 4.0f); + float chatTime = CGeneral::GetRandomNumberInRange(0.0f, 3.0f); + chatAssoc->SetCurrentTime(chatTime); + + bIsTalking = true; + Say(SOUND_PED_CHAT); + } + } + if (m_standardTimer && CTimer::GetTimeInMilliseconds() > m_standardTimer) { + ClearChat(); + m_standardTimer = CTimer::GetTimeInMilliseconds() + 30000; + } +} + +WRAPPER void CPed::PedGetupCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE810); } +WRAPPER void CPed::PedStaggerCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8D0); } +WRAPPER void CPed::PedEvadeCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D36E0); } +WRAPPER void CPed::FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D3950); } +WRAPPER void CPed::FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D6520); } +WRAPPER void CPed::FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7490); } +WRAPPER void CPed::FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A80); } +WRAPPER void CPed::PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEC80); } +WRAPPER void CPed::PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DE500); } +WRAPPER void CPed::PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DEAF0); } +WRAPPER void CPed::PedAnimDoorCloseCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DF1B0); } +WRAPPER void CPed::SetInCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CF220); } +WRAPPER void CPed::PedSetOutCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8F0); } +WRAPPER void CPed::PedAnimAlignCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DE130); } +WRAPPER void CPed::PedAnimStepOutCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DF5C0); } +WRAPPER void CPed::PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E3290); } +WRAPPER void CPed::PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E36E0); } +WRAPPER void CPed::FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E9830); } +WRAPPER void CPed::PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E4B90); } +WRAPPER void CPed::FinishJumpCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D7A50); } +WRAPPER void CPed::PedLandCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8A0); } +WRAPPER void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4C6580); } +WRAPPER void CPed::RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D6550); } + +STARTPATCHES + InjectHook(0x4C41C0, &CPed::ctor, PATCH_JUMP); + InjectHook(0x4C50D0, &CPed::dtor, PATCH_JUMP); + InjectHook(0x4CF8F0, &CPed::AddWeaponModel, PATCH_JUMP); + InjectHook(0x4C6AA0, &CPed::AimGun, PATCH_JUMP); + InjectHook(0x4EB470, &CPed::ApplyHeadShot, PATCH_JUMP); + InjectHook(0x4EAEE0, &CPed::RemoveBodyPart, PATCH_JUMP); + InjectHook(0x4C6460, (void (CPed::*)(CEntity*, bool)) &CPed::SetLookFlag, PATCH_JUMP); + InjectHook(0x4C63E0, (void (CPed::*)(float, bool)) &CPed::SetLookFlag, PATCH_JUMP); + InjectHook(0x4D12E0, &CPed::SetLookTimer, PATCH_JUMP); + InjectHook(0x4C5700, &CPed::OurPedCanSeeThisOne, PATCH_JUMP); + InjectHook(0x4D2BB0, &CPed::Avoid, PATCH_JUMP); + InjectHook(0x4C6A50, &CPed::ClearAimFlag, PATCH_JUMP); + InjectHook(0x4C64F0, &CPed::ClearLookFlag, PATCH_JUMP); + InjectHook(0x4EB670, &CPed::IsPedHeadAbovePos, PATCH_JUMP); + InjectHook(0x4E68A0, &CPed::FinishedAttackCB, PATCH_JUMP); + InjectHook(0x4E5BD0, &CheckForPedsOnGroundToAttack, PATCH_JUMP); + InjectHook(0x4E6BA0, &CPed::Attack, PATCH_JUMP); + InjectHook(0x4CF980, &CPed::RemoveWeaponModel, PATCH_JUMP); + InjectHook(0x4CFA60, &CPed::SetCurrentWeapon, PATCH_JUMP); + InjectHook(0x4E4A10, &CPed::Duck, PATCH_JUMP); + InjectHook(0x4E4A30, &CPed::ClearDuck, PATCH_JUMP); + InjectHook(0x4E6180, &CPed::ClearPointGunAt, PATCH_JUMP); + InjectHook(0x4E07D0, &CPed::BeingDraggedFromCar, PATCH_JUMP); + InjectHook(0x4CF000, &CPed::PedSetDraggedOutCarCB, PATCH_JUMP); + InjectHook(0x4C5D80, &CPed::RestartNonPartialAnims, PATCH_JUMP); + InjectHook(0x4E4730, &CPed::GetLocalPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4E4660, (void (*)(CVector*, CVehicle*, uint32, float)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4E1A30, (void (*)(CVector*, CVehicle*, uint32)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4DF940, &CPed::LineUpPedWithCar, PATCH_JUMP); + InjectHook(0x4CC6C0, &CPed::PlayFootSteps, PATCH_JUMP); + InjectHook(0x4C5350, &CPed::BuildPedLists, PATCH_JUMP); + InjectHook(0x4CF9B0, &CPed::GiveWeapon, PATCH_JUMP); + InjectHook(0x4C52A0, &CPed::SetModelIndex_, PATCH_JUMP); + InjectHook(0x4D6570, &CPed::FlagToDestroyWhenNextProcessed_, PATCH_JUMP); + InjectHook(0x4A7D30, &CPed::SetupLighting_, PATCH_JUMP); + InjectHook(0x4A7DC0, &CPed::RemoveLighting_, PATCH_JUMP); + InjectHook(0x4D3E70, &CPed::Teleport_, PATCH_JUMP); + InjectHook(0x4C7EA0, &CPed::CalculateNewOrientation, PATCH_JUMP); + InjectHook(0x4C78F0, &CPed::WorkOutHeadingForMovingFirstPerson, PATCH_JUMP); + InjectHook(0x4C73F0, &CPed::CalculateNewVelocity, PATCH_JUMP); + InjectHook(0x4D72F0, &CPed::CanPedJumpThis, PATCH_JUMP); + InjectHook(0x4DD820, &CPed::CanSeeEntity, PATCH_JUMP); + InjectHook(0x4D9460, &CPed::RestorePreviousObjective, PATCH_JUMP); + InjectHook(0x4D82C0, (void (CPed::*)(eObjective)) &CPed::SetObjective, PATCH_JUMP); + InjectHook(0x4D83E0, (void (CPed::*)(eObjective, void*)) &CPed::SetObjective, PATCH_JUMP); + InjectHook(0x4D89A0, (void (CPed::*)(eObjective, int16, int16)) &CPed::SetObjective, PATCH_JUMP); + InjectHook(0x4DDEC0, &CPed::ReactToAttack, PATCH_JUMP); + InjectHook(0x4D0600, &CPed::SetIdle, PATCH_JUMP); + InjectHook(0x4E0E00, &CPed::QuitEnteringCar, PATCH_JUMP); + InjectHook(0x4E4AD0, &CPed::InformMyGangOfAttack, PATCH_JUMP); + InjectHook(0x4D3C80, &CPed::ClearChat, PATCH_JUMP); + InjectHook(0x4D1390, &CPed::TurnBody, PATCH_JUMP); + InjectHook(0x4D3AC0, &CPed::Chat, PATCH_JUMP); +ENDPATCHES diff --git a/src/peds/Ped.h b/src/peds/Ped.h new file mode 100644 index 00000000..95731e15 --- /dev/null +++ b/src/peds/Ped.h @@ -0,0 +1,577 @@ +#pragma once + +#include "Physical.h" +#include "Weapon.h" +#include "PedStats.h" +#include "PedType.h" +#include "PedIK.h" +#include "AnimManager.h" +#include "AnimBlendClumpData.h" +#include "AnimBlendAssociation.h" +#include "WeaponInfo.h" +#include "Fire.h" + +struct CPathNode; + +enum eWaitState : uint32 { + WAITSTATE_FALSE, + WAITSTATE_TRAFFIC_LIGHTS, + WAITSTATE_CROSS_ROAD, + WAITSTATE_CROSS_ROAD_LOOK, + WAITSTATE_LOOK_PED, + WAITSTATE_LOOK_SHOP, + WAITSTATE_LOOK_ACCIDENT, + WAITSTATE_FACEOFF_GANG, + WAITSTATE_DOUBLEBACK, + WAITSTATE_HITWALL, + WAITSTATE_TURN180, + WAITSTATE_SURPRISE, + WAITSTATE_STUCK, + WAITSTATE_LOOK_ABOUT, + WAITSTATE_PLAYANIM_DUCK, + WAITSTATE_PLAYANIM_COWER, + WAITSTATE_PLAYANIM_TAXI, + WAITSTATE_PLAYANIM_HANDSUP, + WAITSTATE_PLAYANIM_HANDSCOWER, + WAITSTATE_PLAYANIM_CHAT, + WAITSTATE_FINISH_FLEE +}; + +enum eObjective : uint32 { + OBJECTIVE_NONE, + OBJECTIVE_IDLE, + OBJECTIVE_FLEE_TILL_SAFE, + OBJECTIVE_GUARD_SPOT, + OBJECTIVE_GUARD_AREA, + OBJECTIVE_WAIT_IN_CAR, + OBJECTIVE_WAIT_IN_CAR_THEN_GETOUT, + OBJECTIVE_KILL_CHAR_ON_FOOT, + OBJECTIVE_KILL_CHAR_ANY_MEANS, + OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, + OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, + OBJECTIVE_GOTO_CHAR_ON_FOOT, + OBJECTIVE_FOLLOW_PED_IN_FORMATION, + OBJECTIVE_LEAVE_VEHICLE, + OBJECTIVE_ENTER_CAR_AS_PASSENGER, + OBJECTIVE_ENTER_CAR_AS_DRIVER, + OBJECTIVE_FOLLOW_CAR_IN_CAR, + OBJECTIVE_FIRE_AT_OBJ_FROM_VEHICLE, + OBJECTIVE_DESTROY_OBJ, + OBJECTIVE_DESTROY_CAR, + OBJECTIVE_GOTO_AREA_ANY_MEANS, + OBJECTIVE_GOTO_AREA_ON_FOOT, + OBJECTIVE_RUN_TO_AREA, + OBJECTIVE_23, + OBJECTIVE_24, + OBJECTIVE_FIGHT_CHAR, + OBJECTIVE_SET_LEADER, + OBJECTIVE_FOLLOW_ROUTE, + OBJECTIVE_SOLICIT, + OBJECTIVE_HAIL_TAXI, + OBJECTIVE_CATCH_TRAIN, + OBJECTIVE_BUY_ICE_CREAM, + OBJECTIVE_STEAL_ANY_CAR, + OBJECTIVE_MUG_CHAR, + OBJECTIVE_FLEE_CAR, + OBJECTIVE_35 +}; + +enum eVehEnter : uint16 { + VEHICLE_ENTER_FRONT_RIGHT = 11, + VEHICLE_ENTER_REAR_RIGHT = 12, + VEHICLE_ENTER_FRONT_LEFT = 15, + VEHICLE_ENTER_REAR_LEFT = 16, +}; + +enum { + RANDOM_CHAR = 1, + MISSION_CHAR, +}; + +enum PedLineUpPhase { + LINE_UP_TO_CAR_START, + LINE_UP_TO_CAR_END, + LINE_UP_TO_CAR_2 +}; + +enum PedOnGroundState { + NO_PED, + PED_BELOW_PLAYER, + PED_ON_THE_FLOOR, + PED_DEAD_ON_THE_FLOOR +}; + +enum PedState +{ + PED_NONE, + PED_IDLE, + PED_LOOK_ENTITY, + PED_LOOK_HEADING, + PED_WANDER_RANGE, + PED_WANDER_PATH, + PED_SEEK_POS, + PED_SEEK_ENTITY, + PED_FLEE_POS, + PED_FLEE_ENTITY, + PED_PURSUE, + PED_FOLLOW_PATH, + PED_SNIPER_MODE, + PED_ROCKET_ODE, + PED_DUMMY, + PED_PAUSE, + PED_ATTACK, + PED_FIGHT, + PED_FACE_PHONE, + PED_MAKE_CALL, + PED_CHAT, + PED_MUG, + PED_AIM_GUN, + PED_AI_CONTROL, + PED_SEEK_CAR, + PED_SEEK_IN_BOAT, + PED_FOLLOW_ROUTE, + PED_CPR, + PED_SOLICIT, + PED_BUY_ICECREAM, + PED_INVESTIGATE, + PED_STEP_AWAY, + PED_ON_FIRE, + + PED_UNKNOWN, // HANG_OUT in Fire_Head's idb + + PED_STATES_NO_AI, + PED_JUMP, + PED_FALL, + PED_GETUP, + PED_STAGGER, + PED_DIVE_AWAY, + + PED_STATES_NO_ST, + PED_ENTER_TRAIN, + PED_EXIT_TRAIN, + PED_ARREST_PLAYER, + PED_DRIVING, + PED_PASSENGER, + PED_TAXI_PASSENGER, + PED_OPEN_DOOR, + PED_DIE, + PED_DEAD, + PED_CARJACK, + PED_DRAG_FROM_CAR, + PED_ENTER_CAR, + PED_STEAL_CAR, + PED_EXIT_CAR, + PED_HANDS_UP, + PED_ARRESTED, +}; + +enum eMoveState { + PEDMOVE_NONE, + PEDMOVE_STILL, + PEDMOVE_WALK, + PEDMOVE_RUN, + PEDMOVE_SPRINT, +}; + +class CVehicle; + +class CPed : public CPhysical +{ +public: + // 0x128 + CStoredCollPoly m_collPoly; + float m_fCollisionSpeed; + + // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPed.h from R* + uint8 bIsStanding : 1; + uint8 m_ped_flagA2 : 1; + uint8 m_ped_flagA4 : 1; // stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime) + uint8 bIsPointingGunAt : 1; + uint8 bIsLooking : 1; + uint8 m_ped_flagA20 : 1; // "look" method? - probably missing in SA + uint8 bIsRestoringLook : 1; + uint8 bIsAimingGun : 1; + + uint8 bIsRestoringGun : 1; + uint8 bCanPointGunAtTarget : 1; + uint8 bIsTalking : 1; + uint8 bIsInTheAir : 1; + uint8 bIsLanding : 1; + uint8 m_ped_flagB20 : 1; + uint8 m_ped_flagB40 : 1; + uint8 m_ped_flagB80 : 1; + + uint8 m_ped_flagC1 : 1; + uint8 bRespondsToThreats : 1; + uint8 m_ped_flagC4 : 1; // false when in bus, bRenderPedInCar? + uint8 m_ped_flagC8 : 1; + uint8 m_ped_flagC10 : 1; + uint8 m_ped_flagC20 : 1; // just left some body part? + uint8 m_ped_flagC40 : 1; + uint8 m_ped_flagC80 : 1; + + uint8 m_ped_flagD1 : 1; + uint8 m_ped_flagD2 : 1; + uint8 m_ped_flagD4 : 1; + uint8 m_ped_flagD8 : 1; + uint8 m_ped_flagD10 : 1; + uint8 m_ped_flagD20 : 1; + uint8 m_ped_flagD40 : 1; // reset when objective changes + uint8 m_ped_flagD80 : 1; + + uint8 m_ped_flagE1 : 1; + uint8 m_ped_flagE2 : 1; + uint8 bNotAllowedToDuck : 1; + uint8 bCrouchWhenShooting : 1; + uint8 bIsDucking : 1; // set if you don't want ped to attack + uint8 m_ped_flagE20 : 1; + uint8 bDoBloodyFootprints : 1; + uint8 m_ped_flagE80 : 1; + + uint8 m_ped_flagF1 : 1; + uint8 m_ped_flagF2 : 1; + uint8 m_ped_flagF4 : 1; + uint8 m_ped_flagF8 : 1; + uint8 m_ped_flagF10 : 1; + uint8 m_ped_flagF20 : 1; + uint8 m_ped_flagF40 : 1; + uint8 m_ped_flagF80 : 1; + + uint8 m_ped_flagG1 : 1; + uint8 m_ped_flagG2 : 1; + uint8 m_ped_flagG4 : 1; + uint8 m_ped_flagG8 : 1; + uint8 m_ped_flagG10 : 1; + uint8 m_ped_flagG20 : 1; + uint8 m_ped_flagG40 : 1; + uint8 m_ped_flagG80 : 1; + + uint8 m_ped_flagH1 : 1; + uint8 m_ped_flagH2 : 1; + uint8 m_ped_flagH4 : 1; + uint8 m_ped_flagH8 : 1; + uint8 m_ped_flagH10 : 1; + uint8 m_ped_flagH20 : 1; + uint8 m_ped_flagH40 : 1; + uint8 m_ped_flagH80 : 1; + + uint8 m_ped_flagI1 : 1; + uint8 m_ped_flagI2 : 1; + uint8 m_ped_flagI4 : 1; + uint8 bRecordedForReplay : 1; + uint8 m_ped_flagI10 : 1; + uint8 m_ped_flagI20 : 1; + uint8 m_ped_flagI40 : 1; + uint8 m_ped_flagI80 : 1; + + uint8 stuff10[3]; + uint8 CharCreatedBy; + uint8 field_161; + uint8 pad_162[2]; + eObjective m_objective; + eObjective m_prevObjective; + CPed *m_pedInObjective; + CVehicle *m_carInObjective; + uint32 field_174; + uint32 field_178; + uint32 field_17C; + CPed *m_leader; + uint32 m_pedFormation; + uint32 m_fearFlags; + CEntity *m_threatEntity; + CVector2D m_eventOrThread; + uint32 m_eventType; + CEntity* m_pEventEntity; + float m_fAngleToEvent; + AnimBlendFrameData *m_pFrames[PED_NODE_MAX]; + AssocGroupId m_animGroup; + CAnimBlendAssociation *m_pVehicleAnim; + CVector2D m_vecAnimMoveDelta; + CVector m_vecOffsetSeek; + CPedIK m_pedIK; + float m_actionX; + float m_actionY; + uint32 m_nPedStateTimer; + PedState m_nPedState; + PedState m_nLastPedState; + eMoveState m_nMoveState; + int32 m_nStoredActionState; + int32 m_nPrevActionState; + eWaitState m_nWaitState; + uint32 m_nWaitTimer; + void *m_pPathNodesStates[8]; + CVector2D m_stPathNodeStates[10]; + uint16 m_nPathNodes; + uint8 m_nCurPathNode; + int8 m_nPathState; +private: + int8 _pad2B5[3]; +public: + CPathNode *m_pNextPathNode; + CPathNode *m_pLastPathNode; + float m_fHealth; + float m_fArmour; + int16 m_routeLastPoint; + uint16 m_routePoints; + int16 m_routePos; + int16 m_routeType; + int16 m_routeCurDir; + uint16 field_2D2; + CVector2D m_moved; + float m_fRotationCur; + float m_fRotationDest; + float m_headingRate; + eVehEnter m_vehEnterType; + uint16 m_walkAroundType; + CEntity *m_pCurrentPhysSurface; + CVector m_vecOffsetFromPhysSurface; + CEntity *m_pCurSurface; + CVector m_vecSeekVehicle; + CEntity *m_pSeekTarget; + CVehicle *m_pMyVehicle; + bool bInVehicle; + uint8 pad_315[3]; + uint32 field_318; + uint8 field_31C; + uint8 field_31D; + int16 m_phoneId; + uint32 m_lookingForPhone; + uint32 m_phoneTalkTimer; + void *m_lastAccident; + int32 m_nPedType; + CPedStats *m_pedStats; + float m_fleeFromPosX; + float m_fleeFromPosY; + CEntity *m_fleeFrom; + uint32 m_fleeTimer; + uint32 field_344; + uint32 m_lastThreatTimer; + CEntity *m_pCollidingEntity; + uint8 m_stateUnused; + uint8 pad_351[3]; + uint32 m_timerUnused; + CEntity *m_targetUnused; + CWeapon m_weapons[NUM_PED_WEAPONTYPES]; + eWeaponType m_storedWeapon; + uint8 m_currentWeapon; // eWeaponType + uint8 m_maxWeaponTypeAllowed; // eWeaponType + uint8 m_wepSkills; + uint8 m_wepAccuracy; + CEntity *m_pPointGunAt; + CVector m_vecHitLastPos; + uint32 m_lastHitState; + uint8 m_fightFlags1; + uint8 m_fightFlags2; + uint8 pad_4B2[2]; + CFire* m_pFire; + CEntity *m_pLookTarget; + float m_fLookDirection; + int32 m_wepModelID; + uint32 m_leaveCarTimer; + uint32 m_getUpTimer; + uint32 m_lookTimer; + uint32 m_standardTimer; + uint32 m_attackTimer; + uint32 m_lastHitTime; + uint32 m_hitRecoverTimer; + uint32 m_objectiveTimer; + uint32 m_duckTimer; + uint32 field_4E8; + int32 m_bloodyFootprintCount; + uint8 stuff9[2]; + int8 m_bodyPartBleeding; // PedNode + uint8 m_field_4F3; + CPed *m_nearPeds[10]; + uint16 m_numNearPeds; + int8 m_lastWepDam; + uint8 pad_51F; + uint8 field_520; + uint8 pad_521[3]; + uint32 m_talkTimer; + uint16 m_talkTypeLast; + uint16 m_talkType; + CVector m_vecSeekPosEx; + float m_seekExAngle; + + static void *operator new(size_t); + static void *operator new(size_t, int); + static void operator delete(void*, size_t); + static void operator delete(void*, int); + + CPed(uint32 pedType); + virtual ~CPed(void); + + virtual void SetModelIndex(uint32 mi); + virtual void ProcessControl(void); + virtual void Teleport(CVector); + virtual void PreRender(void); + virtual void Render(void); + virtual bool SetupLighting(void); + virtual void RemoveLighting(bool); + virtual void FlagToDestroyWhenNextProcessed(void); + virtual int32 ProcessEntityCollision(CEntity*, CColPoint*); + virtual void SetMoveAnim(void); + + CPed* ctor(uint32 pedType) { return ::new (this) CPed(pedType); } + void dtor(void) { this->CPed::~CPed(); } + + void AddWeaponModel(int id); + void AimGun(void); + void KillPedWithCar(CVehicle *veh, float impulse); + void Say(uint16 audio); + void SetLookFlag(CEntity *target, bool unknown); + void SetLookFlag(float direction, bool unknown); + void SetLookTimer(int time); + void SetDie(AnimationId anim, float arg1, float arg2); + void ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer); + void RemoveBodyPart(PedNode nodeId, int8 unknown); + void SpawnFlyingComponent(int, int8 unknown); + bool OurPedCanSeeThisOne(CEntity *target); + void Avoid(void); + void Attack(void); + void ClearAimFlag(void); + void ClearLookFlag(void); + void RestorePreviousState(void); + void ClearAttack(void); + bool IsPedHeadAbovePos(float zOffset); + void RemoveWeaponModel(int modelId); + void SetCurrentWeapon(uint32 weaponType); + void Duck(void); + void ClearDuck(void); + void ClearPointGunAt(void); + void BeingDraggedFromCar(void); + void RestartNonPartialAnims(void); + void LineUpPedWithCar(PedLineUpPhase phase); + void SetPedPositionInCar(void); + void PlayFootSteps(void); + void QuitEnteringCar(void); + void BuildPedLists(void); + uint32 GiveWeapon(eWeaponType weaponType, uint32 ammo); + void CalculateNewOrientation(void); + float WorkOutHeadingForMovingFirstPerson(float); + void CalculateNewVelocity(void); + bool CanPedJumpThis(int32); + bool CanSeeEntity(CEntity*, float); + void RestorePreviousObjective(void); + void SetIdle(void); + void SetObjective(eObjective, void*); + void SetObjective(eObjective); + void SetObjective(eObjective, int16, int16); + void ClearChat(void); + void InformMyGangOfAttack(CEntity*); + void SetFollowRoute(int16, int16); + void ReactToAttack(CEntity*); + void SetDuck(uint32); + void RegisterThreatWithGangPeds(CEntity*); + bool TurnBody(void); + void Chat(void); + + // Static methods + static void GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float offset); + static void GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float seatPosMult); + static void GetPositionToOpenCarDoor(CVector* output, CVehicle* veh, uint32 enterType); + + // Callbacks + static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data); + static RwFrame *RecurseFrameChildrenVisibilityCB(RwFrame *frame, void *data); + static void PedGetupCB(CAnimBlendAssociation *assoc, void *arg); + static void PedStaggerCB(CAnimBlendAssociation *assoc, void *arg); + static void PedEvadeCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorCloseCB(CAnimBlendAssociation *assoc, void *arg); + static void SetInCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimAlignCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetDraggedOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimStepOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishedAttackCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishJumpCB(CAnimBlendAssociation *assoc, void *arg); + static void PedLandCB(CAnimBlendAssociation *assoc, void *arg); + static void RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); + + // functions that I see unnecessary to hook + bool IsPlayer(void); + bool UseGroundColModel(void); + bool CanSetPedState(void); + bool IsPedInControl(void); + bool CanPedDriveOff(void); + bool CanBeDeleted(void); + bool CanStrafeOrMouseControl(void); + bool CanPedReturnToState(void); + void SetMoveState(eMoveState); + bool IsTemporaryObjective(eObjective objective); + void SetObjectiveTimer(int); + bool SelectGunIfArmed(void); + bool IsPointerValid(void); + void SortPeds(CPed**, int, int); + void ForceStoredObjective(eObjective); + void SetStoredObjective(void); + void SetLeader(CEntity* leader); + void SetPedStats(ePedStats); + bool IsGangMember(void); + + bool HasWeapon(uint8 weaponType) { return m_weapons[weaponType].m_eWeaponType == weaponType; } + CWeapon &GetWeapon(uint8 weaponType) { return m_weapons[weaponType]; } + CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } + RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } + static uint8 GetVehEnterExitFlag(eVehEnter vehEnter) { + switch (vehEnter) { + case VEHICLE_ENTER_FRONT_RIGHT: + return 4; + case VEHICLE_ENTER_REAR_RIGHT: + return 8; + case VEHICLE_ENTER_FRONT_LEFT: + return 1; + case VEHICLE_ENTER_REAR_LEFT: + return 2; + default: + return 0; + } + } + PedState GetPedState(void) { return m_nPedState; } + void SetPedState(PedState state) { m_nPedState = state; } + + // to make patching virtual functions possible + void SetModelIndex_(uint32 mi) { CPed::SetModelIndex(mi); } + void FlagToDestroyWhenNextProcessed_(void) { CPed::FlagToDestroyWhenNextProcessed(); } + bool SetupLighting_(void) { return CPed::SetupLighting(); } + void RemoveLighting_(bool reset) { CPed::RemoveLighting(reset); } + void Teleport_(CVector pos) { CPed::Teleport(pos); } + + // set by 0482:set_threat_reaction_range_multiplier opcode + static uint16 &distanceMultToCountPedNear; + + static CVector &offsetToOpenRegularCarDoor; + static CVector &offsetToOpenLowCarDoor; + static CVector &offsetToOpenVanDoor; + static bool &bNastyLimbsCheat; + static bool &bPedCheat2; + static bool &bPedCheat3; +}; + +void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg); + +static_assert(offsetof(CPed, m_nPedState) == 0x224, "CPed: error"); +static_assert(offsetof(CPed, m_pCurSurface) == 0x2FC, "CPed: error"); +static_assert(offsetof(CPed, m_pMyVehicle) == 0x310, "CPed: error"); +static_assert(offsetof(CPed, m_nPedType) == 0x32C, "CPed: error"); +static_assert(offsetof(CPed, m_pCollidingEntity) == 0x34C, "CPed: error"); +static_assert(offsetof(CPed, m_weapons) == 0x35C, "CPed: error"); +static_assert(offsetof(CPed, m_currentWeapon) == 0x498, "CPed: error"); +static_assert(offsetof(CPed, m_lookTimer) == 0x4CC, "CPed: error"); +static_assert(offsetof(CPed, m_bodyPartBleeding) == 0x4F2, "CPed: error"); +static_assert(offsetof(CPed, m_pedInObjective) == 0x16C, "CPed: error"); +static_assert(offsetof(CPed, m_pEventEntity) == 0x19C, "CPed: error"); +static_assert(sizeof(CPed) == 0x53C, "CPed: error"); diff --git a/src/peds/PedIK.cpp b/src/peds/PedIK.cpp new file mode 100644 index 00000000..b9baf49c --- /dev/null +++ b/src/peds/PedIK.cpp @@ -0,0 +1,109 @@ +#include "common.h" +#include "patcher.h" +#include "PedIK.h" +#include "Ped.h" + +WRAPPER bool CPedIK::PointGunInDirection(float phi, float theta) { EAXJMP(0x4ED9B0); } +WRAPPER bool CPedIK::PointGunAtPosition(CVector *position) { EAXJMP(0x4ED920); } +WRAPPER void CPedIK::ExtractYawAndPitchLocal(RwMatrixTag*, float*, float*) { EAXJMP(0x4ED2C0); } +WRAPPER void CPedIK::ExtractYawAndPitchWorld(RwMatrixTag*, float*, float*) { EAXJMP(0x4ED140); } + +CPedIK::CPedIK(CPed *ped) +{ + m_ped = ped; + m_flags = 0; + m_headOrient.phi = 0.0f; + m_headOrient.theta = 0.0f; + m_torsoOrient.phi = 0.0f; + m_torsoOrient.theta = 0.0f; + m_upperArmOrient.phi = 0.0f; + m_upperArmOrient.theta = 0.0f; + m_lowerArmOrient.phi = 0.0f; + m_lowerArmOrient.theta = 0.0f; +} + +void +CPedIK::RotateTorso(AnimBlendFrameData *animBlend, LimbOrientation *limb, bool changeRoll) +{ + RwFrame *f = animBlend->frame; + RwMatrix *mat = CPedIK::GetWorldMatrix(RwFrameGetParent(f), RwMatrixCreate()); + + RwV3d upVector = { mat->right.z, mat->up.z, mat->at.z }; + RwV3d rightVector; + RwV3d pos = RwFrameGetMatrix(f)->pos; + + // rotation == 0 -> looking in y direction + // left? vector + float c = cos(m_ped->m_fRotationCur); + float s = sin(m_ped->m_fRotationCur); + rightVector.x = -(c*mat->right.x + s*mat->right.y); + rightVector.y = -(c*mat->up.x + s*mat->up.y); + rightVector.z = -(c*mat->at.x + s*mat->at.y); + + if(changeRoll){ + // Used when aiming only involves over the legs.(canAimWithArm) + // Automatically changes roll(forward rotation) axis of the parts above upper legs while moving, based on position of upper legs. + // Not noticeable in normal conditions... + + RwV3d forwardVector; + CVector inversedForward = CrossProduct(CVector(0.0f, 0.0f, 1.0f), mat->up); + inversedForward.Normalise(); + float dotProduct = DotProduct(mat->at, inversedForward); + if(dotProduct > 1.0f) dotProduct = 1.0f; + if(dotProduct < -1.0f) dotProduct = -1.0f; + float alpha = acos(dotProduct); + + if(mat->at.z < 0.0f) + alpha = -alpha; + + forwardVector.x = s * mat->right.x - c * mat->right.y; + forwardVector.y = s * mat->up.x - c * mat->up.y; + forwardVector.z = s * mat->at.x - c * mat->at.y; + + float curYaw, curPitch; + CPedIK::ExtractYawAndPitchWorld(mat, &curYaw, &curPitch); + RwMatrixRotate(RwFrameGetMatrix(f), &rightVector, RADTODEG(limb->theta), rwCOMBINEPOSTCONCAT); + RwMatrixRotate(RwFrameGetMatrix(f), &upVector, RADTODEG(limb->phi - (curYaw - m_ped->m_fRotationCur)), rwCOMBINEPOSTCONCAT); + RwMatrixRotate(RwFrameGetMatrix(f), &forwardVector, RADTODEG(alpha), rwCOMBINEPOSTCONCAT); + }else{ + // pitch + RwMatrixRotate(RwFrameGetMatrix(f), &rightVector, RADTODEG(limb->theta), rwCOMBINEPOSTCONCAT); + // yaw + RwMatrixRotate(RwFrameGetMatrix(f), &upVector, RADTODEG(limb->phi), rwCOMBINEPOSTCONCAT); + } + RwFrameGetMatrix(f)->pos = pos; + RwMatrixDestroy(mat); +} + +void +CPedIK::GetComponentPosition(RwV3d *pos, PedNode node) +{ + RwFrame *f; + RwMatrix *mat; + + f = m_ped->GetNodeFrame(node); + mat = RwFrameGetMatrix(f); + *pos = mat->pos; + + for (f = RwFrameGetParent(f); f; f = RwFrameGetParent(f)) + RwV3dTransformPoints(pos, pos, 1, RwFrameGetMatrix(f)); +} + +RwMatrix* +CPedIK::GetWorldMatrix(RwFrame *source, RwMatrix *destination) +{ + RwFrame *i; + + *destination = *RwFrameGetMatrix(source); + + for (i = RwFrameGetParent(source); i; i = RwFrameGetParent(i)) + RwMatrixTransform(destination, RwFrameGetMatrix(i), rwCOMBINEPOSTCONCAT); + + return destination; +} + +STARTPATCHES + InjectHook(0x4ED0F0, &CPedIK::GetComponentPosition, PATCH_JUMP); + InjectHook(0x4ED060, &CPedIK::GetWorldMatrix, PATCH_JUMP); + InjectHook(0x4EDDB0, &CPedIK::RotateTorso, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/peds/PedIK.h b/src/peds/PedIK.h new file mode 100644 index 00000000..e17d52eb --- /dev/null +++ b/src/peds/PedIK.h @@ -0,0 +1,40 @@ +#pragma once +#include "common.h" +#include "PedModelInfo.h" +#include "AnimBlendClumpData.h" + +struct LimbOrientation +{ + float phi; + float theta; +}; + +class CPed; + +class CPedIK +{ +public: + // TODO + enum { + FLAG_1 = 1, + FLAG_2 = 2, // related to looking somewhere + FLAG_4 = 4, // aims with arm + }; + + CPed *m_ped; + LimbOrientation m_headOrient; + LimbOrientation m_torsoOrient; + LimbOrientation m_upperArmOrient; + LimbOrientation m_lowerArmOrient; + int32 m_flags; + + CPedIK(CPed *ped); + bool PointGunInDirection(float phi, float theta); + bool PointGunAtPosition(CVector *position); + void GetComponentPosition(RwV3d *pos, PedNode node); + static RwMatrix *GetWorldMatrix(RwFrame *source, RwMatrix *destination); + void RotateTorso(AnimBlendFrameData* animBlend, LimbOrientation* limb, bool changeRoll); + void ExtractYawAndPitchLocal(RwMatrixTag*, float*, float*); + void ExtractYawAndPitchWorld(RwMatrixTag*, float*, float*); +}; +static_assert(sizeof(CPedIK) == 0x28, "CPedIK: error"); diff --git a/src/peds/PedPlacement.cpp b/src/peds/PedPlacement.cpp new file mode 100644 index 00000000..e9a3f7d9 --- /dev/null +++ b/src/peds/PedPlacement.cpp @@ -0,0 +1,40 @@ +#include "common.h" +#include "patcher.h" +#include "PedPlacement.h" +#include "World.h" + +void +CPedPlacement::FindZCoorForPed(CVector* pos) +{ + float zForPed; + float startZ = pos->z - 100.0f; + float foundColZ = -100.0f; + float foundColZ2 = -100.0f; + CColPoint foundCol; + CEntity* foundEnt; + + CVector vec( + pos->x, + pos->y, + pos->z + 1.0f + ); + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) + foundColZ = foundCol.point.z; + + // Adjust coords and do a second test + vec.x += 0.1f; + vec.y += 0.1f; + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) + foundColZ2 = foundCol.point.z; + + zForPed = max(foundColZ, foundColZ2); + + if (zForPed > -99.0f) + pos->z = 1.04f + zForPed; +} + +STARTPATCHES + InjectHook(0x4EE340, &CPedPlacement::FindZCoorForPed, PATCH_JUMP); +ENDPATCHES diff --git a/src/peds/PedPlacement.h b/src/peds/PedPlacement.h new file mode 100644 index 00000000..4bd48b62 --- /dev/null +++ b/src/peds/PedPlacement.h @@ -0,0 +1,8 @@ +#pragma once + +class CVector; + +class CPedPlacement { +public: + static void FindZCoorForPed(CVector* pos); +}; \ No newline at end of file diff --git a/src/peds/PedStats.cpp b/src/peds/PedStats.cpp new file mode 100644 index 00000000..f6508580 --- /dev/null +++ b/src/peds/PedStats.cpp @@ -0,0 +1,125 @@ +#include "common.h" +#include "patcher.h" +#include "FileMgr.h" +#include "PedStats.h" + +CPedStats *(&CPedStats::ms_apPedStats)[NUM_PEDSTATS] = *(CPedStats *(*)[NUM_PEDSTATS]) *(uintptr*)0x9404D4; + +void +CPedStats::Initialise(void) +{ + int i; + + debug("Initialising CPedStats...\n"); + for(i = 0; i < NUM_PEDSTATS; i++){ + ms_apPedStats[i] = new CPedStats; + ms_apPedStats[i]->m_type = PEDSTAT_PLAYER; + ms_apPedStats[i]->m_name[8] = 'R'; // WHAT? + ms_apPedStats[i]->m_fleeDistance = 20.0f; + ms_apPedStats[i]->m_headingChangeRate = 15.0f; + ms_apPedStats[i]->m_fear = 50; + ms_apPedStats[i]->m_temper = 50; + ms_apPedStats[i]->m_lawfulness = 50; + ms_apPedStats[i]->m_sexiness = 50; + ms_apPedStats[i]->m_attackStrength = 1.0f; + ms_apPedStats[i]->m_defendWeakness = 1.0f; + ms_apPedStats[i]->m_flags = 0; + } + debug("Loading pedstats data...\n"); + CPedStats::LoadPedStats(); + debug("CPedStats ready\n"); +} + +void +CPedStats::Shutdown(void) +{ + int i; + debug("Shutting down CPedStats...\n"); + for(i = 0; i < NUM_PEDSTATS; i++) + delete ms_apPedStats[i]; + debug("CPedStats shut down\n"); +} + +void +CPedStats::LoadPedStats(void) +{ + char *buf; + char line[256]; + char name[32]; + int bp, buflen; + int lp, linelen; + int type; + float fleeDist, headingChangeRate, attackStrength, defendWeakness; + int fear, temper, lawfullness, sexiness, flags; + + + type = 0; + buf = new char[16 * 1024]; + + CFileMgr::SetDir("DATA"); + buflen = CFileMgr::LoadFile("PEDSTATS.DAT", (uint8*)buf, 16 * 1024, "r"); + CFileMgr::SetDir(""); + + for(bp = 0; bp < buflen; ){ + // read file line by line + for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ + if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') + line[linelen++] = ' '; + else + line[linelen++] = buf[bp]; + line[linelen] = '\0'; + } + bp++; + + // skip white space + for(lp = 0; line[lp] <= ' '; lp++); + + if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines + line[lp] == '#') + continue; + + sscanf(&line[lp], "%s %f %f %d %d %d %d %f %f %d", + name, + &fleeDist, + &headingChangeRate, + &fear, + &temper, + &lawfullness, + &sexiness, + &attackStrength, + &defendWeakness, + &flags); + ms_apPedStats[type]->m_type = (ePedStats)type; + strncpy(ms_apPedStats[type]->m_name, name, 24); // FIX: game uses strcpy + ms_apPedStats[type]->m_fleeDistance = fleeDist; + ms_apPedStats[type]->m_headingChangeRate = headingChangeRate; + ms_apPedStats[type]->m_fear = fear; + ms_apPedStats[type]->m_temper = temper; + ms_apPedStats[type]->m_lawfulness = lawfullness; + ms_apPedStats[type]->m_sexiness = sexiness; + ms_apPedStats[type]->m_attackStrength = attackStrength; + ms_apPedStats[type]->m_defendWeakness = defendWeakness; + ms_apPedStats[type]->m_flags = flags; + type++; + } + + delete[] buf; +} + +int32 +CPedStats::GetPedStatType(char *name) +{ + int type; + + for(type = 0; type < NUM_PEDSTATS; type++) + if(strcmp(ms_apPedStats[type]->m_name, name) == 0) + return type; + return NUM_PEDSTATS; +} + +STARTPATCHES + InjectHook(0x4EF460, &CPedStats::Initialise, PATCH_JUMP); + InjectHook(0x4EF540, &CPedStats::Shutdown, PATCH_JUMP); + InjectHook(0x4EF580, &CPedStats::LoadPedStats, PATCH_JUMP); + InjectHook(0x4EF780, &CPedStats::GetPedStatType, PATCH_JUMP); +ENDPATCHES diff --git a/src/peds/PedStats.h b/src/peds/PedStats.h new file mode 100644 index 00000000..ce92d4da --- /dev/null +++ b/src/peds/PedStats.h @@ -0,0 +1,79 @@ +#pragma once + +enum ePedStats +{ + PEDSTAT_PLAYER, + PEDSTAT_COP, + PEDSTAT_MEDIC, + PEDSTAT_FIREMAN, + PEDSTAT_GANG1, + PEDSTAT_GANG2, + PEDSTAT_GANG3, + PEDSTAT_GANG4, + PEDSTAT_GANG5, + PEDSTAT_GANG6, + PEDSTAT_GANG7, + PEDSTAT_STREET_GUY, + PEDSTAT_SUIT_GUY, + PEDSTAT_SENSIBLE_GUY, + PEDSTAT_GEEK_GUY, + PEDSTAT_OLD_GUY, + PEDSTAT_TOUGH_GUY, + PEDSTAT_STREET_GIRL, + PEDSTAT_SUIT_GIRL, + PEDSTAT_SENSIBLE_GIRL, + PEDSTAT_GEEK_GIRL, + PEDSTAT_OLD_GIRL, + PEDSTAT_TOUGH_GIRL, + PEDSTAT_TRAMP_MALE, + PEDSTAT_TRAMP_FEMALE, + PEDSTAT_TOURIST, + PEDSTAT_PROSTITUTE, + PEDSTAT_CRIMINAL, + PEDSTAT_BUSKER, + PEDSTAT_TAXIDRIVER, + PEDSTAT_PSYCHO, + PEDSTAT_STEWARD, + PEDSTAT_SPORTSFAN, + PEDSTAT_SHOPPER, + PEDSTAT_OLDSHOPPER, + + NUM_PEDSTATS +}; + +// flags +enum +{ + STAT_PUNCH_ONLY = 1, + STAT_CAN_KNEE_HEAD = 2, + STAT_CAN_KICK = 4, + STAT_CAN_ROUNDHOUSE = 8, + STAT_NO_DIVE = 0x10, + STAT_ONE_HIT_KNOCKDOWN = 0x20, + STAT_SHOPPING_BAGS = 0x40, + STAT_GUN_PANIC = 0x80 +}; + +class CPedStats +{ +public: + ePedStats m_type; + char m_name[24]; + float m_fleeDistance; + float m_headingChangeRate; + int8 m_fear; + int8 m_temper; + int8 m_lawfulness; + int8 m_sexiness; + float m_attackStrength; + float m_defendWeakness; + int16 m_flags; + + static CPedStats* (&ms_apPedStats)[NUM_PEDSTATS]; + + static void Initialise(void); + static void Shutdown(void); + static void LoadPedStats(void); + static int32 GetPedStatType(char *name); +}; +static_assert(sizeof(CPedStats) == 0x34, "CPedStats: error"); diff --git a/src/peds/PedType.cpp b/src/peds/PedType.cpp new file mode 100644 index 00000000..66eb49a1 --- /dev/null +++ b/src/peds/PedType.cpp @@ -0,0 +1,243 @@ +#include "common.h" +#include "patcher.h" +#include "FileMgr.h" +#include "PedType.h" + +CPedType *(&CPedType::ms_apPedType)[NUM_PEDTYPES] = *(CPedType *(*)[NUM_PEDTYPES]) *(uintptr*)0x941594; + +void +CPedType::Initialise(void) +{ + int i; + + debug("Initialising CPedType...\n"); + for(i = 0; i < NUM_PEDTYPES; i++){ + ms_apPedType[i] = new CPedType; + ms_apPedType[i]->m_flag = PED_FLAG_PLAYER1; + ms_apPedType[i]->unknown1 = 0.0f; + ms_apPedType[i]->unknown2 = 0.0f; + // unknown3 not initialized + ms_apPedType[i]->unknown4 = 0.0f; + ms_apPedType[i]->unknown5 = 0.0f; + ms_apPedType[i]->m_threats = 0; + ms_apPedType[i]->m_avoid = 0; + } + debug("Loading ped data...\n"); + LoadPedData(); + debug("CPedType ready\n"); +} + +void +CPedType::Shutdown(void) +{ + int i; + debug("Shutting down CPedType...\n"); + for(i = 0; i < NUM_PEDTYPES; i++) + delete ms_apPedType[i]; + debug("CPedType shut down\n"); +} + +void +CPedType::LoadPedData(void) +{ + char *buf; + char line[256]; + char word[32]; + int bp, buflen; + int lp, linelen; + int type; + uint32 flags; + float f1, f2, f3, f4, f5; + + type = NUM_PEDTYPES; + buf = new char[16 * 1024]; + + CFileMgr::SetDir("DATA"); + buflen = CFileMgr::LoadFile("PED.DAT", (uint8*)buf, 16 * 1024, "r"); + CFileMgr::SetDir(""); + + for(bp = 0; bp < buflen; ){ + // read file line by line + for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ + if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') + line[linelen++] = ' '; + else + line[linelen++] = buf[bp]; + line[linelen] = '\0'; + } + bp++; + + // skip white space + for(lp = 0; line[lp] <= ' '; lp++); + + if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines + line[lp] == '#') + continue; + + // FIX: game just uses line here + sscanf(&line[lp], "%s", word); + + if(strncmp(word, "Threat", 7) == 0){ + flags = 0; + lp += 7; + while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ + flags |= FindPedFlag(word); + // skip word + while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') + lp++; + // skip white space + while(line[lp] == ' ') + lp++; + } + ms_apPedType[type]->m_threats = flags; + }else if(strncmp(word, "Avoid", 6) == 0){ + flags = 0; + lp += 6; + while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ + flags |= FindPedFlag(word); + // skip word + while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') + lp++; + // skip white space + while(line[lp] == ' ') + lp++; + } + ms_apPedType[type]->m_avoid = flags; + }else{ + sscanf(line, "%s %f %f %f %f %f", word, &f1, &f2, &f3, &f4, &f5); + type = FindPedType(word); + ms_apPedType[type]->m_flag = FindPedFlag(word); + // unknown values + ms_apPedType[type]->unknown1 = f1 / 50.0f; + ms_apPedType[type]->unknown2 = f2 / 50.0f; + ms_apPedType[type]->unknown3 = f3 / 50.0f; + ms_apPedType[type]->unknown4 = f4; + ms_apPedType[type]->unknown5 = f5; + + } + } + + delete[] buf; +} + +int32 +CPedType::FindPedType(char *type) +{ + if(strcmp(type, "PLAYER1") == 0) return PEDTYPE_PLAYER1; + if(strcmp(type, "PLAYER2") == 0) return PEDTYPE_PLAYER2; + if(strcmp(type, "PLAYER3") == 0) return PEDTYPE_PLAYER3; + if(strcmp(type, "PLAYER4") == 0) return PEDTYPE_PLAYER4; + if(strcmp(type, "CIVMALE") == 0) return PEDTYPE_CIVMALE; + if(strcmp(type, "CIVFEMALE") == 0) return PEDTYPE_CIVFEMALE; + if(strcmp(type, "COP") == 0) return PEDTYPE_COP; + if(strcmp(type, "GANG1") == 0) return PEDTYPE_GANG1; + if(strcmp(type, "GANG2") == 0) return PEDTYPE_GANG2; + if(strcmp(type, "GANG3") == 0) return PEDTYPE_GANG3; + if(strcmp(type, "GANG4") == 0) return PEDTYPE_GANG4; + if(strcmp(type, "GANG5") == 0) return PEDTYPE_GANG5; + if(strcmp(type, "GANG6") == 0) return PEDTYPE_GANG6; + if(strcmp(type, "GANG7") == 0) return PEDTYPE_GANG7; + if(strcmp(type, "GANG8") == 0) return PEDTYPE_GANG8; + if(strcmp(type, "GANG9") == 0) return PEDTYPE_GANG9; + if(strcmp(type, "EMERGENCY") == 0) return PEDTYPE_EMERGENCY; + if(strcmp(type, "FIREMAN") == 0) return PEDTYPE_FIREMAN; + if(strcmp(type, "CRIMINAL") == 0) return PEDTYPE_CRIMINAL; + if(strcmp(type, "SPECIAL") == 0) return PEDTYPE_SPECIAL; + if(strcmp(type, "PROSTITUTE") == 0) return PEDTYPE_PROSTITUTE; + Error("Unknown ped type, Pedtype.cpp"); + return NUM_PEDTYPES; +} + +uint32 +CPedType::FindPedFlag(char *type) +{ + if(strcmp(type, "PLAYER1") == 0) return PED_FLAG_PLAYER1; + if(strcmp(type, "PLAYER2") == 0) return PED_FLAG_PLAYER2; + if(strcmp(type, "PLAYER3") == 0) return PED_FLAG_PLAYER3; + if(strcmp(type, "PLAYER4") == 0) return PED_FLAG_PLAYER4; + if(strcmp(type, "CIVMALE") == 0) return PED_FLAG_CIVMALE; + if(strcmp(type, "CIVFEMALE") == 0) return PED_FLAG_CIVFEMALE; + if(strcmp(type, "COP") == 0) return PED_FLAG_COP; + if(strcmp(type, "GANG1") == 0) return PED_FLAG_GANG1; + if(strcmp(type, "GANG2") == 0) return PED_FLAG_GANG2; + if(strcmp(type, "GANG3") == 0) return PED_FLAG_GANG3; + if(strcmp(type, "GANG4") == 0) return PED_FLAG_GANG4; + if(strcmp(type, "GANG5") == 0) return PED_FLAG_GANG5; + if(strcmp(type, "GANG6") == 0) return PED_FLAG_GANG6; + if(strcmp(type, "GANG7") == 0) return PED_FLAG_GANG7; + if(strcmp(type, "GANG8") == 0) return PED_FLAG_GANG8; + if(strcmp(type, "GANG9") == 0) return PED_FLAG_GANG9; + if(strcmp(type, "EMERGENCY") == 0) return PED_FLAG_EMERGENCY; + if(strcmp(type, "FIREMAN") == 0) return PED_FLAG_FIREMAN; + if(strcmp(type, "CRIMINAL") == 0) return PED_FLAG_CRIMINAL; + if(strcmp(type, "SPECIAL") == 0) return PED_FLAG_SPECIAL; + if(strcmp(type, "GUN") == 0) return PED_FLAG_GUN; + if(strcmp(type, "COP_CAR") == 0) return PED_FLAG_COP_CAR; + if(strcmp(type, "FAST_CAR") == 0) return PED_FLAG_FAST_CAR; + if(strcmp(type, "EXPLOSION") == 0) return PED_FLAG_EXPLOSION; + if(strcmp(type, "PROSTITUTE") == 0) return PED_FLAG_PROSTITUTE; + if(strcmp(type, "DEADPEDS") == 0) return PED_FLAG_DEADPEDS; + return 0; +} + +void +CPedType::Save(uint8 *buffer, uint32 *length) +{ + int i; + + *length = 8 + NUM_PEDTYPES*32; + + buffer[0] = 'P'; + buffer[1] = 'T'; + buffer[2] = 'P'; + buffer[3] = '\0'; + *(uint32*)(buffer+4) = *length - 8; + buffer += 8; + + for(i = 0; i < NUM_PEDTYPES; i++){ + *(uint32*)(buffer) = ms_apPedType[i]->m_flag; + *(float*)(buffer+4) = ms_apPedType[i]->unknown1; + *(float*)(buffer+8) = ms_apPedType[i]->unknown2; + *(float*)(buffer+12) = ms_apPedType[i]->unknown3; + *(float*)(buffer+16) = ms_apPedType[i]->unknown4; + *(float*)(buffer+20) = ms_apPedType[i]->unknown5; + *(uint32*)(buffer+24) = ms_apPedType[i]->m_threats; + *(uint32*)(buffer+28) = ms_apPedType[i]->m_avoid; + buffer += 32; + } +} + +void +CPedType::Load(uint8 *buffer, uint32 length) +{ + int i; + + assert(length == 8 + NUM_PEDTYPES*32); + assert(buffer[0] == 'P'); + assert(buffer[1] == 'T'); + assert(buffer[2] == 'P'); + assert(buffer[3] == '\0'); + assert(*(uint32*)(buffer+4) == length - 8); + buffer += 8; + + for(i = 0; i < NUM_PEDTYPES; i++){ + ms_apPedType[i]->m_flag = *(uint32*)(buffer); + ms_apPedType[i]->unknown1 = *(float*)(buffer+4); + ms_apPedType[i]->unknown2 = *(float*)(buffer+8); + ms_apPedType[i]->unknown3 = *(float*)(buffer+12); + ms_apPedType[i]->unknown4 = *(float*)(buffer+16); + ms_apPedType[i]->unknown5 = *(float*)(buffer+20); + ms_apPedType[i]->m_threats = *(uint32*)(buffer+24); + ms_apPedType[i]->m_avoid = *(uint32*)(buffer+28); + buffer += 32; + } +} + +STARTPATCHES + InjectHook(0x4EE7E0, &CPedType::Initialise, PATCH_JUMP); + InjectHook(0x4EE890, &CPedType::Shutdown, PATCH_JUMP); + InjectHook(0x4EEC10, &CPedType::FindPedType, PATCH_JUMP); + InjectHook(0x4EEF40, &CPedType::FindPedFlag, PATCH_JUMP); + InjectHook(0x4EF320, &CPedType::Save, PATCH_JUMP); + InjectHook(0x4EF3D0, &CPedType::Load, PATCH_JUMP); +ENDPATCHES diff --git a/src/peds/PedType.h b/src/peds/PedType.h new file mode 100644 index 00000000..455d8d8d --- /dev/null +++ b/src/peds/PedType.h @@ -0,0 +1,90 @@ +#pragma once + +// Index into the PedType array +enum +{ + PEDTYPE_PLAYER1, + PEDTYPE_PLAYER2, + PEDTYPE_PLAYER3, + PEDTYPE_PLAYER4, + PEDTYPE_CIVMALE, + PEDTYPE_CIVFEMALE, + PEDTYPE_COP, + PEDTYPE_GANG1, + PEDTYPE_GANG2, + PEDTYPE_GANG3, + PEDTYPE_GANG4, + PEDTYPE_GANG5, + PEDTYPE_GANG6, + PEDTYPE_GANG7, + PEDTYPE_GANG8, + PEDTYPE_GANG9, + PEDTYPE_EMERGENCY, + PEDTYPE_FIREMAN, + PEDTYPE_CRIMINAL, + PEDTYPE_UNUSED1, + PEDTYPE_PROSTITUTE, + PEDTYPE_SPECIAL, + PEDTYPE_UNUSED2, + + NUM_PEDTYPES +}; + +enum +{ + PED_FLAG_PLAYER1 = 1 << 0, + PED_FLAG_PLAYER2 = 1 << 1, + PED_FLAG_PLAYER3 = 1 << 2, + PED_FLAG_PLAYER4 = 1 << 3, + PED_FLAG_CIVMALE = 1 << 4, + PED_FLAG_CIVFEMALE = 1 << 5, + PED_FLAG_COP = 1 << 6, + PED_FLAG_GANG1 = 1 << 7, + PED_FLAG_GANG2 = 1 << 8, + PED_FLAG_GANG3 = 1 << 9, + PED_FLAG_GANG4 = 1 << 10, + PED_FLAG_GANG5 = 1 << 11, + PED_FLAG_GANG6 = 1 << 12, + PED_FLAG_GANG7 = 1 << 13, + PED_FLAG_GANG8 = 1 << 14, + PED_FLAG_GANG9 = 1 << 15, + PED_FLAG_EMERGENCY = 1 << 16, + PED_FLAG_PROSTITUTE = 1 << 17, + PED_FLAG_CRIMINAL = 1 << 18, + PED_FLAG_SPECIAL = 1 << 19, + PED_FLAG_GUN = 1 << 20, + PED_FLAG_COP_CAR = 1 << 21, + PED_FLAG_FAST_CAR = 1 << 22, + PED_FLAG_EXPLOSION = 1 << 23, + PED_FLAG_FIREMAN = 1 << 24, + PED_FLAG_DEADPEDS = 1 << 25, +}; + +class CPedType +{ + uint32 m_flag; + float unknown1; + float unknown2; + float unknown3; + float unknown4; + float unknown5; + uint32 m_threats; + uint32 m_avoid; + + static CPedType *(&ms_apPedType)[NUM_PEDTYPES]; +public: + + static void Initialise(void); + static void Shutdown(void); + static void LoadPedData(void); + static int32 FindPedType(char *type); + static uint32 FindPedFlag(char *type); + static void Save(uint8 *buffer, uint32 *length); + static void Load(uint8 *buffer, uint32 length); + + static uint32 GetFlag(int type) { return ms_apPedType[type]->m_flag; } + static uint32 GetAvoid(int type) { return ms_apPedType[type]->m_avoid; } + static uint32 GetThreats(int type) { return ms_apPedType[type]->m_threats; } +}; + +static_assert(sizeof(CPedType) == 0x20, "CPedType: error"); diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp new file mode 100644 index 00000000..2d67d5b2 --- /dev/null +++ b/src/peds/PlayerPed.cpp @@ -0,0 +1,14 @@ +#include "common.h" +#include "patcher.h" +#include "PlayerPed.h" + +CPlayerPed::~CPlayerPed() +{ + delete m_pWanted; +} + +WRAPPER void CPlayerPed::ReApplyMoveAnims(void) { EAXJMP(0x4F07C0); } + +STARTPATCHES + InjectHook(0x4EFB30, &CPlayerPed::dtor, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/peds/PlayerPed.h b/src/peds/PlayerPed.h new file mode 100644 index 00000000..15ad74a6 --- /dev/null +++ b/src/peds/PlayerPed.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Ped.h" +#include "Wanted.h" + +class CPlayerPed : public CPed +{ +public: + CWanted *m_pWanted; + CCopPed *m_pArrestingCop; + float m_fMoveSpeed; + float m_fCurrentStamina; + float m_fMaxStamina; + float m_fStaminaProgress; + bool m_bWeaponSlot; + bool m_bSpeedTimerFlag; + bool m_bShouldEvade; + int8 field_1367; + int32 m_nSpeedTimer; + int32 m_nShotDelay; + float field_1376; + int8 field_1380; // set if can't attack, why? + int8 field_1381; + int8 field_1382; + int8 field_1383; + CEntity *m_pEvadingFrom; + int32 m_nTargettableObjects[4]; + bool m_bAdrenalineActive; + bool m_bHasLockOnTarget; + int8 field_1406; + int8 field_1407; + bool m_bAdrenalineTime; + bool m_bCanBeDamaged; + int8 field_1413; + int8 field_1414; + int8 field_1415; + CVector field_1416[6]; + int32 field_1488[6]; + float m_fWalkAngle; + float m_fFPSMoveHeading; + + ~CPlayerPed(); + + void dtor(void) { this->CPlayerPed::~CPlayerPed(); } + void ReApplyMoveAnims(void); +}; + +static_assert(sizeof(CPlayerPed) == 0x5F0, "CPlayerPed: error"); -- cgit v1.2.3