From 1e09bf9c30094b4387431be4842876181a1425fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Mon, 24 Jun 2019 17:57:54 +0300 Subject: CPed, CVehicle, mostly entering/exiting car MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: eray orçunus --- src/animation/AnimBlendAssociation.h | 5 +- src/audio/AudioManager.cpp | 15 + src/audio/AudioManager.h | 8 + src/control/CarCtrl.cpp | 2 + src/control/CarCtrl.h | 2 + src/control/PedPlacement.cpp | 40 +++ src/control/PedPlacement.h | 8 + src/entities/Ped.cpp | 531 +++++++++++++++++++++++++++++++++-- src/entities/Ped.h | 41 ++- src/entities/Vehicle.cpp | 52 ++++ src/entities/Vehicle.h | 88 +++++- src/math/Matrix.h | 24 ++ src/modelinfo/VehicleModelInfo.h | 10 +- src/weapons/Weapon.h | 3 +- 14 files changed, 789 insertions(+), 40 deletions(-) create mode 100644 src/audio/AudioManager.cpp create mode 100644 src/audio/AudioManager.h create mode 100644 src/control/PedPlacement.cpp create mode 100644 src/control/PedPlacement.h (limited to 'src') diff --git a/src/animation/AnimBlendAssociation.h b/src/animation/AnimBlendAssociation.h index 7eec69a0..076fa810 100644 --- a/src/animation/AnimBlendAssociation.h +++ b/src/animation/AnimBlendAssociation.h @@ -2,8 +2,7 @@ #include "AnimBlendList.h" #include "AnimBlendNode.h" - -class CAnimBlendHierarchy; +#include "AnimBlendHierarchy.h" enum { // TODO @@ -78,6 +77,8 @@ public: void UpdateTime(float timeDelta, float relSpeed); bool UpdateBlend(float timeDelta); + float GetTimeLeft() { return hierarchy->totalLength - currentTime; } + static CAnimBlendAssociation *FromLink(CAnimBlendLink *l) { return (CAnimBlendAssociation*)((uint8*)l - offsetof(CAnimBlendAssociation, link)); } diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp new file mode 100644 index 00000000..5da1d489 --- /dev/null +++ b/src/audio/AudioManager.cpp @@ -0,0 +1,15 @@ +#include "common.h" +#include "patcher.h" +#include "AudioManager.h" + +cAudioManager &AudioManager = *(cAudioManager*)0x880FC0; + +void +cAudioManager::PlayerJustLeftCar(void) +{ + // UNUSED: This is a perfectly empty function. +} + +STARTPATCHES + InjectHook(0x56AD20, &cAudioManager::PlayerJustLeftCar, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h new file mode 100644 index 00000000..37dd3218 --- /dev/null +++ b/src/audio/AudioManager.h @@ -0,0 +1,8 @@ +#pragma once + +class cAudioManager { +public: + void PlayerJustLeftCar(void); +}; + +extern cAudioManager &AudioManager; \ No newline at end of file diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp index d59ae2d0..ff73e405 100644 --- a/src/control/CarCtrl.cpp +++ b/src/control/CarCtrl.cpp @@ -2,5 +2,7 @@ #include "patcher.h" #include "CarCtrl.h" +int &CCarCtrl::NumLawEnforcerCars = *(int*)0x8F1B38; + WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); } WRAPPER void CCarCtrl::AddToCarArray(int id, int vehclass) { EAXJMP(0x4182F0); } diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h index 1f468475..06204ddd 100644 --- a/src/control/CarCtrl.h +++ b/src/control/CarCtrl.h @@ -7,4 +7,6 @@ class CCarCtrl public: static void SwitchVehicleToRealPhysics(CVehicle*); static void AddToCarArray(int id, int vehclass); + + static int32 &NumLawEnforcerCars; }; diff --git a/src/control/PedPlacement.cpp b/src/control/PedPlacement.cpp new file mode 100644 index 00000000..de4cdae1 --- /dev/null +++ b/src/control/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, false)) + 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, false)) + 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/control/PedPlacement.h b/src/control/PedPlacement.h new file mode 100644 index 00000000..4bd48b62 --- /dev/null +++ b/src/control/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/entities/Ped.cpp b/src/entities/Ped.cpp index f3e212d4..1b3e8a0e 100644 --- a/src/entities/Ped.cpp +++ b/src/entities/Ped.cpp @@ -10,11 +10,19 @@ #include "PlayerPed.h" #include "General.h" #include "VisibilityPlugins.h" +#include "AudioManager.h" +#include "HandlingMgr.h" +#include "Replay.h" +#include "PedPlacement.h" bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; bool &CPed::bPedCheat3 = *(bool*)0x95CD59; +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 delete(void *p, size_t sz) { CPools::GetPedPool()->Delete((CPed*)p); } @@ -24,6 +32,9 @@ WRAPPER void CPed::SetDie(AnimationId anim, float arg1, float arg2) { EAXJMP(0x4 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); } static char ObjectiveText[34][28] = { "No Obj", @@ -183,9 +194,8 @@ static char WaitStateText[21][16] = { static RwObject* RemoveAllModelCB(RwObject *object, void *data) { - RpAtomic* atomic = (RpAtomic*)object; - if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) - { + RpAtomic *atomic = (RpAtomic*)object; + if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) { RpClumpRemoveAtomic(atomic->clump, atomic); RpAtomicDestroy(atomic); } @@ -299,6 +309,14 @@ CPed::UseGroundColModel(void) 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_CARJACK && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_STEAL_CAR; +} + + void CPed::AddWeaponModel(int id) { @@ -398,8 +416,9 @@ CPed::RemoveBodyPart(PedNode nodeId, int8 unk) RecurseFrameChildrenVisibilityCB(frame, 0); pos.x = 0.0f; - pos.z = 0.0f; pos.y = 0.0f; + pos.z = 0.0f; + for (frame = RwFrameGetParent(frame); frame; frame = RwFrameGetParent(frame)) RwV3dTransformPoints(&pos, &pos, 1, RwFrameGetMatrix(frame)); @@ -602,7 +621,7 @@ CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) if (attackAssoc) { switch (attackAssoc->animId) { case ANIM_WEAPON_START_THROW: - if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1376) && ped->IsPlayer()) + if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1380) && ped->IsPlayer()) { attackAssoc->blendDelta = -1000.0; newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROWU); @@ -658,7 +677,7 @@ CPed::Attack(void) ourWeaponFire = ourWeapon->m_eWeaponFire; weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_AnimToPlay); lastReloadWasInFuture = m_ped_flagA4; - reloadAnimAssoc = 0; + reloadAnimAssoc = nil; reloadAnim = NUM_ANIMS; delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; weaponAnim = ourWeapon->m_AnimToPlay; @@ -676,7 +695,7 @@ CPed::Attack(void) if (reloadAnimAssoc) { if (!CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) - CPed::ClearAttack(); + ClearAttack(); return; } @@ -686,12 +705,13 @@ CPed::Attack(void) lastReloadWasInFuture = true; if (!weaponAnimAssoc) { - if (ourWeapon->m_bThrow) { + 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; - } else { - weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ourWeapon->m_Anim2ToPlay); - delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; } } if (weaponAnimAssoc) { @@ -742,7 +762,7 @@ CPed::Attack(void) CPed::ClearLookFlag(); CPed::ClearAimFlag(); m_ped_flagA4 = false; - m_ped_flagA8 = false; + bIsPointingGunAt = false; m_lastHitTime = CTimer::GetTimeInMilliseconds(); return; } @@ -885,7 +905,7 @@ CPed::RemoveWeaponModel(int modelId) void CPed::SetCurrentWeapon(eWeaponType weaponType) { - CWeaponInfo* weaponInfo; + CWeaponInfo *weaponInfo; if (HasWeapon(weaponType)) { weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); @@ -909,8 +929,8 @@ CPed::SelectGunIfArmed(void) if (m_weapons[i].m_nAmmoTotal > 0) { weaponType = m_weapons[i].m_eWeaponType; - // Original condition was ridiculous - // if (weaponType == WEAPONTYPE_COLT45 || weaponType < WEAPONTYPE_M16 || weaponType < WEAPONTYPE_FLAMETHROWER || weaponType == WEAPONTYPE_FLAMETHROWER) + // I GOT THAT WRONG AND SHOULD BE FIXED!! (but I don't know how) Original code was; + // if ( v3 == 2 || (unsigned int)(v3 - 3) <= 2 || (unsigned int)(v3 - 7) <= 1 || v3 == 9 ) if (weaponType < WEAPONTYPE_MOLOTOV) { SetCurrentWeapon(weaponType); return true; @@ -960,7 +980,7 @@ CPed::ClearPointGunAt(void) ClearLookFlag(); ClearAimFlag(); - m_ped_flagA8 = false; + bIsPointingGunAt = false; if (m_nPedState == PED_AIM_GUN) { RestorePreviousState(); weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); @@ -975,6 +995,478 @@ CPed::ClearPointGunAt(void) } } +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->bIsLow) { + 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->bIsLow) + 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; + + if (ped->m_vehEnterType <= VEHICLE_ENTER_REAR_LEFT) { + switch (ped->m_vehEnterType) { + case VEHICLE_ENTER_FRONT_RIGHT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_FR; + break; + case VEHICLE_ENTER_REAR_RIGHT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_RR; + break; + case VEHICLE_ENTER_FRONT_LEFT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_FL; + break; + case VEHICLE_ENTER_REAR_LEFT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_RL; + break; + default: + break; + } + } + + 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->bIsLow) { + 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 = *vehMat.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.x = 0.0; + m_vecMoveSpeed.y = 0.0; + m_vecMoveSpeed.z = 0.0; + } + 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 + atan2(-veh->GetForward().x, veh->GetForward().y); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5 * PI + atan2(-veh->GetForward().x, veh->GetForward().y); + } else { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } + } else if (m_vehEnterType == VEHICLE_ENTER_FRONT_LEFT || m_vehEnterType == VEHICLE_ENTER_REAR_LEFT) { + if (vehIsUpsideDown) { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } else if (veh->bIsBus) { + m_fRotationDest = -0.5 * PI + atan2(-veh->GetForward().x, veh->GetForward().y); + } else { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } + } + + 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; + + if (!veh->bIsBus || (veh->bIsBus && vehIsUpsideDown)) { + float vehNextZSpeed = m_vecMoveSpeed.z - 0.008f * CTimer::GetTimeStep(); + + if (neededPos.z + vehNextZSpeed > autoZPos.z) { + m_vecMoveSpeed.z = vehNextZSpeed; + veh->ApplyMoveSpeed(); + } 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_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); + } + } + } + } + + // I hope + bool notInWater = false; + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) + notInWater = veh->m_vehType != VEHICLE_TYPE_BOAT || m_ped_flagG10; + + if (!notInWater) { + m_fRotationCur = m_fRotationDest; + } else { + float limitedAngle = CGeneral::LimitRadianAngle(m_fRotationDest); + float timeUntilStateChange = (m_nPedStateTimer - CTimer::GetTimeInMilliseconds()) / 500.0f; // * 0.0016666667f; + + 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) { + 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; + } +} + STARTPATCHES InjectHook(0x4CF8F0, &CPed::AddWeaponModel, PATCH_JUMP); InjectHook(0x4C6AA0, &CPed::AimGun, PATCH_JUMP); @@ -997,4 +1489,11 @@ STARTPATCHES 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); ENDPATCHES diff --git a/src/entities/Ped.h b/src/entities/Ped.h index 800c5bb9..187bd08c 100644 --- a/src/entities/Ped.h +++ b/src/entities/Ped.h @@ -12,6 +12,19 @@ struct CPathNode; +enum { + VEHICLE_ENTER_FRONT_RIGHT = 11, + VEHICLE_ENTER_REAR_RIGHT = 12, + VEHICLE_ENTER_FRONT_LEFT = 15, + VEHICLE_ENTER_REAR_LEFT = 16, +}; + +enum PedLineUpPhase { + LINE_UP_TO_CAR_START, + LINE_UP_TO_CAR_END, + LINE_UP_TO_CAR_2 +}; + enum PedOnGroundState { NO_PED, PED_BELOW_PLAYER, @@ -102,7 +115,7 @@ public: uint8 m_ped_flagA1 : 1; uint8 m_ped_flagA2 : 1; uint8 m_ped_flagA4 : 1; // stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime) - uint8 m_ped_flagA8 : 1; + uint8 bIsPointingGunAt : 1; uint8 bIsLooking : 1; uint8 m_ped_flagA20 : 1; // "look" method? - probably missing in SA uint8 bIsRestoringLook : 1; @@ -177,15 +190,15 @@ public: int32 m_pEventEntity; float m_fAngleToEvent; AnimBlendFrameData *m_pFrames[PED_NODE_MAX]; - int32 m_animGroup; - int32 m_pVehicleAnim; + AssocGroupId m_animGroup; + CAnimBlendAssociation *m_pVehicleAnim; CVector2D m_vecAnimMoveDelta; CVector m_vecOffsetSeek; CPedIK m_pedIK; uint8 stuff1[8]; uint32 m_nPedStateTimer; PedState m_nPedState; - int32 m_nLastPedState; + PedState m_nLastPedState; int32 m_nMoveState; int32 m_nStoredActionState; int32 m_nPrevActionState; @@ -207,7 +220,9 @@ public: uint8 stuff2[20]; float m_fRotationCur; float m_fRotationDest; - uint8 stuff13[6]; + uint32 m_headingRate; + uint16 m_vehEnterType; + uint16 m_walkAroundType; CEntity *m_pCurrentPhysSurface; CVector m_vecOffsetFromPhysSurface; CEntity *m_pCurSurface; @@ -222,7 +237,7 @@ public: CEntity *m_pCollidingEntity; uint8 stuff6[12]; CWeapon m_weapons[NUM_PED_WEAPONTYPES]; - int32 stuff7; + eWeaponType m_storedWeapon; uint8 m_currentWeapon; // eWeaponType uint8 m_maxWeaponTypeAllowed; // eWeaponType uint8 stuff[2]; @@ -253,6 +268,7 @@ public: bool IsPlayer(void); bool UseGroundColModel(void); + bool CanSetPedState(void); void AddWeaponModel(int id); void AimGun(void); void KillPedWithCar(CVehicle *veh, float impulse); @@ -278,14 +294,27 @@ public: void Duck(void); void ClearDuck(void); void ClearPointGunAt(void); + void BeingDraggedFromCar(void); + void RestartNonPartialAnims(void); + void LineUpPedWithCar(PedLineUpPhase phase); + void SetPedPositionInCar(void); + 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); static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data); static RwFrame *RecurseFrameChildrenVisibilityCB(RwFrame *frame, void *data); static void FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg); + static void PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg); + static void PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg); + static void PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg); bool HasWeapon(eWeaponType weaponType) { return m_weapons[weaponType].m_eWeaponType == weaponType; } CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } + static CVector &offsetToOpenRegularCarDoor; + static CVector &offsetToOpenLowCarDoor; + static CVector &offsetToOpenVanDoor; static bool &bNastyLimbsCheat; static bool &bPedCheat2; static bool &bPedCheat3; diff --git a/src/entities/Vehicle.cpp b/src/entities/Vehicle.cpp index bac05f7b..ced504a3 100644 --- a/src/entities/Vehicle.cpp +++ b/src/entities/Vehicle.cpp @@ -2,6 +2,8 @@ #include "patcher.h" #include "Vehicle.h" #include "Pools.h" +#include "CarCtrl.h" +#include "ModelIndices.h" bool &CVehicle::bWheelsOnlyCheat = *(bool *)0x95CD78; bool &CVehicle::bAllDodosCheat = *(bool *)0x95CD75; @@ -11,3 +13,53 @@ bool &CVehicle::bCheat5 = *(bool *)0x95CD64; void *CVehicle::operator new(size_t sz) { return CPools::GetVehiclePool()->New(); } void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); } + +bool +CVehicle::IsLawEnforcementVehicle(void) +{ + switch (m_modelIndex) { + case MI_FBICAR: + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + case MI_RHINO: + case MI_BARRACKS: + return true; + default: + return false; + } +} + +void +CVehicle::ChangeLawEnforcerState(bool enable) +{ + if (enable) { + if (!bIsLawEnforcer) { + bIsLawEnforcer = true; + CCarCtrl::NumLawEnforcerCars++; + } + } else { + if (bIsLawEnforcer) { + bIsLawEnforcer = false; + CCarCtrl::NumLawEnforcerCars--; + } + } +} + +void +CVehicle::RemoveDriver(void) +{ + m_status = STATUS_ABANDONED; + pDriver = nil; +} + +bool +CVehicle::IsUpsideDown(void) +{ + return GetUp().z <= -0.9f; +} + +STARTPATCHES + InjectHook(0x552820, &CVehicle::ChangeLawEnforcerState, PATCH_JUMP); + InjectHook(0x5520A0, &CVehicle::RemoveDriver, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/entities/Vehicle.h b/src/entities/Vehicle.h index e5d1cfb3..f11e9e97 100644 --- a/src/entities/Vehicle.h +++ b/src/entities/Vehicle.h @@ -3,23 +3,53 @@ #include "Physical.h" class CPed; +class CFire; +struct tHandlingData; + +enum { + GETTING_IN_OUT_FL = 1, + GETTING_IN_OUT_RL = 2, + GETTING_IN_OUT_FR = 4, + GETTING_IN_OUT_RR = 8 +}; + +enum eCarLock : uint8 { + CARLOCK_NOT_USED, + CARLOCK_UNLOCKED, + CARLOCK_LOCKED, + CARLOCK_LOCKOUT_PLAYER_ONLY, + CARLOCK_LOCKED_PLAYER_INSIDE, + CARLOCK_COP_CAR, + CARLOCK_FORCE_SHUT_DOORS, + CARLOCK_SKIP_SHUT_DOORS +}; class CVehicle : public CPhysical { public: // 0x128 - uint8 stuff1[116]; + tHandlingData *m_handling; + uint8 stuff1[112]; uint8 m_currentColour1; uint8 m_currentColour2; -uint8 m_extra1; -uint8 m_extra2; - int16 m_nAlarmState; + uint8 m_anExtras[2]; + int16 m_nAlarmState; // m_nWantedStarsOnEnter on DK22 + int16 m_nMissionValue; CPed *pDriver; CPed *pPassengers[8]; - uint8 stuff2[24]; + uint8 m_nNumPassengers; + int8 m_nNumGettingIn; + int8 m_nGettingInFlags; + int8 m_nGettingOutFlags; + uint8 m_nNumMaxPassengers; + char field_1CD[19]; CEntity *m_pCurSurface; - uint8 stuff3[17]; - uint8 m_veh_flagA1 : 1; + CFire *m_pCarFire; + float m_fSteerAngle; + float m_fGasPedal; + float m_fBreakPedal; + uint8 m_nCreatedBy; // eVehicleCreatedBy + uint8 bIsLawEnforcer : 1; uint8 m_veh_flagA2 : 1; uint8 m_veh_flagA4 : 1; uint8 m_veh_flagA8 : 1; @@ -27,10 +57,10 @@ uint8 m_extra2; uint8 m_veh_flagA20 : 1; uint8 m_veh_flagA40 : 1; uint8 m_veh_flagA80 : 1; - uint8 m_veh_flagB1 : 1; - uint8 m_veh_flagB2 : 1; - uint8 m_veh_flagB4 : 1; - uint8 m_veh_flagB8 : 1; + uint8 bIsVan : 1; + uint8 bIsBus : 1; + uint8 bIsBig : 1; + uint8 bIsLow : 1; uint8 m_veh_flagB10 : 1; uint8 m_veh_flagB20 : 1; uint8 m_veh_flagB40 : 1; @@ -51,8 +81,34 @@ uint8 m_extra2; uint8 m_veh_flagD20 : 1; uint8 m_veh_flagD40 : 1; uint8 m_veh_flagD80 : 1; - uint8 stuff4[139]; - int32 m_vehType; + int8 field_1F9; + uint8 m_nAmmoInClip[1]; // Used to make the guns on boat do a reload (20 by default) + int8 field_1FB; + int8 field_1FC[4]; + float m_fHealth; // 1000.0f = full health. 0 -> explode + uint8 m_nCurrentGear; + int8 field_205[3]; + int field_208; + uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats) + uint32 m_nTimeOfDeath; + int16 field_214; + int16 m_nBombTimer; // goes down with each frame + CPed *m_pWhoDetonatedMe; + float field_21C; + float field_220; + eCarLock m_nDoorLock; + int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage + int8 m_nRadioStation; + int8 field_22A; + int8 field_22B; + uint8 m_nCarHornTimer; + int8 field_22D; + uint8 m_nSirenOrAlarm; + int8 field_22F; + CStoredCollPoly m_frontCollPoly; // poly which is under front part of car + CStoredCollPoly m_rearCollPoly; // poly which is under rear part of car + float m_fSteerRatio; + eVehicleType m_vehType; static void *operator new(size_t); static void operator delete(void*, size_t); @@ -62,6 +118,10 @@ uint8 m_extra2; bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } + bool IsLawEnforcementVehicle(void); + void ChangeLawEnforcerState(bool enable); + void RemoveDriver(void); + bool IsUpsideDown(void); static bool &bWheelsOnlyCheat; static bool &bAllDodosCheat; @@ -69,5 +129,7 @@ uint8 m_extra2; static bool &bCheat4; static bool &bCheat5; }; + static_assert(sizeof(CVehicle) == 0x288, "CVehicle: error"); static_assert(offsetof(CVehicle, m_pCurSurface) == 0x1E0, "CVehicle: error"); +static_assert(offsetof(CVehicle, m_nAlarmState) == 0x1A0, "CVehicle: error"); diff --git a/src/math/Matrix.h b/src/math/Matrix.h index a93de636..74e3d70d 100644 --- a/src/math/Matrix.h +++ b/src/math/Matrix.h @@ -145,6 +145,30 @@ public: m_matrix.pos.y = 0.0f; m_matrix.pos.z = 0.0f; } + void SetRotate(float xAngle, float yAngle, float zAngle) { + float cX = cos(xAngle); + float sX = sin(xAngle); + float cY = cos(yAngle); + float sY = sin(yAngle); + float cZ = cos(zAngle); + float sZ = sin(zAngle); + + m_matrix.right.x = cZ * cY - (sZ * sX) * sY; + m_matrix.right.y = (cZ * sX) * sY + sZ * cY; + m_matrix.right.z = -cX * sY; + + m_matrix.up.x = -sZ * cX; + m_matrix.up.y = cZ * cX; + m_matrix.up.z = sX; + + m_matrix.at.x = (sZ * sX) * cY + cZ * sY; + m_matrix.at.y = sZ * sY - (cZ * sX) * cY; + m_matrix.at.z = cX * cY; + + m_matrix.pos.x = 0.0f; + m_matrix.pos.y = 0.0f; + m_matrix.pos.z = 0.0f; + } void Reorthogonalise(void){ CVector &r = *GetRight(); CVector &f = *GetForward(); diff --git a/src/modelinfo/VehicleModelInfo.h b/src/modelinfo/VehicleModelInfo.h index 8068d359..2e2f1be2 100644 --- a/src/modelinfo/VehicleModelInfo.h +++ b/src/modelinfo/VehicleModelInfo.h @@ -3,7 +3,6 @@ #include "ClumpModelInfo.h" enum { - NUM_VEHICLE_POSITIONS = 10, NUM_FIRST_MATERIALS = 26, NUM_SECOND_MATERIALS = 26, NUM_VEHICLE_COLOURS = 8, @@ -25,7 +24,7 @@ enum { ATOMIC_FLAG_NOCULL = 0x800, }; -enum { +enum eVehicleType { VEHICLE_TYPE_CAR, VEHICLE_TYPE_BOAT, VEHICLE_TYPE_TRAIN, @@ -46,6 +45,13 @@ enum { NUM_VEHICLE_CLASSES }; +enum { + VEHICLE_DUMMY_BOAT_RUDDER = 0, + VEHICLE_DUMMY_FRONT_SEATS = 2, + VEHICLE_DUMMY_REAR_SEATS = 3, + NUM_VEHICLE_POSITIONS = 10 +}; + class CVehicleModelInfo : public CClumpModelInfo { public: diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h index 6009a549..aebcb2c6 100644 --- a/src/weapons/Weapon.h +++ b/src/weapons/Weapon.h @@ -18,7 +18,8 @@ enum eWeaponType WEAPONTYPE_DETONATOR, NUM_PED_WEAPONTYPES = 13, WEAPONTYPE_HELICANNON = 13, - NUM_WEAPONTYPES + NUM_WEAPONTYPES, + NO_STORED_WEAPON = 22 }; enum eWeaponFire { -- cgit v1.2.3