diff options
author | aap <aap@papnet.eu> | 2019-07-05 14:34:41 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-05 14:34:41 +0200 |
commit | 9d13ab28a8a625a60a38729129c8d2c8e02e7b8f (patch) | |
tree | 86d117096a709da2f395c3c6145fc1f7791e3140 /src/entities | |
parent | More AudioManager (diff) | |
parent | implemented some CVehicle functions (diff) | |
download | re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar.gz re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar.bz2 re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar.lz re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar.xz re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.tar.zst re3-9d13ab28a8a625a60a38729129c8d2c8e02e7b8f.zip |
Diffstat (limited to 'src/entities')
-rw-r--r-- | src/entities/Ped.cpp | 551 | ||||
-rw-r--r-- | src/entities/Ped.h | 123 | ||||
-rw-r--r-- | src/entities/PedIK.cpp | 56 | ||||
-rw-r--r-- | src/entities/PedIK.h | 4 | ||||
-rw-r--r-- | src/entities/Physical.h | 6 | ||||
-rw-r--r-- | src/entities/PlayerPed.h | 2 | ||||
-rw-r--r-- | src/entities/Vehicle.cpp | 398 | ||||
-rw-r--r-- | src/entities/Vehicle.h | 249 |
8 files changed, 1233 insertions, 156 deletions
diff --git a/src/entities/Ped.cpp b/src/entities/Ped.cpp index 4d55de15..df965f32 100644 --- a/src/entities/Ped.cpp +++ b/src/entities/Ped.cpp @@ -1,5 +1,6 @@ #include "common.h" #include "patcher.h" +#include "main.h" #include "Pools.h" #include "Particle.h" #include "Stats.h" @@ -18,6 +19,10 @@ #include "Weather.h" #include "CullZones.h" #include "Population.h" +#include "Renderer.h" +#include "Lights.h" +#include "PointLights.h" +#include "Pad.h" WRAPPER void CPed::QuitEnteringCar() { EAXJMP(0x4E0E00); } WRAPPER void CPed::KillPedWithCar(CVehicle* veh, float impulse) { EAXJMP(0x4EC430); } @@ -30,11 +35,8 @@ WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation* dra 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::Teleport(CVector) { EAXJMP(0x4D3E70); } WRAPPER void CPed::PreRender(void) { EAXJMP(0x4CFDD0); } WRAPPER void CPed::Render(void) { EAXJMP(0x4D03F0); } -WRAPPER bool CPed::SetupLighting(void) { EAXJMP(0x4A7D30); } -WRAPPER void CPed::RemoveLighting(bool) { EAXJMP(0x4A7DC0); } WRAPPER int32 CPed::ProcessEntityCollision(CEntity*, CColPoint*) { EAXJMP(0x4CBB30); } WRAPPER void CPed::SetMoveAnim(void) { EAXJMP(0x4C5A40); } @@ -102,7 +104,7 @@ CPed::FlagToDestroyWhenNextProcessed(void) } bInVehicle = false; m_pMyVehicle = nil; - if (m_nCreatedBy == 2) /* TODO: enum (MISSION) */ + if (CharCreatedBy == MISSION_CHAR) m_nPedState = PED_DEAD; else m_nPedState = PED_NONE; @@ -279,12 +281,12 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_talkTimer = 0; m_talkTypeLast = 167; m_talkType = 167; - m_objective = 0; - m_prevObjective = 0; - m_nCreatedBy = 1; - field_180 = 0; - m_field_16C = nil; - field_170 = 0; + 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; @@ -307,8 +309,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_lastHitTime = 0; m_hitRecoverTimer = 0; field_4E8 = 0; - m_movedX = 0; - m_movedY = 0; + m_moved = CVector2D(0.0f, 0.0f); m_fRotationCur = 0.0f; m_headingRate = 15.0f; m_fRotationDest = 0.0f; @@ -334,13 +335,13 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_pCollidingEntity = nil; m_nPedState = PED_IDLE; m_nLastPedState = PED_NONE; - m_nMoveState = 1; + m_nMoveState = PEDMOVE_STILL; m_nStoredActionState = 0; m_pFire = nil; m_pPointGunAt = nil; - m_pLookTarget = 0; + m_pLookTarget = nil; m_fLookDirection = 0.0f; - m_pCurSurface = 0; + m_pCurSurface = nil; m_targetUnused = nil; m_nPathNodes = 0; m_nCurPathNode = 0; @@ -630,6 +631,21 @@ CPed::CanSetPedState(void) 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 + && !m_ped_flagB8 && !m_ped_flagB10 + && 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) { @@ -847,7 +863,7 @@ CPed::Avoid(void) 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_field_16C) { + 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)) { @@ -1325,13 +1341,13 @@ CPed::BeingDraggedFromCar(void) 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) { + } 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->bIsLow) + if (m_pMyVehicle->bLowVehicle) enterAnim = ANIM_CAR_LJACKED_RHS; else enterAnim = ANIM_CAR_JACKED_RHS; @@ -1454,7 +1470,7 @@ CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enter vehDoorOffset = offsetToOpenVanDoor; } else { seatOffset = veh->m_handling->fSeatOffsetDistance * seatPosMult; - if (veh->bIsLow) { + if (veh->bLowVehicle) { vehDoorOffset = offsetToOpenLowCarDoor; } else { vehDoorOffset = offsetToOpenRegularCarDoor; @@ -2037,15 +2053,496 @@ CPed::SetModelIndex(uint32 mi) RpAnimBlendClumpInit((RpClump*) m_rwObject); RpAnimBlendClumpFillFrameArray((RpClump*) m_rwObject, m_pFrames); CPedModelInfo *modelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex); - SetPedStats(static_cast<ePedStats>(modelInfo->m_pedStatType)); + SetPedStats((ePedStats) modelInfo->m_pedStatType); m_headingRate = m_pedStats->m_headingChangeRate; - m_animGroup = static_cast<AssocGroupId>(modelInfo->m_animGroup); + 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; + } + } +} + 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); } @@ -2090,7 +2587,6 @@ STARTPATCHES InjectHook(0x4E6BA0, &CPed::Attack, PATCH_JUMP); InjectHook(0x4CF980, &CPed::RemoveWeaponModel, PATCH_JUMP); InjectHook(0x4CFA60, &CPed::SetCurrentWeapon, PATCH_JUMP); - InjectHook(0x4DD920, &CPed::SelectGunIfArmed, PATCH_JUMP); InjectHook(0x4E4A10, &CPed::Duck, PATCH_JUMP); InjectHook(0x4E4A30, &CPed::ClearDuck, PATCH_JUMP); InjectHook(0x4E6180, &CPed::ClearPointGunAt, PATCH_JUMP); @@ -2104,7 +2600,16 @@ STARTPATCHES InjectHook(0x4CC6C0, &CPed::PlayFootSteps, PATCH_JUMP); InjectHook(0x4C5350, &CPed::BuildPedLists, PATCH_JUMP); InjectHook(0x4CF9B0, &CPed::GiveWeapon, PATCH_JUMP); - InjectHook(0x4C5330, &CPed::SetPedStats, 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(0x4D83E0, (void (CPed::*)(eObjective, void*)) &CPed::SetObjective, PATCH_JUMP); ENDPATCHES diff --git a/src/entities/Ped.h b/src/entities/Ped.h index 92fa32c1..0e2b14a7 100644 --- a/src/entities/Ped.h +++ b/src/entities/Ped.h @@ -13,6 +13,45 @@ struct CPathNode; +enum eObjective { + 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 { VEHICLE_ENTER_FRONT_RIGHT = 11, VEHICLE_ENTER_REAR_RIGHT = 12, @@ -20,6 +59,11 @@ enum { VEHICLE_ENTER_REAR_LEFT = 16, }; +enum { + RANDOM_CHAR = 1, + MISSION_CHAR, +}; + enum PedLineUpPhase { LINE_UP_TO_CAR_START, LINE_UP_TO_CAR_END, @@ -86,18 +130,18 @@ enum PedState PED_PASSENGER, PED_TAXI_PASSENGER, PED_OPEN_DOOR, - PED_DIE = 48, - PED_DEAD = 49, + PED_DIE, + PED_DEAD, PED_CARJACK, PED_DRAG_FROM_CAR, PED_ENTER_CAR, PED_STEAL_CAR, PED_EXIT_CAR, PED_HANDS_UP, - PED_ARRESTED = 56, + PED_ARRESTED, }; -enum { +enum eMoveState { PEDMOVE_NONE, PEDMOVE_STILL, PEDMOVE_WALK, @@ -113,6 +157,8 @@ 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) @@ -133,7 +179,7 @@ public: uint8 m_ped_flagC1 : 1; uint8 m_ped_flagC2 : 1; - uint8 m_ped_flagC4 : 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? @@ -146,7 +192,7 @@ public: uint8 m_ped_flagD8 : 1; uint8 m_ped_flagD10 : 1; uint8 m_ped_flagD20 : 1; - uint8 m_ped_flagD40 : 1; + uint8 m_ped_flagD40 : 1; // reset when objective changes uint8 m_ped_flagD80 : 1; uint8 m_ped_flagE1 : 1; @@ -193,18 +239,19 @@ public: uint8 m_ped_flagI20 : 1; uint8 m_ped_flagI40 : 1; uint8 m_ped_flagI80 : 1; + uint8 stuff10[3]; - uint8 m_nCreatedBy; + uint8 CharCreatedBy; uint8 field_161; uint8 pad_162[2]; - uint32 m_objective; - uint32 m_prevObjective; - CPed* m_field_16C; - uint32 field_170; + eObjective m_objective; + eObjective m_prevObjective; + CPed *m_pedInObjective; + CVehicle *m_carInObjective; uint32 field_174; uint32 field_178; uint32 field_17C; - uint32 field_180; + CPed *m_leader; uint32 m_pedFormation; uint32 m_fearFlags; CEntity *m_threatEntity; @@ -223,7 +270,7 @@ public: uint32 m_nPedStateTimer; PedState m_nPedState; PedState m_nLastPedState; - int32 m_nMoveState; + eMoveState m_nMoveState; int32 m_nStoredActionState; int32 m_nPrevActionState; int32 m_nWaitState; @@ -246,8 +293,7 @@ public: uint16 m_routeType; uint16 m_routeCurDir; uint16 field_2D2; - float m_movedX; - float m_movedY; + CVector2D m_moved; float m_fRotationCur; float m_fRotationDest; float m_headingRate; @@ -304,7 +350,7 @@ public: uint32 m_attackTimer; uint32 m_lastHitTime; uint32 m_hitRecoverTimer; - uint32 field_4E0; + uint32 m_objectiveTimer; uint32 m_duckTimer; uint32 field_4E8; int32 m_bloodyFootprintCount; @@ -345,9 +391,6 @@ public: CPed* ctor(uint32 pedType) { return ::new (this) CPed(pedType); } void dtor(void) { this->CPed::~CPed(); } - bool IsPlayer(void); - bool UseGroundColModel(void); - bool CanSetPedState(void); void AddWeaponModel(int id); void AimGun(void); void KillPedWithCar(CVehicle *veh, float impulse); @@ -369,7 +412,6 @@ public: bool IsPedHeadAbovePos(float zOffset); void RemoveWeaponModel(int modelId); void SetCurrentWeapon(uint32 weaponType); - bool SelectGunIfArmed(void); void Duck(void); void ClearDuck(void); void ClearPointGunAt(void); @@ -379,14 +421,22 @@ public: void SetPedPositionInCar(void); void PlayFootSteps(void); void QuitEnteringCar(void); - bool IsPointerValid(void); - void SortPeds(CPed**, int, int); void BuildPedLists(void); uint32 GiveWeapon(eWeaponType weaponType, uint32 ammo); - void SetPedStats(ePedStats); + void CalculateNewOrientation(void); + float WorkOutHeadingForMovingFirstPerson(float); + void CalculateNewVelocity(void); + bool CanPedJumpThis(int32); + bool CanSeeEntity(CEntity*, float); + void RestorePreviousObjective(void); + void SetObjective(eObjective, 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); @@ -416,14 +466,39 @@ public: 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); + inline bool HasWeapon(uint8 weaponType) { return m_weapons[weaponType].m_eWeaponType == weaponType; } inline CWeapon &GetWeapon(uint8 weaponType) { return m_weapons[weaponType]; } inline CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } inline RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } + 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; @@ -447,6 +522,6 @@ 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_field_16C) == 0x16C, "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/entities/PedIK.cpp b/src/entities/PedIK.cpp index fa773bbf..b9baf49c 100644 --- a/src/entities/PedIK.cpp +++ b/src/entities/PedIK.cpp @@ -5,6 +5,8 @@ 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) { @@ -21,6 +23,59 @@ CPedIK::CPedIK(CPed *ped) } 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; @@ -50,4 +105,5 @@ CPedIK::GetWorldMatrix(RwFrame *source, RwMatrix *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/entities/PedIK.h b/src/entities/PedIK.h index 7c798dc2..e17d52eb 100644 --- a/src/entities/PedIK.h +++ b/src/entities/PedIK.h @@ -1,6 +1,7 @@ #pragma once #include "common.h" #include "PedModelInfo.h" +#include "AnimBlendClumpData.h" struct LimbOrientation { @@ -32,5 +33,8 @@ public: 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/entities/Physical.h b/src/entities/Physical.h index 7776da92..0f517cf3 100644 --- a/src/entities/Physical.h +++ b/src/entities/Physical.h @@ -109,9 +109,9 @@ public: // Force actually means Impulse here void ApplyMoveForce(float jx, float jy, float jz); void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); } - // v(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied - void ApplyTurnForce(float jx, float jy, float jz, float rx, float ry, float rz); - // v is direction of force, p is point relative to model center where force is applied + // j(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied + void ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz); + // j is direction of force, p is point relative to model center where force is applied void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); } void ApplyFrictionMoveForce(float jx, float jy, float jz); void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); } diff --git a/src/entities/PlayerPed.h b/src/entities/PlayerPed.h index d09deb49..15ad74a6 100644 --- a/src/entities/PlayerPed.h +++ b/src/entities/PlayerPed.h @@ -36,7 +36,7 @@ public: int8 field_1415; CVector field_1416[6]; int32 field_1488[6]; - float field_1512; + float m_fWalkAngle; float m_fFPSMoveHeading; ~CPlayerPed(); diff --git a/src/entities/Vehicle.cpp b/src/entities/Vehicle.cpp index f7c4b065..25bede65 100644 --- a/src/entities/Vehicle.cpp +++ b/src/entities/Vehicle.cpp @@ -1,9 +1,17 @@ #include "common.h" +#include "main.h" #include "patcher.h" +#include "Timer.h" #include "Vehicle.h" #include "Pools.h" +#include "HandlingMgr.h" #include "CarCtrl.h" +#include "Population.h" #include "ModelIndices.h" +#include "World.h" +#include "Lights.h" +#include "PointLights.h" +#include "Renderer.h" #include "DMAudio.h" #include "Radar.h" @@ -40,30 +48,238 @@ CVehicle::~CVehicle() CCarCtrl::NumAmbulancesOnDuty--; bIsAmbulanceOnDuty = false; } - if (bIsFiretruckOnDuty){ + if (bIsFireTruckOnDuty){ CCarCtrl::NumFiretrucksOnDuty--; - bIsFiretruckOnDuty = false; + bIsFireTruckOnDuty = false; } } +void +CVehicle::SetModelIndex(uint32 id) +{ + CEntity::SetModelIndex(id); + m_aExtras[0] = CVehicleModelInfo::ms_compsUsed[0]; + m_aExtras[1] = CVehicleModelInfo::ms_compsUsed[1]; + m_nNumMaxPassengers = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(id); +} + +bool +CVehicle::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + + if(bRenderScorched){ + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + }else{ + CVector coors = GetPosition(); + float lighting = CPointLights::GenerateLightsAffectingObject(&coors); + if(!bHasBlip && lighting != 1.0f){ + SetAmbientAndDirectionalColours(lighting); + return true; + } + } + + return false; +} + +void +CVehicle::RemoveLighting(bool reset) +{ + CRenderer::RemoveVehiclePedLights(this, reset); +} + +float +CVehicle::GetHeightAboveRoad(void) +{ + return -1.0f * CModelInfo::GetModelInfo(GetModelIndex())->GetColModel()->boundingBox.min.z; +} + + bool CVehicle::IsLawEnforcementVehicle(void) { - switch (m_modelIndex) { - case MI_FBICAR: + switch(GetModelIndex()){ + case MI_FBICAR: + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + case MI_RHINO: + case MI_BARRACKS: + return true; + default: + return false; + } +} + +bool +CVehicle::UsesSiren(uint32 id) +{ + switch(id){ + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_FBICAR: + case MI_MRWHOOP: + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + return true; + default: + return false; + } +} + +bool +CVehicle::IsVehicleNormal(void) +{ + if(pDriver && m_nNumPassengers == 0 && m_status != STATUS_WRECKED){ + switch(GetModelIndex()) + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_TAXI: case MI_POLICE: case MI_ENFORCER: - case MI_PREDATOR: + case MI_BUS: case MI_RHINO: case MI_BARRACKS: - return true; - default: + case MI_DODO: + case MI_COACH: + case MI_CABBIE: + case MI_RCBANDIT: + case MI_BORGNINE: + return false; + } + return false; +} + +bool +CVehicle::CarHasRoof(void) +{ + if((m_handling->Flags & HANDLING_HAS_NO_ROOF) == 0) + return true; + if(m_aExtras[0] && m_aExtras[1]) + return false; + return true; +} + +bool +CVehicle::IsUpsideDown(void) +{ + if(GetUp().z > -0.9f) + return false; + return true; +} + +bool +CVehicle::IsOnItsSide(void) +{ + if(GetRight().z < 0.8f && GetRight().z > -0.8f) + return false; + return true; +} + +bool +CVehicle::CanBeDeleted(void) +{ + int i; + + if(m_nNumGettingIn || m_nGettingOutFlags) + return false; + + if(pDriver){ + // This looks like it was inlined + if(pDriver->CharCreatedBy == MISSION_CHAR) + return false; + if(pDriver->GetPedState() != PED_DRIVING && + pDriver->GetPedState() != PED_DEAD) + return false; + } + + for(i = 0; i < 8; i++){ + // Same check as above + if(pPassengers[i]){ + if(pPassengers[i]->CharCreatedBy == MISSION_CHAR) + return false; + if(pPassengers[i]->GetPedState() != PED_DRIVING && + pPassengers[i]->GetPedState() != PED_DEAD) + return false; + } + // and then again... probably because something was inlined + if(pPassengers[i]){ + if(pPassengers[i]->GetPedState() != PED_DRIVING && + pPassengers[i]->GetPedState() != PED_DEAD) + return false; + } + } + + switch(VehicleCreatedBy){ + case RANDOM_VEHICLE: return true; + case MISSION_VEHICLE: return false; + case PARKED_VEHICLE: return true; + case PERMANENT_VEHICLE: return false; + } + return true; +} + +bool +CVehicle::CanPedOpenLocks(CPed *ped) +{ + if(m_nDoorLock == CARLOCK_LOCKED || + m_nDoorLock == CARLOCK_COP_CAR || + m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE) + return false; + if(ped->IsPlayer() && m_nDoorLock == CARLOCK_LOCKOUT_PLAYER_ONLY) + return false; + return true; +} + +bool +CVehicle::CanPedEnterCar(void) +{ + CVector up = GetUp(); + // can't enter when car is on side + if(up.z > 0.1f || up.z < -0.1f){ + // also when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f)) + return false; + if(m_vecTurnSpeed.MagnitudeSqr() > sq(0.2f)) + return false; + return true; + } + return false; +} + +bool +CVehicle::CanPedExitCar(void) +{ + CVector up = GetUp(); + if(up.z > 0.1f || up.z < -0.1f){ + // can't exit when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f) + return false; + // if car is slow enough, check turn speed + if(fabs(m_vecTurnSpeed.x) > 0.01f || + fabs(m_vecTurnSpeed.y) > 0.01f || + fabs(m_vecTurnSpeed.z) > 0.01f) + return false; + return true; + }else{ + // What is this? just > replaced by >= ?? + + // can't exit when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() >= 0.005f) return false; + // if car is slow enough, check turn speed + if(fabs(m_vecTurnSpeed.x) >= 0.01f || + fabs(m_vecTurnSpeed.y) >= 0.01f || + fabs(m_vecTurnSpeed.z) >= 0.01f) + return false; + return true; } } void -CVehicle::ChangeLawEnforcerState(bool enable) +CVehicle::ChangeLawEnforcerState(uint8 enable) { if (enable) { if (!bIsLawEnforcer) { @@ -78,6 +294,111 @@ CVehicle::ChangeLawEnforcerState(bool enable) } } +CPed* +CVehicle::SetUpDriver(void) +{ + if(pDriver) + return pDriver; + if(VehicleCreatedBy != RANDOM_VEHICLE) + return nil; + + pDriver = CPopulation::AddPedInCar(this); + pDriver->m_pMyVehicle = this; + pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle); + pDriver->bInVehicle = true; + pDriver->SetPedState(PED_DRIVING); + if(bIsBus) + pDriver->m_ped_flagC4 = false; + return pDriver; +} + +CPed* +CVehicle::SetupPassenger(int n) +{ + if(pPassengers[n]) + return pPassengers[n]; + + pPassengers[n] = CPopulation::AddPedInCar(this); + pPassengers[n]->m_pMyVehicle = this; + pPassengers[n]->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle); + pPassengers[n]->bInVehicle = true; + pPassengers[n]->SetPedState(PED_DRIVING); + if(bIsBus) + pPassengers[n]->m_ped_flagC4 = false; + return pPassengers[n]; +} + +void +CVehicle::SetDriver(CPed *driver) +{ + pDriver = driver; + pDriver->RegisterReference((CEntity**)&pDriver); + + if(bFreebies && driver == FindPlayerPed()){ + if(GetModelIndex() == MI_AMBULAN) + FindPlayerPed()->m_fHealth = min(FindPlayerPed()->m_fHealth + 20.0f, 100.0f); + else if(GetModelIndex() == MI_TAXI) + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; + else if(GetModelIndex() == MI_POLICE) + driver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5); + else if(GetModelIndex() == MI_ENFORCER) + driver->m_fArmour = max(driver->m_fArmour, 100.0f); + else if(GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE) + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; + bFreebies = false; + } + + ApplyTurnForce(0.0f, 0.0f, -0.2f*driver->m_fMass, + driver->GetPosition().x - GetPosition().x, + driver->GetPosition().y - GetPosition().y, + 0.0f); +} + +bool +CVehicle::AddPassenger(CPed *passenger) +{ + int i; + + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); + + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i] == nil){ + pPassengers[i] = passenger; + m_nNumPassengers++; + return true; + } + return false; +} + +bool +CVehicle::AddPassenger(CPed *passenger, uint8 n) +{ + if(bIsBus) + return AddPassenger(passenger); + + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); + + if(n < m_nNumMaxPassengers && pPassengers[n] == nil){ + pPassengers[n] = passenger; + m_nNumPassengers++; + return true; + } + return false; +} + +void +CVehicle::RemoveDriver(void) +{ + m_status = STATUS_ABANDONED; + pDriver = nil; +} + void CVehicle::RemovePassenger(CPed *p) { @@ -101,19 +422,68 @@ CVehicle::RemovePassenger(CPed *p) } void -CVehicle::RemoveDriver(void) +CVehicle::ProcessCarAlarm(void) { - m_status = STATUS_ABANDONED; - pDriver = nil; + uint32 step; + + if(m_nAlarmState == 0 || m_nAlarmState == -1) + return; + + step = CTimer::GetTimeStep()/50.0f * 1000.0f; + if((uint16)m_nAlarmState < step) + m_nAlarmState = 0; + else + m_nAlarmState -= step; } bool -CVehicle::IsUpsideDown(void) +CVehicle::IsSphereTouchingVehicle(float sx, float sy, float sz, float radius) { - return GetUp().z <= -0.9f; + float x, y, z; + // sphere relative to vehicle + CVector sph = CVector(sx, sy, sz) - GetPosition(); + CColModel *colmodel = CModelInfo::GetModelInfo(GetModelIndex())->GetColModel(); + + x = DotProduct(sph, GetRight()); + if(colmodel->boundingBox.min.x - radius > x || + colmodel->boundingBox.max.x + radius < x) + return false; + y = DotProduct(sph, GetForward()); + if(colmodel->boundingBox.min.y - radius > y || + colmodel->boundingBox.max.y + radius < y) + return false; + z = DotProduct(sph, GetUp()); + if(colmodel->boundingBox.min.z - radius > z || + colmodel->boundingBox.max.z + radius < z) + return false; + + return true; } STARTPATCHES + InjectHook(0x551170, &CVehicle::SetModelIndex_, PATCH_JUMP); + InjectHook(0x4A7DD0, &CVehicle::SetupLighting_, PATCH_JUMP); + InjectHook(0x4A7E60, &CVehicle::RemoveLighting_, PATCH_JUMP); + InjectHook(0x417E60, &CVehicle::GetHeightAboveRoad_, PATCH_JUMP); + + InjectHook(0x552880, &CVehicle::IsLawEnforcementVehicle, PATCH_JUMP); InjectHook(0x552820, &CVehicle::ChangeLawEnforcerState, PATCH_JUMP); + InjectHook(0x552200, &CVehicle::UsesSiren, PATCH_JUMP); + InjectHook(0x5527E0, &CVehicle::IsVehicleNormal, PATCH_JUMP); + InjectHook(0x552B70, &CVehicle::CarHasRoof, PATCH_JUMP); + InjectHook(0x552230, &CVehicle::IsUpsideDown, PATCH_JUMP); + InjectHook(0x552260, &CVehicle::IsOnItsSide, PATCH_JUMP); + InjectHook(0x5511B0, &CVehicle::CanBeDeleted, PATCH_JUMP); + InjectHook(0x5522A0, &CVehicle::CanPedOpenLocks, PATCH_JUMP); + InjectHook(0x5522F0, &CVehicle::CanPedEnterCar, PATCH_JUMP); + InjectHook(0x5523C0, &CVehicle::CanPedExitCar, PATCH_JUMP); + InjectHook(0x5520C0, &CVehicle::SetUpDriver, PATCH_JUMP); + InjectHook(0x552160, &CVehicle::SetupPassenger, PATCH_JUMP); + InjectHook(0x551F20, &CVehicle::SetDriver, PATCH_JUMP); + InjectHook(0x551D90, (bool (CVehicle::*)(CPed*))&CVehicle::AddPassenger, PATCH_JUMP); + InjectHook(0x551E10, (bool (CVehicle::*)(CPed*,uint8))&CVehicle::AddPassenger, PATCH_JUMP); InjectHook(0x5520A0, &CVehicle::RemoveDriver, PATCH_JUMP); -ENDPATCHES
\ No newline at end of file + InjectHook(0x551EB0, &CVehicle::RemovePassenger, PATCH_JUMP); + InjectHook(0x5525A0, &CVehicle::ProcessCarAlarm, PATCH_JUMP); + InjectHook(0x552620, &CVehicle::IsSphereTouchingVehicle, PATCH_JUMP); +ENDPATCHES diff --git a/src/entities/Vehicle.h b/src/entities/Vehicle.h index 8739e5db..b69c828f 100644 --- a/src/entities/Vehicle.h +++ b/src/entities/Vehicle.h @@ -7,13 +7,21 @@ class CFire; struct tHandlingData; enum { + RANDOM_VEHICLE = 1, + MISSION_VEHICLE = 2, + PARKED_VEHICLE = 3, + PERMANENT_VEHICLE = 4, +}; + + +enum { GETTING_IN_OUT_FL = 1, GETTING_IN_OUT_RL = 2, GETTING_IN_OUT_FR = 4, GETTING_IN_OUT_RR = 8 }; -enum eCarLock : uint8 { +enum eCarLock { CARLOCK_NOT_USED, CARLOCK_UNLOCKED, CARLOCK_LOCKED, @@ -24,78 +32,82 @@ enum eCarLock : uint8 { CARLOCK_SKIP_SHUT_DOORS }; -enum eVehicleModel : int32 +// TODO: where is this used? Is Vehicle.h the right file? +enum eVehicleModel { - LANDSTAL = 0x0, - IDAHO = 0x1, - STINGER = 0x2, - LINERUN = 0x3, - PEREN = 0x4, - SENTINEL = 0x5, - PATRIOT = 0x6, - FIRETRUK = 0x7, - TRASH = 0x8, - STRETCH = 0x9, - MANANA = 0xA, - INFERNUS = 0xB, - BLISTA = 0xC, - PONY = 0xD, - MULE = 0xE, - CHEETAH = 0xF, - AMBULAN = 0x10, - FBICAR = 0x11, - MOONBEAM = 0x12, - ESPERANT = 0x13, - TAXI = 0x14, - KURUMA = 0x15, - BOBCAT = 0x16, - MRWHOOP = 0x17, - BFINJECT = 0x18, - CORPSE = 0x19, - POLICE = 0x1A, - ENFORCER = 0x1B, - SECURICA = 0x1C, - BANSHEE = 0x1D, - PREDATOR = 0x1E, - BUS = 0x1F, - RHINO = 0x20, - BARRACKS = 0x21, - TRAIN = 0x22, - CHOPPER = 0x23, - DODO = 0x24, - COACH = 0x25, - CABBIE = 0x26, - STALLION = 0x27, - RUMPO = 0x28, - RCBANDIT = 0x29, - BELLYUP = 0x2A, - MRWONGS = 0x2B, - MAFIA = 0x2C, - YARDIE = 0x2D, - YAKUZA = 0x2E, - DIABLOS = 0x2F, - COLUMB = 0x30, - HOODS = 0x31, - AIRTRAIN = 0x32, - DEADDODO = 0x33, - SPEEDER = 0x34, - REEFER = 0x35, - PANLANT = 0x36, - FLATBED = 0x37, - YANKEE = 0x38, - ESCAPE = 0x39, - BORGNINE = 0x3A, - TOYZ = 0x3B, - GHOST = 0x3C, - CAR151 = 0x3D, - CAR152 = 0x3E, - CAR153 = 0x3F, - CAR154 = 0x40, - CAR155 = 0x41, - CAR156 = 0x42, - CAR157 = 0x43, - CAR158 = 0x44, - CAR159 = 0x45, + LANDSTAL, + IDAHO, + STINGER, + LINERUN, + PEREN, + SENTINEL, + PATRIOT, + FIRETRUK, + TRASH, + STRETCH, + MANANA, + INFERNUS, + BLISTA, + PONY, + MULE, + CHEETAH, + AMBULAN, + FBICAR, + MOONBEAM, + ESPERANT, + TAXI, + KURUMA, + BOBCAT, + MRWHOOP, + BFINJECT, + CORPSE, + POLICE, + ENFORCER, + SECURICA, + BANSHEE, + PREDATOR, + BUS, + RHINO, + BARRACKS, + TRAIN, + CHOPPER, + DODO, + COACH, + CABBIE, + STALLION, + RUMPO, + RCBANDIT, + BELLYUP, + MRWONGS, + MAFIA, + YARDIE, + YAKUZA, + DIABLOS, + COLUMB, + HOODS, + AIRTRAIN, + DEADDODO, + SPEEDER, + REEFER, + PANLANT, + FLATBED, + YANKEE, + ESCAPE, + BORGNINE, + TOYZ, + GHOST, + CAR151, + CAR152, + CAR153, + CAR154, + CAR155, + CAR156, + CAR157, + CAR158, + CAR159, +}; + +enum eDoors { }; class CVehicle : public CPhysical @@ -103,10 +115,13 @@ class CVehicle : public CPhysical public: // 0x128 tHandlingData *m_handling; + + // CAutoPilot uint8 stuff1[112]; + uint8 m_currentColour1; uint8 m_currentColour2; - uint8 m_anExtras[2]; + uint8 m_aExtras[2]; int16 m_nAlarmState; // m_nWantedStarsOnEnter on DK22 int16 m_nMissionValue; CPed *pDriver; @@ -122,23 +137,27 @@ public: float m_fSteerAngle; float m_fGasPedal; float m_fBreakPedal; - uint8 m_nCreatedBy; // eVehicleCreatedBy - uint8 bIsLawEnforcer : 1; - uint8 bIsAmbulanceOnDuty : 1; - uint8 bIsFiretruckOnDuty : 1; - uint8 m_veh_flagA8 : 1; - uint8 m_veh_flagA10 : 1; - uint8 m_veh_flagA20 : 1; - uint8 m_veh_flagA40 : 1; - uint8 m_veh_flagA80 : 1; - uint8 bIsVan : 1; - uint8 bIsBus : 1; - uint8 bIsBig : 1; - uint8 bIsLow : 1; + uint8 VehicleCreatedBy; + + // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R* + uint8 bIsLawEnforcer: 1; // Is this guy chasing the player at the moment + uint8 bIsAmbulanceOnDuty: 1; // Ambulance trying to get to an accident + uint8 bIsFireTruckOnDuty: 1; // Firetruck trying to get to a fire + uint8 bIsLocked: 1; // Is this guy locked by the script (cannot be removed) + uint8 bEngineOn: 1; // For sound purposes. Parked cars have their engines switched off (so do destroyed cars) + uint8 bIsHandbrakeOn: 1; // How's the handbrake doing ? + uint8 bLightsOn: 1; // Are the lights switched on ? + uint8 bFreebies: 1; // Any freebies left in this vehicle ? + + uint8 bIsVan: 1; // Is this vehicle a van (doors at back of vehicle) + uint8 bIsBus: 1; // Is this vehicle a bus + uint8 bIsBig: 1; // Is this vehicle a bus + uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims uint8 m_veh_flagB10 : 1; uint8 m_veh_flagB20 : 1; uint8 m_veh_flagB40 : 1; uint8 m_veh_flagB80 : 1; + uint8 m_veh_flagC1 : 1; uint8 m_veh_flagC2 : 1; uint8 m_veh_flagC4 : 1; @@ -147,6 +166,7 @@ public: uint8 m_veh_flagC20 : 1; uint8 m_veh_flagC40 : 1; uint8 m_veh_flagC80 : 1; + uint8 m_veh_flagD1 : 1; uint8 m_veh_flagD2 : 1; uint8 m_veh_flagD4 : 1; @@ -155,8 +175,9 @@ public: uint8 m_veh_flagD20 : 1; uint8 m_veh_flagD40 : 1; uint8 m_veh_flagD80 : 1; + int8 field_1F9; - uint8 m_nAmmoInClip[1]; // Used to make the guns on boat do a reload (20 by default) + uint8 m_nAmmoInClip; // 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 @@ -179,6 +200,7 @@ public: int8 field_22D; uint8 m_nSirenOrAlarm; int8 field_22F; + // TODO: this is an array 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; @@ -190,8 +212,29 @@ public: static void operator delete(void*, int); ~CVehicle(void); + // from CEntity + void SetModelIndex(uint32 i); + bool SetupLighting(void); + void RemoveLighting(bool); + void FlagToDestroyWhenNextProcessed(void) {} - void dtor(void) { this->CVehicle::~CVehicle(); } + virtual void ProcessControlInputs(uint8) {} + virtual void GetComponentWorldPosition(int32 component, CVector &pos) {} + virtual bool IsComponentPresent(int32 component) { return false; } + virtual void SetComponentRotation(int32 component, CVector rotation) {} + virtual void OpenDoor(int32, eDoors door, float) {} + virtual void ProcessOpenDoor(uint32, uint32, float) {} + virtual bool IsDoorReady(eDoors door) { return false; } + virtual bool IsDoorFullyOpen(eDoors door) { return false; } + virtual bool IsDoorClosed(eDoors door) { return false; } + virtual bool IsDoorMissing(eDoors door) { return false; } + virtual void RemoveRefsToVehicle(CEntity *ent) {} + virtual void BlowUpCar(CEntity *ent) {} + virtual bool SetUpWheelColModel(CColModel *colModel) { return false; } + virtual void BurstTyre(uint8 tyre) {} + virtual bool IsRoomForPedToLeaveCar(uint32, CVector *) { return false;} + virtual float GetHeightAboveRoad(void); + virtual void PlayCarHorn(void) {} bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; } bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; } @@ -199,10 +242,26 @@ public: 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 RemovePassenger(CPed *); - void RemoveDriver(void); + void ChangeLawEnforcerState(uint8 enable); + bool UsesSiren(uint32 id); + bool IsVehicleNormal(void); + bool CarHasRoof(void); bool IsUpsideDown(void); + bool IsOnItsSide(void); + bool CanBeDeleted(void); + bool CanPedOpenLocks(CPed *ped); + bool CanPedEnterCar(void); + bool CanPedExitCar(void); + // do these two actually return something? + CPed *SetUpDriver(void); + CPed *SetupPassenger(int n); + void SetDriver(CPed *driver); + bool AddPassenger(CPed *passenger); + bool AddPassenger(CPed *passenger, uint8 n); + void RemovePassenger(CPed *passenger); + void RemoveDriver(void); + void ProcessCarAlarm(void); + bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius); static bool &bWheelsOnlyCheat; static bool &bAllDodosCheat; @@ -210,8 +269,16 @@ public: static bool &bCheat4; static bool &bCheat5; static bool &m_bDisableMouseSteering; + + + void dtor(void) { CVehicle::~CVehicle(); } + void SetModelIndex_(uint32 id) { CVehicle::SetModelIndex(id); } + bool SetupLighting_(void) { return CVehicle::SetupLighting(); } + void RemoveLighting_(bool reset) { CVehicle::RemoveLighting(reset); } + float GetHeightAboveRoad_(void) { return CVehicle::GetHeightAboveRoad(); } }; 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"); +static_assert(offsetof(CVehicle, m_nLastWeaponDamage) == 0x228, "CVehicle: error"); |