From d5d1c7ec5fc98f8355d812587938314406a6df80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Mon, 7 Oct 2019 00:39:25 +0300 Subject: Peds --- src/control/Cranes.cpp | 1 + src/control/Cranes.h | 1 + src/control/Darkel.cpp | 6 +- src/control/Darkel.h | 7 +- src/core/Streaming.cpp | 8 +- src/core/config.h | 5 +- src/core/re3.cpp | 4 +- src/modelinfo/ModelIndices.h | 4 + src/peds/Ped.cpp | 315 ++++++++++++++++++++++++++++++++++++++----- src/peds/Ped.h | 28 +++- src/peds/PedRoutes.cpp | 26 +++- src/peds/PedRoutes.h | 4 + 12 files changed, 354 insertions(+), 55 deletions(-) diff --git a/src/control/Cranes.cpp b/src/control/Cranes.cpp index f641bc75..9610e37a 100644 --- a/src/control/Cranes.cpp +++ b/src/control/Cranes.cpp @@ -3,3 +3,4 @@ #include "Cranes.h" WRAPPER bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle*) { EAXJMP(0x5451E0); } +WRAPPER bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle*) { EAXJMP(0x545190); } \ No newline at end of file diff --git a/src/control/Cranes.h b/src/control/Cranes.h index e262d0c3..3af2c9bc 100644 --- a/src/control/Cranes.h +++ b/src/control/Cranes.h @@ -7,4 +7,5 @@ class CCranes { public: static bool IsThisCarBeingTargettedByAnyCrane(CVehicle*); + static bool IsThisCarBeingCarriedByAnyCrane(CVehicle*); }; diff --git a/src/control/Darkel.cpp b/src/control/Darkel.cpp index da28faa7..915e280a 100644 --- a/src/control/Darkel.cpp +++ b/src/control/Darkel.cpp @@ -28,8 +28,8 @@ int8 &CDarkel::InterruptedWeapon = *(int8*)0x95CD60; int8 &CDarkel::bStandardSoundAndMessages = *(int8*)0x95CDB6; int8 &CDarkel::bNeedHeadShot = *(int8*)0x95CDCA; int8 &CDarkel::bProperKillFrenzy = *(int8*)0x95CD98; -eKillFrenzyStatus &CDarkel::Status = *(eKillFrenzyStatus*)0x95CCB4; -uint16 (&CDarkel::RegisteredKills)[NUMDEFAULTMODELS] = *(uint16(*)[NUMDEFAULTMODELS]) * (uintptr*)0x6EDBE0; +uint16 &CDarkel::Status = *(uint16*)0x95CCB4; +uint16 (&CDarkel::RegisteredKills)[NUM_DEFAULT_MODELS] = *(uint16(*)[NUM_DEFAULT_MODELS]) * (uintptr*)0x6EDBE0; int32 &CDarkel::ModelToKill = *(int32*)0x8F2C78; int32 &CDarkel::ModelToKill2 = *(int32*)0x885B40; int32 &CDarkel::ModelToKill3 = *(int32*)0x885B3C; @@ -185,7 +185,7 @@ CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype) void CDarkel::ResetModelsKilledByPlayer() { - for (int i = 0; i < NUMDEFAULTMODELS; i++) + for (int i = 0; i < NUM_DEFAULT_MODELS; i++) RegisteredKills[i] = 0; } diff --git a/src/control/Darkel.h b/src/control/Darkel.h index 0171cd2c..f17d7581 100644 --- a/src/control/Darkel.h +++ b/src/control/Darkel.h @@ -1,10 +1,11 @@ #pragma once #include "Weapon.h" +#include "ModelIndices.h" class CVehicle; class CPed; -enum eKillFrenzyStatus +enum { KILLFRENZY_NONE, KILLFRENZY_ONGOING, @@ -25,8 +26,8 @@ private: static int8 &bStandardSoundAndMessages; static int8 &bNeedHeadShot; static int8 &bProperKillFrenzy; - static eKillFrenzyStatus &Status; - static uint16 (&RegisteredKills)[NUMDEFAULTMODELS]; + static uint16 &Status; + static uint16 (&RegisteredKills)[NUM_DEFAULT_MODELS]; static int32 &ModelToKill; static int32 &ModelToKill2; static int32 &ModelToKill3; diff --git a/src/core/Streaming.cpp b/src/core/Streaming.cpp index 227a4a9f..e59b2b39 100644 --- a/src/core/Streaming.cpp +++ b/src/core/Streaming.cpp @@ -1021,7 +1021,7 @@ CStreaming::RemoveAllUnusedModels(void) for(i = 0; i < MAXVEHICLESLOADED; i++) RemoveLoadedVehicle(); - for(i = NUMDEFAULTMODELS; i < MODELINFOSIZE; i++){ + for(i = NUM_DEFAULT_MODELS; i < MODELINFOSIZE; i++){ if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED && ms_aInfoForModel[i].m_flags & STREAMFLAGS_DONT_REMOVE && CModelInfo::GetModelInfo(i)->m_refCount == 0){ @@ -2405,8 +2405,8 @@ CStreaming::MemoryCardSave(uint8 *buffer, uint32 *length) { int i; - *length = NUMDEFAULTMODELS; - for(i = 0; i < NUMDEFAULTMODELS; i++) + *length = NUM_DEFAULT_MODELS; + for(i = 0; i < NUM_DEFAULT_MODELS; i++) if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) buffer[i] = ms_aInfoForModel[i].m_flags; else @@ -2418,7 +2418,7 @@ CStreaming::MemoryCardLoad(uint8 *buffer, uint32 length) { uint32 i; - assert(length == NUMDEFAULTMODELS); + assert(length == NUM_DEFAULT_MODELS); for(i = 0; i < length; i++) if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) if(buffer[i] != 0xFF) diff --git a/src/core/config.h b/src/core/config.h index cfad6d85..a3fbac1b 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -32,7 +32,6 @@ enum Config { NUMDUMMIES = 2802, // 2368 on PS2 NUMAUDIOSCRIPTOBJECTS = 256, NUMCUTSCENEOBJECTS = 50, - NUMDEFAULTMODELS = 200, NUMTEMPOBJECTS = 30, @@ -84,7 +83,9 @@ enum Config { NUM_PATH_NODES_IN_AUTOPILOT = 8, NUM_ACCIDENTS = 20, - NUM_FIRES = 40 + NUM_FIRES = 40, + + NUMPEDROUTES = 200, }; // We'll use this once we're ready to become independent of the game diff --git a/src/core/re3.cpp b/src/core/re3.cpp index 19b3c691..8e20ffb3 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -146,7 +146,7 @@ LetThemFollowYou(void) { CPed *nearPed = player->m_nearPeds[i]; if (nearPed && !nearPed->IsPlayer()) { nearPed->SetObjective(OBJECTIVE_FOLLOW_PED_IN_FORMATION, (void*)player); - nearPed->m_pedFormation = rand() & 7; + nearPed->m_pedFormation = (eFormation)(rand() & 7); nearPed->bScriptObjectiveCompleted = false; } } @@ -349,7 +349,7 @@ DebugMenuPopulate(void) DebugMenuAddVarBool8("Debug", "Don't render Vehicles", (int8*)&gbDontRenderVehicles, nil); DebugMenuAddVarBool8("Debug", "Don't render Objects", (int8*)&gbDontRenderObjects, nil); - DebugMenuAddCmd("Debug", "Make peds around you follow you", LetThemFollowYou); + DebugMenuAddCmd("Debug", "Make peds follow you in formation", LetThemFollowYou); #ifndef MASTER DebugMenuAddVarBool8("Debug", "Toggle unused fight feature", (int8*)&CPed::bUnusedFightThingOnPlayer, nil); #endif diff --git a/src/modelinfo/ModelIndices.h b/src/modelinfo/ModelIndices.h index 8f6d0e48..4a6e80d0 100644 --- a/src/modelinfo/ModelIndices.h +++ b/src/modelinfo/ModelIndices.h @@ -1,3 +1,5 @@ +#pragma once + #define MODELINDICES \ X("fire_hydrant", MI_FIRE_HYDRANT, 0x5F5A00) \ X("bagelstnd02", MI_BAGELSTAND2, 0x5F59FC) \ @@ -352,6 +354,8 @@ enum MI_AIRTRAIN_VLO = 198, MI_LOPOLYGUY, + + NUM_DEFAULT_MODELS }; enum{ diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index cbd32cea..1cf15cb6 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -44,26 +44,23 @@ #include "WaterLevel.h" #include "CarAI.h" #include "Zones.h" +#include "Cranes.h" +#include "MusicManager.h" WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } WRAPPER void CPed::SetPedPositionInCar(void) { EAXJMP(0x4D4970); } WRAPPER void CPed::PreRender(void) { EAXJMP(0x4CFDD0); } 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::SetFollowPath(CVector) { EAXJMP(0x4D2EA0); } WRAPPER void CPed::StartFightDefend(uint8, uint8, uint8) { EAXJMP(0x4E7780); } -WRAPPER void CPed::SetRadioStation(void) { EAXJMP(0x4D7BC0); } WRAPPER void CPed::ProcessBuoyancy(void) { EAXJMP(0x4C7FF0); } WRAPPER void CPed::ServiceTalking(void) { EAXJMP(0x4E5870); } WRAPPER void CPed::UpdatePosition(void) { EAXJMP(0x4C7A00); } -WRAPPER void CPed::WanderRange(void) { EAXJMP(0x4D26C0); } WRAPPER void CPed::WanderPath(void) { EAXJMP(0x4D28D0); } WRAPPER void CPed::SeekCar(void) { EAXJMP(0x4D3F90); } -WRAPPER void CPed::SeekBoatPosition(void) { EAXJMP(0x4E4C70); } WRAPPER void CPed::UpdateFromLeader(void) { EAXJMP(0x4D8F30); } WRAPPER int CPed::ScanForThreats(void) { EAXJMP(0x4C5FE0); } -WRAPPER void CPed::SetEnterCar(CVehicle*, uint32) { EAXJMP(0x4E0920); } +WRAPPER void CPed::SetEnterCar_AllClear(CVehicle*, uint32, uint32) { EAXJMP(0x4E0A40); } WRAPPER bool CPed::WarpPedToNearEntityOffScreen(CEntity*) { EAXJMP(0x4E5570); } WRAPPER void CPed::SetExitCar(CVehicle*, uint32) { EAXJMP(0x4E1010); } @@ -391,7 +388,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_vecOffsetSeek.x = 0.0f; m_vecOffsetSeek.y = 0.0f; m_vecOffsetSeek.z = 0.0f; - m_pedFormation = 0; + m_pedFormation = FORMATION_REAR; m_collidingThingTimer = 0; m_nPedStateTimer = 0; m_actionX = 0; @@ -440,7 +437,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_pLookTarget = nil; m_fLookDirection = 0.0f; m_pCurSurface = nil; - m_targetUnused = nil; + m_wanderRangeBounds = nil; m_nPathNodes = 0; m_nCurPathNode = 0; m_nPathState = 0; @@ -2689,7 +2686,7 @@ CPed::SetObjective(eObjective newObj, void *entity) case OBJECTIVE_FOLLOW_PED_IN_FORMATION: m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); - m_pedFormation = 1; + m_pedFormation = FORMATION_REAR_LEFT; break; case OBJECTIVE_LEAVE_VEHICLE: #ifdef VC_PED_PORTS @@ -6325,8 +6322,14 @@ CPed::ExitCar(void) void CPed::Fall(void) { - if (m_getUpTimer != -1 && CTimer::GetTimeInMilliseconds() > m_getUpTimer) + if (m_getUpTimer != -1 && CTimer::GetTimeInMilliseconds() > m_getUpTimer +#ifdef VC_PED_PORTS + && bIsStanding +#endif + ) ClearFall(); + + // VC plays animations ANIM_STD_FALL_ONBACK and ANIM_STD_FALL_ONFRONT in here, which doesn't exist in III. } void @@ -7612,28 +7615,28 @@ CPed::GetFormationPosition(void) CVector formationOffset; switch (m_pedFormation) { - case 1: + case FORMATION_REAR: formationOffset = CVector(0.0f, -1.5f, 0.0f); break; - case 2: + case FORMATION_REAR_LEFT: formationOffset = CVector(-1.5f, -1.5f, 0.0f); break; - case 3: + case FORMATION_REAR_RIGHT: formationOffset = CVector(1.5f, -1.5f, 0.0f); break; - case 4: + case FORMATION_FRONT_LEFT: formationOffset = CVector(-1.5f, 1.5f, 0.0f); break; - case 5: + case FORMATION_FRONT_RIGHT: formationOffset = CVector(1.5f, 1.5f, 0.0f); break; - case 6: + case FORMATION_LEFT: formationOffset = CVector(-1.5f, 0.0f, 0.0f); break; - case 7: + case FORMATION_RIGHT: formationOffset = CVector(1.5f, 0.0f, 0.0f); break; - case 8: + case FORMATION_FRONT: formationOffset = CVector(0.0f, 1.5f, 0.0f); break; default: @@ -7769,7 +7772,7 @@ CPed::GetNextPointOnRoute(void) int16 nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; // Route is complete - if (nextPoint < 0 || nextPoint > 200 || m_routeLastPoint != CRouteNode::GetRouteThisPointIsOn(nextPoint)) { + if (nextPoint < 0 || nextPoint > NUMPEDROUTES || m_routeLastPoint != CRouteNode::GetRouteThisPointIsOn(nextPoint)) { switch (m_routeType) { case PEDROUTE_STOP_WHEN_DONE: @@ -7790,7 +7793,7 @@ CPed::GetNextPointOnRoute(void) return nextPoint; } -// TODO: enum +// These categories are purely random, most of ped models have no correlation. So I don't think making an enum. uint8 CPed::GetPedRadioCategory(uint32 modelIndex) { @@ -10083,7 +10086,7 @@ CPed::ProcessControl(void) if (IsPedInControl() && !bIsStanding && !m_pDamageEntity && CheckIfInTheAir()) { SetInTheAir(); #ifdef VC_PED_PORTS - bKnockedUpIntoAir = true; + bKnockedUpIntoAir = false; #endif } #ifdef VC_PED_PORTS @@ -10416,7 +10419,9 @@ CPed::ProcessControl(void) ServiceTalking(); if (bInVehicle && !m_pMyVehicle) bInVehicle = false; +#ifndef VC_PED_PORTS m_pCurrentPhysSurface = nil; +#endif } else { if (bIsStanding && (!m_pCurrentPhysSurface || IsPlayer()) || bIsInWater || !bUsesCollision) { @@ -13538,18 +13543,19 @@ CPed::SetSeekBoatPosition(CVehicle *boat) void CPed::SetExitTrain(CVehicle* train) { - if (m_nPedState != PED_EXIT_TRAIN && train->m_status == STATUS_TRAIN_NOT_MOVING && ((CTrain*)train)->Doors[0].IsFullyOpen()) { - /* - // Not used - CVector exitPos; - GetNearestTrainPedPosition(train, exitPos); - */ - m_nPedState = PED_EXIT_TRAIN; - m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_TRAIN_GETOUT, 4.0f); - m_pVehicleAnim->SetFinishCallback(PedSetOutTrainCB, this); - bUsesCollision = false; - LineUpPedWithTrain(); - } + if (m_nPedState == PED_EXIT_TRAIN || train->m_status != STATUS_TRAIN_NOT_MOVING || !((CTrain*)train)->Doors[0].IsFullyOpen()) + return; + + /* + // Not used + CVector exitPos; + GetNearestTrainPedPosition(train, exitPos); + */ + m_nPedState = PED_EXIT_TRAIN; + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_TRAIN_GETOUT, 4.0f); + m_pVehicleAnim->SetFinishCallback(PedSetOutTrainCB, this); + bUsesCollision = false; + LineUpPedWithTrain(); } #ifdef NEW_WALK_AROUND_ALGORITHM @@ -14386,6 +14392,243 @@ CPed::ProcessEntityCollision(CEntity *collidingEnt, CColPoint *collidingPoints) return ourCollidedSpheres; } +void +CPed::SetFormation(eFormation type) +{ + switch (m_pedFormation) { + case FORMATION_REAR: + case FORMATION_REAR_LEFT: + case FORMATION_REAR_RIGHT: + case FORMATION_FRONT_LEFT: + case FORMATION_FRONT_RIGHT: + case FORMATION_LEFT: + case FORMATION_RIGHT: + case FORMATION_FRONT: + break; + default: + Error("Unknown formation type, PedAI.cpp"); + break; + } + m_pedFormation = type; +} + +void +CPed::SetFollowRoute(int16 currentPoint, int16 routeType) +{ + m_routeLastPoint = currentPoint; + m_routeStartPoint = CRouteNode::GetRouteStart(currentPoint); + m_routePointsPassed = 0; + m_routeType = routeType; + m_routePointsBeingPassed = 1; + m_objective = OBJECTIVE_FOLLOW_ROUTE; + m_nextRoutePointPos = CRouteNode::GetPointPosition(GetNextPointOnRoute()); +} + +// "Wander range" state is unused in game, and you can't use it without SetWanderRange anyway +void +CPed::WanderRange(void) +{ + bool arrived = Seek(); + if (arrived) { + Idle(); + if (((m_randomSeed % 256) + 3 * CTimer::GetFrameCounter()) % 1000 > 997) { + + int xDiff = Abs(m_wanderRangeBounds[1].x - m_wanderRangeBounds[0].x); + int yDiff = Abs(m_wanderRangeBounds[1].y - m_wanderRangeBounds[0].y); + + CVector newCoords( + (CGeneral::GetRandomNumber() % xDiff) + m_wanderRangeBounds[0].x, + (CGeneral::GetRandomNumber() % yDiff) + m_wanderRangeBounds[0].y, + GetPosition().z); + + SetSeek(newCoords, 2.5f); + } + } +} + +bool +CPed::WillChat(CPed *stranger) +{ + if (m_pNextPathNode && m_pLastPathNode) { + if (m_pNextPathNode != m_pLastPathNode && ThePaths.TestCrossesRoad(m_pNextPathNode, m_pLastPathNode)) { + return false; + } + } + if (m_nSurfaceTouched == SURFACE_TARMAC) + return false; + if (this == stranger) + return false; + if (m_nPedType == stranger->m_nPedType) + return true; + if (m_nPedType == PEDTYPE_CRIMINAL) + return false; + if ((IsGangMember() || stranger->IsGangMember()) && m_nPedType != stranger->m_nPedType) + return false; + return true; +} + +void +CPed::SetEnterTrain(CVehicle *train, uint32 unused) +{ + if (m_nPedState == PED_ENTER_TRAIN || !((CTrain*)train)->Doors[0].IsFullyOpen()) + return; + + /* + // Not used + CVector enterPos; + GetNearestTrainPedPosition(train, enterPos); + */ + m_fRotationCur = train->GetForward().Heading() - HALFPI; + m_pMyVehicle = train; + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + + m_nPedState = PED_ENTER_TRAIN; + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_TRAIN_GETIN, 4.0f); + m_pVehicleAnim->SetFinishCallback(PedSetInTrainCB, this); + bUsesCollision = false; + LineUpPedWithTrain(); + if (IsPlayer()) { + if (((CPlayerPed*)this)->m_bAdrenalineActive) + ((CPlayerPed*)this)->ClearAdrenaline(); + } +} + +void +CPed::SetDuck(uint32 time) +{ + if (bIsDucking || CTimer::GetTimeInMilliseconds() <= m_duckTimer) + return; + + if (bCrouchWhenShooting && (m_nPedState == PED_ATTACK || m_nPedState == PED_AIM_GUN)) { + CAnimBlendAssociation *duckAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DUCK_LOW); + if (!duckAssoc || duckAssoc->blendDelta < 0.0f) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DUCK_LOW, 4.0f); + bIsDucking = true; + m_duckTimer = CTimer::GetTimeInMilliseconds() + time; + } + } else { + CAnimBlendAssociation *duckAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DUCK_DOWN); + if (!duckAssoc || duckAssoc->blendDelta < 0.0f) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DUCK_DOWN, 4.0f); + bIsDucking = true; + m_duckTimer = CTimer::GetTimeInMilliseconds() + time; + } + } +} + +void +CPed::SeekBoatPosition(void) +{ + if (m_carInObjective && !m_carInObjective->pDriver) { + CVehicleModelInfo *boatModel = m_carInObjective->GetModelInfo(); + + CVector enterOffset; + if (boatModel->m_vehicleType == VEHICLE_TYPE_BOAT) + enterOffset = boatModel->m_positions[BOAT_POS_FRONTSEAT]; + else + enterOffset = boatModel->m_positions[CAR_POS_FRONTSEAT]; + + enterOffset.x = 0.0f; + CMatrix boatMat(m_carInObjective->GetMatrix()); + SetMoveState(PEDMOVE_WALK); + m_vecSeekPos = boatMat * enterOffset; + if (Seek()) { + // We arrived to the boat + m_vehEnterType = 0; + SetEnterCar(m_carInObjective, 0); + } + } else + RestorePreviousState(); +} + +void +CPed::SetEnterCar(CVehicle *car, uint32 unused) +{ + if (CCranes::IsThisCarBeingCarriedByAnyCrane(car)) { + RestorePreviousState(); + RestorePreviousObjective(); + } else { + uint8 doorFlag; + eDoors door; + switch (m_vehEnterType) { + case CAR_DOOR_RF: + doorFlag = CAR_DOOR_FLAG_RF; + door = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + doorFlag = CAR_DOOR_FLAG_RR; + door = DOOR_REAR_RIGHT; + break; + case CAR_DOOR_LF: + doorFlag = CAR_DOOR_FLAG_LF; + door = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + doorFlag = CAR_DOOR_FLAG_LR; + door = DOOR_REAR_LEFT; + break; + default: + doorFlag = CAR_DOOR_FLAG_UNKNOWN; + break; + } + if (!IsPedInControl() || m_fHealth <= 0.0f + || doorFlag & car->m_nGettingInFlags || doorFlag & car->m_nGettingOutFlags + || car->m_veh_flagC10 || m_pVehicleAnim + || doorFlag && !car->IsDoorReady(door) && !car->IsDoorFullyOpen(door)) + SetMoveState(PEDMOVE_STILL); + else + SetEnterCar_AllClear(car, m_vehEnterType, doorFlag); + } +} + +void +CPed::SetRadioStation(void) +{ + uint8 radiosPerRadioCategories[10][4] = { + {JAH_RADIO, RISE_FM, GAME_FM, MSX_FM}, + {HEAD_RADIO, DOUBLE_CLEF, LIPS_106, FLASHBACK}, + {RISE_FM, GAME_FM, MSX_FM, FLASHBACK}, + {HEAD_RADIO, RISE_FM, LIPS_106, MSX_FM}, + {HEAD_RADIO, RISE_FM, MSX_FM, FLASHBACK}, + {JAH_RADIO, RISE_FM, LIPS_106, FLASHBACK}, + {HEAD_RADIO, RISE_FM, LIPS_106, FLASHBACK}, + {HEAD_RADIO, JAH_RADIO, LIPS_106, FLASHBACK}, + {HEAD_RADIO, DOUBLE_CLEF, LIPS_106, FLASHBACK}, + {CHATTERBOX, HEAD_RADIO, LIPS_106, GAME_FM} + }; + uint8 orderInCat; + + if (IsPlayer() || !m_pMyVehicle || m_pMyVehicle->pDriver != this) + return; + + uint8 category = GetPedRadioCategory(m_modelIndex); + if (DMAudio.IsMP3RadioChannelAvailable()) { + if (CGeneral::GetRandomNumber() & 15) { + for (orderInCat = 0; orderInCat < 4; orderInCat++) { + if (m_pMyVehicle->m_nRadioStation == radiosPerRadioCategories[category][orderInCat]) + break; + } + } else { + m_pMyVehicle->m_nRadioStation = USERTRACK; + } + } else { + for (orderInCat = 0; orderInCat < 4; orderInCat++) { + if (m_pMyVehicle->m_nRadioStation == radiosPerRadioCategories[category][orderInCat]) + break; + } + } + if (orderInCat == 4) { + if (DMAudio.IsMP3RadioChannelAvailable()) { + if (CGeneral::GetRandomNumber() & 15) + m_pMyVehicle->m_nRadioStation = radiosPerRadioCategories[category][CGeneral::GetRandomNumber() & 3]; + else + m_pMyVehicle->m_nRadioStation = USERTRACK; + } else { + m_pMyVehicle->m_nRadioStation = radiosPerRadioCategories[category][CGeneral::GetRandomNumber() & 3]; + } + } +} + class CPed_ : public CPed { public: @@ -14589,4 +14832,10 @@ STARTPATCHES InjectHook(0x4D6A00, &CPed::PossiblyFindBetterPosToSeekCar, PATCH_JUMP); InjectHook(0x4D94E0, &CPed::ProcessObjective, PATCH_JUMP); InjectHook(0x4CCEB0, &CPed::SetDirectionToWalkAroundObject, PATCH_JUMP); + InjectHook(0x4DF3E0, &CPed::SetFormation, PATCH_JUMP); + InjectHook(0x4C7340, &CPed::WillChat, PATCH_JUMP); + InjectHook(0x4E32D0, &CPed::SetEnterTrain, PATCH_JUMP); + InjectHook(0x4E4920, &CPed::SetDuck, PATCH_JUMP); + InjectHook(0x4E0920, &CPed::SetEnterCar, PATCH_JUMP); + InjectHook(0x4D7BC0, &CPed::SetRadioStation, PATCH_JUMP); ENDPATCHES \ No newline at end of file diff --git a/src/peds/Ped.h b/src/peds/Ped.h index 0b1b80d6..3d7354a3 100644 --- a/src/peds/Ped.h +++ b/src/peds/Ped.h @@ -24,6 +24,18 @@ struct CPedAudioData int m_nMaxRandomDelayTime; }; +enum eFormation +{ + FORMATION_REAR, + FORMATION_REAR_LEFT, + FORMATION_REAR_RIGHT, + FORMATION_FRONT_LEFT, + FORMATION_FRONT_RIGHT, + FORMATION_LEFT, + FORMATION_RIGHT, + FORMATION_FRONT +}; + enum FightState : int8 { FIGHTSTATE_MOVE_FINISHED = -2, FIGHTSTATE_JUST_ATTACKED, @@ -360,7 +372,7 @@ public: CVehicle *m_carInObjective; CVector m_nextRoutePointPos; CPed *m_leader; - uint32 m_pedFormation; + eFormation m_pedFormation; uint32 m_fearFlags; CEntity *m_threatEntity; CVector2D m_eventOrThreat; @@ -398,7 +410,7 @@ public: int16 m_routeLastPoint; uint16 m_routeStartPoint; int16 m_routePointsPassed; - int16 m_routeType; + int16 m_routeType; // See PedRouteType int16 m_routePointsBeingPassed; uint16 field_2D2; CVector2D m_moved; @@ -434,7 +446,7 @@ public: uint8 m_stateUnused; uint8 pad_351[3]; uint32 m_timerUnused; - CEntity *m_targetUnused; + CVector2D *m_wanderRangeBounds; // array with 2 CVector2D (actually unused CRange2D class) - unused CWeapon m_weapons[WEAPONTYPE_TOTAL_INVENTORY_WEAPONS]; eWeaponType m_storedWeapon; uint8 m_currentWeapon; // eWeaponType @@ -542,7 +554,6 @@ public: void SetObjective(eObjective, int16, int16); void ClearChat(void); void InformMyGangOfAttack(CEntity*); - void SetFollowRoute(int16, int16); void ReactToAttack(CEntity*); void SetDuck(uint32); void RegisterThreatWithGangPeds(CEntity*); @@ -647,11 +658,9 @@ public: void ServiceTalking(void); void SetJump(void); void UpdatePosition(void); - void WanderRange(void); void WanderPath(void); void ReactToPointGun(CEntity*); void SeekCar(void); - void SeekBoatPosition(void); bool PositionPedOutOfCollision(void); bool RunToReportCrime(eCrimeType); bool PlacePedOnDryLand(void); @@ -661,6 +670,10 @@ public: void SetEnterCar(CVehicle*, uint32); bool WarpPedToNearEntityOffScreen(CEntity*); void SetExitCar(CVehicle*, uint32); + void SetFormation(eFormation); + bool WillChat(CPed*); + void SetEnterTrain(CVehicle*, uint32); + void SetEnterCar_AllClear(CVehicle*, uint32, uint32); // Static methods static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset); @@ -733,6 +746,9 @@ public: void SetSeekCar(CVehicle*, uint32); void SetSeekBoatPosition(CVehicle*); void SetExitTrain(CVehicle*); + void WanderRange(void); + void SetFollowRoute(int16, int16); + void SeekBoatPosition(void); #ifdef VC_PED_PORTS bool CanPedJumpThis(CEntity*, CVector*); #else diff --git a/src/peds/PedRoutes.cpp b/src/peds/PedRoutes.cpp index f1f73988..a8e8d2ab 100644 --- a/src/peds/PedRoutes.cpp +++ b/src/peds/PedRoutes.cpp @@ -3,5 +3,27 @@ #include "main.h" #include "PedRoutes.h" -WRAPPER int16 CRouteNode::GetRouteThisPointIsOn(int16) { EAXJMP(0x4EE7A0); } -WRAPPER CVector CRouteNode::GetPointPosition(int16) { EAXJMP(0x4EE780); } \ No newline at end of file +CRouteNode (&gaRoutes)[NUMPEDROUTES] = *(CRouteNode(*)[NUMPEDROUTES]) * (uintptr*)0x62E090; + +int16 +CRouteNode::GetRouteThisPointIsOn(int16 point) +{ + return gaRoutes[point].m_route; +} + +// Actually GetFirstPointOfRoute +int16 +CRouteNode::GetRouteStart(int16 route) +{ + for (int i = 0; i < NUMPEDROUTES; i++) { + if (route == gaRoutes[i].m_route) + return i; + } + return -1; +} + +CVector +CRouteNode::GetPointPosition(int16 point) +{ + return gaRoutes[point].m_pos; +} \ No newline at end of file diff --git a/src/peds/PedRoutes.h b/src/peds/PedRoutes.h index c9a175a8..9df9a29d 100644 --- a/src/peds/PedRoutes.h +++ b/src/peds/PedRoutes.h @@ -3,6 +3,10 @@ class CRouteNode { public: + int16 m_route; + CVector m_pos; + static int16 GetRouteThisPointIsOn(int16); static CVector GetPointPosition(int16); + static int16 GetRouteStart(int16); }; \ No newline at end of file -- cgit v1.2.3