diff options
author | Fire-Head <Fire-Head@users.noreply.github.com> | 2019-08-02 22:23:05 +0200 |
---|---|---|
committer | Fire-Head <Fire-Head@users.noreply.github.com> | 2019-08-02 22:23:05 +0200 |
commit | 73e2a4b035e19ace0ad5c55c58a15ee1d8265fee (patch) | |
tree | eafd98b48c650c84139804ca89259ae61e00e3ea /src | |
parent | fix (diff) | |
parent | restore original shadows fading (diff) | |
download | re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar.gz re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar.bz2 re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar.lz re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar.xz re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.tar.zst re3-73e2a4b035e19ace0ad5c55c58a15ee1d8265fee.zip |
Diffstat (limited to '')
71 files changed, 4953 insertions, 487 deletions
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index ea54c9e6..ebf6a153 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -4037,7 +4037,7 @@ cAudioManager::ProcessEntity(int32 id) case AUDIOTYPE_BRIDGE: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessBridgeOneShots(); + cAudioManager::ProcessBridge(); } break; case AUDIOTYPE_FRONTEND: diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index da20dc31..907755b9 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -20,9 +20,9 @@ enum eSound : int16 SOUND_F = 15, SOUND_CAR_ENGINE_START = 16, SOUND_CAR_LIGHT_BREAK = 17, - SOUND_CAR_HYDRALIC_1 = 18, - SOUND_CAR_HYDRALIC_2 = 19, - SOUND_CAR_HYDRALIC_3 = 20, + SOUND_CAR_HYDRAULIC_1 = 18, + SOUND_CAR_HYDRAULIC_2 = 19, + SOUND_CAR_HYDRAULIC_3 = 20, SOUND_CAR_JERK = 21, SOUND_CAR_SPLASH = 22, SOUND_17 = 23, @@ -35,18 +35,18 @@ enum eSound : int16 SOUND_STEP_END = 30, SOUND_FALL_LAND = 31, SOUND_FALL_COLLAPSE = 32, - SOUND_21 = 33, - SOUND_22 = 34, - SOUND_23 = 35, - SOUND_24 = 36, - SOUND_25 = 37, - SOUND_26 = 38, - SOUND_WEAPON_PUNCH_ATTACK = 39, - SOUND_28 = 40, - SOUND_29 = 41, - SOUND_2A = 42, - SOUND_2B = 43, - SOUND_2C = 44, + SOUND_FIGHT_PUNCH_33 = 33, + SOUND_FIGHT_KICK_34 = 34, + SOUND_FIGHT_HEADBUTT_35 = 35, + SOUND_FIGHT_PUNCH_36 = 36, + SOUND_FIGHT_PUNCH_37 = 37, + SOUND_FIGHT_CLOSE_PUNCH_38 = 38, + SOUND_FIGHT_PUNCH_39 = 39, + SOUND_FIGHT_PUNCH_OR_KICK_BELOW_40 = 40, + SOUND_FIGHT_PUNCH_41 = 41, + SOUND_FIGHT_PUNCH_FROM_BEHIND_42 = 42, + SOUND_FIGHT_KNEE_OR_KICK_43 = 43, + SOUND_FIGHT_KICK_44 = 44, SOUND_2D = 45, SOUND_WEAPON_BAT_ATTACK = 46, SOUND_WEAPON_SHOT_FIRED = 47, diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp index 5e436c84..12b444a2 100644 --- a/src/control/CarCtrl.cpp +++ b/src/control/CarCtrl.cpp @@ -7,6 +7,9 @@ int &CCarCtrl::NumAmbulancesOnDuty = *(int*)0x885BB0; int &CCarCtrl::NumFiretrucksOnDuty = *(int*)0x9411F0; bool &CCarCtrl::bCarsGeneratedAroundCamera = *(bool*)0x95CD8A; float& CCarCtrl::CarDensityMultiplier = *(float*)0x5EC8B4; +int32 &CCarCtrl::NumMissionCars = *(int32*)0x8F1B54; +int32 &CCarCtrl::NumRandomCars = *(int32*)0x943118; +int32 &CCarCtrl::NumParkedCars = *(int32*)0x8F29E0; WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); } WRAPPER void CCarCtrl::AddToCarArray(int32 id, int32 vehclass) { EAXJMP(0x4182F0); } @@ -17,6 +20,7 @@ WRAPPER void CCarCtrl::JoinCarWithRoadSystem(CVehicle*) { EAXJMP(0x41F820); } WRAPPER void CCarCtrl::SteerAICarWithPhysics(CVehicle*) { EAXJMP(0x41DA60); } WRAPPER void CCarCtrl::UpdateCarOnRails(CVehicle*) { EAXJMP(0x418880); } WRAPPER void CCarCtrl::ScanForPedDanger(CVehicle *veh) { EAXJMP(0x418F40); } +WRAPPER void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* v) { EAXJMP(0x41F7A0); } bool CCarCtrl::MapCouldMoveInThisArea(float x, float y) diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h index 677dcf36..2ad52d49 100644 --- a/src/control/CarCtrl.h +++ b/src/control/CarCtrl.h @@ -15,10 +15,14 @@ public: static void UpdateCarOnRails(CVehicle*); static bool MapCouldMoveInThisArea(float x, float y); static void ScanForPedDanger(CVehicle *veh); + static void RemoveFromInterestingVehicleList(CVehicle*); static int32 &NumLawEnforcerCars; static int32 &NumAmbulancesOnDuty; static int32 &NumFiretrucksOnDuty; + static int32 &NumRandomCars; + static int32 &NumMissionCars; + static int32 &NumParkedCars; static bool &bCarsGeneratedAroundCamera; static float &CarDensityMultiplier; }; diff --git a/src/control/CarGen.cpp b/src/control/CarGen.cpp new file mode 100644 index 00000000..65a23c8c --- /dev/null +++ b/src/control/CarGen.cpp @@ -0,0 +1,308 @@ +#include "common.h" +#include "patcher.h" +#include "CarGen.h" + +#include "Automobile.h" +#include "Boat.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CutsceneMgr.h" +#include "General.h" +#include "Pools.h" +#include "Streaming.h" +#include "Timer.h" +#include "Vehicle.h" +#include "World.h" + +uint8 &CTheCarGenerators::ProcessCounter = *(uint8*)0x95CDAF; +uint32 &CTheCarGenerators::NumOfCarGenerators = *(uint32*)0x8E2C1C; +CCarGenerator (&CTheCarGenerators::CarGeneratorArray)[NUM_CARGENS] = *(CCarGenerator(*)[NUM_CARGENS])*(uintptr*)0x87CB18; +uint8 &CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter = *(uint8*)0x95CDC6; +uint32 &CTheCarGenerators::CurrentActiveCount = *(uint32*)0x8F2C5C; + +void CCarGenerator::SwitchOff() +{ + m_nUsesRemaining = 0; + --CTheCarGenerators::CurrentActiveCount; +} + +void CCarGenerator::SwitchOn() +{ + m_nUsesRemaining = -1; + m_nTimer = CalcNextGen(); + ++CTheCarGenerators::CurrentActiveCount; +} + +uint32 CCarGenerator::CalcNextGen() +{ + return CTimer::GetTimeInMilliseconds() + 4; +} + +void CCarGenerator::DoInternalProcessing() +{ + if (CheckForBlockage()) { + m_nTimer += 4; + if (m_nUsesRemaining == 0) + --CTheCarGenerators::CurrentActiveCount; + return; + } + if (CCarCtrl::NumParkedCars >= 10) + return; + CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); + if (!CStreaming::HasModelLoaded(m_nModelIndex)) + return; + if (CModelInfo::IsBoatModel(m_nModelIndex)){ + CBoat* pBoat = new CBoat(m_nModelIndex, PARKED_VEHICLE); + pBoat->bIsStatic = false; + pBoat->bEngineOn = false; + CVector pos = m_vecPos; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += pBoat->GetDistanceFromCentreOfMassToBaseOfModel(); + pBoat->GetPosition() = pos; + pBoat->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pBoat->m_status = STATUS_ABANDONED; + pBoat->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(pBoat); + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) + pBoat->m_nAlarmState = -1; + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) + pBoat->m_nDoorLock = CARLOCK_LOCKED; + if (m_nColor1 != -1 && m_nColor2){ + pBoat->m_currentColour1 = m_nColor1; + pBoat->m_currentColour2 = m_nColor2; + } + m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pBoat); + }else{ + bool groundFound = false; + CVector pos = m_vecPos; + if (pos.z > -100.0f){ + pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &groundFound); + }else{ + CColPoint cp; + CEntity* pEntity; + groundFound = CWorld::ProcessVerticalLine(CVector(pos.x, pos.y, 1000.0f), -1000.0f, + cp, pEntity, true, false, false, false, false, false, nil); + if (groundFound) + pos.z = cp.point.z; + } + if (!groundFound) { + debug("CCarGenerator::DoInternalProcessing - can't find ground z for new car x = %f y = %f \n", m_vecPos.x, m_vecPos.y); + }else{ + CAutomobile* pCar = new CAutomobile(m_nModelIndex, PARKED_VEHICLE); + pCar->bIsStatic = false; + pCar->bEngineOn = false; + pos.z += pCar->GetDistanceFromCentreOfMassToBaseOfModel(); + pCar->GetPosition() = pos; + pCar->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pCar->m_status = STATUS_ABANDONED; + pCar->bLightsOn = false; + pCar->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(pCar); + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) + pCar->m_nAlarmState = -1; + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) + pCar->m_nDoorLock = CARLOCK_LOCKED; + if (m_nColor1 != -1 && m_nColor2) { + pCar->m_currentColour1 = m_nColor1; + pCar->m_currentColour2 = m_nColor2; + } + m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pCar); + } + } + if (m_nUsesRemaining < -1) /* I don't think this is a correct comparasion */ + --m_nUsesRemaining; + m_nTimer = CalcNextGen(); + if (m_nUsesRemaining == 0) + --CTheCarGenerators::CurrentActiveCount; +} + +void CCarGenerator::Process() +{ + if (m_nVehicleHandle == -1 && + (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) && + m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayer()) + DoInternalProcessing(); + if (m_nVehicleHandle == -1) + return; + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_nVehicleHandle); + if (!pVehicle){ + m_nVehicleHandle = -1; + return; + } + if (pVehicle->m_status != STATUS_PLAYER) + return; + m_nTimer += 60000; + m_nVehicleHandle = -1; + m_bIsBlocking = true; + pVehicle->bExtendedRange = false; +} + +void CCarGenerator::Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) +{ + CMatrix m1, m2, m3; /* Unused but present on stack, so I'll leave them. */ + m_vecPos = CVector(x, y, z); + m_fAngle = angle; + m_nModelIndex = mi; + m_nColor1 = color1; + m_nColor2 = color2; + m_bForceSpawn = force; + m_nAlarm = alarm; + m_nDoorlock = lock; + m_nMinDelay = min_delay; + m_nMaxDelay = max_delay; + m_nVehicleHandle = -1; + m_nTimer = CTimer::GetTimeInMilliseconds() + 1; + m_nUsesRemaining = 0; + m_bIsBlocking = false; + m_vecInf = CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->boundingBox.min; + m_vecSup = CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->boundingBox.max; + m_fSize = max(m_vecInf.Magnitude(), m_vecSup.Magnitude()); +} + +bool CCarGenerator::CheckForBlockage() +{ + int16 entities; + CWorld::FindObjectsKindaColliding(CVector(m_vecPos), m_fSize, 1, &entities, 2, nil, false, true, true, false, false); + return entities > 0; +} + +bool CCarGenerator::CheckIfWithinRangeOfAnyPlayer() +{ + CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos; + float distance = direction.Magnitude(); + float farclip = 120.0f * TheCamera.GenerationDistMultiplier; + float nearclip = farclip - 20.0f; + if (distance >= farclip){ + if (m_bIsBlocking) + m_bIsBlocking = false; + return false; + } + if (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter) + return true; + if (m_bIsBlocking) + return false; + if (distance < nearclip) + return false; + return DotProduct2D(direction, FindPlayerSpeed()) <= 0; +} + +void CCarGenerator::Save(uint8* buffer) +{ + *(uint32*)(buffer) = m_nModelIndex; + *(CVector*)(buffer + 4) = m_vecPos; + *(float*)(buffer + 16) = m_fAngle; + *(int16*)(buffer + 20) = m_nColor1; + *(int16*)(buffer + 22) = m_nColor2; + *(bool*)(buffer + 24) = m_bForceSpawn; + *(uint8*)(buffer + 25) = m_nAlarm; + *(uint8*)(buffer + 26) = m_nDoorlock; + *(uint8*)(buffer + 27) = 0; + *(uint16*)(buffer + 28) = m_nMinDelay; + *(uint16*)(buffer + 30) = m_nMaxDelay; + *(uint32*)(buffer + 32) = m_nTimer; + *(int32*)(buffer + 36) = m_nVehicleHandle; + *(uint16*)(buffer + 40) = m_nUsesRemaining; + *(bool*)(buffer + 42) = m_bIsBlocking; + *(uint8*)(buffer + 43) = 0; + *(CVector*)(buffer + 44) = m_vecInf; + *(CVector*)(buffer + 56) = m_vecSup; + *(float*)(buffer + 68) = m_fSize; +} + +void CCarGenerator::Load(uint8* buffer) +{ + m_nModelIndex = *(uint32*)(buffer); + m_vecPos = *(CVector*)(buffer + 4); + m_fAngle = *(float*)(buffer + 16); + m_nColor1 = *(int16*)(buffer + 20); + m_nColor2 = *(int16*)(buffer + 22); + m_bForceSpawn = *(bool*)(buffer + 24); + m_nAlarm = *(uint8*)(buffer + 25); + m_nDoorlock = *(uint8*)(buffer + 26); + m_nMinDelay = *(uint16*)(buffer + 28); + m_nMaxDelay = *(uint16*)(buffer + 30); + m_nTimer = *(uint32*)(buffer + 32); + m_nVehicleHandle = *(int32*)(buffer + 36); + m_nUsesRemaining = *(uint16*)(buffer + 40); + m_bIsBlocking = *(bool*)(buffer + 42); + m_vecInf = *(CVector*)(buffer + 44); + m_vecSup = *(CVector*)(buffer + 56); + m_fSize = *(float*)(buffer + 68); +} + +void CTheCarGenerators::Process() +{ + if (FindPlayerTrain() || CCutsceneMgr::IsRunning()) + return; + if (++CTheCarGenerators::ProcessCounter == 4) + CTheCarGenerators::ProcessCounter = 0; + for (uint32 i = ProcessCounter; i < NumOfCarGenerators; i += 4) + CTheCarGenerators::CarGeneratorArray[i].Process(); + if (GenerateEvenIfPlayerIsCloseCounter) + GenerateEvenIfPlayerIsCloseCounter--; +} + +int32 CTheCarGenerators::CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) +{ + CarGeneratorArray[NumOfCarGenerators].Setup(x, y, z, angle, mi, color1, color2, force, alarm, lock, min_delay, max_delay); + return NumOfCarGenerators++; +} + +void CTheCarGenerators::Init() +{ + GenerateEvenIfPlayerIsCloseCounter = 0; + NumOfCarGenerators = 0; + ProcessCounter = 0; + CurrentActiveCount = 0; +} + +void CTheCarGenerators::SaveAllCarGenerators(uint8 *buffer, uint32 *size) +{ + *size = 28 + 72 * NUM_CARGENS; + buffer[0] = 'C'; + buffer[1] = 'G'; + buffer[2] = 'N'; + buffer[3] = '\0'; + *(uint32*)(buffer + 4) = *size - 8; + *(uint32*)(buffer + 8) = 12; /* what is this? */ + *(uint32*)(buffer + 12) = NumOfCarGenerators; + *(uint32*)(buffer + 16) = CurrentActiveCount; + *(uint8*)(buffer + 20) = ProcessCounter; + *(uint8*)(buffer + 21) = GenerateEvenIfPlayerIsCloseCounter; + *(uint16*)(buffer + 22) = 0; + *(uint32*)(buffer + 24) = 72 * NUM_CARGENS; + buffer += 28; + for (int i = 0; i < NUM_CARGENS; i++){ + CarGeneratorArray[i].Save(buffer); + buffer += 72; + } +} + +void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) +{ + Init(); + assert(size == 28 + NUM_CARGENS * 72); + assert(buffer[0] == 'C'); + assert(buffer[1] == 'G'); + assert(buffer[2] == 'N'); + assert(buffer[3] == '\0'); + assert(*(uint32*)(buffer + 4) == size - 8); + NumOfCarGenerators = *(uint32*)(buffer + 12); + CurrentActiveCount = *(uint32*)(buffer + 16); + ProcessCounter = *(uint8*)(buffer + 20); + GenerateEvenIfPlayerIsCloseCounter = *(uint8*)(buffer + 21); + assert(*(uint32*)(buffer + 24) == 72 * NUM_CARGENS); + buffer += 28; + for (int i = 0; i < NUM_CARGENS; i++) { + CarGeneratorArray[i].Load(buffer); + buffer += 72; + } +} + +STARTPATCHES +InjectHook(0x543020, CTheCarGenerators::Init, PATCH_JUMP); +InjectHook(0x542F40, CTheCarGenerators::Process, PATCH_JUMP); +InjectHook(0x543050, CTheCarGenerators::SaveAllCarGenerators, PATCH_JUMP); +InjectHook(0x5431E0, CTheCarGenerators::LoadAllCarGenerators, PATCH_JUMP); +ENDPATCHES diff --git a/src/control/CarGen.h b/src/control/CarGen.h new file mode 100644 index 00000000..c1ca304c --- /dev/null +++ b/src/control/CarGen.h @@ -0,0 +1,56 @@ +#pragma once +#include "common.h" +#include "config.h" + +enum { + CARGEN_MAXACTUALLIMIT = 100 +}; + +class CCarGenerator +{ + int32 m_nModelIndex; + CVector m_vecPos; + float m_fAngle; + int16 m_nColor1; + int16 m_nColor2; + uint8 m_bForceSpawn; + uint8 m_nAlarm; + uint8 m_nDoorlock; + int16 m_nMinDelay; + int16 m_nMaxDelay; + uint32 m_nTimer; + int32 m_nVehicleHandle; + uint16 m_nUsesRemaining; + bool m_bIsBlocking; + CVector m_vecInf; + CVector m_vecSup; + float m_fSize; +public: + void SwitchOff(); + void SwitchOn(); + uint32 CalcNextGen(); + void DoInternalProcessing(); + void Process(); + void Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); + bool CheckForBlockage(); + bool CheckIfWithinRangeOfAnyPlayer(); + void Save(uint8*); + void Load(uint8*); + void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } +}; + +class CTheCarGenerators +{ +public: + static uint8 &ProcessCounter; + static uint32 &NumOfCarGenerators; + static CCarGenerator (&CarGeneratorArray)[NUM_CARGENS]; + static uint8 &GenerateEvenIfPlayerIsCloseCounter; + static uint32 &CurrentActiveCount; + + static void Process(); + static int32 CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); + static void Init(); + static void SaveAllCarGenerators(uint8 *, uint32 *); + static void LoadAllCarGenerators(uint8 *, uint32); +}; diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp index acc2b459..0629ac0c 100644 --- a/src/control/Garages.cpp +++ b/src/control/Garages.cpp @@ -63,6 +63,11 @@ CGarages::IsModelIndexADoor(uint32 id) id == MI_CRUSHERLID; } +bool CGarages::HasCarBeenCrushed(int32 handle) +{ + return CrushedCarId == handle; +} + WRAPPER void CGarages::TriggerMessage(char *text, int16, uint16 time, int16) { EAXJMP(0x426B20); } #if 0 diff --git a/src/control/Garages.h b/src/control/Garages.h index 69f9d256..45a9345d 100644 --- a/src/control/Garages.h +++ b/src/control/Garages.h @@ -24,4 +24,5 @@ public: static bool IsModelIndexADoor(uint32 id); static void TriggerMessage(char *text, int16, uint16 time, int16); static void PrintMessages(void); + static bool HasCarBeenCrushed(int32); }; diff --git a/src/control/Phones.cpp b/src/control/Phones.cpp index 376e2757..e2a9ee13 100644 --- a/src/control/Phones.cpp +++ b/src/control/Phones.cpp @@ -76,6 +76,7 @@ CPhoneInfo::Load(CPhoneInfo *source, uint8 buffer) m_aPhones[phoneId].m_vecPos = phone->m_vecPos; memcpy(m_aPhones[phoneId].m_apMessages, phone->m_apMessages, sizeof(wchar*) * 6); + m_aPhones[phoneId].m_lastTimeRepeatedMsgShown = phone->m_lastTimeRepeatedMsgShown; m_aPhones[phoneId].m_pEntity = phone->m_pEntity; m_aPhones[phoneId].m_nState = phone->m_nState; m_aPhones[phoneId].field_30 = phone->field_30; @@ -183,13 +184,14 @@ CPhoneInfo::Save(CPhoneInfo *destination, uint32 *size) phone->m_vecPos = m_aPhones[phoneId].m_vecPos; memcpy(phone->m_apMessages, m_aPhones[phoneId].m_apMessages, sizeof(wchar*) * 6); + phone->m_lastTimeRepeatedMsgShown = m_aPhones[phoneId].m_lastTimeRepeatedMsgShown; phone->m_pEntity = m_aPhones[phoneId].m_pEntity; phone->m_nState = m_aPhones[phoneId].m_nState; phone->field_30 = m_aPhones[phoneId].field_30; // Convert entity pointer to building pool index while saving if (phone->m_pEntity) { - phone->m_pEntity = (CEntity*) CPools::GetBuildingPool()->GetJustIndex((CBuilding*)phone->m_pEntity) + 1; + phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex((CBuilding*)phone->m_pEntity) + 1); } } } @@ -210,7 +212,7 @@ PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg) CPed *ped = (CPed*)arg; if (assoc->blendAmount > 0.5f) - ped->m_ped_flagC10 = true; + ped->bUpdateAnimHeading = true; if (ped->m_nPedState == PED_MAKE_CALL) ped->m_nPedState = PED_IDLE; @@ -244,10 +246,10 @@ PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg) CPed *ped = CPhoneInfo::pedWhoPickingUpPhone; ped->m_nMoveState = PEDMOVE_STILL; - CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_IDLE_STANCE, 8.0f); + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_IDLE_STANCE, 8.0f); if (assoc->blendAmount > 0.5f && ped) - CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_PHONE_TALK, 8.0f); + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_PHONE_TALK, 8.0f); CPhoneInfo::pedWhoPickingUpPhone = nil; } diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp index 56254d56..c93798fe 100644 --- a/src/control/Pickups.cpp +++ b/src/control/Pickups.cpp @@ -9,5 +9,6 @@ WRAPPER void CPickups::DoCollectableEffects(CEntity *ent) { EAXJMP(0x431C30); } WRAPPER void CPickups::DoMoneyEffects(CEntity *ent) { EAXJMP(0x431F40); } WRAPPER void CPickups::DoMineEffects(CEntity *ent) { EAXJMP(0x4321C0); } WRAPPER void CPickups::DoPickUpEffects(CEntity *ent) { EAXJMP(0x431520); } +WRAPPER void CPickups::RemoveAllFloatingPickups() { EAXJMP(0x430800); } WRAPPER void CPacManPickups::Render(void) { EAXJMP(0x432F60); } diff --git a/src/control/Pickups.h b/src/control/Pickups.h index 9cf485d0..b740e72e 100644 --- a/src/control/Pickups.h +++ b/src/control/Pickups.h @@ -41,6 +41,7 @@ public: static void DoMoneyEffects(CEntity *ent); static void DoMineEffects(CEntity *ent); static void DoPickUpEffects(CEntity *ent); + static void RemoveAllFloatingPickups(); static CPickup (&aPickUps)[NUMPICKUPS]; }; diff --git a/src/control/Remote.cpp b/src/control/Remote.cpp index 8d8d08f2..32ee4eda 100644 --- a/src/control/Remote.cpp +++ b/src/control/Remote.cpp @@ -2,4 +2,5 @@ #include "patcher.h" #include "Remote.h" +WRAPPER void CRemote::GivePlayerRemoteControlledCar(float, float, float, float, uint16) { EAXJMP(0x435C30); } WRAPPER void CRemote::TakeRemoteControlledCarFromPlayer(void) { EAXJMP(0x435DA0); } diff --git a/src/control/Remote.h b/src/control/Remote.h index f8ef96bf..5e474586 100644 --- a/src/control/Remote.h +++ b/src/control/Remote.h @@ -3,5 +3,6 @@ class CRemote { public: + static void GivePlayerRemoteControlledCar(float, float, float, float, uint16); static void TakeRemoteControlledCarFromPlayer(void); }; diff --git a/src/control/Replay.cpp b/src/control/Replay.cpp index d9453ad6..1e77da3a 100644 --- a/src/control/Replay.cpp +++ b/src/control/Replay.cpp @@ -240,15 +240,15 @@ void CReplay::Update(void) if (CDraw::FadeValue || !bReplayEnabled) return; if (Mode == MODE_PLAYBACK){ - if (CPad::NewKeyState.F[0] && !CPad::OldKeyState.F[0]) + if (CPad::GetPad(0)->GetFJustDown(0)) FinishPlayback(); } else if (Mode == MODE_RECORD){ - if (CPad::NewKeyState.F[0] && !CPad::OldKeyState.F[0]) + if (CPad::GetPad(0)->GetFJustDown(0)) TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false); - if (CPad::NewKeyState.F[1] && !CPad::OldKeyState.F[1]) + if (CPad::GetPad(0)->GetFJustDown(1)) SaveReplayToHD(); - if (CPad::NewKeyState.F[2] && !CPad::OldKeyState.F[2]) + if (CPad::GetPad(0)->GetFJustDown(2)) PlayReplayFromHD(); } } diff --git a/src/control/Script.cpp b/src/control/Script.cpp index bd43d301..b61a466b 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -7,22 +7,26 @@ #include "Boat.h" #include "Camera.h" #include "CarCtrl.h" +#include "CarGen.h" #include "CivilianPed.h" #include "Clock.h" #include "CopPed.h" #include "DMAudio.h" #include "EmergencyPed.h" #include "FileMgr.h" +#include "Garages.h" #include "General.h" #include "HandlingMgr.h" #include "Hud.h" #include "Messages.h" #include "ModelIndices.h" #include "Pad.h" +#include "Pickups.h" #include "PlayerInfo.h" #include "PlayerPed.h" #include "Pools.h" #include "Population.h" +#include "Remote.h" #include "Replay.h" #include "Streaming.h" #include "Text.h" @@ -170,6 +174,9 @@ void CMissionCleanup::Process() default: break; } + m_sEntities[i].id = 0; + m_sEntities[i].type = CLEANUP_UNUSED; + m_nCount--; } } @@ -426,8 +433,8 @@ void CRunningScript::Init() m_anLocalVariables[i] = 0; m_nAndOrState = 0; m_bNotFlag = false; - m_bWBCheckEnabled = true; - m_bWBChecked = false; + m_bDeatharrestEnabled = true; + m_bDeatharrestExecuted = false; m_bMissionFlag = false; } @@ -1113,6 +1120,13 @@ int8 CRunningScript::ProcessCommandsFrom0To99(int32 command) UpdateCompareFlag(*ptr1 == *ptr2); return 0; } + /* Following commands are not implemented, and go to default case + case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER: + case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER: + case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR: + case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR: + case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR: + */ case COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); @@ -1148,6 +1162,13 @@ int8 CRunningScript::ProcessCommandsFrom0To99(int32 command) UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); return 0; } + /* Following commands are not implemented, and go to default case + case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER: + case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER: + case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR: + case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR: + case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR: + */ case COMMAND_GOTO_IF_TRUE: CollectParameters(&m_nIp, 1); if (m_bCondResult) @@ -1311,7 +1332,7 @@ int8 CRunningScript::ProcessCommandsFrom0To99(int32 command) UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); if (!ScriptParams[7]) return 0; - CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); + CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; @@ -1730,6 +1751,9 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) ped->SetWanderPath(path); return 0; } + /* Not implemented. + case COMMAND_CHAR_WANDER_RANGE: + */ case COMMAND_CHAR_FOLLOW_PATH: { CollectParameters(&m_nIp, 4); @@ -1747,7 +1771,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); assert(ped); - ped->m_bScriptObjectiveCompleted = false; + ped->bScriptObjectiveCompleted = false; ped->SetObjective(OBJECTIVE_IDLE); return 0; } @@ -1826,7 +1850,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); - UpdateCompareFlag(ped && ped->m_status != PED_DEAD && ped->m_status != PED_DIE); + UpdateCompareFlag(ped && ped->GetPedState() != PED_DEAD && ped->GetPedState() != PED_DIE); return 0; } case COMMAND_IS_CHAR_IN_AREA_2D: @@ -1848,6 +1872,8 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, x2, y2)); else UpdateCompareFlag(ped->IsWithinArea(x1, y1, x2, y2)); + if (!ScriptParams[5]) + return 0; CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); @@ -1874,7 +1900,9 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); else UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); - CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); + if (!ScriptParams[7]) + return 0; + CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; @@ -2043,7 +2071,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) } case COMMAND_IS_CAR_STILL_ALIVE: { - CollectParameters(&m_nIp, 4); + CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); UpdateCompareFlag(car && car->m_status != STATUS_WRECKED && (car->IsBoat() || !car->bIsInWater)); return 0; @@ -2085,6 +2113,8 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) x2 = *(float*)&ScriptParams[3]; y2 = *(float*)&ScriptParams[4]; UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, x2, y2)); + if (!ScriptParams[5]) + return 0; CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); @@ -2103,7 +2133,9 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) y2 = *(float*)&ScriptParams[5]; z2 = *(float*)&ScriptParams[6]; UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); - CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); + if (!ScriptParams[7]) + return 0; + CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; @@ -2187,6 +2219,583 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) case COMMAND_RETURN_FALSE: UpdateCompareFlag(false); return 0; + /* Special command only used by compiler. + case COMMAND_VAR_INT: + */ + default: + assert(0); + break; + } + return -1; +} + +int8 CRunningScript::ProcessCommandsFrom200To299(int32 command) +{ + switch (command) { + /* Special commands. + case COMMAND_VAR_FLOAT: + case COMMAND_LVAR_INT: + case COMMAND_LVAR_FLOAT: + case COMMAND_LBRACKET: + case COMMAND_RBRACKET: + case COMMAND_REPEAT: + case COMMAND_ENDREPEAT: + case COMMAND_IF: + case COMMAND_IFNOT: + case COMMAND_ELSE: + case COMMAND_ENDIF: + case COMMAND_WHILE: + case COMMAND_WHILENOT: + case COMMAND_ENDWHILE: + */ + case COMMAND_ANDOR: + CollectParameters(&m_nIp, 1); + m_nAndOrState = ScriptParams[0]; + if (m_nAndOrState == ANDOR_NONE){ + m_bCondResult = false; // pointless + }else if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8){ + m_bCondResult = true; + m_nAndOrState++; + }else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8){ + m_bCondResult = false; + m_nAndOrState++; + }else{ + assert(0 && "COMMAND_ANDOR: invalid ANDOR state"); + } + return 0; + case COMMAND_LAUNCH_MISSION: + { + CollectParameters(&m_nIp, 1); + CRunningScript* pNew = CTheScripts::StartNewScript(ScriptParams[0]); + pNew->m_bIsMissionScript = true; + return 0; + } + case COMMAND_MISSION_HAS_FINISHED: + { + if (!m_bIsMissionScript) + return 0; + if (strcmp(m_abScriptName, "love3") == 0) /* A Drop in the Ocean */ + CPickups::RemoveAllFloatingPickups(); + CTheScripts::MissionCleanup.Process(); + return 0; + } + case COMMAND_STORE_CAR_CHAR_IS_IN: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(ped); + CVehicle* pCurrent = nil; + if (ped->bInVehicle) { + pCurrent = ped->m_pMyVehicle; + } + assert(pCurrent); // GetIndex(0) doesn't look good + int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); + if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript){ + CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); + if (pOld){ + CCarCtrl::RemoveFromInterestingVehicleList(pOld); + if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom){ + pOld->VehicleCreatedBy = RANDOM_VEHICLE; + pOld->bIsLocked = false; + CCarCtrl::NumRandomCars++; + CCarCtrl::NumMissionCars--; + CTheScripts::MissionCleanup.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + } + } + CTheScripts::StoreVehicleIndex = handle; + switch (pCurrent->VehicleCreatedBy){ + case RANDOM_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumRandomCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanup.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case PARKED_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumParkedCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanup.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case MISSION_VEHICLE: + case PERMANENT_VEHICLE: + CTheScripts::StoreVehicleWasRandom = false; + break; + default: + break; + } + } + ScriptParams[0] = CTheScripts::StoreVehicleIndex; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_STORE_CAR_PLAYER_IS_IN: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; + assert(ped); + if (!ped->bInVehicle) + return 0; // No value written to output variable + CVehicle* pCurrent = ped->m_pMyVehicle; + assert(pCurrent); // Here pCurrent shouldn't be NULL anyway + int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); + if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript) { + CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); + if (pOld) { + CCarCtrl::RemoveFromInterestingVehicleList(pOld); + if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom) { + pOld->VehicleCreatedBy = RANDOM_VEHICLE; + pOld->bIsLocked = false; + CCarCtrl::NumRandomCars++; + CCarCtrl::NumMissionCars--; + CTheScripts::MissionCleanup.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + } + } + CTheScripts::StoreVehicleIndex = handle; + switch (pCurrent->VehicleCreatedBy) { + case RANDOM_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumRandomCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanup.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case PARKED_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumParkedCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanup.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case MISSION_VEHICLE: + case PERMANENT_VEHICLE: + CTheScripts::StoreVehicleWasRandom = false; + break; + default: + break; + } + } + ScriptParams[0] = CTheScripts::StoreVehicleIndex; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_CHAR_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + UpdateCompareFlag(pActualVehicle && pActualVehicle == pCheckedVehicle); + return 0; + } + case COMMAND_IS_PLAYER_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle == pCheckedVehicle); + return 0; + } + case COMMAND_IS_CHAR_IN_MODEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + UpdateCompareFlag(pActualVehicle && pActualVehicle->GetModelIndex() == ScriptParams[1]); + return 0; + } + case COMMAND_IS_PLAYER_IN_MODEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetModelIndex() == ScriptParams[1]); + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pPed->bInVehicle); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle); + return 0; + } + case COMMAND_IS_BUTTON_PRESSED: + { + CollectParameters(&m_nIp, 2); + bool value = GetPadState(ScriptParams[0], ScriptParams[1]) != 0; + if (CGame::playingIntro && ScriptParams[0] == 0 && ScriptParams[1] == 12){ /* pad1, start */ + if (CPad::GetPad(0)->GetLeftMouseJustDown() || + CPad::GetPad(0)->GetPadEnterJustDown() || + CPad::GetPad(0)->GetCharJustDown(' ')) + value = true; + } + UpdateCompareFlag(value); + return 0; + } + case COMMAND_GET_PAD_STATE: + { + CollectParameters(&m_nIp, 1); + ScriptParams[0] = GetPadState(ScriptParams[0], ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_LOCATE_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: + LocatePlayerCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D: + LocatePlayerCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: + LocateCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D: + LocateCharCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: + LocatePlayerCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: + LocatePlayerCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: + LocateCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: + LocateCharCharCommand(command, &m_nIp); + return 0; + case COMMAND_CREATE_OBJECT: + { + CollectParameters(&m_nIp, 4); + int mi = ScriptParams[0] >= 0 ? ScriptParams[0] : CTheScripts::UsedObjectArray[-ScriptParams[0]].index; + CObject* pObj = new CObject(mi, 0); + pObj->ObjectCreatedBy = MISSION_OBJECT; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += pObj->GetDistanceFromCentreOfMassToBaseOfModel(); + pObj->GetPosition() = pos; + pObj->SetOrientation(0.0f, 0.0f, 0.0f); + pObj->GetMatrix().UpdateRW(); + pObj->UpdateRwFrame(); + CTheScripts::ClearSpaceForMissionEntity(pos, pObj); + CWorld::Add(pObj); + ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObj); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanup.AddEntityToList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_DELETE_OBJECT: + { + CollectParameters(&m_nIp, 1); + CObject* pObj = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + if (pObj){ + CWorld::Remove(pObj); + CWorld::RemoveReferencesToDeletedObject(pObj); + delete pObj; + } + if (m_bIsMissionScript) + CTheScripts::MissionCleanup.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_ADD_SCORE: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_nMoney += ScriptParams[1]; + return 0; + case COMMAND_IS_SCORE_GREATER: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_nMoney > ScriptParams[1]); + return 0; + case COMMAND_STORE_SCORE: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CWorld::Players[ScriptParams[0]].m_nMoney; + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRemote::GivePlayerRemoteControlledCar(pos.x, pos.y, pos.z, DEGTORAD(*(float*)&ScriptParams[4]), MI_RCBANDIT); + return 0; + } + case COMMAND_ALTER_WANTED_LEVEL: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(ScriptParams[1]); + return 0; + case COMMAND_ALTER_WANTED_LEVEL_NO_DROP: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevelNoDrop(ScriptParams[1]); + return 0; + case COMMAND_IS_WANTED_LEVEL_GREATER: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->m_pWanted->m_nWantedLevel > ScriptParams[1]); + return 0; + case COMMAND_CLEAR_WANTED_LEVEL: + CollectParameters(&m_nIp, 1); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(0); + return 0; + case COMMAND_SET_DEATHARREST_STATE: + CollectParameters(&m_nIp, 1); + m_bDeatharrestEnabled = (ScriptParams[0] == 1); + return 0; + case COMMAND_HAS_DEATHARREST_BEEN_EXECUTED: + UpdateCompareFlag(m_bDeatharrestExecuted); + return 0; + case COMMAND_ADD_AMMO_TO_PLAYER: + { + CollectParameters(&m_nIp, 3); + CWorld::Players[ScriptParams[0]].m_pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_ADD_AMMO_TO_CHAR: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + /* Not implemented + case COMMAND_ADD_AMMO_TO_CAR: + case COMMAND_IS_PLAYER_STILL_ALIVE: + */ + case COMMAND_IS_PLAYER_DEAD: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_WASTED); + return 0; + case COMMAND_IS_CHAR_DEAD: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(!pPed || pPed->GetPedState() == PED_DIE || pPed->GetPedState() == PED_DEAD); + return 0; + } + case COMMAND_IS_CAR_DEAD: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(!pVehicle || pVehicle->m_status == STATUS_WRECKED || !pVehicle->IsBoat() && pVehicle->bIsInWater); + return 0; + } + case COMMAND_SET_CHAR_THREAT_SEARCH: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + pPed->m_fearFlags |= ScriptParams[1]; + return 0; + } + /* Not implemented. + case COMMAND_SET_CHAR_THREAT_REACTION: + */ + case COMMAND_SET_CHAR_OBJ_NO_OBJ: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->ClearObjective(); + return 0; + } + /* Not implemented. + case COMMAND_ORDER_DRIVER_OUT_OF_CAR: + case COMMAND_ORDER_CHAR_TO_DRIVE_CAR: + case COMMAND_ADD_PATROL_POINT: + case COMMAND_IS_PLAYER_IN_GANGZONE: + */ + case COMMAND_IS_PLAYER_IN_ZONE: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + int zoneToCheck = CTheZones::FindZoneByLabelAndReturnIndex(label); + if (zoneToCheck != -1) + m_nIp += 8; /* why only if zone != 1? */ + CVector pos = pPlayer->GetPos(); + CZone* pZone = CTheZones::GetZone(zoneToCheck); + UpdateCompareFlag(CTheZones::PointLiesWithinZone(pos, pZone)); + return 0; + } + case COMMAND_IS_PLAYER_PRESSING_HORN: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->GetPedState() == PED_DRIVING && + CPad::GetPad(ScriptParams[0])->GetHorn()); + /* Is it correct that same parameter is used both as index of Players */ + /* and as ID of pad? Pratically this parameter is always 0 anyway of course. */ + return 0; + case COMMAND_HAS_CHAR_SPOTTED_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + UpdateCompareFlag(pPed->OurPedCanSeeThisOne(CWorld::Players[ScriptParams[1]].m_pPed)); + return 0; + } + /* Not implemented. + case COMMAND_ORDER_CHAR_TO_BACKDOOR: + case COMMAND_ADD_CHAR_TO_GANG: + */ + case COMMAND_IS_CHAR_OBJECTIVE_PASSED: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + UpdateCompareFlag(pPed->bScriptObjectiveCompleted); + return 0; + } + /* Not implemented. + case COMMAND_SET_CHAR_DRIVE_AGGRESSION: + case COMMAND_SET_CHAR_MAX_DRIVESPEED: + */ + case COMMAND_CREATE_CHAR_INSIDE_CAR: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + switch (ScriptParams[2]) { + case MI_COP: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_STREET; + break; + case MI_SWAT: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_SWAT; + break; + case MI_FBI: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_FBI; + break; + case MI_ARMY: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_ARMY; + break; + case MI_MEDIC: + if (ScriptParams[1] == PEDTYPE_EMERGENCY) + ScriptParams[2] = PEDTYPE_EMERGENCY; + break; + case MI_FIREMAN: + if (ScriptParams[1] == PEDTYPE_FIREMAN) + ScriptParams[2] = PEDTYPE_FIREMAN; + break; + default: + break; + } + CPed* pPed; + if (ScriptParams[1] == PEDTYPE_COP) + pPed = new CCopPed((eCopType)ScriptParams[2]); + else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN) + pPed = new CEmergencyPed(ScriptParams[2]); + else + pPed = new CCivilianPed(ScriptParams[1], ScriptParams[2]); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + pPed->m_ped_flagG2 = false; + pPed->GetPosition() = pVehicle->GetPosition(); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + pPed->SetPedState(PED_DRIVING); + CPopulation::ms_nTotalMissionPeds++; + assert(!pVehicle->pDriver); + pVehicle->pDriver = pPed; + pVehicle->pDriver->RegisterReference((CEntity**)&pVehicle->pDriver); + pPed->m_pMyVehicle = pVehicle; + pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); + pPed->bInVehicle = true; + pVehicle->m_status = STATUS_PHYSICS; + if (!pVehicle->IsBoat()) + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->bEngineOn = true; + pPed->bUsesCollision = false; + AnimationId anim = pVehicle->bLowVehicle ? ANIM_CAR_LSIT : ANIM_CAR_SIT; + pPed->m_pVehicleAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, anim, 100.0f); + pPed->StopNonPartialAnims(); + pPed->m_level = CTheZones::GetLevelFromPosition(pPed->GetPosition()); + CWorld::Add(pPed); + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanup.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[1]; + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + if (pPlayer->m_pPed->bInVehicle){ + assert(pPlayer->m_pPed->m_pMyVehicle); + if (pPlayer->m_pPed->m_pMyVehicle->bIsBus) + pPlayer->m_pPed->bRenderPedInCar = true; + if (pPlayer->m_pPed->m_pMyVehicle->pDriver == pPlayer->m_pPed){ + pPlayer->m_pPed->m_pMyVehicle->RemoveDriver(); + pPlayer->m_pPed->m_pMyVehicle->m_status = STATUS_ABANDONED; + pPlayer->m_pPed->m_pMyVehicle->bEngineOn = false; + pPlayer->m_pPed->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + }else{ + pPlayer->m_pPed->m_pMyVehicle->RemovePassenger(pPlayer->m_pPed); + } + } + pPlayer->m_pPed->bInVehicle = false; + pPlayer->m_pPed->m_pMyVehicle = nil; + pPlayer->m_pPed->SetPedState(PED_IDLE); + pPlayer->m_pPed->bUsesCollision = true; + pPlayer->m_pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pPlayer->m_pPed->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pPlayer->m_pPed->GetWeapon()->m_eWeaponType)->m_nModelId); + pPlayer->m_pPed->RemoveInCarAnims(); + if (pPlayer->m_pPed->m_pVehicleAnim) + pPlayer->m_pPed->m_pVehicleAnim->blendDelta = -1000.0f; + pPlayer->m_pPed->m_pVehicleAnim = nil; + pPlayer->m_pPed->SetMoveState(PEDMOVE_NONE); + CAnimManager::BlendAnimation(pPlayer->m_pPed->GetClump(), pPlayer->m_pPed->m_animGroup, ANIM_IDLE_STANCE, 100.0f); + pPlayer->m_pPed->RestartNonPartialAnims(); + AudioManager.PlayerJustLeftCar(); + pos.z += pPlayer->m_pPed->GetDistanceFromCentreOfMassToBaseOfModel(); + pPlayer->m_pPed->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, pPlayer->m_pPed); + return 0; + } + /* Not implemented. + case COMMAND_MAKE_CHAR_DO_NOTHING: + */ default: assert(0); break; @@ -2194,8 +2803,310 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) return -1; } -WRAPPER int8 CRunningScript::ProcessCommandsFrom200To299(int32 command) { EAXJMP(0x43D530); } +#if 1 WRAPPER int8 CRunningScript::ProcessCommandsFrom300To399(int32 command) { EAXJMP(0x43ED30); } +#else +int8 CRunningScript::ProcessCommandsFrom300To399(int32 command) +{ + switch (command) { + /* Not implemented. + case COMMAND_SET_CHAR_INVINCIBLE: + case COMMAND_SET_PLAYER_INVINCIBLE: + case COMMAND_SET_CHAR_GRAPHIC_TYPE: + case COMMAND_SET_PLAYER_GRAPHIC_TYPE: + */ + case COMMAND_HAS_PLAYER_BEEN_ARRESTED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_BUSTED); + return 0; + /* Not implemented. + case COMMAND_STOP_CHAR_DRIVING: + case COMMAND_KILL_CHAR: + case COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR: + case COMMAND_SET_CHAR_OCCUPATION: + */ + case COMMAND_CHANGE_CAR_LOCK: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + pVehicle->m_nDoorLock = (eCarLock)ScriptParams[1]; + return 0; + } + case COMMAND_SHAKE_CAM_WITH_POINT: + CollectParameters(&m_nIp, 4); + TheCamera.CamShake(ScriptParams[0] / 1000.0f, + *(float*)&ScriptParams[1], + *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3]); + return 0; + case COMMAND_IS_CAR_MODEL: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + UpdateCompareFlag(pVehicle->GetModelIndex() == ScriptParams[1]); + } + /* Not implemented. + case COMMAND_IS_CAR_REMAP: + case COMMAND_HAS_CAR_JUST_SUNK: + case COMMAND_SET_CAR_NO_COLLIDE: + */ + case COMMAND_IS_CAR_DEAD_IN_AREA_2D: + { + CollectParameters(&m_nIp, 6); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + float x1, y1, x2, y2; + x1 = *(float*)&ScriptParams[1]; + y1 = *(float*)&ScriptParams[2]; + x2 = *(float*)&ScriptParams[3]; + y2 = *(float*)&ScriptParams[4]; + UpdateCompareFlag(pVehicle->m_status == STATUS_WRECKED && + pVehicle->IsWithinArea(x1, y1, x2, y2)); + if (!ScriptParams[5]) + return 0; + CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, -100.0f); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CAR_DEAD_IN_AREA_3D: + { + CollectParameters(&m_nIp, 8); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + float x1, y1, z1, x2, y2, z2; + x1 = *(float*)&ScriptParams[1]; + y1 = *(float*)&ScriptParams[2]; + z1 = *(float*)&ScriptParams[3]; + x2 = *(float*)&ScriptParams[4]; + y2 = *(float*)&ScriptParams[5]; + z2 = *(float*)&ScriptParams[6]; + UpdateCompareFlag(pVehicle->m_status == STATUS_WRECKED && + pVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); + if (!ScriptParams[7]) + return 0; + CTheScripts::HighlightImportantArea((uint32)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); + return 0; + } + /* Not implemented. + case COMMAND_IS_TRAILER_ATTACHED: + case COMMAND_IS_CAR_ON_TRAILER: + case COMMAND_HAS_CAR_GOT_WEAPON: + case COMMAND_PARK: + case COMMAND_HAS_PARK_FINISHED: + case COMMAND_KILL_ALL_PASSENGERS: + case COMMAND_SET_CAR_BULLETPROOF: + case COMMAND_SET_CAR_FLAMEPROOF: + case COMMAND_SET_CAR_ROCKETPROOF: + case COMMAND_IS_CARBOMB_ACTIVE: + case COMMAND_GIVE_CAR_ALARM: + case COMMAND_PUT_CAR_ON_TRAILER: + */ + case COMMAND_IS_CAR_CRUSHED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::HasCarBeenCrushed(ScriptParams[0])); + return 0; + /* Not implemented. + case COMMAND_CREATE_GANG_CAR: + */ + case COMMAND_CREATE_CAR_GENERATOR: + CollectParameters(&m_nIp, 12); + ScriptParams[0] = CTheCarGenerators::CreateCarGenerator( + *(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], + ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], + ScriptParams[8], ScriptParams[9], ScriptParams[10], ScriptParams[11]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_SWITCH_CAR_GENERATOR: + { + CollectParameters(&m_nIp, 2); + CCarGenerator* pCarGen = &CTheCarGenerators::CarGeneratorArray[ScriptParams[0]]; + if (ScriptParams[1] == 0){ + pCarGen->SwitchOff(); + }else if (ScriptParams[1] <= 100){ + pCarGen->SwitchOn(); + }else{ + pCarGen->SwitchOn(); + pCarGen->SetUsesRemaining(ScriptParams[1]); + } + return 0; + } + case COMMAND_ADD_PAGER_MESSAGE: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CUserDisplay::Pager.AddMessage(text, ScriptParams[0], ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_DISPLAY_ONSCREEN_TIMER: + { + assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + CUserDisplay::OnscnTimer.AddClock(CTheScripts::Read2BytesFromScript(&m_nIp), nil); + return 0; + } + case COMMAND_CLEAR_ONSCREEN_TIMER: + { + assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + CUserDisplay::OnscnTimer.ClearClock(CTheScripts::Read2BytesFromScript(&m_nIp)); + return 0; + } + case COMMAND_DISPLAY_ONSCREEN_COUNTER: + { + assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + int32 counter = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + CUserDisplay::OnscnTimer.AddCounter(counter, ScriptParams[0], nil); + return 0; + } + case COMMAND_CLEAR_ONSCREEN_COUNTER: + { + assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + CUserDisplay::OnscnTimer.ClearCounter(CTheScripts::Read2BytesFromScript(&m_nIp)); + return 0; + } + case COMMAND_SET_ZONE_CAR_INFO: + { + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + m_nIp += 8; + CollectParameters(&m_nIp, 16); + int zone = CTheZones::FindZoneByLabelAndReturnIndex(label); + if (zone < 0) { + debug("Couldn't find zone - %s\n", label); + return 0; + } + CTheZones::SetZoneCarInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], + ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8], 0, 0, + ScriptParams[9], ScriptParams[10], ScriptParams[11], ScriptParams[12], + ScriptParams[13], ScriptParams[14], ScriptParams[15]); + return 0; + } + /* Not implemented. + case COMMAND_IS_CHAR_IN_GANG_ZONE: + */ + case COMMAND_IS_CHAR_IN_ZONE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + int zone = CTheZones::FindZoneByLabelAndReturnIndex(label); + if (zone != -1) + m_nIp += 8; + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + UpdateCompareFlag(CTheZones::PointLiesWithinZone(pos, CTheZones::GetZone(zone))); + return 0; + } + case COMMAND_SET_CAR_DENSITY: + { + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + m_nIp += 8; + + } + case COMMAND_SET_PED_DENSITY: + case COMMAND_POINT_CAMERA_AT_PLAYER: + case COMMAND_POINT_CAMERA_AT_CAR: + case COMMAND_POINT_CAMERA_AT_CHAR: + case COMMAND_RESTORE_CAMERA: + case COMMAND_SHAKE_PAD: + case COMMAND_SET_ZONE_PED_INFO: + case COMMAND_SET_TIME_SCALE: + case COMMAND_IS_CAR_IN_AIR: + case COMMAND_SET_FIXED_CAMERA_POSITION: + case COMMAND_POINT_CAMERA_AT_POINT: + case COMMAND_ADD_BLIP_FOR_CAR_OLD: + case COMMAND_ADD_BLIP_FOR_CHAR_OLD: + case COMMAND_ADD_BLIP_FOR_OBJECT_OLD: + case COMMAND_REMOVE_BLIP: + case COMMAND_CHANGE_BLIP_COLOUR: + case COMMAND_DIM_BLIP: + case COMMAND_ADD_BLIP_FOR_COORD_OLD: + case COMMAND_CHANGE_BLIP_SCALE: + case COMMAND_SET_FADING_COLOUR: + case COMMAND_DO_FADE: + case COMMAND_GET_FADING_STATUS: + case COMMAND_ADD_HOSPITAL_RESTART: + case COMMAND_ADD_POLICE_RESTART: + case COMMAND_OVERRIDE_NEXT_RESTART: + case COMMAND_DRAW_SHADOW: + case COMMAND_GET_PLAYER_HEADING: + case COMMAND_SET_PLAYER_HEADING: + case COMMAND_GET_CHAR_HEADING: + case COMMAND_SET_CHAR_HEADING: + case COMMAND_GET_CAR_HEADING: + case COMMAND_SET_CAR_HEADING: + case COMMAND_GET_OBJECT_HEADING: + case COMMAND_SET_OBJECT_HEADING: + case COMMAND_IS_PLAYER_TOUCHING_OBJECT: + case COMMAND_IS_CHAR_TOUCHING_OBJECT: + case COMMAND_SET_PLAYER_AMMO: + case COMMAND_SET_CHAR_AMMO: + case COMMAND_SET_CAR_AMMO: + case COMMAND_LOAD_CAMERA_SPLINE: + case COMMAND_MOVE_CAMERA_ALONG_SPLINE: + case COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE: + case COMMAND_DECLARE_MISSION_FLAG: + case COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT: + case COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT: + case COMMAND_IS_PLAYER_HEALTH_GREATER: + case COMMAND_IS_CHAR_HEALTH_GREATER: + case COMMAND_IS_CAR_HEALTH_GREATER: + case COMMAND_ADD_BLIP_FOR_CAR: + case COMMAND_ADD_BLIP_FOR_CHAR: + case COMMAND_ADD_BLIP_FOR_OBJECT: + case COMMAND_ADD_BLIP_FOR_CONTACT_POINT: + case COMMAND_ADD_BLIP_FOR_COORD: + case COMMAND_CHANGE_BLIP_DISPLAY: + case COMMAND_ADD_ONE_OFF_SOUND: + case COMMAND_ADD_CONTINUOUS_SOUND: + case COMMAND_REMOVE_SOUND: + case COMMAND_IS_CAR_STUCK_ON_ROOF: + default: + assert(0); + } + return -1; +} +#endif + +int16 CRunningScript::GetPadState(uint16 pad, uint16 button) +{ + CPad* pPad = CPad::GetPad(pad); + switch (button){ + case 0: return pPad->NewState.LeftStickX; + case 1: return pPad->NewState.LeftStickY; + case 2: return pPad->NewState.RightStickX; + case 3: return pPad->NewState.RightStickY; + case 4: return pPad->NewState.LeftShoulder1; + case 5: return pPad->NewState.LeftShoulder2; + case 6: return pPad->NewState.RightShoulder1; + case 7: return pPad->NewState.RightShoulder2; + case 8: return pPad->NewState.DPadUp; + case 9: return pPad->NewState.DPadDown; + case 10: return pPad->NewState.DPadLeft; + case 11: return pPad->NewState.DPadRight; + case 12: return pPad->NewState.Start; + case 13: return pPad->NewState.Select; + case 14: return pPad->NewState.Square; + case 15: return pPad->NewState.Triangle; + case 16: return pPad->NewState.Cross; + case 17: return pPad->NewState.Circle; + case 18: return pPad->NewState.LeftShock; + case 19: return pPad->NewState.RightShock; + default: break; + } + return 0; +} + WRAPPER int8 CRunningScript::ProcessCommandsFrom400To499(int32 command) { EAXJMP(0x440CB0); } WRAPPER int8 CRunningScript::ProcessCommandsFrom500To599(int32 command) { EAXJMP(0x4429C0); } WRAPPER int8 CRunningScript::ProcessCommandsFrom600To699(int32 command) { EAXJMP(0x444B20); } @@ -2205,6 +3116,20 @@ WRAPPER int8 CRunningScript::ProcessCommandsFrom900To999(int32 command) { EAXJMP WRAPPER int8 CRunningScript::ProcessCommandsFrom1000To1099(int32 command) { EAXJMP(0x588490); } WRAPPER int8 CRunningScript::ProcessCommandsFrom1100To1199(int32 command) { EAXJMP(0x589D00); } +WRAPPER void CRunningScript::LocatePlayerCommand(int32, uint32*) { EAXJMP(0x44FE10); } +WRAPPER void CRunningScript::LocatePlayerCharCommand(int32, uint32*) { EAXJMP(0x4501E0); } +WRAPPER void CRunningScript::LocatePlayerCarCommand(int32, uint32*) { EAXJMP(0x450540); } +WRAPPER void CRunningScript::LocateCharCommand(int32, uint32*) { EAXJMP(0x450870); } +WRAPPER void CRunningScript::LocateCharCharCommand(int32, uint32*) { EAXJMP(0x450BF0); } +WRAPPER void CRunningScript::LocateCharCarCommand(int32, uint32*) { EAXJMP(0x450F30); } +WRAPPER void CRunningScript::LocateCharObjectCommand(int32, uint32*) { EAXJMP(0x451260); } +WRAPPER void CRunningScript::LocateCarCommand(int32, uint32*) { EAXJMP(0x451590); } +WRAPPER void CRunningScript::LocateSniperBulletCommand(int32, uint32*) { EAXJMP(0x4518A0); } +WRAPPER void CRunningScript::LocatePlayerInAreaCheckCommand(int32, uint32*) { EAXJMP(0x451A60); } +WRAPPER void CRunningScript::LocatePlayerInAngledAreaCheckCommand(int32, uint32*) { EAXJMP(0x451E50); } +WRAPPER void CRunningScript::LocateCharInAreaCheckCommand(int32, uint32*) { EAXJMP(0x4523B0); } +WRAPPER void CRunningScript::LocateCharInAngledAreaCheckCommand(int32, uint32*) { EAXJMP(0x452750); } + WRAPPER void CTheScripts::DrawScriptSpheres() { EAXJMP(0x44FAC0); } WRAPPER void CRunningScript::DoDeatharrestCheck() { EAXJMP(0x452A30); } WRAPPER void CTheScripts::DrawDebugSquare(float, float, float, float) { EAXJMP(0x452D00); } @@ -2244,4 +3169,4 @@ InjectHook(0x439000, &CTheScripts::StartNewScript, PATCH_JUMP); InjectHook(0x439040, &CTheScripts::Process, PATCH_JUMP); InjectHook(0x439400, &CTheScripts::StartTestScript, PATCH_JUMP); InjectHook(0x439410, &CTheScripts::IsPlayerOnAMission, PATCH_JUMP); -ENDPATCHES
\ No newline at end of file +ENDPATCHES diff --git a/src/control/Script.h b/src/control/Script.h index 9e9d9ab6..47c70914 100644 --- a/src/control/Script.h +++ b/src/control/Script.h @@ -1,5 +1,6 @@ #pragma once #include "common.h" +#include "Text.h" #include "Sprite2d.h" class CEntity; @@ -78,8 +79,8 @@ class CRunningScript uint32 m_nWakeTime; uint16 m_nAndOrState; bool m_bNotFlag; - bool m_bWBCheckEnabled; - bool m_bWBChecked; + bool m_bDeatharrestEnabled; + bool m_bDeatharrestExecuted; bool m_bMissionFlag; public: @@ -113,6 +114,40 @@ public: int8 ProcessCommandsFrom1000To1099(int32); int8 ProcessCommandsFrom1100To1199(int32); void UpdateCompareFlag(bool); + int16 GetPadState(uint16, uint16); + void LocatePlayerCommand(int32, uint32*); + void LocatePlayerCharCommand(int32, uint32*); + void LocatePlayerCarCommand(int32, uint32*); + void LocateCharCommand(int32, uint32*); + void LocateCharCharCommand(int32, uint32*); + void LocateCharCarCommand(int32, uint32*); + void LocateCharObjectCommand(int32, uint32*); + void LocateCarCommand(int32, uint32*); + void LocateSniperBulletCommand(int32, uint32*); + void LocatePlayerInAreaCheckCommand(int32, uint32*); + void LocatePlayerInAngledAreaCheckCommand(int32, uint32*); + void LocateCharInAreaCheckCommand(int32, uint32*); + void LocateCharInAngledAreaCheckCommand(int32, uint32*); +private: + enum { + ANDOR_NONE = 0, + ANDS_1 = 1, + ANDS_2, + ANDS_3, + ANDS_4, + ANDS_5, + ANDS_6, + ANDS_7, + ANDS_8, + ORS_1 = 21, + ORS_2, + ORS_3, + ORS_4, + ORS_5, + ORS_6, + ORS_7, + ORS_8 + }; }; enum { @@ -338,4 +373,12 @@ public: static float ReadFloatFromScript(uint32* pIp){ return Read2BytesFromScript(pIp) / 16.0f; } + static void ReadTextLabelFromScript(uint32* pIp, char* buf){ + strncpy(buf, (const char*)&ScriptSpace[*pIp], 8); + } + static wchar* GetTextByKeyFromScript(uint32* pIp) { + wchar* text = TheText.Get((const char*)&ScriptSpace[*pIp]); + *pIp += 8; + return text; + } }; diff --git a/src/control/ScriptCommands.h b/src/control/ScriptCommands.h index 55ac4439..fcc0a23f 100644 --- a/src/control/ScriptCommands.h +++ b/src/control/ScriptCommands.h @@ -208,7 +208,7 @@ enum { COMMAND_RBRACKET, COMMAND_REPEAT, COMMAND_ENDREPEAT, - COMMAND_IF_, + COMMAND_IF, COMMAND_IFNOT, COMMAND_ELSE, COMMAND_ENDIF, diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp index a66d6ac9..b5ba76db 100644 --- a/src/core/Camera.cpp +++ b/src/core/Camera.cpp @@ -1304,6 +1304,17 @@ CCam::GetWeaponFirstPersonOn() return false; } +float +CCamera::Find3rdPersonQuickAimPitch(void) +{ + float clampedFrontZ = clamp(Cams[ActiveCam].Front.z, -1.0f, 1.0f); + + // float rot = atan2(clampedFrontZ, sqrt(1.0f - sq(clampedFrontZ))); + float rot = Asin(clampedFrontZ); + + return -(DEGTORAD(((0.5f - m_f3rdPersonCHairMultY) * 1.8f * 0.5f * Cams[ActiveCam].FOV)) + rot); +} + STARTPATCHES InjectHook(0x42C760, (bool (CCamera::*)(const CVector ¢er, float radius, const CMatrix *mat))&CCamera::IsSphereVisible, PATCH_JUMP); InjectHook(0x46FD00, &CCamera::SetFadeColour, PATCH_JUMP); diff --git a/src/core/Camera.h b/src/core/Camera.h index 1a2aae79..3ce0d9a6 100644 --- a/src/core/Camera.h +++ b/src/core/Camera.h @@ -469,6 +469,8 @@ int m_iModeObbeCamIsInForCar; void Restore(void); void SetWidescreenOff(void); + float Find3rdPersonQuickAimPitch(void); + void dtor(void) { this->CCamera::~CCamera(); } }; static_assert(offsetof(CCamera, m_WideScreenOn) == 0x70, "CCamera: error"); diff --git a/src/core/Collision.cpp b/src/core/Collision.cpp index 7982e77d..cc360f79 100644 --- a/src/core/Collision.cpp +++ b/src/core/Collision.cpp @@ -2023,11 +2023,11 @@ CColModel::operator=(const CColModel &other) numVerts = 0; for(i = 0; i < other.numTriangles; i++){ if(other.triangles[i].a > numVerts) - other.triangles[i].a = numVerts; + numVerts = other.triangles[i].a; if(other.triangles[i].b > numVerts) - other.triangles[i].b = numVerts; + numVerts = other.triangles[i].b; if(other.triangles[i].c > numVerts) - other.triangles[i].c = numVerts; + numVerts = other.triangles[i].c; } numVerts++; if(vertices) diff --git a/src/core/ControllerConfig.h b/src/core/ControllerConfig.h index 8a434245..b064a381 100644 --- a/src/core/ControllerConfig.h +++ b/src/core/ControllerConfig.h @@ -22,7 +22,7 @@ public: int32 m_ContSetOrder; }; - bool field_0; + bool firstCapture; char _pad0[3]; DIJOYSTATE2 m_OldState; DIJOYSTATE2 m_NewState; diff --git a/src/core/Explosion.h b/src/core/Explosion.h index 69508490..93d60ab3 100644 --- a/src/core/Explosion.h +++ b/src/core/Explosion.h @@ -4,8 +4,16 @@ class CEntity; enum eExplosionType { - EXPLOSION_3 = 3, - EXPLOSION_4 + EXPLOSION_GRENADE, + EXPLOSION_MOLOTOV, + EXPLOSION_ROCKET, + EXPLOSION_CAR, + EXPLOSION_CAR_QUICK, + EXPLOSION_HELI, + EXPLOSION_MINE, + EXPLOSION_BARREL, + EXPLOSION_TANK_GRENADE, + EXPLOSION_HELI_BOMB }; class CExplosion diff --git a/src/core/Fire.cpp b/src/core/Fire.cpp index bc59de2f..a2894d43 100644 --- a/src/core/Fire.cpp +++ b/src/core/Fire.cpp @@ -7,3 +7,4 @@ CFireManager &gFireManager = *(CFireManager*)0x8F31D0; WRAPPER void CFire::Extinguish(void) { EAXJMP(0x479D40); } WRAPPER void CFireManager::StartFire(CEntity *entityOnFire, CEntity *culprit, float, uint32) { EAXJMP(0x479590); } +WRAPPER CFire *CFireManager::FindFurthestFire_NeverMindFireMen(CVector coors, float, float) { EAXJMP(0x479430); } diff --git a/src/core/Fire.h b/src/core/Fire.h index 9c9e1dec..040e9a25 100644 --- a/src/core/Fire.h +++ b/src/core/Fire.h @@ -4,6 +4,7 @@ class CEntity; class CFire { +public: bool m_bIsOngoing; bool m_bExists; bool m_bPropogationFlag; @@ -18,7 +19,6 @@ class CFire int field_28; float field_2C; -public: void Extinguish(void); }; @@ -26,5 +26,6 @@ class CFireManager { public: void StartFire(CEntity *entityOnFire, CEntity *culprit, float, uint32); + CFire *FindFurthestFire_NeverMindFireMen(CVector coors, float, float); }; extern CFireManager &gFireManager; diff --git a/src/core/PlayerInfo.cpp b/src/core/PlayerInfo.cpp index 81c7a199..d9a4c0b0 100644 --- a/src/core/PlayerInfo.cpp +++ b/src/core/PlayerInfo.cpp @@ -1,7 +1,9 @@ #include "common.h" #include "patcher.h" +#include "PlayerPed.h" #include "PlayerInfo.h" #include "Frontend.h" +#include "Vehicle.h" WRAPPER void CPlayerInfo::MakePlayerSafe(bool) { EAXJMP(0x4A1400); } WRAPPER void CPlayerInfo::LoadPlayerSkin() { EAXJMP(0x4A1700); } @@ -12,3 +14,10 @@ void CPlayerInfo::SetPlayerSkin(char *skin) strncpy(m_aSkinName, skin, 32); LoadPlayerSkin(); } + +CVector& CPlayerInfo::GetPos() +{ + if (m_pPed->bInVehicle && m_pPed->m_pMyVehicle) + return m_pPed->m_pMyVehicle->GetPosition(); + return m_pPed->GetPosition(); +} diff --git a/src/core/PlayerInfo.h b/src/core/PlayerInfo.h index d8128424..decfb24c 100644 --- a/src/core/PlayerInfo.h +++ b/src/core/PlayerInfo.h @@ -20,7 +20,7 @@ public: CPlayerPed *m_pPed; CVehicle *m_pRemoteVehicle; CColModel m_ColModel; - CVehicle *m_pVehicleEx; + CVehicle *m_pVehicleEx; // vehicle using the col model above char m_aPlayerName[70]; int32 m_nMoney; int32 m_nVisibleMoney; @@ -45,10 +45,10 @@ public: int8 field_225; int8 field_226; int8 field_227; - int32 m_nTimeLostRemoteCar; - int32 m_nTimeLastHealthLoss; - int32 m_nTimeLastArmourLoss; - int32 field_240; + uint32 m_nTimeLostRemoteCar; + uint32 m_nTimeLastHealthLoss; + uint32 m_nTimeLastArmourLoss; + uint32 m_nTimeTankShotGun; int32 m_nUpsideDownCounter; int32 field_248; int16 m_nTrafficMultiplier; @@ -70,6 +70,7 @@ public: void LoadPlayerSkin(); void AwardMoneyForExplosion(CVehicle *vehicle); void SetPlayerSkin(char* skin); + CVector& GetPos(); }; static_assert(sizeof(CPlayerInfo) == 0x13C, "CPlayerInfo: error"); diff --git a/src/core/User.cpp b/src/core/User.cpp index c9cb97cc..f40a06db 100644 --- a/src/core/User.cpp +++ b/src/core/User.cpp @@ -13,6 +13,8 @@ COnscreenTimer& CUserDisplay::OnscnTimer = *(COnscreenTimer*)0x862238; CPager& CUserDisplay::Pager = *(CPager*)0x8F2744; CCurrentVehicle& CUserDisplay::CurrentVehicle = *(CCurrentVehicle*)0x8F5FE8; +WRAPPER void CPager::AddMessage(wchar*, uint16, uint16, uint16) { EAXJMP(0x52B940); } + void COnscreenTimer::Init() { m_bDisabled = false; for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) { diff --git a/src/core/User.h b/src/core/User.h index 8b744c7e..cac2a318 100644 --- a/src/core/User.h +++ b/src/core/User.h @@ -52,6 +52,8 @@ class CCurrentVehicle class CPager { +public: + void AddMessage(wchar*, uint16, uint16, uint16); }; class CUserDisplay diff --git a/src/core/Wanted.cpp b/src/core/Wanted.cpp index 7b865311..26b115e3 100644 --- a/src/core/Wanted.cpp +++ b/src/core/Wanted.cpp @@ -40,19 +40,19 @@ CWanted::Initialise() bool CWanted::AreSwatRequired() { - return m_nWantedLevel >= 4; + return m_nWantedLevel == 4 || m_bSwatRequired; } bool CWanted::AreFbiRequired() { - return m_nWantedLevel >= 5; + return m_nWantedLevel == 5 || m_bFbiRequired; } bool CWanted::AreArmyRequired() { - return m_nWantedLevel >= 6; + return m_nWantedLevel == 6 || m_bArmyRequired; } int32 @@ -77,6 +77,8 @@ void CWanted::SetWantedLevel(int32 level) { ClearQdCrimes(); + if (level > MaximumWantedLevel) + level = MaximumWantedLevel; switch (level) { case 0: m_nChaos = 0; @@ -100,8 +102,6 @@ CWanted::SetWantedLevel(int32 level) m_nChaos = 3220; break; default: - if (level > MaximumWantedLevel) - m_nChaos = MaximumWantedLevel; break; } UpdateWantedLevel(); @@ -275,6 +275,9 @@ CWanted::UpdateWantedLevel() { int32 CurrWantedLevel = m_nWantedLevel; + if (m_nChaos > nMaximumWantedLevel) + m_nChaos = nMaximumWantedLevel; + if (m_nChaos >= 0 && m_nChaos < 40) { m_nWantedLevel = 0; m_MaximumLawEnforcerVehicles = 0; diff --git a/src/core/World.cpp b/src/core/World.cpp index 829a64d4..0440a951 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -29,6 +29,7 @@ bool &CWorld::bForceProcessControl = *(bool*)0x95CD6C; bool &CWorld::bProcessCutsceneOnly = *(bool*)0x95CD8B; WRAPPER void CWorld::RemoveReferencesToDeletedObject(CEntity*) { EAXJMP(0x4B3BF0); } +WRAPPER void CWorld::FindObjectsKindaColliding(const CVector &, float, bool, int16*, int16, CEntity **, bool, bool, bool, bool, bool){ EAXJMP(0x4B2A30); } void CWorld::Add(CEntity *ent) diff --git a/src/core/World.h b/src/core/World.h index fd9d6fc3..e4f46589 100644 --- a/src/core/World.h +++ b/src/core/World.h @@ -103,6 +103,7 @@ public: static float FindGroundZFor3DCoord(float x, float y, float z, bool *found); static float FindRoofZFor3DCoord(float x, float y, float z, bool *found); static void RemoveReferencesToDeletedObject(CEntity*); + static void FindObjectsKindaColliding(const CVector &, float, bool, int16*, int16, CEntity **, bool, bool, bool, bool, bool); static float GetSectorX(float f) { return ((f - WORLD_MIN_X)/SECTOR_SIZE_X); } static float GetSectorY(float f) { return ((f - WORLD_MIN_Y)/SECTOR_SIZE_Y); } diff --git a/src/core/config.h b/src/core/config.h index 38682e22..07c86c0d 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -64,6 +64,8 @@ enum Config { NUMRADARBLIPS = 32, NUMPICKUPS = 336, NUMEVENTS = 64, + + NUM_CARGENS = 160 }; // We'll use this once we're ready to become independent of the game @@ -117,7 +119,7 @@ enum Config { # define CHATTYSPLASH // print what the game is loading #endif -//#define FIX_BUGS // fix bugs in the game, TODO: use this more +#define FIX_BUGS // fix bugs in the game, TODO: use this more #define KANGAROO_CHEAT #define ASPECT_RATIO_SCALE -#define USE_DEBUG_SCRIPT_LOADER
\ No newline at end of file +#define USE_DEBUG_SCRIPT_LOADER diff --git a/src/core/re3.cpp b/src/core/re3.cpp index a0032bc6..35b3cfa4 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -150,6 +150,18 @@ FixCar(void) ((CAutomobile*)veh)->Fix(); } +static int engineStatus; +static void +SetEngineStatus(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + if(!veh->IsCar()) + return; + ((CAutomobile*)veh)->Damage.SetEngineStatus(engineStatus); +} + static void ToggleComedy(void) { @@ -295,9 +307,13 @@ DebugMenuPopulate(void) DebugMenuAddCmd("Spawn", "Spawn Enforcer", [](){ SpawnCar(MI_ENFORCER); }); DebugMenuAddCmd("Spawn", "Spawn Banshee", [](){ SpawnCar(MI_BANSHEE); }); DebugMenuAddCmd("Spawn", "Spawn Yakuza", [](){ SpawnCar(MI_YAKUZA); }); + DebugMenuAddCmd("Spawn", "Spawn Yardie", [](){ SpawnCar(MI_YARDIE); }); DebugMenuAddCmd("Spawn", "Spawn Dodo", [](){ SpawnCar(MI_DODO); }); + DebugMenuAddCmd("Spawn", "Spawn Rhino", [](){ SpawnCar(MI_RHINO); }); + DebugMenuAddCmd("Spawn", "Spawn Firetruck", [](){ SpawnCar(MI_FIRETRUCK); }); - + DebugMenuAddVar("Debug", "Engine Status", &engineStatus, nil, 1, 0, 226, nil); + DebugMenuAddCmd("Debug", "Set Engine Status", SetEngineStatus); DebugMenuAddCmd("Debug", "Fix Car", FixCar); DebugMenuAddCmd("Debug", "Toggle Comedy Controls", ToggleComedy); DebugMenuAddCmd("Debug", "Place Car on Road", PlaceOnRoad); diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp index b795931f..04a93420 100644 --- a/src/entities/Entity.cpp +++ b/src/entities/Entity.cpp @@ -65,7 +65,7 @@ CEntity::CEntity(void) m_flagD8 = false; bIsSubway = false; bDrawLast = false; - m_flagD40 = false; + bNoBrightHeadLights = false; m_flagD80 = false; bDistanceFade = false; diff --git a/src/entities/Entity.h b/src/entities/Entity.h index cdc9a173..11d3e8cd 100644 --- a/src/entities/Entity.h +++ b/src/entities/Entity.h @@ -76,7 +76,7 @@ public: uint32 m_flagD8 : 1; // used by cBuoyancy::ProcessBuoyancy uint32 bIsSubway : 1; // set when subway, but maybe different meaning? uint32 bDrawLast : 1; - uint32 m_flagD40 : 1; + uint32 bNoBrightHeadLights : 1; uint32 m_flagD80 : 1; // CObject visibility? // flagsE diff --git a/src/math/Matrix.h b/src/math/Matrix.h index 05a6eb03..bfe85afa 100644 --- a/src/math/Matrix.h +++ b/src/math/Matrix.h @@ -125,6 +125,24 @@ public: m_matrix.pos.y = 0.0f; m_matrix.pos.z = 0.0f; } + void Scale(float scale) + { + // GTA treats this as 4x4 floats + m_matrix.right.x *= scale; + m_matrix.right.y *= scale; + m_matrix.right.z *= scale; + m_matrix.up.x *= scale; + m_matrix.up.y *= scale; + m_matrix.up.z *= scale; + m_matrix.at.x *= scale; + m_matrix.at.y *= scale; + m_matrix.at.z *= scale; + m_matrix.pos.x *= scale; + m_matrix.pos.y *= scale; + m_matrix.pos.z *= scale; + m_matrix.flags = 0; + } + void SetRotateXOnly(float angle){ float c = Cos(angle); @@ -192,40 +210,10 @@ 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; + void SetRotate(float xAngle, float yAngle, float zAngle); + void Rotate(float x, float y, float z); - 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(); - CVector &u = GetUp(); - u = CrossProduct(r, f); - u.Normalise(); - r = CrossProduct(f, u); - r.Normalise(); - f = CrossProduct(u, r); - } + void Reorthogonalise(void); void CopyOnlyMatrix(CMatrix *other){ m_matrix = other->m_matrix; } @@ -245,35 +233,13 @@ public: } }; -inline CMatrix& -Invert(const CMatrix &src, CMatrix &dst) -{ - // GTA handles this as a raw 4x4 orthonormal matrix - // and trashes the RW flags, let's not do that - // actual copy of librw code: - RwMatrix *d = &dst.m_matrix; - const RwMatrix *s = &src.m_matrix; - d->right.x = s->right.x; - d->right.y = s->up.x; - d->right.z = s->at.x; - d->up.x = s->right.y; - d->up.y = s->up.y; - d->up.z = s->at.y; - d->at.x = s->right.z; - d->at.y = s->up.z; - d->at.z = s->at.z; - d->pos.x = -(s->pos.x*s->right.x + - s->pos.y*s->right.y + - s->pos.z*s->right.z); - d->pos.y = -(s->pos.x*s->up.x + - s->pos.y*s->up.y + - s->pos.z*s->up.z); - d->pos.z = -(s->pos.x*s->at.x + - s->pos.y*s->at.y + - s->pos.z*s->at.z); - d->flags = rwMATRIXTYPEORTHONORMAL; - return dst; -} + +CMatrix &Invert(const CMatrix &src, CMatrix &dst); +CVector operator*(const CMatrix &mat, const CVector &vec); +CMatrix operator*(const CMatrix &m1, const CMatrix &m2); +CVector MultiplyInverse(const CMatrix &mat, const CVector &vec); +CVector Multiply3x3(const CMatrix &mat, const CVector &vec); +CVector Multiply3x3(const CVector &vec, const CMatrix &mat); inline CMatrix Invert(const CMatrix &matrix) @@ -282,64 +248,6 @@ Invert(const CMatrix &matrix) return Invert(matrix, inv); } -inline CVector -operator*(const CMatrix &mat, const CVector &vec) -{ - return CVector( - mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z + mat.m_matrix.pos.x, - mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z + mat.m_matrix.pos.y, - mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z + mat.m_matrix.pos.z); -} - -inline CMatrix -operator*(const CMatrix &m1, const CMatrix &m2) -{ - CMatrix out; - RwMatrix *dst = &out.m_matrix; - const RwMatrix *src1 = &m1.m_matrix; - const RwMatrix *src2 = &m2.m_matrix; - dst->right.x = src1->right.x*src2->right.x + src1->up.x*src2->right.y + src1->at.x*src2->right.z; - dst->right.y = src1->right.y*src2->right.x + src1->up.y*src2->right.y + src1->at.y*src2->right.z; - dst->right.z = src1->right.z*src2->right.x + src1->up.z*src2->right.y + src1->at.z*src2->right.z; - dst->up.x = src1->right.x*src2->up.x + src1->up.x*src2->up.y + src1->at.x*src2->up.z; - dst->up.y = src1->right.y*src2->up.x + src1->up.y*src2->up.y + src1->at.y*src2->up.z; - dst->up.z = src1->right.z*src2->up.x + src1->up.z*src2->up.y + src1->at.z*src2->up.z; - dst->at.x = src1->right.x*src2->at.x + src1->up.x*src2->at.y + src1->at.x*src2->at.z; - dst->at.y = src1->right.y*src2->at.x + src1->up.y*src2->at.y + src1->at.y*src2->at.z; - dst->at.z = src1->right.z*src2->at.x + src1->up.z*src2->at.y + src1->at.z*src2->at.z; - dst->pos.x = src1->right.x*src2->pos.x + src1->up.x*src2->pos.y + src1->at.x*src2->pos.z + src1->pos.x; - dst->pos.y = src1->right.y*src2->pos.x + src1->up.y*src2->pos.y + src1->at.y*src2->pos.z + src1->pos.y; - dst->pos.z = src1->right.z*src2->pos.x + src1->up.z*src2->pos.y + src1->at.z*src2->pos.z + src1->pos.z; - return out; -} - -inline CVector -MultiplyInverse(const CMatrix &mat, const CVector &vec) -{ - CVector v(vec.x - mat.m_matrix.pos.x, vec.y - mat.m_matrix.pos.y, vec.z - mat.m_matrix.pos.z); - return CVector( - mat.m_matrix.right.x * v.x + mat.m_matrix.right.y * v.y + mat.m_matrix.right.z * v.z, - mat.m_matrix.up.x * v.x + mat.m_matrix.up.y * v.y + mat.m_matrix.up.z * v.z, - mat.m_matrix.at.x * v.x + mat.m_matrix.at.y * v.y + mat.m_matrix.at.z * v.z); -} - -inline CVector -Multiply3x3(const CMatrix &mat, const CVector &vec) -{ - return CVector( - mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z, - mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z, - mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z); -} - -inline CVector -Multiply3x3(const CVector &vec, const CMatrix &mat) -{ - return CVector( - mat.m_matrix.right.x * vec.x + mat.m_matrix.right.y * vec.y + mat.m_matrix.right.z * vec.z, - mat.m_matrix.up.x * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.up.z * vec.z, - mat.m_matrix.at.x * vec.x + mat.m_matrix.at.y * vec.y + mat.m_matrix.at.z * vec.z); -} class CCompressedMatrixNotAligned { diff --git a/src/math/Vector.h b/src/math/Vector.h index f794a57f..42087339 100644 --- a/src/math/Vector.h +++ b/src/math/Vector.h @@ -31,7 +31,7 @@ public: void Normalise(void) { float sq = MagnitudeSqr(); if(sq > 0.0f){ - float invsqrt = 1.0f/Sqrt(sq); // CMaths::RecipSqrt + float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; z *= invsqrt; @@ -71,6 +71,10 @@ public: return CVector(-x, -y, -z); } + const bool operator==(CVector const &right) { + return x == right.x && y == right.y && z == right.z; + } + bool IsZero(void) { return x == 0.0f && y == 0.0f && z == 0.0f; } }; diff --git a/src/math/Vector2D.h b/src/math/Vector2D.h index e6b04c14..c8835ec0 100644 --- a/src/math/Vector2D.h +++ b/src/math/Vector2D.h @@ -7,13 +7,14 @@ public: CVector2D(void) {} CVector2D(float x, float y) : x(x), y(y) {} CVector2D(const CVector &v) : x(v.x), y(v.y) {} + float Heading(void) const { return Atan2(-x, y); } float Magnitude(void) const { return Sqrt(x*x + y*y); } float MagnitudeSqr(void) const { return x*x + y*y; } void Normalise(void){ float sq = MagnitudeSqr(); if(sq > 0.0f){ - float invsqrt = 1.0f/Sqrt(sq); + float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; }else diff --git a/src/math/math.cpp b/src/math/math.cpp index c1199fcc..e8b7d933 100644 --- a/src/math/math.cpp +++ b/src/math/math.cpp @@ -5,6 +5,145 @@ // TODO: move more stuff into here void +CMatrix::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 +CMatrix::Rotate(float x, float y, float z) +{ + // TODO? do this directly without creating another matrix + CMatrix rot; + rot.SetRotate(x, y, z); + *this = rot * *this; +} + +void +CMatrix::Reorthogonalise(void) +{ + CVector &r = GetRight(); + CVector &f = GetForward(); + CVector &u = GetUp(); + u = CrossProduct(r, f); + u.Normalise(); + r = CrossProduct(f, u); + r.Normalise(); + f = CrossProduct(u, r); +} + +CMatrix& +Invert(const CMatrix &src, CMatrix &dst) +{ + // GTA handles this as a raw 4x4 orthonormal matrix + // and trashes the RW flags, let's not do that + // actual copy of librw code: + RwMatrix *d = &dst.m_matrix; + const RwMatrix *s = &src.m_matrix; + d->right.x = s->right.x; + d->right.y = s->up.x; + d->right.z = s->at.x; + d->up.x = s->right.y; + d->up.y = s->up.y; + d->up.z = s->at.y; + d->at.x = s->right.z; + d->at.y = s->up.z; + d->at.z = s->at.z; + d->pos.x = -(s->pos.x*s->right.x + + s->pos.y*s->right.y + + s->pos.z*s->right.z); + d->pos.y = -(s->pos.x*s->up.x + + s->pos.y*s->up.y + + s->pos.z*s->up.z); + d->pos.z = -(s->pos.x*s->at.x + + s->pos.y*s->at.y + + s->pos.z*s->at.z); + d->flags = rwMATRIXTYPEORTHONORMAL; + return dst; +} + +CVector +operator*(const CMatrix &mat, const CVector &vec) +{ + return CVector( + mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z + mat.m_matrix.pos.x, + mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z + mat.m_matrix.pos.y, + mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z + mat.m_matrix.pos.z); +} + +CMatrix +operator*(const CMatrix &m1, const CMatrix &m2) +{ + CMatrix out; + RwMatrix *dst = &out.m_matrix; + const RwMatrix *src1 = &m1.m_matrix; + const RwMatrix *src2 = &m2.m_matrix; + dst->right.x = src1->right.x*src2->right.x + src1->up.x*src2->right.y + src1->at.x*src2->right.z; + dst->right.y = src1->right.y*src2->right.x + src1->up.y*src2->right.y + src1->at.y*src2->right.z; + dst->right.z = src1->right.z*src2->right.x + src1->up.z*src2->right.y + src1->at.z*src2->right.z; + dst->up.x = src1->right.x*src2->up.x + src1->up.x*src2->up.y + src1->at.x*src2->up.z; + dst->up.y = src1->right.y*src2->up.x + src1->up.y*src2->up.y + src1->at.y*src2->up.z; + dst->up.z = src1->right.z*src2->up.x + src1->up.z*src2->up.y + src1->at.z*src2->up.z; + dst->at.x = src1->right.x*src2->at.x + src1->up.x*src2->at.y + src1->at.x*src2->at.z; + dst->at.y = src1->right.y*src2->at.x + src1->up.y*src2->at.y + src1->at.y*src2->at.z; + dst->at.z = src1->right.z*src2->at.x + src1->up.z*src2->at.y + src1->at.z*src2->at.z; + dst->pos.x = src1->right.x*src2->pos.x + src1->up.x*src2->pos.y + src1->at.x*src2->pos.z + src1->pos.x; + dst->pos.y = src1->right.y*src2->pos.x + src1->up.y*src2->pos.y + src1->at.y*src2->pos.z + src1->pos.y; + dst->pos.z = src1->right.z*src2->pos.x + src1->up.z*src2->pos.y + src1->at.z*src2->pos.z + src1->pos.z; + return out; +} + +CVector +MultiplyInverse(const CMatrix &mat, const CVector &vec) +{ + CVector v(vec.x - mat.m_matrix.pos.x, vec.y - mat.m_matrix.pos.y, vec.z - mat.m_matrix.pos.z); + return CVector( + mat.m_matrix.right.x * v.x + mat.m_matrix.right.y * v.y + mat.m_matrix.right.z * v.z, + mat.m_matrix.up.x * v.x + mat.m_matrix.up.y * v.y + mat.m_matrix.up.z * v.z, + mat.m_matrix.at.x * v.x + mat.m_matrix.at.y * v.y + mat.m_matrix.at.z * v.z); +} + +CVector +Multiply3x3(const CMatrix &mat, const CVector &vec) +{ + return CVector( + mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z, + mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z, + mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z); +} + +CVector +Multiply3x3(const CVector &vec, const CMatrix &mat) +{ + return CVector( + mat.m_matrix.right.x * vec.x + mat.m_matrix.right.y * vec.y + mat.m_matrix.right.z * vec.z, + mat.m_matrix.up.x * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.up.z * vec.z, + mat.m_matrix.at.x * vec.x + mat.m_matrix.at.y * vec.y + mat.m_matrix.at.z * vec.z); +} + + +void CQuaternion::Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t) { if(theta == 0.0f) diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp index 9b2924e9..33e7195b 100644 --- a/src/modelinfo/VehicleModelInfo.cpp +++ b/src/modelinfo/VehicleModelInfo.cpp @@ -79,7 +79,7 @@ RwObjectNameIdAssocation boatIds[] = { { "boat_moving_hi", 1, VEHICLE_FLAG_COLLAPSE }, { "boat_rudder_hi", 3, VEHICLE_FLAG_COLLAPSE }, { "windscreen", 2, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_COLLAPSE }, - { "ped_frontseat", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_frontseat", BOAT_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; diff --git a/src/modelinfo/VehicleModelInfo.h b/src/modelinfo/VehicleModelInfo.h index 37f47489..b5399d44 100644 --- a/src/modelinfo/VehicleModelInfo.h +++ b/src/modelinfo/VehicleModelInfo.h @@ -47,9 +47,6 @@ enum { }; enum { - VEHICLE_DUMMY_BOAT_RUDDER = 0, - VEHICLE_DUMMY_FRONT_SEATS = 2, - VEHICLE_DUMMY_REAR_SEATS = 3, NUM_VEHICLE_POSITIONS = 10 }; diff --git a/src/objects/Object.cpp b/src/objects/Object.cpp index f3ba8087..bba4d7d9 100644 --- a/src/objects/Object.cpp +++ b/src/objects/Object.cpp @@ -8,6 +8,7 @@ WRAPPER void CObject::ObjectDamage(float amount) { EAXJMP(0x4BB240); } WRAPPER void CObject::DeleteAllTempObjectInArea(CVector, float) { EAXJMP(0x4BBED0); } +WRAPPER void CObject::Init(void) { EAXJMP(0x4BAEC0); } int16 &CObject::nNoTempObjects = *(int16*)0x95CCA2; int16 &CObject::nBodyCastHealth = *(int16*)0x5F7D4C; // 1000 @@ -41,6 +42,15 @@ CObject::CObject(void) m_pCollidingEntity = nil; } +CObject::CObject(int32 mi, bool createRW) +{ + if (createRW) + SetModelIndex(mi); + else + SetModelIndexNoCreate(mi); + Init(); +} + CObject::~CObject(void) { CRadar::ClearBlipForEntity(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(this)); diff --git a/src/objects/Object.h b/src/objects/Object.h index 0ce1a3aa..21348b52 100644 --- a/src/objects/Object.h +++ b/src/objects/Object.h @@ -66,6 +66,7 @@ public: static void operator delete(void*, size_t); CObject(void); + CObject(int32, bool); ~CObject(void); void Render(void); @@ -74,6 +75,7 @@ public: void ObjectDamage(float amount); void RefModelInfo(int32 modelId); + void Init(void); static void DeleteAllTempObjectInArea(CVector, float); }; diff --git a/src/objects/ParticleObject.cpp b/src/objects/ParticleObject.cpp index 25f223e1..f5cd4c94 100644 --- a/src/objects/ParticleObject.cpp +++ b/src/objects/ParticleObject.cpp @@ -2,9 +2,10 @@ #include "patcher.h" #include "ParticleObject.h" -WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, bool remove) { EAXJMP(0x4BC4D0); } -WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, float size, bool remove) { EAXJMP(0x4BC520); } -WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, bool remove) { EAXJMP(0x4BC570); } +WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, uint8 remove) { EAXJMP(0x4BC4D0); } +WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, float size, uint8 remove) { EAXJMP(0x4BC520); } +WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, uint8 remove) { EAXJMP(0x4BC570); } +WRAPPER void CParticleObject::AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, uint32, const RwRGBA &col, uint8 remove) { EAXJMP(0x4BC5B0); } // Converted from static void __cdecl CParticleObject::Initialise() 0x42C760 void CParticleObject::Initialise() diff --git a/src/objects/ParticleObject.h b/src/objects/ParticleObject.h index 6ec090e7..192d3703 100644 --- a/src/objects/ParticleObject.h +++ b/src/objects/ParticleObject.h @@ -29,9 +29,10 @@ enum eParticleObjectType class CParticleObject : CPlaceable { public: - static void AddObject(uint16 type, const CVector &pos, bool remove); - static void AddObject(uint16 type, const CVector &pos, float size, bool remove); - static void AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, bool remove); + static void AddObject(uint16 type, const CVector &pos, uint8 remove); + static void AddObject(uint16 type, const CVector &pos, float size, uint8 remove); + static void AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, uint8 remove); + static void AddObject(uint16 type, const CVector &pos, const CVector &dir, float size, uint32, const RwRGBA &col, uint8 remove); static void Initialise(); static void UpdateAll(); }; diff --git a/src/peds/CivilianPed.cpp b/src/peds/CivilianPed.cpp index 3b1f9e1c..f28a1134 100644 --- a/src/peds/CivilianPed.cpp +++ b/src/peds/CivilianPed.cpp @@ -28,7 +28,7 @@ CCivilianPed::ProcessNearestFreePhone(int unused) if (gPhoneInfo.m_aPhones[phoneId].m_nState != PHONE_STATE_FREE) return false; - field_31C = 1; + bRunningToPhone = true; SetMoveState(PEDMOVE_RUN); SetSeek(gPhoneInfo.m_aPhones[phoneId].m_vecPos, 0.3f); m_phoneId = phoneId; diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index 0c4e33d6..f8b44301 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -5,7 +5,6 @@ #include "Particle.h" #include "Stats.h" #include "World.h" -#include "DMAudio.h" #include "RpAnimBlend.h" #include "Ped.h" #include "PlayerPed.h" @@ -29,9 +28,10 @@ #include "Darkel.h" #include "PathFind.h" #include "ModelIndices.h" +#include "FileMgr.h" +#include "TempColModels.h" WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC430); } -WRAPPER void CPed::Say(uint16 audio) { EAXJMP(0x4E5A10); } WRAPPER void CPed::SetDead(void) { EAXJMP(0x4D3970); } WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } WRAPPER void CPed::SetPedPositionInCar(void) { EAXJMP(0x4D4970); } @@ -47,12 +47,22 @@ WRAPPER void CPed::MakeChangesForNewWeapon(int8) { EAXJMP(0x4F2560); } WRAPPER void CPed::SetSeek(CVector, float) { EAXJMP(0x4D14B0); } WRAPPER bool CPed::Seek(void) { EAXJMP(0x4D1640); } WRAPPER void CPed::SetFollowPath(CVector) { EAXJMP(0x4D2EA0); } +WRAPPER void CPed::RemoveInCarAnims(void) { EAXJMP(0x4E4E20); } +WRAPPER void CPed::StartFightDefend(uint8, uint8, uint8) { EAXJMP(0x4E7780); } +WRAPPER void CPed::SetDirectionToWalkAroundObject(CEntity*) { EAXJMP(0x4CCEB0); } bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; bool &CPed::bPedCheat3 = *(bool*)0x95CD59; + CColPoint &CPed::ms_tempColPoint = *(CColPoint*)0x62DB14; +// TODO: PedAudioData should be hardcoded into exe, and it isn't reversed yet. +CPedAudioData (&CPed::PedAudioData)[38] = *(CPedAudioData(*)[38]) * (uintptr*)0x5F94C4; + +uint16 &CPed::unknownFightThing = *(uint16*)0x95CC58; +FightMove (&CPed::ms_fightMoves)[24] = * (FightMove(*)[24]) * (uintptr*)0x5F9844; + uint16 &CPed::distanceMultToCountPedNear = *(uint16*)0x5F8C98; CVector &CPed::offsetToOpenRegularCarDoor = *(CVector*)0x62E030; @@ -279,17 +289,17 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_fHealth = 100.0f; m_fArmour = 0.0f; m_nPedType = pedType; - field_520 = 0; - m_talkTimer = 0; - m_talkTypeLast = 167; - m_talkType = 167; + m_lastSoundStart = 0; + m_soundStart = 0; + m_lastQueuedSound = SOUND_TOTAL_PED_SOUNDS; + m_queuedSound = SOUND_TOTAL_PED_SOUNDS; m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; CharCreatedBy = RANDOM_CHAR; m_leader = nil; m_pedInObjective = nil; m_carInObjective = nil; - bInVehicle = 0; + bInVehicle = false; m_pMyVehicle = nil; m_pVehicleAnim = nil; m_vecOffsetSeek.x = 0.0f; @@ -323,7 +333,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); m_wepSkills = 0; field_318 = 1.0f; - field_31C = 0; + bRunningToPhone = false; m_phoneId = -1; m_lastAccident = 0; m_fleeFrom = nil; @@ -338,7 +348,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_nPedState = PED_IDLE; m_nLastPedState = PED_NONE; m_nMoveState = PEDMOVE_STILL; - m_nStoredActionState = 0; + m_nStoredMoveState = PEDMOVE_NONE; m_pFire = nil; m_pPointGunAt = nil; m_pLookTarget = nil; @@ -383,7 +393,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) bRespondsToThreats = true; bRenderPedInCar = true; bChangedSeat = false; - m_ped_flagC10 = false; + bUpdateAnimHeading = false; bBodyPartJustCameOff = false; m_ped_flagC40 = false; m_ped_flagC80 = false; @@ -393,16 +403,16 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_ped_flagD4 = false; m_ped_flagD8 = false; bIsPedDieAnimPlaying = false; - m_ped_flagD20 = false; + bIsFleeing = false; m_ped_flagD40 = false; - m_bScriptObjectiveCompleted = false; + bScriptObjectiveCompleted = false; - m_ped_flagE1 = false; + bKindaStayInSamePlace = false; m_ped_flagE2 = false; bNotAllowedToDuck = false; bCrouchWhenShooting = false; bIsDucking = false; - m_ped_flagE20 = false; + bGetUpAnimStarted = false; bDoBloodyFootprints = false; m_ped_flagE80 = false; @@ -427,17 +437,17 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_ped_flagH1 = false; m_ped_flagH2 = false; m_ped_flagH4 = false; - m_ped_flagH8 = false; + bClearObjective = false; m_ped_flagH10 = false; m_ped_flagH20 = false; m_ped_flagH40 = false; m_ped_flagH80 = false; m_ped_flagI1 = false; - m_ped_flagI2 = false; + bNoCriticalHits = false; m_ped_flagI4 = false; bHasAlreadyBeenRecorded = false; - m_ped_flagI10 = false; + bIsFell = false; #ifdef KANGAROO_CHEAT m_ped_flagI80 = false; #endif @@ -449,7 +459,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) DMAudio.SetEntityStatus(m_audioEntityId, 1); m_fearFlags = CPedType::GetThreats(m_nPedType); m_threatEntity = nil; - m_eventOrThread = CVector2D(0.0f, 0.0f); + m_eventOrThreat = CVector2D(0.0f, 0.0f); m_pEventEntity = nil; m_fAngleToEvent = 0.0f; m_numNearPeds = 0; @@ -461,8 +471,8 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) m_pPathNodesStates[i] = nil; } } - m_maxWeaponTypeAllowed = 0; - m_currentWeapon = 0; + m_maxWeaponTypeAllowed = WEAPONTYPE_UNARMED; + m_currentWeapon = WEAPONTYPE_UNARMED; m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; for(int i = 0; i < WEAPONTYPE_TOTAL_INVENTORY_WEAPONS; i++) @@ -475,7 +485,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this) weapon.m_nTimer = 0; } - m_lastHitState = 0; + m_lastFightMove = FIGHTMOVE_NULL; GiveWeapon(WEAPONTYPE_UNARMED, 0); m_wepAccuracy = 60; m_lastWepDam = -1; @@ -657,7 +667,7 @@ CPed::AddWeaponModel(int id) atm = (RpAtomic*)CModelInfo::GetModelInfo(id)->CreateInstance(); RwFrameDestroy(RpAtomicGetFrame(atm)); RpAtomicSetFrame(atm, GetNodeFrame(PED_HANDR)); - RpClumpAddAtomic((RpClump*)m_rwObject, atm); + RpClumpAddAtomic(GetClump(), atm); m_wepModelID = id; } } @@ -954,10 +964,10 @@ CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) case ANIM_WEAPON_START_THROW: if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1380) && ped->IsPlayer()) { attackAssoc->blendDelta = -1000.0f; - newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROWU); + newAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_WEAPON_THROWU); } else { attackAssoc->blendDelta = -1000.0f; - newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROW); + newAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_WEAPON_THROW); } newAnim->SetFinishCallback(FinishedAttackCB, ped); @@ -1006,7 +1016,7 @@ CPed::Attack(void) ourWeaponType = GetWeapon()->m_eWeaponType; ourWeapon = CWeaponInfo::GetWeaponInfo(ourWeaponType); ourWeaponFire = ourWeapon->m_eWeaponFire; - weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_AnimToPlay); + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ourWeapon->m_AnimToPlay); lastReloadWasInFuture = m_ped_flagA4; reloadAnimAssoc = nil; reloadAnim = NUM_ANIMS; @@ -1019,7 +1029,7 @@ CPed::Attack(void) reloadAnim = ANIM_AK_RELOAD; if (reloadAnim != NUM_ANIMS) - reloadAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, reloadAnim); + reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), reloadAnim); if (bIsDucking) return; @@ -1036,12 +1046,12 @@ CPed::Attack(void) lastReloadWasInFuture = true; if (!weaponAnimAssoc) { - weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_Anim2ToPlay); + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ourWeapon->m_Anim2ToPlay); delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; // Long throw granade, molotov if (!weaponAnimAssoc && ourWeapon->m_bThrow) { - weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_WEAPON_THROWU); + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_THROWU); delayBetweenAnimAndFire = 0.2f; } @@ -1049,14 +1059,14 @@ CPed::Attack(void) if (lastReloadWasInFuture) { if (ourWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->field_1380) { if (!CGame::nastyGame || ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), nil) < PED_ON_THE_FLOOR) { - weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); } else { - weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); } weaponAnimAssoc->SetFinishCallback(CPed::FinishedAttackCB, this); - weaponAnimAssoc->flags |= ASSOC_RUNNING; + weaponAnimAssoc->SetRun(); if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) weaponAnimAssoc->SetCurrentTime(0.0f); @@ -1117,7 +1127,7 @@ CPed::Attack(void) if (GetWeapon()->m_eWeaponState != WEAPONSTATE_MELEE_MADECONTACT) { // If reloading just began, start the animation if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING && reloadAnim != NUM_ANIMS && !reloadAnimAssoc) { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, reloadAnim, 8.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, reloadAnim, 8.0f); ClearLookFlag(); ClearAimFlag(); m_ped_flagA4 = false; @@ -1129,7 +1139,7 @@ CPed::Attack(void) if (weaponAnimAssoc->animId == ANIM_WEAPON_BAT_V || weaponAnimAssoc->animId == ANIM_WEAPON_BAT_H) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, 1.0f); } else if (weaponAnimAssoc->animId == ANIM_FIGHT_PPUNCH) { - DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_PUNCH_ATTACK, 0.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); } weaponAnimAssoc->speed = 0.5f; @@ -1184,13 +1194,13 @@ CPed::Attack(void) if (weaponAnim != ourWeapon->m_Anim2ToPlay || weaponAnim == ANIM_RBLOCK_CSHOOT) { weaponAnimAssoc->Start(ourWeapon->m_fAnimLoopStart); } else { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); } } else { if (weaponAnim == ourWeapon->m_Anim2ToPlay) weaponAnimAssoc->SetCurrentTime(0.1f); else - CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); } } else { ClearAimFlag(); @@ -1276,9 +1286,9 @@ CPed::Duck(void) void CPed::ClearDuck(void) { - CAnimBlendAssociation* animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_DUCK_DOWN); + CAnimBlendAssociation* animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DUCK_DOWN); if (!animAssoc) { - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_DUCK_LOW); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DUCK_LOW); if (!animAssoc) { bIsDucking = false; @@ -1292,9 +1302,9 @@ CPed::ClearDuck(void) if (m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN) return; - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_RBLOCK_CSHOOT); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RBLOCK_CSHOOT); if (!animAssoc || animAssoc->blendDelta < 0.0f) { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_RBLOCK_CSHOOT, 4.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_RBLOCK_CSHOOT, 4.0f); } } @@ -1310,9 +1320,9 @@ CPed::ClearPointGunAt(void) if (m_nPedState == PED_AIM_GUN) { RestorePreviousState(); weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weaponInfo->m_AnimToPlay); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_AnimToPlay); if (!animAssoc || animAssoc->blendDelta < 0.0f) { - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weaponInfo->m_Anim2ToPlay); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_Anim2ToPlay); } if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; @@ -1330,14 +1340,14 @@ CPed::BeingDraggedFromCar(void) 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); + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 100.0f); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SIT); if (!animAssoc) { - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_LSIT); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_LSIT); if (!animAssoc) { - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITP); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SITP); if (!animAssoc) - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITPLO); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SITPLO); } } if (animAssoc) @@ -1361,7 +1371,7 @@ CPed::BeingDraggedFromCar(void) if (!dontRunAnim) - m_pVehicleAnim = CAnimManager::AddAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, enterAnim); + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, enterAnim); m_pVehicleAnim->SetFinishCallback(PedSetDraggedOutCarCB, this); lineUpType = LINE_UP_TO_CAR_START; @@ -1380,9 +1390,9 @@ CPed::RestartNonPartialAnims(void) { CAnimBlendAssociation *assoc; - for (assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (!assoc->IsPartial()) - assoc->flags |= ASSOC_RUNNING; + assoc->SetRun(); } } @@ -1394,7 +1404,7 @@ CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) CPed *ped = (CPed*)arg; eWeaponType weaponType = ped->GetWeapon()->m_eWeaponType; - quickJackedAssoc = RpAnimBlendClumpGetAssociation((RpClump*) ped->m_rwObject, ANIM_CAR_QJACKED); + quickJackedAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_CAR_QJACKED); if (ped->m_nPedState != PED_ARRESTED) { ped->m_nLastPedState = PED_NONE; if (dragAssoc) @@ -1432,7 +1442,7 @@ CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) } else { dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); if (ped->CanSetPedState()) - CAnimManager::BlendAnimation((RpClump*) ped->m_rwObject, ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); } // Only uzi can be used on cars, so previous weapon was stored @@ -1444,12 +1454,12 @@ CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) } else { ped->AddWeaponModel(CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId); } - ped->m_nStoredActionState = 0; + ped->m_nStoredMoveState = PEDMOVE_NONE; ped->m_ped_flagI4 = false; } -void -CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float seatPosMult) +CVector +CPed::GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult) { CVehicleModelInfo *vehModel; CVector vehDoorPos; @@ -1457,7 +1467,7 @@ CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enter float seatOffset; vehModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(veh->m_modelIndex); - if (veh->bIsVan && (enterType == CAR_DOOR_LR || enterType == CAR_DOOR_RR)) { + if (veh->bIsVan && (component == CAR_DOOR_LR || component == CAR_DOOR_RR)) { seatOffset = 0.0f; vehDoorOffset = offsetToOpenVanDoor; } else { @@ -1469,63 +1479,63 @@ CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enter } } - switch (enterType) { + switch (component) { case CAR_DOOR_RF: if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + vehDoorPos = vehModel->m_positions[BOAT_POS_FRONTSEAT]; else - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + vehDoorPos = vehModel->m_positions[CAR_POS_FRONTSEAT]; vehDoorPos.x += seatOffset; vehDoorOffset.x = -vehDoorOffset.x; break; case CAR_DOOR_RR: - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; vehDoorPos.x += seatOffset; vehDoorOffset.x = -vehDoorOffset.x; break; case CAR_DOOR_LF: if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + vehDoorPos = vehModel->m_positions[BOAT_POS_FRONTSEAT]; else - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + vehDoorPos = vehModel->m_positions[CAR_POS_FRONTSEAT]; vehDoorPos.x = -(vehDoorPos.x + seatOffset); break; case CAR_DOOR_LR: - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; vehDoorPos.x = -(vehDoorPos.x + seatOffset); break; default: if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + vehDoorPos = vehModel->m_positions[BOAT_POS_FRONTSEAT]; else - vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + vehDoorPos = vehModel->m_positions[CAR_POS_FRONTSEAT]; vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); } - *output = vehDoorPos - vehDoorOffset; + return vehDoorPos - vehDoorOffset; } // This function was mostly duplicate of GetLocalPositionToOpenCarDoor, so I've used it. -void -CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType) +CVector +CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component) { CVector localPos; CVector vehDoorPos; - GetLocalPositionToOpenCarDoor(&localPos, veh, enterType, 1.0f); + localPos = GetLocalPositionToOpenCarDoor(veh, component, 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)) { + if (veh->bIsVan && (component == VEHICLE_ENTER_REAR_LEFT || component == VEHICLE_ENTER_REAR_RIGHT)) { localVehDoorOffset = offsetToOpenVanDoor; } else { if (veh->bIsLow) { @@ -1537,19 +1547,18 @@ CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType) vehDoorPosWithoutOffset = Multiply3x3(veh->GetMatrix(), localPos + localVehDoorOffset) + veh->GetPosition(); */ - *output = vehDoorPos; + return vehDoorPos; } -void -CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float offset) +CVector +CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset) { CVector doorPos; CMatrix vehMat(veh->GetMatrix()); - GetLocalPositionToOpenCarDoor(output, veh, enterType, offset); - doorPos = Multiply3x3(vehMat, *output); + doorPos = Multiply3x3(vehMat, GetLocalPositionToOpenCarDoor(veh, component, offset)); - *output = veh->GetPosition() + doorPos; + return veh->GetPosition() + doorPos; } void @@ -1565,19 +1574,19 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) return; if (!bChangedSeat && phase != LINE_UP_TO_CAR_2) { - if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SIT)) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SIT)) { SetPedPositionInCar(); return; } - if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_LSIT)) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_LSIT)) { SetPedPositionInCar(); return; } - if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITP)) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SITP)) { SetPedPositionInCar(); return; } - if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITPLO)) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SITPLO)) { SetPedPositionInCar(); return; } @@ -1597,7 +1606,7 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) if (vehIsUpsideDown) { m_fRotationDest = -PI + veh->GetForward().Heading(); } else if (veh->bIsBus) { - m_fRotationDest = 0.5 * PI + veh->GetForward().Heading(); + m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); } else { m_fRotationDest = veh->GetForward().Heading(); } @@ -1605,7 +1614,17 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) if (vehIsUpsideDown) { m_fRotationDest = veh->GetForward().Heading(); } else if (veh->bIsBus) { - m_fRotationDest = -0.5 * PI + veh->GetForward().Heading(); + m_fRotationDest = -0.5f * PI + veh->GetForward().Heading(); + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } else { + // I don't know will this part ever run(maybe boats?), but the game also handles that. I don't know is it intentional. + + if (vehIsUpsideDown) { + m_fRotationDest = veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); } else { m_fRotationDest = veh->GetForward().Heading(); } @@ -1670,7 +1689,7 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) if (phase == LINE_UP_TO_CAR_2) { neededPos = GetPosition(); } else { - GetPositionToOpenCarDoor(&neededPos, veh, m_vehEnterType, seatPosMult); + neededPos = GetPositionToOpenCarDoor(veh, m_vehEnterType, seatPosMult); } CVector autoZPos = neededPos; @@ -1752,9 +1771,9 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) neededPos -= timeUntilStateChange * m_vecOffsetSeek; } - if (limitedAngle > PI + m_fRotationCur) { + if (PI + m_fRotationCur < limitedAngle) { limitedAngle -= 2 * PI; - } else if (limitedAngle < m_fRotationCur - PI) { + } else if (m_fRotationCur - PI > limitedAngle) { limitedAngle += 2 * PI; } m_fRotationCur -= (m_fRotationCur - limitedAngle) * (1.0f - timeUntilStateChange); @@ -1768,11 +1787,9 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) // 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); + vehDoorMat.GetPosition() += Multiply3x3(vehDoorMat, GetLocalPositionToOpenCarDoor(veh, m_vehEnterType, 0.0f)); GetMatrix() = vehDoorMat; } @@ -1808,7 +1825,7 @@ particleProduceFootSplash(CPed *ped, CVector *pos, float size, int times) adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); CVector direction = ped->GetForward() * -0.05f; - CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, adjustedPos, direction, nil, size, CRGBA(32, 32, 32, 32), 0, 0, CGeneral::GetRandomNumberInRange(0, 1), 200); + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, adjustedPos, direction, nil, size, CRGBA(32, 32, 32, 32), 0, 0, CGeneral::GetRandomNumber() & 1, 200); } } @@ -1827,7 +1844,7 @@ CPed::PlayFootSteps(void) if (!bIsStanding) return; - CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); CAnimBlendAssociation *walkRunAssoc = nil; float walkRunAssocBlend = 0.0f, idleAssocBlend = 0.0f; @@ -1971,7 +1988,7 @@ CPed::BuildPedLists(void) static CPed *unsortedNearPeds[10]; uint16 nextNearPedSlot = 0; - if ((CTimer::GetFrameCounter() + m_randomSeed) % 16) { + if ((CTimer::GetFrameCounter() + (m_randomSeed % 256)) % 16) { for(int i = 0; i < 10; ) { if (m_nearPeds[i]) { @@ -2041,13 +2058,13 @@ void CPed::SetModelIndex(uint32 mi) { CEntity::SetModelIndex(mi); - RpAnimBlendClumpInit((RpClump*) m_rwObject); - RpAnimBlendClumpFillFrameArray((RpClump*) m_rwObject, m_pFrames); + RpAnimBlendClumpInit(GetClump()); + RpAnimBlendClumpFillFrameArray(GetClump(), m_pFrames); CPedModelInfo *modelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex); SetPedStats((ePedStats) modelInfo->m_pedStatType); m_headingRate = m_pedStats->m_headingChangeRate; m_animGroup = (AssocGroupId) modelInfo->m_animGroup; - CAnimManager::AddAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE); + CAnimManager::AddAnimation(GetClump(), 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; @@ -2120,9 +2137,9 @@ CPed::WorkOutHeadingForMovingFirstPerson(float offset) angle = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); } else { if (leftRight < 0.0f) - angle = 0.5 * PI; + angle = 0.5f * PI; else if (leftRight > 0.0f) - angle = -0.5 * PI; + angle = -0.5f * PI; } return CGeneral::LimitRadianAngle(offset + angle); @@ -2175,9 +2192,9 @@ CPed::CalculateNewVelocity(void) float pedSpeed = m_moved.Magnitude(); float localWalkAngle = CGeneral::LimitRadianAngle(walkAngle - m_fRotationCur); - if (localWalkAngle < -0.5 * PI) { + if (localWalkAngle < -0.5f * PI) { localWalkAngle += PI; - } else if (localWalkAngle > 0.5 * PI) { + } else if (localWalkAngle > 0.5f * PI) { localWalkAngle -= PI; } @@ -2187,8 +2204,8 @@ CPed::CalculateNewVelocity(void) 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); + CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_STANCE); + CAnimBlendAssociation* fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FIGHT_IDLE); if ((!idleAssoc || idleAssoc->blendAmount < 0.5f) && !fightAssoc) { LimbOrientation newUpperLegs; @@ -2466,7 +2483,7 @@ CPed::SetObjective(eObjective newObj, void *entity) case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_MUG_CHAR: m_pLastPathNode = nil; - m_ped_flagD20 = false; + bIsFleeing = false; m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f); m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); @@ -2637,7 +2654,7 @@ CPed::SetObjective(eObjective newObj, int16 routePoint, int16 routeType) void CPed::ClearChat(void) { - CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_CHAT); + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; @@ -2698,8 +2715,8 @@ CPed::QuitEnteringCar(void) RestartNonPartialAnims(); - if (!RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_STANCE)) - CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE, 100.0f); + if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_STANCE)) + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 100.0f); if (veh) { if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_nPedState == PED_CARJACK) @@ -2762,7 +2779,7 @@ CPed::ReactToAttack(CEntity *attacker) field_4E8 = CTimer::GetTimeInMilliseconds(); return; } - } else if (bCrouchWhenShooting || m_ped_flagE1) { + } else if (bCrouchWhenShooting || bKindaStayInSamePlace) { SetDuck(CGeneral::GetRandomNumberInRange(1000,3000)); return; } @@ -2852,22 +2869,22 @@ CPed::Chat(void) } if (bIsTalking) { if (CGeneral::GetRandomNumber() < 512) { - CAnimBlendAssociation *chatAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_CHAT); + CAnimBlendAssociation *chatAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_CHAT); if (chatAssoc) { chatAssoc->blendDelta = -4.0f; - chatAssoc->flags |= ASSOC_FADEOUTWHENDONE; + chatAssoc->flags |= ASSOC_DELETEFADEDOUT; } bIsTalking = false; } else Say(SOUND_PED_CHAT); - } else if (!RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject, ASSOC_FLAG100)) { + } else if (!RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FLAG100)) { if (CGeneral::GetRandomNumber() < 20) { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_XPRESS_SCRATCH, 4.0f); } if (!bIsTalking) { - CAnimBlendAssociation *chatAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_IDLE_CHAT, 4.0f); + CAnimBlendAssociation *chatAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_CHAT, 4.0f); float chatTime = CGeneral::GetRandomNumberInRange(0.0f, 3.0f); chatAssoc->SetCurrentTime(chatTime); @@ -2896,7 +2913,7 @@ CPed::CheckAroundForPossibleCollisions(void) CWorld::FindObjectsInRange(ourCentre, 10.0f, true, &maxObject, 6, objects, false, true, false, true, false); for (int i = 0; i < maxObject; i++) { CEntity *object = objects[i]; - if (field_31C) { + if (bRunningToPhone) { if (gPhoneInfo.PhoneAtThisPosition(object->GetPosition())) break; } @@ -3146,12 +3163,12 @@ CPed::ClearAttackByRemovingAnim(void) return; CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); - CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weapon->m_AnimToPlay); + CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weapon->m_AnimToPlay); if (!weaponAssoc) { - weaponAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, weapon->m_Anim2ToPlay); + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weapon->m_Anim2ToPlay); if (!weaponAssoc && weapon->m_bThrow) - weaponAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_WEAPON_THROWU); + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_THROWU); if (!weaponAssoc) { ClearAttack(); @@ -3169,7 +3186,7 @@ CPed::StopNonPartialAnims(void) { CAnimBlendAssociation* assoc; - for (assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (!assoc->IsPartial()) assoc->flags &= ~ASSOC_RUNNING; } @@ -3187,8 +3204,8 @@ CPed::SetStoredState(void) m_nMoveState = PEDMOVE_WALK; } m_nLastPedState = m_nPedState; - if (m_nMoveState >= m_nPrevActionState) - m_nPrevActionState = m_nMoveState; + if (m_nMoveState >= m_nPrevMoveState) + m_nPrevMoveState = m_nMoveState; } void @@ -3224,7 +3241,7 @@ CPed::SetDie(AnimationId animId, float delta, float speed) if (animId == NUM_ANIMS) { bIsPedDieAnimPlaying = false; } else { - CAnimBlendAssociation *dieAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, animId, delta); + CAnimBlendAssociation *dieAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, delta); if (speed > 0.0f) dieAssoc->speed = speed; @@ -3241,7 +3258,7 @@ CPed::SetDie(AnimationId animId, float delta, float speed) if (!bInVehicle) StopNonPartialAnims(); - // ??? + // BUG: This is not timer. m_bloodyFootprintCount = CTimer::GetTimeInMilliseconds(); } @@ -3282,7 +3299,7 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi bool detectDieAnim = true; if (m_nPedState == PED_FALL || m_nPedState == PED_GETUP) { if (!IsPedHeadAbovePos(-0.3f)) { - if (RpAnimBlendClumpGetFirstAssociation((RpClump*) m_rwObject, ASSOC_FLAG800)) + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FLAG800)) dieAnim = ANIM_FLOOR_HIT_F; else dieAnim = ANIM_FLOOR_HIT; @@ -3304,7 +3321,7 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi if (IsPedHeadAbovePos(-0.3f)) { dieAnim = NUM_ANIMS; } else { - if (RpAnimBlendClumpGetFirstAssociation((RpClump*) m_rwObject, ASSOC_FLAG800)) + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FLAG800)) dieAnim = ANIM_FLOOR_HIT_F; else dieAnim = ANIM_FLOOR_HIT; @@ -3338,7 +3355,7 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi if (IsPedHeadAbovePos(-0.3f)) { dieAnim = NUM_ANIMS; } else { - if (RpAnimBlendClumpGetFirstAssociation((RpClump*) m_rwObject, 0x800u)) + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FLAG800)) dieAnim = ANIM_FLOOR_HIT_F; else dieAnim = ANIM_FLOOR_HIT; @@ -3374,7 +3391,7 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi return false; bool dontRemoveLimb; - if (IsPlayer() || m_ped_flagI2) + if (IsPlayer() || bNoCriticalHits) dontRemoveLimb = true; else { switch (method) @@ -3386,10 +3403,10 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi dontRemoveLimb = false; break; case WEAPONTYPE_SHOTGUN: - dontRemoveLimb = CGeneral::GetRandomNumberInRange(0,7); + dontRemoveLimb = CGeneral::GetRandomNumber() & 7; break; default: - dontRemoveLimb = CGeneral::GetRandomNumberInRange(0,15); + dontRemoveLimb = CGeneral::GetRandomNumber() & 15; break; } } @@ -3513,7 +3530,7 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi if (bCollisionProof) return false; - random = CGeneral::GetRandomNumberInRange(0, 3); + random = CGeneral::GetRandomNumber() & 3; switch (random) { case 0: if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) @@ -3654,7 +3671,7 @@ void CPed::ClearFlee(void) { RestorePreviousState(); - m_ped_flagD20 = false; + bIsFleeing = false; m_standardTimer = 0; m_fleeTimer = 0; } @@ -3668,17 +3685,17 @@ CPed::ClearFall(void) void CPed::SetGetUp(void) { - if (m_nPedState == PED_GETUP && m_ped_flagE20) + if (m_nPedState == PED_GETUP && bGetUpAnimStarted) return; if (!CanSetPedState()) return; if (m_fHealth >= 1.0f || IsPedHeadAbovePos(-0.3f)) { - if (m_ped_flagC10) { + if (bUpdateAnimHeading) { m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); - m_fRotationCur -= 0.5*PI; - m_ped_flagC10 = false; + m_fRotationCur -= 0.5f*PI; + bUpdateAnimHeading = false; } if (m_nPedState != PED_GETUP) { SetStoredState(); @@ -3694,7 +3711,7 @@ CPed::SetGetUp(void) collidingVeh->GetMatrix(), *CModelInfo::GetModelInfo(collidingVeh->m_modelIndex)->GetColModel(), &ms_tempColPoint, nil, nil) > 0)) { - m_ped_flagE20 = false; + bGetUpAnimStarted = false; if (IsPlayer()) InflictDamage(nil, WEAPONTYPE_RUNOVERBYCAR, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); else { @@ -3705,23 +3722,23 @@ CPed::SetGetUp(void) } return; } - m_ped_flagE20 = true; + bGetUpAnimStarted = true; m_pCollidingEntity = nil; m_ped_flagH1 = false; - CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_SPRINT); + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_SPRINT); if (animAssoc) { - if (RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_RUN)) { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_RUN, 8.0f); + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN)) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_RUN, 8.0f); } else { - CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE, 8.0f); + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 8.0f); } animAssoc->flags |= ASSOC_DELETEFADEDOUT; } - if (RpAnimBlendClumpGetFirstAssociation((RpClump*) m_rwObject, ASSOC_FLAG800)) - animAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_GETUP_FRONT, 1000.0f); + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FLAG800)) + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_GETUP_FRONT, 1000.0f); else - animAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); animAssoc->SetFinishCallback(PedGetupCB,this); } else { @@ -3733,13 +3750,13 @@ CPed::SetGetUp(void) void CPed::ClearInvestigateEvent(void) { - CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_ROAD_CROSS); + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS); if (!animAssoc) - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_XPRESS_SCRATCH); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH); if (!animAssoc) - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_HBHB); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB); if (!animAssoc) - animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_IDLE_CHAT); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; @@ -3770,7 +3787,7 @@ CPed::ClearLeader(void) SetWanderPath(CGeneral::GetRandomNumberInRange(0,8)); } } else if (m_objective != OBJECTIVE_NONE) { - m_ped_flagH8 = true; + bClearObjective = true; } } @@ -3799,7 +3816,7 @@ CPed::ClearObjective(void) SetMoveState(PEDMOVE_STILL); } } else { - m_ped_flagH8 = true; + bClearObjective = true; } } @@ -3813,7 +3830,7 @@ void CPed::ClearSeek(void) { SetIdle(); - field_31C = 0; + bRunningToPhone = false; } bool @@ -3822,7 +3839,7 @@ CPed::SetWanderPath(int8 pathStateDest) uint8 nextPathState; if (IsPedInControl()) { - if (m_ped_flagE1) { + if (bKindaStayInSamePlace) { SetIdle(); return false; } else { @@ -3918,7 +3935,7 @@ CPed::RestorePreviousState(void) if(!CanSetPedState() || m_nPedState == PED_FALL) return; - if (m_nPedState == PED_GETUP && !m_ped_flagE20) + if (m_nPedState == PED_GETUP && !bGetUpAnimStarted) return; if (bInVehicle && m_pMyVehicle) { @@ -3927,7 +3944,7 @@ CPed::RestorePreviousState(void) } else { if (m_nLastPedState == PED_NONE) { if (!IsPlayer() && CharCreatedBy != MISSION_CHAR && m_objective == OBJECTIVE_NONE) { - if (SetWanderPath(CGeneral::GetRandomNumberInRange(0, 7)) != 0) + if (SetWanderPath(CGeneral::GetRandomNumber() & 7) != 0) return; } SetIdle(); @@ -3950,11 +3967,11 @@ CPed::RestorePreviousState(void) } } } - SetWanderPath(CGeneral::GetRandomNumberInRange(0, 7)); + SetWanderPath(CGeneral::GetRandomNumber() & 7); break; default: m_nPedState = m_nLastPedState; - SetMoveState((eMoveState) m_nPrevActionState); + SetMoveState((eMoveState) m_nPrevMoveState); break; } m_nLastPedState = PED_NONE; @@ -4010,15 +4027,15 @@ CPed::SetPointGunAt(CEntity *to) CAnimBlendAssociation *aimAssoc; if (bCrouchWhenShooting) - aimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, curWeapon->m_Anim2ToPlay); + aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), curWeapon->m_Anim2ToPlay); else - aimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, curWeapon->m_AnimToPlay); + aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), curWeapon->m_AnimToPlay); if (!aimAssoc || aimAssoc->blendDelta < 0.0f) { if (bCrouchWhenShooting) - aimAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, 4.0f); + aimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, 4.0f); else - aimAssoc = CAnimManager::AddAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, curWeapon->m_AnimToPlay); + aimAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay); aimAssoc->blendAmount = 0.0f; aimAssoc->blendDelta = 8.0f; @@ -4039,6 +4056,18 @@ CPed::SetAmmo(eWeaponType weaponType, uint32 ammo) } void +CPed::GrantAmmo(eWeaponType weaponType, uint32 ammo) +{ + if (HasWeapon(weaponType)) { + GetWeapon(weaponType).m_nAmmoTotal += ammo; + } + else { + GetWeapon(weaponType).Initialise(weaponType, ammo); + m_maxWeaponTypeAllowed++; + } +} + +void CPed::SetEvasiveStep(CEntity *reason, uint8 animType) { AnimationId stepAnim; @@ -4067,25 +4096,29 @@ CPed::SetEvasiveStep(CEntity *reason, uint8 animType) } if (neededTurn <= DEGTORAD(90.0f) || veh->m_modelIndex == MI_RCBANDIT || vehPressedHorn || animType != 0) { SetLookFlag(veh, 1); - if (CGeneral::GetRandomNumberInRange(0,1) && veh->m_modelIndex != MI_RCBANDIT && animType == 0) { + if ((CGeneral::GetRandomNumber() & 1) && veh->m_modelIndex != MI_RCBANDIT && animType == 0) { stepAnim = ANIM_IDLE_TAXI; } else { - // I didn't get these things too much. float vehDirection = CGeneral::GetRadianAngleBetweenPoints( veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, 0.0f, 0.0f); - float inversedAngleToFace = angleToFace + PI; - if (inversedAngleToFace > PI) - inversedAngleToFace -= 2*PI; + // Let's turn our back to the "reason" + angleToFace += PI; + + if (angleToFace > PI) + angleToFace -= 2*PI; + + // We don't want to run towards car's direction + float dangerZone = angleToFace - vehDirection; + dangerZone = CGeneral::LimitRadianAngle(dangerZone); - neededTurn = inversedAngleToFace - vehDirection; - neededTurn = CGeneral::LimitRadianAngle(neededTurn); - if (neededTurn <= 0.0) - angleToFace = 0.5*PI + vehDirection; + // So, add or subtract 90deg (jump to left/right) according to that + if (dangerZone <= 0.0f) + angleToFace = 0.5f*PI + vehDirection; else - angleToFace = vehDirection - 0.5*PI; + angleToFace = vehDirection - 0.5f*PI; if (animType == 2) stepAnim = ANIM_HANDSCOWER; @@ -4094,8 +4127,8 @@ CPed::SetEvasiveStep(CEntity *reason, uint8 animType) else stepAnim = NUM_ANIMS; } - if (!RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, stepAnim)) { - CAnimBlendAssociation *stepAssoc = CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, stepAnim, 8.0f); + if (!RpAnimBlendClumpGetAssociation(GetClump(), stepAnim)) { + CAnimBlendAssociation *stepAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, stepAnim, 8.0f); stepAssoc->flags &= ~ASSOC_DELETEFADEDOUT; stepAssoc->SetFinishCallback(PedEvadeCB, this); @@ -4110,6 +4143,1130 @@ CPed::SetEvasiveStep(CEntity *reason, uint8 animType) } } +void +CPed::SetEvasiveDive(CPhysical* reason, uint8 onlyRandomJump) +{ + if (!IsPedInControl() || !bRespondsToThreats) + return; + + CAnimBlendAssociation* animAssoc; + float angleToFace, neededTurn; + bool handsUp = false; + + angleToFace = m_fRotationCur; + CVehicle *veh = (CVehicle*) reason; + if (reason->IsVehicle() && veh->m_vehType == VEHICLE_TYPE_CAR && veh->m_nCarHornTimer && !IsPlayer()) { + onlyRandomJump = true; + } + + if (onlyRandomJump) { + if (reason) { + // Simple version of my bug fix below. Doesn't calculate "danger zone", selects jump direction randomly. + // Also doesn't include random hands up, sound etc. Only used on player ped and peds running from gun shots. + + float vehDirection = CGeneral::GetRadianAngleBetweenPoints( + veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, + 0.0f, 0.0f); + angleToFace = (CGeneral::GetRandomNumber() & 1) * PI + (-0.5f*PI) + vehDirection; + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + } + } else { + if (IsPlayer()) { + ((CPlayerPed*)this)->m_bShouldEvade = 5; + ((CPlayerPed*)this)->m_pEvadingFrom = reason; + reason->RegisterReference((CEntity**) &((CPlayerPed*)this)->m_pEvadingFrom); + return; + } + + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + reason->GetPosition().x, reason->GetPosition().y, + GetPosition().x, GetPosition().y); + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + + // FIX: Peds no more dive into cars. Taken from SetEvasiveStep, last if statement inverted +#ifdef FIX_BUGS + float vehDirection = CGeneral::GetRadianAngleBetweenPoints( + veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, + 0.0f, 0.0f); + + // Let's turn our back to the "reason" + angleToFace += PI; + + if (angleToFace > PI) + angleToFace -= 2 * PI; + + // We don't want to dive towards car's direction + float dangerZone = angleToFace - vehDirection; + dangerZone = CGeneral::LimitRadianAngle(dangerZone); + + // So, add or subtract 90deg (jump to left/right) according to that + if (dangerZone > 0.0f) + angleToFace = 0.5f * PI + vehDirection; + else + angleToFace = vehDirection - 0.5f * PI; +#endif + + neededTurn = Abs(angleToFace - m_fRotationCur); + + if (neededTurn > PI) + neededTurn = 2 * PI - neededTurn; + + if (neededTurn <= 0.5f*PI) { + if (CGeneral::GetRandomNumber() & 1) + handsUp = true; + } else { + if (CGeneral::GetRandomNumber() & 7) + return; + } + Say(SOUND_PED_EVADE); + } + + if (handsUp || !IsPlayer() && m_pedStats->m_flags & STAT_NO_DIVE) { + m_fRotationCur = angleToFace; + ClearLookFlag(); + ClearAimFlag(); + SetLookFlag(reason, 1); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_HANDSUP); + if (animAssoc) + return; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_HANDSUP, 8.0f); + animAssoc->flags &= ~ASSOC_DELETEFADEDOUT; + animAssoc->SetFinishCallback(PedEvadeCB, this); + SetStoredState(); + m_nPedState = PED_STEP_AWAY; + } else { + m_fRotationCur = angleToFace; + ClearLookFlag(); + ClearAimFlag(); + SetStoredState(); + m_nPedState = PED_DIVE_AWAY; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_EV_DIVE, 8.0f); + animAssoc->SetFinishCallback(PedEvadeCB, this); + } + + if (reason->IsVehicle() && m_nPedType == PEDTYPE_COP) { + if (veh->pDriver && veh->pDriver->IsPlayer()) { + CWanted *wanted = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted; + wanted->RegisterCrime_Immediately(CRIME_RECKLESS_DRIVING, GetPosition(), (int)this, false); + wanted->RegisterCrime_Immediately(CRIME_SPEEDING, GetPosition(), (int)this, false); + } + } +} + +void +CPed::SetAttack(CEntity* victim) +{ + CPed *victimPed = nil; + if (victim && victim->IsPed()) + victimPed = (CPed*)victim; + + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_ARMED); + if (animAssoc) { + animAssoc->blendDelta = -1000.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + + if (m_attackTimer > CTimer::GetTimeInMilliseconds() || m_nWaitState == WAITSTATE_SURPRISE) + return; + + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_HGUN_RELOAD)) { + m_ped_flagA4 = false; + return; + } + + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_AK_RELOAD)) { + if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->field_1380) + m_ped_flagA4 = false; + else + m_ped_flagA4 = true; + + return; + } + + CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT && !IsPlayer()) { + if (GetWeapon()->HitsGround(this, nil, victim)) + return; + } + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) { + if (IsPlayer() || + (m_nPedState != PED_FIGHT && m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL && !(m_pedStats->m_flags & STAT_SHOPPING_BAGS))) { + + if (m_nPedState != PED_ATTACK) { + m_nPedState = PED_ATTACK; + m_ped_flagA4 = false; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, 8.0f); + animAssoc->SetRun(); + if (animAssoc->currentTime != animAssoc->hierarchy->totalLength) + animAssoc->SetCurrentTime(0.0f); + + animAssoc->SetFinishCallback(FinishedAttackCB, this); + } + } else { + StartFightAttack(CGeneral::GetRandomNumber() % 256); + } + return; + } + + m_pSeekTarget = victim; + if (m_pSeekTarget) + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + + if (curWeapon->m_bCanAim) { + CVector aimPos = GetRight() * 0.1f + GetForward() * 0.2f + GetPosition(); + CEntity *foundEntity = CWorld::TestSphereAgainstWorld(aimPos, 0.2f, nil, true, false, false, true, false, false); + if (foundEntity) + return; + + m_pLookTarget = victim; + if (victim) { + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + } + if (m_pLookTarget) { + SetAimFlag(m_pLookTarget); + } else { + SetAimFlag(m_fRotationCur); + + if (FindPlayerPed() == this && TheCamera.Cams[0].Using3rdPersonMouseCam()) + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); + } + } + if (m_nPedState == PED_ATTACK) { + m_ped_flagA4 = true; + return; + } + + if (IsPlayer() || !victimPed || victimPed->IsPedInControl()) { + if(IsPlayer()) + CPad::GetPad(0)->ResetAverageWeapon(); + + if ((curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER) + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16FIRSTPERSON_34 + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_FIRSTPERSONPEDONPC_41 + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUN_AROUND + && CheckForPointBlankPeds(victimPed) != 0) { + ClearAimFlag(); + + // This condition is pointless, we already check it in above + // if (CheckForPointBlankPeds(victimPed) == 1 || !victimPed) + StartFightAttack(200); + } else { + if (!curWeapon->m_bCanAim) + m_pSeekTarget = nil; + + if (m_nPedState != PED_AIM_GUN) + SetStoredState(); + + m_nPedState = PED_ATTACK; + SetMoveState(PEDMOVE_NONE); + if (bCrouchWhenShooting) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_RBLOCK_CSHOOT, 4.0f); + } else { + float animDelta = 8.0f; + if (curWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE) + animDelta = 1000.0f; + + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_BASEBALLBAT + || CheckForPedsOnGroundToAttack((CPlayerPed*)this, nil) < PED_ON_THE_FLOOR) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, animDelta); + } else { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, animDelta); + } + } + + animAssoc->SetRun(); + if (animAssoc->currentTime != animAssoc->hierarchy->totalLength) + animAssoc->SetCurrentTime(0.0f); + + animAssoc->SetFinishCallback(FinishedAttackCB, this); + } + return; + } + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && victimPed->m_nPedState == PED_GETUP) + SetWaitState(WAITSTATE_SURPRISE, nil); + + SetLookFlag(victim, 0); + SetLookTimer(100); +} + +void +CPed::StartFightAttack(uint8 buttonPressure) +{ + if (!IsPedInControl() || m_attackTimer > CTimer::GetTimeInMilliseconds()) + return; + + if (m_nPedState == PED_FIGHT) { + m_fightButtonPressure = buttonPressure; + return; + } + + if (m_nPedState != PED_AIM_GUN) + SetStoredState(); + + if (m_nWaitState != WAITSTATE_FALSE) { + m_nWaitState = WAITSTATE_FALSE; + RestoreHeadingRate(); + } + + m_nPedState = PED_FIGHT; + m_fightButtonPressure = 0; + RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT); + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WALK_START); + + if (animAssoc) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->blendDelta = -1000.0f; + } + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP_R); + + if (animAssoc) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->blendDelta = -1000.0f; + RestoreHeadingRate(); + } + + SetMoveState(PEDMOVE_NONE); + m_nStoredMoveState = PEDMOVE_NONE; + + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE)->blendAmount = 1.0f; + + CPed *pedOnGround = nil; + if (IsPlayer() && CheckForPedsOnGroundToAttack((CPlayerPed*)this, &pedOnGround) > PED_BELOW_PLAYER) { + m_lastFightMove = FIGHTMOVE_GROUNDKICK; + } else if (m_pedStats->m_flags & STAT_SHOPPING_BAGS) { + m_lastFightMove = FIGHTMOVE_ROUNDHOUSE; + } else { + m_lastFightMove = FIGHTMOVE_STDPUNCH; + } + + if (pedOnGround && IsPlayer()) { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + pedOnGround->GetPosition().x, pedOnGround->GetPosition().y, + GetPosition().x, GetPosition().y); + + m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); + m_fRotationCur = m_fRotationDest; + m_lookTimer = 0; + SetLookFlag(pedOnGround, 1); + SetLookTimer(1500); + } + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ms_fightMoves[m_lastFightMove].animId, 4.0f); + animAssoc->SetFinishCallback(FinishFightMoveCB, this); + m_fightUnk2 = 0; + m_fightUnk1 = 0; + m_ped_flagA4 = true; + + if (IsPlayer()) + unknownFightThing = 0; +} + +void +CPed::LoadFightData(void) +{ + float startFireTime, endFireTime, comboFollowOnTime, strikeRadius; + int damage, flags; + char line[256], moveName[32], animName[32], hitLevel; + int moveId = 0; + + CAnimBlendAssociation* animAssoc; + + int bp, buflen; + int lp, linelen; + + buflen = CFileMgr::LoadFile("DATA\\fistfite.dat", work_buff, sizeof(work_buff), "r"); + + for (bp = 0; bp < buflen; ) { + // read file line by line + for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { + line[linelen++] = work_buff[bp]; + } + bp++; + line[linelen] = '\0'; + + // skip white space + for (lp = 0; line[lp] <= ' '; lp++); + + if (lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines + line[lp] == '#') + continue; + + sscanf( + &line[lp], + "%s %f %f %f %f %c %s %d %d", + &moveName, + &startFireTime, + &endFireTime, + &comboFollowOnTime, + &strikeRadius, + &hitLevel, + &animName, + &damage, + &flags); + + if (strncmp(moveName, "ENDWEAPONDATA", 13) == 0) + return; + + ms_fightMoves[moveId].startFireTime = startFireTime / 30.0f; + ms_fightMoves[moveId].endFireTime = endFireTime / 30.0f; + ms_fightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f; + ms_fightMoves[moveId].strikeRadius = strikeRadius; + ms_fightMoves[moveId].damage = damage; + ms_fightMoves[moveId].flags = flags; + + switch (hitLevel) { + case 'G': + ms_fightMoves[moveId].hitLevel = 1; + break; + case 'H': + ms_fightMoves[moveId].hitLevel = 4; + break; + case 'L': + ms_fightMoves[moveId].hitLevel = 2; + break; + case 'M': + ms_fightMoves[moveId].hitLevel = 3; + break; + case 'N': + ms_fightMoves[moveId].hitLevel = 0; + break; + default: + break; + } + + if (strncmp(animName, "null", 4) != 0) { + animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName); + ms_fightMoves[moveId].animId = (AnimationId)animAssoc->animId; + } else { + ms_fightMoves[moveId].animId = ANIM_WALK; + } + moveId++; + } +} + +// Actually GetLocalDirectionTo(Turn/Look) +int +CPed::GetLocalDirection(CVector2D &posOffset) +{ + float direction; + + for (direction = posOffset.Heading() - m_fRotationCur + DEGTORAD(45.0f); direction < 0.0f; direction += 2 * PI); + + for (direction = (int)RADTODEG(direction) / 90; direction > 3; direction -= 4); + + // Should be 0-east, 1-north, 2-west, 3-south. Not sure about order. + return direction; +} + +bool +CPed::FightStrike(CVector &touchedNodePos) +{ + CColModel* ourCol; + CVector attackDistance; + ePedPieceTypes closestPedPiece = PEDPIECE_TORSO; + float maxDistanceToBeBeaten; + CPed *nearPed; + int unk = m_fightUnk2; + bool pedFound = false; + + if (unk == -1) + return false; + + if (unk > 0) + attackDistance = touchedNodePos - m_vecHitLastPos; + + for (int i = 0; i < m_numNearPeds; i++) { + nearPed = m_nearPeds[i]; + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) + maxDistanceToBeBeaten = nearPed->GetBoundRadius() + ms_fightMoves[m_lastFightMove].strikeRadius + 0.1f; + else + maxDistanceToBeBeaten = nearPed->GetBoundRadius() + ms_fightMoves[m_lastFightMove].strikeRadius; + + if (nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) { + CVector nearPedCentre; + nearPed->GetBoundCentre(nearPedCentre); + CVector potentialAttackDistance = nearPedCentre - touchedNodePos; + + // He can beat us + if (sq(maxDistanceToBeBeaten) > potentialAttackDistance.MagnitudeSqr()) { + ourCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->GetHitColModel(); + if (nearPed->m_nPedState == PED_FALL + || nearPed->m_nPedState == PED_DEAD || nearPed->m_nPedState == PED_DIE + || !nearPed->IsPedHeadAbovePos(-0.3f)) { + ourCol = &CTempColModels::ms_colModelPedGroundHit; + } + for (int j = 0; j < ourCol->numSpheres; j++) { + attackDistance = nearPed->GetPosition() + ourCol->spheres[j].center; + attackDistance -= touchedNodePos; + CColSphere *ourPieces = ourCol->spheres; + float maxDistanceToBeat = ourPieces[j].radius + ms_fightMoves[m_lastFightMove].strikeRadius; + + // We can beat him too + if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) { + pedFound = true; + closestPedPiece = (ePedPieceTypes) ourPieces[j].piece; + break; + } + } + } + } + if (pedFound) + break; + } + + if (pedFound) { + if (nearPed->IsPlayer() && nearPed->m_nPedState == PED_GETUP) + return false; + + float oldVictimHealth = nearPed->m_fHealth; + CVector bloodPos = 0.5f * attackDistance + touchedNodePos; + int damageMult = ms_fightMoves[m_lastFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1; + + CVector2D diff (GetPosition() - nearPed->GetPosition()); + int direction = nearPed->GetLocalDirection(diff); + if (IsPlayer()) { + if (((CPlayerPed*)this)->m_bAdrenalineActive) + damageMult = 20; + } else { + damageMult *= m_pedStats->m_attackStrength; + } + + // Change direction if we used kick. + if (m_lastFightMove == FIGHTMOVE_KICK) { + if (CGeneral::GetRandomNumber() & 1) { + direction++; + if (direction > 3) + direction -= 4; + } + } + nearPed->ReactToAttack(this); + + // Mostly unused. + int unk2; + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && !nearPed->IsPlayer()) + unk2 = 101; + else + unk2 = damageMult; + + nearPed->StartFightDefend(direction, ms_fightMoves[m_lastFightMove].hitLevel, unk2); + PlayHitSound(nearPed); + m_fightUnk2 = -1; + RpAnimBlendClumpGetAssociation(GetClump(), ms_fightMoves[m_lastFightMove].animId)->speed = 0.6f; + if (nearPed->m_nPedState != PED_DIE && nearPed->m_nPedState != PED_DEAD) { + nearPed->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, closestPedPiece, direction); + } + + if (CGame::nastyGame + && ms_fightMoves[m_lastFightMove].hitLevel > 3 + && nearPed->m_nPedState == PED_DIE + && nearPed->GetIsOnScreen()) { + + // Just for blood particle. We will restore it below. + attackDistance /= (10.0f * attackDistance.Magnitude()); + for(int i=0; i<4; i++) { + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, attackDistance, nil, 0.0f, 0, 0, 0, 0); + } + } + if (nearPed->m_nPedState != PED_FALL && nearPed->m_nPedState != PED_DIE && nearPed->m_nPedState != PED_DEAD) { + float curVictimHealth = nearPed->m_fHealth; + if (curVictimHealth > 0.0f + && (curVictimHealth < 40.0f && oldVictimHealth > 40.0f && !nearPed->IsPlayer() + || nearPed->m_fHealth < 20.0f && oldVictimHealth > 20.0f + || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && IsPlayer() + || nearPed->m_pedStats->m_flags & STAT_ONE_HIT_KNOCKDOWN)) { + + nearPed->SetFall(0, (AnimationId)(direction + 25), 0); + if (nearPed->m_nPedState == PED_FALL) + nearPed->bIsStanding = false; + } + } + if (nearPed->m_nPedState == PED_DIE || !nearPed->bIsStanding) { + attackDistance = nearPed->GetPosition() - GetPosition(); + attackDistance.Normalise(); + attackDistance.z = 1.0f; + nearPed->bIsStanding = false; + + float moveMult; + if (m_lastFightMove == FIGHTMOVE_GROUNDKICK) { + moveMult = min(damageMult * 0.6f, 4.0f); + } else { + if (nearPed->m_nPedState != PED_DIE || damageMult >= 20) { + moveMult = damageMult; + } else { + moveMult = min(damageMult * 2.0f, 14.0f); + } + } + + nearPed->ApplyMoveForce(moveMult * 0.6f * attackDistance); + } + CEventList::RegisterEvent(nearPed->m_nPedType == PEDTYPE_COP ? EVENT_ASSAULT_POLICE : EVENT_ASSAULT, EVENT_ENTITY_PED, nearPed, this, 2000); + } + + if (!m_fightUnk2) + m_fightUnk2 = 1; + + m_vecHitLastPos = *touchedNodePos; + return false; +} + +void +CPed::SetFall(int extraTime, AnimationId animId, uint8 evenIfNotInControl) +{ + if (!IsPedInControl() && (!evenIfNotInControl || m_nPedState == PED_DIE || m_nPedState == PED_DEAD)) + return; + + ClearLookFlag(); + ClearAimFlag(); + SetStoredState(); + m_nPedState = PED_FALL; + CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), animId); + + if (fallAssoc) { + fallAssoc->SetCurrentTime(0.0f); + fallAssoc->blendAmount = 0.0f; + fallAssoc->blendDelta = 8.0f; + fallAssoc->SetRun(); + } else { + fallAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, 8.0f); + } + + if (extraTime == -1) { + m_getUpTimer = -1; + } else if (fallAssoc) { + if (IsPlayer()) { + m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + + CTimer::GetTimeInMilliseconds() + + 500.0f; + } else { + m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + + CTimer::GetTimeInMilliseconds() + + extraTime + + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); + } + } else { + m_getUpTimer = extraTime + + CTimer::GetTimeInMilliseconds() + + 1000 + + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); + } + bIsFell = true; +} + +void +CPed::SetFlee(CEntity* fleeFrom, int time) +{ + if (!IsPedInControl() || bKindaStayInSamePlace || !fleeFrom) + return; + + SetStoredState(); + m_nPedState = PED_FLEE_ENTITY; + bIsFleeing = true; + SetMoveState(PEDMOVE_RUN); + m_fleeFrom = fleeFrom; + m_fleeFrom->RegisterReference((CEntity **) &m_fleeFrom); + + if (time <= 0) + m_fleeTimer = 0; + else + m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, GetPosition().y, + fleeFrom->GetPosition().x, fleeFrom->GetPosition().y); + + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); + if (m_fRotationCur - PI > m_fRotationDest) { + m_fRotationDest += 2 * PI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + m_fRotationDest -= 2 * PI; + } +} + +void +CPed::SetFlee(CVector2D &from, int time) +{ + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer || !IsPedInControl() || bKindaStayInSamePlace) + return; + + if (m_nPedState != PED_FLEE_ENTITY) { + SetStoredState(); + m_nPedState = PED_FLEE_POS; + SetMoveState(PEDMOVE_RUN); + m_fleeFromPosX = from.x; + m_fleeFromPosY = from.y; + } + + bIsFleeing = true; + m_pLastPathNode = nil; + m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, GetPosition().y, + from.x, from.y); + + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); + if (m_fRotationCur - PI > m_fRotationDest) { + m_fRotationDest += 2 * PI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + m_fRotationDest -= 2 * PI; + } +} + +void +CPed::SetWaitState(eWaitState state, void *time) +{ + AnimationId waitAnim = NUM_ANIMS; + CAnimBlendAssociation *animAssoc; + + if (!IsPedInControl()) + return; + + if (state != m_nWaitState) + FinishedWaitCB(nil, this); + + switch (state) { + case WAITSTATE_TRAFFIC_LIGHTS: + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; + SetMoveState(PEDMOVE_STILL); + break; + case WAITSTATE_CROSS_ROAD: + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 1000; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_HBHB, 3000.0f); + break; + case WAITSTATE_CROSS_ROAD_LOOK: + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_ROAD_CROSS, 8.0f); + + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000,5000); + + break; + case WAITSTATE_LOOK_PED: + case WAITSTATE_LOOK_SHOP: + case WAITSTATE_LOOK_ACCIDENT: + case WAITSTATE_FACEOFF_GANG: + break; + case WAITSTATE_DOUBLEBACK: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3500; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_HBHB, 3000.0f); + break; + case WAITSTATE_HITWALL: + m_headingRate = 2.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_HIT_WALL, 16.0f); + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { + ClearObjective(); + RestorePreviousState(); + } + break; + case WAITSTATE_TURN180: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_TURN_180, 3000.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + animAssoc->SetDeleteCallback(RestoreHeadingRateCB, this); + break; + case WAITSTATE_SURPRISE: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_HIT_WALL, 3000.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + break; + case WAITSTATE_STUCK: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_TIRED, 3000.0f); + + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { + ClearObjective(); + RestorePreviousState(); + } + break; + case WAITSTATE_LOOK_ABOUT: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_HBHB, 3000.0f); + break; + case WAITSTATE_PLAYANIM_COWER: + waitAnim = ANIM_HANDSCOWER; + case WAITSTATE_PLAYANIM_HANDSUP: + if (waitAnim == NUM_ANIMS) + waitAnim = ANIM_HANDSUP; + case WAITSTATE_PLAYANIM_HANDSCOWER: + if (waitAnim == NUM_ANIMS) + waitAnim = ANIM_HANDSCOWER; + m_headingRate = 0.0f; + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 3000.0f); + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + break; + case WAITSTATE_PLAYANIM_DUCK: + waitAnim = ANIM_DUCK_DOWN; + case WAITSTATE_PLAYANIM_TAXI: + if (waitAnim == NUM_ANIMS) + waitAnim = ANIM_IDLE_TAXI; + case WAITSTATE_PLAYANIM_CHAT: + if (waitAnim == NUM_ANIMS) + waitAnim = ANIM_IDLE_CHAT; + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 3000.0f); + animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + break; + case WAITSTATE_FINISH_FLEE: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2500; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_TIRED, 3000.0f); + break; + default: + m_nWaitState = WAITSTATE_FALSE; + RestoreHeadingRate(); + return; + } + m_nWaitState = state; +} + + +void +CPed::PlayHitSound(CPed *hitTo) +{ + // That was very complicated to reverse for me... + // First index is our fight move ID (from 1 to 12, total 12), second is the one of we fight with (from 13 to 22, total 10). + + uint16 hitSoundsByFightMoves[12][10] = { + {S39,S42,S43,S43,S39,S39,S39,S39,S39,S42}, + {NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND}, + {NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND}, + {S39,S39,S39,S39,S33,S43,S39,S39,S39,S39}, + {S39,S39,S39,S39,S35,S39,S38,S38,S39,S39}, + {S39,S39,S39,S39,S33,S39,S41,S36,S39,S39}, + {S39,S39,S39,S39,S37,S40,S38,S38,S39,S39}, + {S39,S39,S39,S39,S34,S43,S44,S37,S39,S39}, + {S39,S39,S39,S39,S34,S43,S44,S37,S39,S39}, + {S39,S39,S39,S39,S34,S43,S44,S37,S39,S40}, + {S39,S39,S39,S39,S33,S39,S41,S37,S39,S40}, + {S39,S39,S39,S39,S39,S39,S39,S39,S33,S33} + }; + + // This is why first dimension is between FightMove 1 and 12. + if (m_lastFightMove == FIGHTMOVE_NULL || m_lastFightMove >= FIGHTMOVE_HITFRONT) + return; + + uint16 soundId; + + // And this is why second dimension is between 13 and 22. + if (hitTo->m_lastFightMove <= FIGHTMOVE_GROUNDKICK || hitTo->m_lastFightMove >= FIGHTMOVE_IDLE2NORM) { + + if (hitTo->m_nPedState == PED_DEAD || hitTo->UseGroundColModel()) { + soundId = hitSoundsByFightMoves[m_lastFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITONFLOOR - FIGHTMOVE_HITFRONT]; + } else { + soundId = hitSoundsByFightMoves[m_lastFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITFRONT - FIGHTMOVE_HITFRONT]; + } + } else { + soundId = hitSoundsByFightMoves[m_lastFightMove - FIGHTMOVE_STDPUNCH][hitTo->m_lastFightMove - FIGHTMOVE_HITFRONT]; + } + + if (soundId != NO_SND) + DMAudio.PlayOneShot(m_audioEntityId, soundId, 0.0f); +} + +void +CPed::Say(uint16 audio) +{ + uint16 audioToPlay = audio; + + if (IsPlayer()) { + switch (audio) { + case SOUND_PED_DEATH: + audioToPlay = SOUND_PED_DAMAGE; + break; + case SOUND_PED_DAMAGE: + case SOUND_PED_HIT: + case SOUND_PED_LAND: + break; + case SOUND_PED_BULLET_HIT: + case SOUND_PED_CAR_JACKED: + case SOUND_PED_DEFEND: + audioToPlay = SOUND_PED_HIT; + break; + default: + return; + } + } else { + if (3.0f + TheCamera.GetPosition().z < GetPosition().z) + return; + + if (TheCamera.m_CameraAverageSpeed > 1.65f) { + return; + } else if (TheCamera.m_CameraAverageSpeed > 1.25f) { + if (audio != SOUND_PED_DEATH && audio != SOUND_PED_TAXI_WAIT && audio != SOUND_PED_EVADE) + return; + + } else if (TheCamera.m_CameraAverageSpeed > 0.9f) { + switch (audio) { + case SOUND_PED_DEATH: + case SOUND_PED_BURNING: + case SOUND_PED_FLEE_SPRINT: + case SOUND_PED_TAXI_WAIT: + case SOUND_PED_EVADE: + case SOUND_PED_CAR_COLLISION: + break; + default: + return; + } + } + } + + if (audioToPlay < m_queuedSound) { + if (audioToPlay != m_lastQueuedSound || audioToPlay == SOUND_PED_DEATH + || PedAudioData[audioToPlay - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime + + m_lastSoundStart + + (uint32) CGeneral::GetRandomNumberInRange(0, PedAudioData[audioToPlay - SOUND_PED_DEATH].m_nMaxRandomDelayTime) <= CTimer::GetTimeInMilliseconds()) { + m_queuedSound = audioToPlay; + } + } +} + +void +CPed::CollideWithPed(CPed *collideWith) +{ + CAnimBlendAssociation *animAssoc; + AnimationId animToRun; + + bool weAreMissionChar = CharCreatedBy == MISSION_CHAR; + bool heIsMissionChar = collideWith->CharCreatedBy == MISSION_CHAR; + CVector posDiff = collideWith->GetPosition() - GetPosition(); + int waitTime = 0; + + if (weAreMissionChar || !collideWith->IsPlayer() || collideWith->m_nPedState != PED_MAKE_CALL) { + bool weDontLookToHim = DotProduct(posDiff, GetForward()) > 0.0f; + bool heLooksToUs = DotProduct(posDiff, collideWith->GetForward()) < 0.0f; + + if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { + + if ((!IsPlayer() || ((CPlayerPed*)this)->m_fMoveSpeed <= 1.8f) + && (IsPlayer() || heIsMissionChar && weAreMissionChar || m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT)) { + + if (m_objective != OBJECTIVE_FOLLOW_PED_IN_FORMATION && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT) { + + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + if (heIsMissionChar || !weAreMissionChar && collideWith->m_nMoveState != PEDMOVE_STILL) { + + if (weAreMissionChar && ((m_nPedState == PED_SEEK_POS) || m_nPedState == PED_SEEK_ENTITY)) { + + if (collideWith->m_nMoveState != PEDMOVE_STILL + && (!collideWith->IsPlayer() || collideWith->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled())) { + float weAndCarDist = (GetPosition() - m_vecSeekVehicle).MagnitudeSqr2D(); + float heAndCarDist = (collideWith->GetPosition() - m_vecSeekVehicle).MagnitudeSqr2D(); + + if (weAndCarDist <= heAndCarDist) { + waitTime = 1000; + collideWith->SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); + collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; + } else { + waitTime = 500; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; + } + } else if (collideWith->m_nMoveState == PEDMOVE_STILL) { + SetDirectionToWalkAroundObject(collideWith); + } + } else if (weAreMissionChar || m_pedStats->m_fear <= 100 - collideWith->m_pedStats->m_temper + || (collideWith->IsPlayer() || collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) && + (!collideWith->IsPlayer() || ((CPlayerPed*)collideWith)->m_fMoveSpeed <= 1.0f)) { + SetDirectionToWalkAroundObject(collideWith); + if (!weAreMissionChar) + Say(SOUND_PED_CHAT); + } else { + SetEvasiveStep(collideWith, 2); + } + } else { + if (m_pedStats->m_temper <= m_pedStats->m_fear + || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED + || weAreMissionChar + || collideWith->m_nPedType == PEDTYPE_CIVFEMALE + || collideWith->m_nPedType == m_nPedType + || collideWith->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + SetDirectionToWalkAroundObject(collideWith); + Say(SOUND_PED_CHAT); + } else { + TurnBody(); + SetAttack(collideWith); + } + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(250, 450); + } + } + } else { + if (m_pedInObjective && collideWith == m_pedInObjective && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + if (heLooksToUs) { + SetEvasiveStep(collideWith, 1); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } + } else if (weDontLookToHim && IsPedInControl()) { + + if (m_pedStats != collideWith->m_pedStats) { + + if (collideWith->m_pedStats->m_fear <= 100 - m_pedStats->m_temper) { + + if (collideWith->IsPlayer()) { + // He's on our right side + if (DotProduct(posDiff,GetRight()) <= 0.0f) + m_fRotationCur -= m_headingRate; + else + m_fRotationCur += m_headingRate; + } else { + // He's on our right side + if (DotProduct(posDiff, GetRight()) <= 0.0f) + m_fRotationCur -= m_headingRate; + else + m_fRotationCur += m_headingRate; + } + } else { + SetLookFlag(collideWith, 0); + TurnBody(); + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_PPUNCH, 8.0f); + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + if (!heIsMissionChar) { + int direction = collideWith->GetLocalDirection(CVector2D(posDiff.x, posDiff.y)); + collideWith->StartFightDefend(direction, 4, 5); + } + } + } + } + } + } else if (collideWith->m_pedStats->m_defendWeakness <= 1.5f || heIsMissionChar) { + // He looks us and we're not at his right side + if (heLooksToUs && DotProduct(posDiff,collideWith->GetRight()) > 0.0f) { + CVector moveForce = GetRight(); + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToRun = ANIM_HIT_LEFT; + else + animToRun = ANIM_SHOT_LEFT_PARTIAL; + } else if (heLooksToUs) { + CVector moveForce = GetRight() * -1.0f; + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToRun = ANIM_HIT_RIGHT; + else + animToRun = ANIM_SHOT_RIGHT_PARTIAL; + } else { + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToRun = ANIM_HIT_BACK; + else + animToRun = ANIM_SHOT_BACK_PARTIAL; + } + + if (collideWith->IsPedInControl() && CTimer::GetTimeInMilliseconds() > collideWith->m_nPedStateTimer) { + animAssoc = CAnimManager::BlendAnimation(collideWith->GetClump(), ASSOCGRP_STD, animToRun, 8.0f); + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 1000; + if (m_nPedState == PED_ATTACK) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); + } + } else { + // We're at his right side + if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) { + CVector moveForce = GetRight() * -1.0f; + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (heLooksToUs) + animToRun = ANIM_KO_SPIN_L; + else + animToRun = ANIM_KD_RIGHT; + } else { + CVector moveForce = GetRight(); + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (heLooksToUs) + animToRun = ANIM_KO_SPIN_R; + else + animToRun = ANIM_KD_LEFT; + } + + if (m_nPedState == PED_ATTACK && collideWith->IsPedInControl()) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); + + collideWith->SetFall(3000, animToRun, 0); + } + } else { + if (!IsPedInControl()) + return; + + if (collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) + return; + + if (m_nPedType != collideWith->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE) { + + if (!weAreMissionChar && heLooksToUs && m_pedStats->m_fear > 100 - collideWith->m_pedStats->m_temper) { + + if (CGeneral::GetRandomNumber() & 1 && CTimer::GetTimeInMilliseconds() < m_nPedStateTimer){ + SetEvasiveStep(collideWith, 2); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } else if (collideWith->m_nMoveState > PEDMOVE_WALK) { + waitTime = 2000; + SetWaitState(WAITSTATE_PLAYANIM_DUCK, &waitTime); + } + } + } else if (heLooksToUs + && collideWith->m_nPedState != PED_STEP_AWAY + && m_nPedState != PED_STEP_AWAY + && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + SetEvasiveStep(collideWith, 1); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } + } + + if (IsPlayer()) { + SetLookFlag(collideWith, 1); + SetLookTimer(800); + } + } else { + bool doWeRun = true; + if (m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT) + doWeRun = false; + + SetFlee(collideWith, 5000); + bIsFleeing = true; + m_pLastPathNode = nil; + if (!doWeRun) + SetMoveState(PEDMOVE_WALK); + } +} + 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); } @@ -4181,8 +5338,8 @@ STARTPATCHES 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(0x4E4660, (CVector (*)(CVehicle*, uint32, float)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4E1A30, (CVector (*)(CVehicle*, uint32)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); InjectHook(0x4DF940, &CPed::LineUpPedWithCar, PATCH_JUMP); InjectHook(0x4CC6C0, &CPed::PlayFootSteps, PATCH_JUMP); InjectHook(0x4C5350, &CPed::BuildPedLists, PATCH_JUMP); @@ -4237,5 +5394,19 @@ STARTPATCHES InjectHook(0x4D6540, &CPed::RestoreHeadingRate, PATCH_JUMP); InjectHook(0x4C69E0, (void (CPed::*)(CEntity*)) &CPed::SetAimFlag, PATCH_JUMP); InjectHook(0x4C6960, (void (CPed::*)(float)) &CPed::SetAimFlag, PATCH_JUMP); + InjectHook(0x4CFAD0, &CPed::GrantAmmo, PATCH_JUMP); InjectHook(0x4CFB20, &CPed::SetAmmo, PATCH_JUMP); -ENDPATCHES + InjectHook(0x4D33A0, &CPed::SetEvasiveDive, PATCH_JUMP); + InjectHook(0x4D09B0, &CPed::SetFall, PATCH_JUMP); + InjectHook(0x4E6220, &CPed::SetAttack, PATCH_JUMP); + InjectHook(0x4E7530, &CPed::StartFightAttack, PATCH_JUMP); + InjectHook(0x4E9870, &CPed::LoadFightData, PATCH_JUMP); + InjectHook(0x4E8EC0, &CPed::FightStrike, PATCH_JUMP); + InjectHook(0x4CCE20, &CPed::GetLocalDirection, PATCH_JUMP); + InjectHook(0x4E8E20, &CPed::PlayHitSound, PATCH_JUMP); + InjectHook(0x4E5A10, &CPed::Say, PATCH_JUMP); + InjectHook(0x4D58D0, &CPed::SetWaitState, PATCH_JUMP); + InjectHook(0x4D1D70, (void (CPed::*)(CEntity*, int)) &CPed::SetFlee, PATCH_JUMP); + InjectHook(0x4D1C40, (void (CPed::*)(CVector2D&, int)) &CPed::SetFlee, PATCH_JUMP); + InjectHook(0x4EB9A0, &CPed::CollideWithPed, PATCH_JUMP); +ENDPATCHES
\ No newline at end of file diff --git a/src/peds/Ped.h b/src/peds/Ped.h index 7b8bc2ce..dcbe247a 100644 --- a/src/peds/Ped.h +++ b/src/peds/Ped.h @@ -10,9 +10,78 @@ #include "AnimBlendAssociation.h" #include "WeaponInfo.h" #include "Fire.h" +#include "DMAudio.h" struct CPathNode; +struct CPedAudioData +{ + int m_nFixedDelayTime; + int m_nOverrideFixedDelayTime; + int m_nOverrideMaxRandomDelayTime; + int m_nMaxRandomDelayTime; +}; + +// For hit sounds in fight +enum { + S33 = SOUND_FIGHT_PUNCH_33, + S34 = SOUND_FIGHT_KICK_34, + S35 = SOUND_FIGHT_HEADBUTT_35, + S36 = SOUND_FIGHT_PUNCH_36, + S37 = SOUND_FIGHT_PUNCH_37, + S38 = SOUND_FIGHT_CLOSE_PUNCH_38, + S39 = SOUND_FIGHT_PUNCH_39, + S40 = SOUND_FIGHT_PUNCH_OR_KICK_BELOW_40 , + S41 = SOUND_FIGHT_PUNCH_41, + S42 = SOUND_FIGHT_PUNCH_FROM_BEHIND_42, + S43 = SOUND_FIGHT_KNEE_OR_KICK_43, + S44 = SOUND_FIGHT_KICK_44, + NO_SND = SOUND_TOTAL_PED_SOUNDS +}; + +struct FightMove +{ + AnimationId animId; + float startFireTime; + float endFireTime; + float comboFollowOnTime; + float strikeRadius; + uint8 hitLevel; + uint8 damage; + uint8 flags; +}; +static_assert(sizeof(FightMove) == 0x18, "FightMove: error"); + +enum PedFightMoves +{ + FIGHTMOVE_NULL, + // Attacker + FIGHTMOVE_STDPUNCH, + FIGHTMOVE_IDLE, + FIGHTMOVE_SHUFFLE_F, + FIGHTMOVE_KNEE, + FIGHTMOVE_HEADBUTT, + FIGHTMOVE_PUNCHJAB, + FIGHTMOVE_PUNCHHOOK, + FIGHTMOVE_KICK, + FIGHTMOVE_LONGKICK, + FIGHTMOVE_ROUNDHOUSE, + FIGHTMOVE_BODYBLOW, + FIGHTMOVE_GROUNDKICK, + // Opponent + FIGHTMOVE_HITFRONT, + FIGHTMOVE_HITBACK, + FIGHTMOVE_HITRIGHT, + FIGHTMOVE_HITLEFT, + FIGHTMOVE_HITBODY, + FIGHTMOVE_HITCHEST, + FIGHTMOVE_HITHEAD, + FIGHTMOVE_HITBIGSTEP, + FIGHTMOVE_HITONFLOOR, + FIGHTMOVE_HITBEHIND, + FIGHTMOVE_IDLE2NORM +}; + enum ePedPieceTypes { PEDPIECE_TORSO, @@ -209,7 +278,7 @@ public: uint8 bRespondsToThreats : 1; uint8 bRenderPedInCar : 1; uint8 bChangedSeat : 1; - uint8 m_ped_flagC10 : 1; // related with phone + uint8 bUpdateAnimHeading : 1; uint8 bBodyPartJustCameOff : 1; uint8 m_ped_flagC40 : 1; uint8 m_ped_flagC80 : 1; @@ -219,16 +288,16 @@ public: uint8 m_ped_flagD4 : 1; uint8 m_ped_flagD8 : 1; uint8 bIsPedDieAnimPlaying : 1; - uint8 m_ped_flagD20 : 1; + uint8 bIsFleeing : 1; uint8 m_ped_flagD40 : 1; // reset when objective changes - uint8 m_bScriptObjectiveCompleted : 1; + uint8 bScriptObjectiveCompleted : 1; - uint8 m_ped_flagE1 : 1; + uint8 bKindaStayInSamePlace : 1; uint8 m_ped_flagE2 : 1; uint8 bNotAllowedToDuck : 1; uint8 bCrouchWhenShooting : 1; uint8 bIsDucking : 1; // set if you don't want ped to attack - uint8 m_ped_flagE20 : 1; // getup complete? + uint8 bGetUpAnimStarted : 1; uint8 bDoBloodyFootprints : 1; uint8 m_ped_flagE80 : 1; @@ -253,17 +322,17 @@ public: uint8 m_ped_flagH1 : 1; uint8 m_ped_flagH2 : 1; uint8 m_ped_flagH4 : 1; - uint8 m_ped_flagH8 : 1; + uint8 bClearObjective : 1; uint8 m_ped_flagH10 : 1; uint8 m_ped_flagH20 : 1; uint8 m_ped_flagH40 : 1; uint8 m_ped_flagH80 : 1; uint8 m_ped_flagI1 : 1; - uint8 m_ped_flagI2 : 1; // if set, limbs won't came off + uint8 bNoCriticalHits : 1; // if set, limbs won't came off uint8 m_ped_flagI4 : 1; uint8 bHasAlreadyBeenRecorded : 1; - uint8 m_ped_flagI10 : 1; + uint8 bIsFell : 1; uint8 m_ped_flagI20 : 1; uint8 m_ped_flagI40 : 1; uint8 m_ped_flagI80 : 1; @@ -283,7 +352,7 @@ public: uint32 m_pedFormation; uint32 m_fearFlags; CEntity *m_threatEntity; - CVector2D m_eventOrThread; + CVector2D m_eventOrThreat; uint32 m_eventType; CEntity* m_pEventEntity; float m_fAngleToEvent; @@ -299,8 +368,8 @@ public: PedState m_nPedState; PedState m_nLastPedState; eMoveState m_nMoveState; - int32 m_nStoredActionState; - int32 m_nPrevActionState; + int32 m_nStoredMoveState; + int32 m_nPrevMoveState; eWaitState m_nWaitState; uint32 m_nWaitTimer; void *m_pPathNodesStates[8]; // seems unused @@ -336,7 +405,7 @@ public: bool bInVehicle; uint8 pad_315[3]; float field_318; - uint8 field_31C; // may be cutscene or phone cutscene status + bool bRunningToPhone; uint8 field_31D; int16 m_phoneId; uint32 m_lookingForPhone; // unused @@ -363,11 +432,12 @@ public: uint8 m_wepAccuracy; CEntity *m_pPointGunAt; CVector m_vecHitLastPos; - uint32 m_lastHitState; - uint8 m_fightFlags1; - uint8 m_fightFlags2; - uint8 pad_4B2[2]; - CFire* m_pFire; + PedFightMoves m_lastFightMove; + uint8 m_fightButtonPressure; + int8 m_fightUnk2; // TODO + uint8 m_fightUnk1; // TODO + uint8 pad_4B3; + CFire *m_pFire; CEntity *m_pLookTarget; float m_fLookDirection; int32 m_wepModelID; @@ -382,18 +452,18 @@ public: uint32 m_duckTimer; uint32 field_4E8; int32 m_bloodyFootprintCount; - uint8 stuff9[2]; + uint8 m_panicCounter; + uint8 m_deadBleeding; int8 m_bodyPartBleeding; // PedNode uint8 m_field_4F3; CPed *m_nearPeds[10]; uint16 m_numNearPeds; int8 m_lastWepDam; uint8 pad_51F; - uint8 field_520; - uint8 pad_521[3]; - uint32 m_talkTimer; - uint16 m_talkTypeLast; - uint16 m_talkType; + uint32 m_lastSoundStart; + uint32 m_soundStart; + uint16 m_lastQueuedSound; + uint16 m_queuedSound; CVector m_vecSeekPosEx; float m_seekExAngle; @@ -503,11 +573,27 @@ public: void SetAimFlag(float angle); void SetAmmo(eWeaponType weaponType, uint32 ammo); void SetEvasiveStep(CEntity*, uint8); + void GrantAmmo(eWeaponType, uint32); + void SetEvasiveDive(CPhysical*, uint8); + void SetAttack(CEntity*); + void StartFightAttack(uint8); + void LoadFightData(void); + void SetWaitState(eWaitState, void*); + bool FightStrike(CVector&); + int GetLocalDirection(CVector2D&); + void StartFightDefend(uint8, uint8, uint8); + void PlayHitSound(CPed*); + void SetFall(int, AnimationId, uint8); + void SetFlee(CEntity*, int); + void SetFlee(CVector2D&, int); + void RemoveInCarAnims(void); + void CollideWithPed(CPed*); + void SetDirectionToWalkAroundObject(CEntity*); // 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); + static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset); + static CVector GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult); + static CVector GetPositionToOpenCarDoor(CVehicle* veh, uint32 component); // Callbacks static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data); @@ -577,6 +663,9 @@ public: static bool &bPedCheat2; static bool &bPedCheat3; static CColPoint &ms_tempColPoint; + static uint16 &unknownFightThing; // TODO + static FightMove (&ms_fightMoves)[24]; + static CPedAudioData (&PedAudioData)[38]; }; void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg); diff --git a/src/peds/PedIK.cpp b/src/peds/PedIK.cpp index 3d5bcfb5..8909fcc4 100644 --- a/src/peds/PedIK.cpp +++ b/src/peds/PedIK.cpp @@ -8,6 +8,7 @@ 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); } +// TODO: Hardcoded into exe, reverse it. LimbMovementInfo &CPedIK::ms_torsoInfo = *(LimbMovementInfo*)0x5F9F8C; CPedIK::CPedIK(CPed *ped) @@ -104,8 +105,7 @@ CPedIK::GetWorldMatrix(RwFrame *source, RwMatrix *destination) return destination; } -// A helper function that adjusts "limb" parameter according to limitations. Doesn't move the limb. -int8 +uint32 CPedIK::MoveLimb(LimbOrientation &limb, float approxPhi, float approxTheta, LimbMovementInfo &moveInfo) { int result = 1; diff --git a/src/peds/PedIK.h b/src/peds/PedIK.h index 5f321280..a8d9c75d 100644 --- a/src/peds/PedIK.h +++ b/src/peds/PedIK.h @@ -47,7 +47,7 @@ public: void RotateTorso(AnimBlendFrameData* animBlend, LimbOrientation* limb, bool changeRoll); void ExtractYawAndPitchLocal(RwMatrixTag*, float*, float*); void ExtractYawAndPitchWorld(RwMatrixTag*, float*, float*); - int8 MoveLimb(LimbOrientation &a1, float a2, float a3, LimbMovementInfo &a4); + uint32 MoveLimb(LimbOrientation &a1, float a2, float a3, LimbMovementInfo &a4); bool RestoreGunPosn(void); }; static_assert(sizeof(CPedIK) == 0x28, "CPedIK: error"); diff --git a/src/render/Coronas.cpp b/src/render/Coronas.cpp index 5ac9b149..1a6cfea3 100644 --- a/src/render/Coronas.cpp +++ b/src/render/Coronas.cpp @@ -203,7 +203,7 @@ CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 al } void -CCoronas::UpdateCoronaCoors(int id, const CVector &coors, float drawDist, float someAngle) +CCoronas::UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle) { int i; diff --git a/src/render/Coronas.h b/src/render/Coronas.h index ed69b1e6..359a34ed 100644 --- a/src/render/Coronas.h +++ b/src/render/Coronas.h @@ -93,7 +93,7 @@ public: static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, const CVector &coors, float size, float drawDist, uint8 type, int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle); - static void UpdateCoronaCoors(int id, const CVector &coors, float drawDist, float someAngle); + static void UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle); static void Render(void); static void RenderReflections(void); static void DoSunAndMoon(void); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 7bf4593f..77489e60 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -359,10 +359,10 @@ CRenderer::SetupEntityVisibility(CEntity *ent) ent->GetModelIndex() == MI_RHINO || ent->GetModelIndex() == MI_COACH || TheCamera.m_bInATunnelAndABigVehicle){ - ent->m_flagD40 = true; + ent->bNoBrightHeadLights = true; }else{ m_pFirstPersonVehicle = (CVehicle*)ent; - ent->m_flagD40 = false; + ent->bNoBrightHeadLights = false; } return VIS_OFFSCREEN; }else{ diff --git a/src/render/Shadows.h b/src/render/Shadows.h index 585518ee..c12327b1 100644 --- a/src/render/Shadows.h +++ b/src/render/Shadows.h @@ -177,3 +177,4 @@ public: extern RwTexture *&gpBloodPoolTex; extern RwTexture *&gpShadowExplosionTex; +extern RwTexture *&gpShadowHeadLightsTex; diff --git a/src/render/Skidmarks.cpp b/src/render/Skidmarks.cpp index fd5e7d2b..bbadd54c 100644 --- a/src/render/Skidmarks.cpp +++ b/src/render/Skidmarks.cpp @@ -5,3 +5,4 @@ WRAPPER void CSkidmarks::Clear(void) { EAXJMP(0x518130); } WRAPPER void CSkidmarks::Render(void) { EAXJMP(0x5182E0); } +WRAPPER void CSkidmarks::RegisterOne(uint32 id, CVector pos, float fwdx, float fwdY, bool *isMuddy, bool *isBloddy) { EAXJMP(0x5185C0); } diff --git a/src/render/Skidmarks.h b/src/render/Skidmarks.h index d1e1d996..280150a7 100644 --- a/src/render/Skidmarks.h +++ b/src/render/Skidmarks.h @@ -5,4 +5,5 @@ class CSkidmarks public: static void Clear(void); static void Render(void); + static void RegisterOne(uint32 id, CVector pos, float fwdx, float fwdY, bool *isMuddy, bool *isBloddy); }; diff --git a/src/render/SpecialFX.cpp b/src/render/SpecialFX.cpp index 4fa2677a..bed5af50 100644 --- a/src/render/SpecialFX.cpp +++ b/src/render/SpecialFX.cpp @@ -11,5 +11,6 @@ CBulletTrace (&CBulletTraces::aTraces)[16] = *(CBulletTrace(*)[16])*(uintptr*)0x WRAPPER void CBulletTraces::Init(void) { EAXJMP(0x518DE0); } +WRAPPER void CBrightLights::RegisterOne(CVector pos, CVector up, CVector right, CVector fwd, uint8 type, uint8 unk1, uint8 unk2, uint8 unk3) { EAXJMP(0x51A410); } WRAPPER void C3dMarkers::PlaceMarkerSet(uint32 id, uint16 type, CVector& pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { EAXJMP(0x51BB80); }
\ No newline at end of file diff --git a/src/render/SpecialFX.h b/src/render/SpecialFX.h index 08f0f08a..9c61c0fb 100644 --- a/src/render/SpecialFX.h +++ b/src/render/SpecialFX.h @@ -29,8 +29,15 @@ public: static void Init(void); }; +class CBrightLights +{ +public: + static void RegisterOne(CVector pos, CVector up, CVector right, CVector fwd, uint8 type, uint8 unk1 = 0, uint8 unk2 = 0, uint8 unk3 = 0); +}; + class C3dMarkers { public: static void PlaceMarkerSet(uint32 id, uint16 type, CVector& pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); -};
\ No newline at end of file +}; + diff --git a/src/render/WaterCannon.cpp b/src/render/WaterCannon.cpp index afb40f6f..9398c847 100644 --- a/src/render/WaterCannon.cpp +++ b/src/render/WaterCannon.cpp @@ -2,4 +2,5 @@ #include "patcher.h" #include "WaterCannon.h" +WRAPPER void CWaterCannons::UpdateOne(uint32 id, CVector *pos, CVector *dir) { EAXJMP(0x522470); } WRAPPER void CWaterCannons::Render(void) { EAXJMP(0x522550); } diff --git a/src/render/WaterCannon.h b/src/render/WaterCannon.h index 1a18e75f..55949803 100644 --- a/src/render/WaterCannon.h +++ b/src/render/WaterCannon.h @@ -3,5 +3,6 @@ class CWaterCannons { public: + static void UpdateOne(uint32 id, CVector *pos, CVector *dir); static void Render(void); }; diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index ac0460f1..04b89803 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -2265,9 +2265,6 @@ HRESULT _InputInitialiseMouse() return S_OK; } -//#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) -//#define FAILED(Status) ((HRESULT)(Status)<0) - RwV2d leftStickPos; RwV2d rightStickPos; @@ -2275,15 +2272,20 @@ HRESULT CapturePad(RwInt32 padID) { HRESULT hr; DIJOYSTATE2 js; - LPDIRECTINPUTDEVICE8 pPad = nil; - - pPad = ( padID == 0 ) ? PSGLOBAL(joy1) : PSGLOBAL(joy2); + LPDIRECTINPUTDEVICE8 *pPad = nil; + + if( padID == 0 ) + pPad = &PSGLOBAL(joy1); + else if( padID == 1) + pPad = &PSGLOBAL(joy2); + else + assert("invalid padID"); - if ( nil == pPad ) + if ( nil == (*pPad) ) return S_OK; // Poll the device to read the current state - hr = pPad->Poll(); + hr = (*pPad)->Poll(); if( FAILED(hr) ) { @@ -2291,9 +2293,9 @@ HRESULT CapturePad(RwInt32 padID) // interrupted. We aren't tracking any state between polls, so // we don't have any special reset that needs to be done. We // just re-acquire and try again. - hr = pPad->Acquire(); + hr = (*pPad)->Acquire(); while( hr == DIERR_INPUTLOST ) - hr = pPad->Acquire(); + hr = (*pPad)->Acquire(); // hr may be DIERR_OTHERAPPHASPRIO or other errors. This // may occur when the app is minimized or in the process of @@ -2302,26 +2304,26 @@ HRESULT CapturePad(RwInt32 padID) if( FAILED(hr) ) return hr; - hr = pPad->Poll(); + hr = (*pPad)->Poll(); if( FAILED(hr) ) return hr; } // Get the input's device state - if( FAILED( hr = pPad->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) ) + if( FAILED( hr = (*pPad)->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) ) return hr; // The device should have been acquired during the Poll() - if ( ControlsManager.field_0 == true ) + if ( ControlsManager.firstCapture == true ) { memcpy(&ControlsManager.m_OldState, &js, sizeof(DIJOYSTATE2)); memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); - ControlsManager.field_0 = false; + ControlsManager.firstCapture = false; } else { memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(DIJOYSTATE2)); - memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); + memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); } RsPadButtonStatus bs; @@ -2329,14 +2331,14 @@ HRESULT CapturePad(RwInt32 padID) RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); - bool deviceAvailable = pPad != nil; + bool deviceAvailable = (*pPad) != nil; if ( deviceAvailable ) { leftStickPos.x = (float)js.lX / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); leftStickPos.y = (float)js.lY / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); - if (LOWORD(js.rgdwPOV[0]) != -1) + if (LOWORD(js.rgdwPOV[0]) != 0xFFFF) { float angle = DEGTORAD((float)js.rgdwPOV[0] / 100.0f); @@ -2548,40 +2550,49 @@ BOOL CALLBACK _InputEnumDevicesCallback( const DIDEVICEINSTANCE* pdidInstance, V static INT Count = 0; - LPDIRECTINPUTDEVICE8 pJoystick = nil; + LPDIRECTINPUTDEVICE8 *pJoystick = nil; if ( Count == 0 ) - pJoystick = PSGLOBAL(joy1); + pJoystick = &PSGLOBAL(joy1); else if ( Count == 1 ) - pJoystick = PSGLOBAL(joy2); + pJoystick = &PSGLOBAL(joy2); + else + assert("too many pads"); // Obtain an interface to the enumerated joystick. - hr = PSGLOBAL(dinterface)->CreateDevice( pdidInstance->guidInstance, &pJoystick, nil ); + hr = PSGLOBAL(dinterface)->CreateDevice( pdidInstance->guidInstance, pJoystick, nil ); // If it failed, then we can't use this joystick. (Maybe the user unplugged // it while we were in the middle of enumerating it.) - if( FAILED(hr) ) + if( hr != S_OK ) return DIENUM_CONTINUE; - - if( FAILED( hr = pJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) ) + + hr = (*pJoystick)->SetDataFormat( &c_dfDIJoystick2 ); + if( hr != S_OK ) { - pJoystick->Release(); + (*pJoystick)->Release(); return DIENUM_CONTINUE; } ++Count; - - if( FAILED( hr = pJoystick->SetCooperativeLevel( PSGLOBAL(window), DISCL_NONEXCLUSIVE) ) ) + + hr = (*pJoystick)->SetCooperativeLevel( PSGLOBAL(window), DISCL_NONEXCLUSIVE|DISCL_FOREGROUND ); + if( hr != S_OK ) { - pJoystick->Release(); + (*pJoystick)->Release(); +#ifdef FIX_BUGS + // BUG: enum will be called with Count == 2, which will write to a null pointer + // So decrement count again since we're not using this pad + --Count; +#endif return DIENUM_CONTINUE; } + // Stop enumeration. Note: we're just taking the first two joysticks we get. You + // could store all the enumerated joysticks and let the user pick. if ( Count == 2 ) return DIENUM_STOP; - // Stop enumeration. Note: we're just taking the first joystick we get. You - // could store all the enumerated joysticks and let the user pick. return DIENUM_CONTINUE; } diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index fb42e6e6..28ede4cd 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -7,14 +7,28 @@ #include "ModelIndices.h" #include "VisibilityPlugins.h" #include "DMAudio.h" +#include "Clock.h" +#include "TimeCycle.h" +#include "ZoneCull.h" #include "Camera.h" #include "Darkel.h" #include "Rubbish.h" #include "Fire.h" #include "Explosion.h" #include "Particle.h" +#include "ParticleObject.h" +#include "Antennas.h" +#include "Skidmarks.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Coronas.h" +#include "SpecialFX.h" +#include "WaterCannon.h" +#include "WaterLevel.h" +#include "Floater.h" #include "World.h" #include "SurfaceTable.h" +#include "Weather.h" #include "HandlingMgr.h" #include "Record.h" #include "Remote.h" @@ -49,7 +63,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) bTaxiLight = m_sAllTaxiLights; m_auto_flagA20 = false; m_auto_flagA40 = false; - m_auto_flagA80 = false; + bWaterTight = false; SetModelIndex(id); @@ -118,7 +132,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) bNotDamagedUpsideDown = false; bMoreResistantToDamage = false; m_fVelocityChangeForAudio = 0.f; - field_4E2 = 0; + m_hydraulicState = 0; for(i = 0; i < 4; i++){ m_aGroundPhysical[i] = nil; @@ -128,7 +142,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_aWheelTimer[i] = 0.0f; m_aWheelRotation[i] = 0.0f; m_aWheelSpeed[i] = 0.0f; - m_aWheelState[i] = WHEEL_STATE_0; + m_aWheelState[i] = WHEEL_STATE_NORMAL; m_aWheelSkidmarkMuddy[i] = false; m_aWheelSkidmarkBloody[i] = false; } @@ -162,7 +176,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) m_fCarGunLR = 0.0f; m_fCarGunUD = 0.05f; - m_fWindScreenRotation = 0.0f; + m_fPropellerRotation = 0.0f; m_weaponDoorTimerLeft = 0.0f; m_weaponDoorTimerRight = m_weaponDoorTimerLeft; @@ -201,7 +215,7 @@ CAutomobile::ProcessControl(void) int i; CColModel *colModel; - if(m_veh_flagC80) + if(bUsingSpecialColModel) colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; else colModel = GetColModel(); @@ -358,7 +372,7 @@ CAutomobile::ProcessControl(void) pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); { - float wheelRot = ProcessWheelRotation(WHEEL_STATE_0, GetForward(), m_vecMoveSpeed, 0.35f); + float wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.35f); for(i = 0; i < 4; i++) m_aWheelRotation[i] += wheelRot; } @@ -484,7 +498,7 @@ CAutomobile::ProcessControl(void) m_aSuspensionSpringRatio[0] < 1.0f && CPad::GetPad(0)->HornJustDown()){ - DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRALIC_1, 0.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, 1.0f); CParticle::AddParticle(PARTICLE_ENGINE_STEAM, @@ -1179,10 +1193,6 @@ CAutomobile::ProcessControl(void) m_vecTurnSpeed.z = 0.0f; } } - -// TEMP -if(pDriver) - pDriver->m_fHealth = 100.0f; } void @@ -1200,9 +1210,911 @@ CAutomobile::Teleport(CVector pos) CWorld::Add(this); } -WRAPPER void CAutomobile::PreRender(void) { EAXJMP(0x535B40); } -WRAPPER void CAutomobile::Render(void) { EAXJMP(0x539EA0); } +void +CAutomobile::PreRender(void) +{ + int i, j, n; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if(GetModelIndex() == MI_RCBANDIT){ + CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f); + CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f); + } + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; + + + // Wheel particles + + if(GetModelIndex() == MI_DODO){ + ; // nothing + }else if(GetModelIndex() == MI_RCBANDIT){ + for(i = 0; i < 4; i++){ + // Game has same code three times here + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.05f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.1f); + break; + } + } + }else{ + if(m_status == STATUS_SIMPLE){ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_LEFT]; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB = SURFACE_DEFAULT; + } + + int drawParticles = Abs(fwdSpeed) < 90.0f; + if(m_status == STATUS_SIMPLE || m_status == STATUS_PHYSICS || + m_status == STATUS_PLAYER || m_status == STATUS_PLAYER_PLAYBACKFROMBUFFER){ + bool rearSkidding = false; + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SKIDDING || + m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SKIDDING) + rearSkidding = true; + + for(i = 0; i < 4; i++){ + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){ + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.05f)); + } + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + break; + + case WHEEL_STATE_SKIDDING: + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){ + // same as below + + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + } + break; + + case WHEEL_STATE_FIXED: + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + break; + + default: + if(Abs(fwdSpeed) > 0.5f) + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if(m_aWheelSkidmarkBloody[i] && m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + } + } + } + } + + if(m_aCarNodes[CAR_WHEEL_RM]){ + // assume middle wheels are two units before rear ones + CVector offset = GetForward()*2.0f; + + switch(m_aWheelState[CARWHEEL_REAR_LEFT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_LEFT, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + offset, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); + break; + } + + switch(m_aWheelState[CARWHEEL_REAR_RIGHT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_RIGHT, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + offset, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); + break; + } + } + + + // Rain on roof + if(!CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() && + Abs(fwdSpeed) < 20.0f && CWeather::Rain > 0.02f){ + CColModel *colModel = GetColModel(); + + for(i = 0; i < colModel->numTriangles; i++){ + CVector p1, p2, p3, c; + + colModel->GetTrianglePoint(p1, colModel->triangles[i].a); + p1 = GetMatrix() * p1; + colModel->GetTrianglePoint(p2, colModel->triangles[i].b); + p2 = GetMatrix() * p2; + colModel->GetTrianglePoint(p3, colModel->triangles[i].c); + p3 = GetMatrix() * p3; + c = (p1 + p2 + p3)/3.0f; + + n = 6.0f*CWeather::Rain; + for(j = 0; j <= n; j++) + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, + c + CVector(CGeneral::GetRandomNumberInRange(-.04f, 0.4f), CGeneral::GetRandomNumberInRange(-.04f, 0.4f), 0.0f), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1); + } + } + + AddDamagedVehicleParticles(); + + // Exhaust smoke + if(bEngineOn && fwdSpeed < 90.0f){ + CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; + CVector pos1, pos2, dir; + + if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ + dir.z = 0.0f; + if(fwdSpeed < 10.0f){ + CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); + steerFwd = Multiply3x3(GetMatrix(), steerFwd); + float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); + dir.x = steerFwd.x * r; + dir.y = steerFwd.y * r; + }else{ + dir.x = m_vecMoveSpeed.x; + dir.y = m_vecMoveSpeed.y; + } + + bool dblExhaust = false; + pos1 = GetMatrix() * exhaustPos; + if(pHandling->Flags & HANDLING_DBL_EXHAUST){ + dblExhaust = true; + pos2 = exhaustPos; + pos2.x = -pos2.x; + pos2 = GetMatrix() * pos2; + } + + n = 4.0f*m_fGasPedal; + if(dblExhaust) + for(i = 0; i <= n; i++){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); + } + else + for(i = 0; i <= n; i++) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + } + } + + + // Siren and taxi lights + switch(GetModelIndex()){ + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_POLICE: + case MI_ENFORCER: + if(m_bSirenOrAlarm){ + CVector pos1, pos2; + uint8 r1, g1, b1; + uint8 r2, g2, b2; + uint8 r, g, b; + + switch(GetModelIndex()){ + case MI_FIRETRUCK: + pos1 = CVector(1.1f, 1.7f, 2.0f); + pos2 = CVector(-1.1f, 1.7f, 2.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 0; + break; + case MI_AMBULAN: + pos1 = CVector(1.1f, 0.9f, 1.6f); + pos2 = CVector(-1.1f, 0.9f, 1.6f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 255; + break; + case MI_POLICE: + pos1 = CVector(0.7f, -0.4f, 1.0f); + pos2 = CVector(-0.7f, -0.4f, 1.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + case MI_ENFORCER: + pos1 = CVector(1.1f, 0.8f, 1.2f); + pos2 = CVector(-1.1f, 0.8f, 1.2f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + } + + uint32 t = CTimer::GetTimeInMilliseconds() & 0x3FF; // 1023 + if(t < 512){ + r = r1/6; + g = g1/6; + b = b1/6; + }else{ + r = r2/6; + g = g2/6; + b = b2/6; + } + + t = CTimer::GetTimeInMilliseconds() & 0x1FF; // 511 + if(t < 100){ + float f = t/100.0f; + r *= f; + g *= f; + b *= f; + }else if(t > 412){ + float f = (512-t)/100.0f; + r *= f; + g *= f; + b *= f; + } + + CVector pos = GetPosition(); + float angle = (CTimer::GetTimeInMilliseconds() & 0x3FF)*TWOPI/0x3FF; + float s = 8.0f*Sin(angle); + float c = 8.0f*Cos(angle); + CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex, + &pos, c, s, s, -c, r, g, b, 8.0f); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos + GetUp()*2.0f, CVector(0.0f, 0.0f, 0.0f), 12.0f, + r*0.02f, g*0.02f, b*0.02f, CPointLights::FOG_NONE, true); + + pos1 = GetMatrix() * pos1; + pos2 = GetMatrix() * pos2; + + for(i = 0; i < 4; i++){ + uint8 sirenTimer = ((CTimer::GetTimeInMilliseconds() + (i<<6))>>8) & 3; + pos = (pos1*i + pos2*(3.0f-i))/3.0f; + + switch(sirenTimer){ + case 0: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r1, g1, b1, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + i == 1 ? CCoronas::FLARE_HEADLIGHTS : CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + case 2: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r2, g2, b2, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + default: + CCoronas::UpdateCoronaCoors((uintptr)this + 21 + i, pos, 50.0f, 0.0f); + break; + } + } + } + break; + + case MI_FBICAR: + if(m_bSirenOrAlarm){ + CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f); + if(CTimer::GetTimeInMilliseconds() & 0x100 && + DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f) + CCoronas::RegisterCorona((uintptr)this + 21, + 0, 0, 255, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors((uintptr)this + 21, pos, 50.0f, 0.0f); + } + break; + + case MI_TAXI: + case MI_CABBIE: + case MI_BORGNINE: + if(bTaxiLight){ + CVector pos = GetPosition() + GetUp()*0.95f; + CCoronas::RegisterCorona((uintptr)this + 21, + 128, 128, 0, 255, + pos, 0.8f, 50.0f, + CCoronas::TYPE_NORMAL, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), 10.0f, + 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true); + } + break; + } + + if(GetModelIndex() == MI_RCBANDIT || + GetModelIndex() == MI_DODO || + GetModelIndex() == MI_RHINO) + goto nolights; + + // Turn lights on/off + bool shouldLightsBeOn = + CClock::GetHours() > 20 || + CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) || + CClock::GetHours() < 7 || + CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) || + m_randomSeed/50000.0f < CWeather::Foggyness || + m_randomSeed/50000.0f < CWeather::WetRoads; + if(shouldLightsBeOn != bLightsOn && m_status != STATUS_WRECKED){ + if(m_status == STATUS_ABANDONED){ + // Turn off lights on abandoned vehicles only when we they're far away + if(bLightsOn && + Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f) + bLightsOn = false; + }else + bLightsOn = shouldLightsBeOn; + } + + // Actually render the lights + bool alarmOn = false; + bool alarmOff = false; + if(IsAlarmOn()){ + if(CTimer::GetTimeInMilliseconds() & 0x100) + alarmOn = true; + else + alarmOff = true; + } + if(bEngineOn && bLightsOn || alarmOn || alarmOff){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + + // 1.0 if directly behind car, -1.0 if in front + // BUG on PC: Abs of DotProduct is taken + float behindness = DotProduct(lookVector, GetForward()); + behindness = clamp(behindness, -1.0f, 1.0f); // shouldn't be necessary + // 0.0 if behind car, PI if in front + // Abs not necessary + float angle = Abs(Acos(behindness)); + + // Headlights + + CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; + CVector lightR = GetMatrix() * headLightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*headLightPos.x; + + // Headlight coronas + if(behindness < 0.0f){ + // In front of car + float intensity = -0.5f*behindness + 0.3f; + float size = 1.0f - behindness; + + if(behindness < -0.97f && camDist < 30.0f){ + // Directly in front and not too far away + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 150, 150, 195, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 160, 160, 140, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + } + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 190*intensity, 190*intensity, 255*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 210*intensity, 210*intensity, 195*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // Behind car + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this, lightL, 50.0f*TheCamera.LODDistMultiplier, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 1, lightR, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + + // Taillights + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + lightR = GetMatrix() * tailLightPos; + lightL = lightR; + lightL -= GetRight()*2.0f*tailLightPos.x; + + // Taillight coronas + if(behindness > 0.0f){ + // Behind car + float intensity = 0.4f*behindness + 0.4; + float size = (behindness + 1.0f)/2.0f; + + if(m_fGasPedal < 0.0f){ + // reversing + intensity += 0.4f; + size += 0.3f; + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 128*intensity, 128*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 128*intensity, 128*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(m_fBrakePedal > 0.0){ + intensity += 0.4f; + size += 0.3f; + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 0, 0, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 0, 0, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // In front of car + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + + // Light shadows + if(!alarmOff){ + CVector pos = GetPosition(); + CVector2D fwd(GetForward()); + fwd.Normalise(); + float f = headLightPos.y + 6.0f; + pos += CVector(f*fwd.x, f*fwd.y, 2.0f); + + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowHeadLightsTex, &pos, + 7.0f*fwd.x, 7.0f*fwd.y, 7.0f*fwd.y, -7.0f*fwd.x, 45, 45, 45, 7.0f); + + f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f); + pos += CVector(f*fwd.x, f*fwd.y, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos, + 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f); + } + + if(this == FindPlayerVehicle() && !alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(), + 20.0f, 1.0f, 1.0f, 1.0f, + FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE, + false); + CVector pos = GetPosition() - 4.0f*GetForward(); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + if(m_fBrakePedal > 0.0f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 10.0f, 1.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + else + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 7.0f, 0.6f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + } + }else{ + // Lights off + + if(m_status == STATUS_ABANDONED || m_status == STATUS_WRECKED) + goto nolights; + + CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + lookVector.Normalise(); + float behindness = DotProduct(lookVector, GetForward()); + if(behindness > 0.0f){ + if(m_fGasPedal < 0.0f){ + // reversing + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 120, 120, 120, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 120, 120, 120, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + }else{ + // braking + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 120, 0, 0, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 120, 0, 0, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + } + +nolights: + CShadows::StoreShadowForCar(this); +} + +void +CAutomobile::Render(void) +{ + int i; + CMatrix mat; + CVector pos; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){ + // Rhino has no bonnet...what are we doing here? + CMatrix m; + CVector p; + m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + p = m.GetPosition(); + m.SetRotateZ(m_fCarGunLR); + m.Translate(p); + m.UpdateRW(); + } + + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); + CVector rearWheelFwd = GetForward(); + for(i = 0; i < 4; i++){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], frontWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + else + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], rearWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + m_aWheelRotation[i] += m_aWheelSpeed[i]; + } + + // Rear right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); + + // Rear left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); + + // Mid right wheel + if(m_aCarNodes[CAR_WHEEL_RM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); + } + + // Mid left wheel + if(m_aCarNodes[CAR_WHEEL_LM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); + } + + if(GetModelIndex() == MI_DODO){ + // Front wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Rotate propeller + if(m_aCarNodes[CAR_WINDSCREEN]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateY(m_fPropellerRotation); + mat.Translate(pos); + mat.UpdateRW(); + + m_fPropellerRotation += m_fGasPedal != 0.0f ? TWOPI/13.0f : TWOPI/26.0f; + if(m_fPropellerRotation > TWOPI) + m_fPropellerRotation -= TWOPI; + } + + // Rudder + if(Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING && m_aCarNodes[CAR_BOOT]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + pos = mat.GetPosition(); + mat.SetRotate(0.0f, 0.0f, -m_fSteerAngle); + mat.Rotate(0.0f, Sin(m_fSteerAngle)*DEGTORAD(22.0f), 0.0f); + mat.Translate(pos); + mat.UpdateRW(); + } + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + }else if(GetModelIndex() == MI_RHINO){ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + // no damaged wheels or steering + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, 0.0f); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + // no damaged wheels or steering + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + }else{ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle+0.3f*Sin(-m_aWheelRotation[CARWHEEL_FRONT_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + ProcessSwingingDoor(CAR_DOOR_LR, DOOR_REAR_LEFT); + ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT); + ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET); + ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + } + + if(!CVehicle::bWheelsOnlyCheat) + CEntity::Render(); +} int32 CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) @@ -1213,7 +2125,7 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) if(m_status != STATUS_SIMPLE) bVehicleColProcessed = true; - if(m_veh_flagC80) + if(bUsingSpecialColModel) colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; else colModel = GetColModel(); @@ -1416,24 +2328,594 @@ CAutomobile::ProcessControlInputs(uint8 pad) } } -WRAPPER void +void CAutomobile::FireTruckControl(void) -{ EAXJMP(0x522590); +{ + if(this == FindPlayerVehicle()){ + if(!CPad::GetPad(0)->GetWeapon()) + return; + m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight()*0.00025f*CTimer::GetTimeStep(); + m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown()*0.0001f*CTimer::GetTimeStep(); + m_fCarGunUD = clamp(m_fCarGunUD, 0.05f, 0.3f); + + CVector cannonPos(0.0f, 1.5f, 1.9f); + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + }else if(m_status == STATUS_PHYSICS){ + CFire *fire = gFireManager.FindFurthestFire_NeverMindFireMen(GetPosition(), 10.0f, 35.0f); + if(fire == nil) + return; + + // Target cannon onto fire + float targetAngle = CGeneral::GetATanOfXY(fire->m_vecPos.x-GetPosition().x, fire->m_vecPos.y-GetPosition().y); + float fwdAngle = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); + float targetCannonAngle = fwdAngle - targetAngle; + float angleDelta = CTimer::GetTimeStep()*0.01f; + float cannonDelta = targetCannonAngle - m_fCarGunLR; + while(cannonDelta < PI) cannonDelta += TWOPI; + while(cannonDelta > PI) cannonDelta -= TWOPI; + if(Abs(cannonDelta) < angleDelta) + m_fCarGunLR = targetCannonAngle; + else if(cannonDelta > 0.0f) + m_fCarGunLR += angleDelta; + else + m_fCarGunLR -= angleDelta; + + // Go up and down a bit + float upDown = Sin((float)(CTimer::GetTimeInMilliseconds() & 0xFFF)/0x1000 * TWOPI); + m_fCarGunUD = 0.2f + 0.2f*upDown; + + // Spray water every once in a while + if((CTimer::GetTimeInMilliseconds()>>10) & 3){ + CVector cannonPos(0.0f, 0.0f, 2.2f); // different position than player's firetruck! + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + } + } } -WRAPPER void +void CAutomobile::TankControl(void) -{ EAXJMP(0x53D530); +{ + int i; + + // These coords are 1 unit higher then they should be relative to model center + CVector turrentBase(0.0f, -1.394f, 2.296f); + CVector gunEnd(0.0f, 1.813f, 2.979f); + CVector baseToEnd = gunEnd - turrentBase; + + if(this != FindPlayerVehicle()) + return; + if(CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING) + return; + + // Rotate turret + float prevAngle = m_fCarGunLR; + m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep(); + if(m_fCarGunLR < 0.0f) + m_fCarGunLR += TWOPI; + if(m_fCarGunLR > TWOPI) + m_fCarGunLR -= TWOPI; + if(m_fCarGunLR != prevAngle) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(m_fCarGunLR - prevAngle)); + + // Shoot + if(CPad::GetPad(0)->CarGunJustDown() && + CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun + 800){ + CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun = CTimer::GetTimeInMilliseconds(); + + // more like -sin(angle), cos(angle), i.e. rotated (0,1,0) + CVector turretDir = CVector(Sin(-m_fCarGunLR), Cos(-m_fCarGunLR), 0.0f); + turretDir = Multiply3x3(GetMatrix(), turretDir); + + float c = Cos(m_fCarGunLR); + float s = Sin(m_fCarGunLR); + CVector rotatedEnd( + c*baseToEnd.x - s*baseToEnd.y, + s*baseToEnd.x + c*baseToEnd.y, + baseToEnd.z - 1.0f); // correct offset here + rotatedEnd += turrentBase; + + CVector point1 = GetMatrix() * rotatedEnd; + CVector point2 = point1 + 60.0f*turretDir; + m_vecMoveSpeed -= 0.06f*turretDir; + m_vecMoveSpeed.z += 0.05f; + + CWeapon::DoTankDoomAiming(FindPlayerVehicle(), FindPlayerPed(), &point1, &point2); + CColPoint colpoint; + CEntity *entity = nil; + CWorld::ProcessLineOfSight(point1, point2, colpoint, entity, true, true, true, true, true, true, false); + if(entity) + point2 = colpoint.point - 0.04f*(colpoint.point - point1); + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_TANK_GRENADE, point2, 0); + + // Add particles on the way to the explosion; + float shotDist = (point2 - point1).Magnitude(); + int n = shotDist/4.0f; + RwRGBA black = { 0, 0, 0, 0 }; + for(i = 0; i < n; i++){ + float f = (float)i/n; + CParticle::AddParticle(PARTICLE_HELI_DUST, + point1 + f*(point2 - point1), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, black); + } + + // More particles + CVector shotDir = point2 - point1; + shotDir.Normalise(); + for(i = 0; i < 15; i++){ + float f = i/15.0f; + CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1, + shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f, + nil, CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*f, black); + } + + // And some gun flashes near the gun + CVector flashPos = point1; + CVector nullDir(0.0f, 0.0f, 0.0f); + int lifeSpan = 250; + if(m_vecMoveSpeed.Magnitude() > 0.08f){ + lifeSpan = 125; + flashPos.x += 0.5f*m_vecMoveSpeed.x; + flashPos.y += 0.5f*m_vecMoveSpeed.y; + } + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.4f, black, 0, 0, 0, lifeSpan); + flashPos += 0.3f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.2f, black, 0, 0, 0, lifeSpan); + flashPos += 0.1f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.15f, black, 0, 0, 0, lifeSpan); + } + + // Actually update turret node + if(m_aCarNodes[CAR_WINDSCREEN]){ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fCarGunLR); + mat.Translate(pos); + mat.UpdateRW(); + } } -WRAPPER void +void CAutomobile::HydraulicControl(void) -{ EAXJMP(0x52D4E0); +{ + int i; + float wheelPositions[4]; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *normalColModel = mi->GetColModel(); + float wheelRadius = 0.5f*mi->m_wheelScale; + CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; + CColModel *specialColModel = &playerInfo->m_ColModel; + + if(m_status != STATUS_PLAYER){ + // reset hydraulics for non-player cars + + if(!bUsingSpecialColModel) + return; + if(specialColModel != nil) // this is always true + for(i = 0; i < 4; i++) + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + for(i = 0; i < 4; i++){ + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = normalColModel->lines[i].p0.z - normalColModel->lines[i].p1.z; + m_aSuspensionSpringRatio[i] = (normalColModel->lines[i].p0.z - wheelPositions[i]) / m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + else if(m_hydraulicState >= 100) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + if(playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx = nil; + bUsingSpecialColModel = false; + m_hydraulicState = 0; + return; + } + + // Player car + + float normalUpperLimit = pHandling->fSuspensionUpperLimit; + float normalLowerLimit = pHandling->fSuspensionLowerLimit; + float normalSpringLength = normalUpperLimit - normalLowerLimit; + float extendedUpperLimit = normalUpperLimit - 0.2f; + float extendedLowerLimit = normalLowerLimit - 0.2f; + float extendedSpringLength = extendedUpperLimit - extendedLowerLimit; + + if(!bUsingSpecialColModel){ + // Init special col model + + if(playerInfo->m_pVehicleEx && playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx->bUsingSpecialColModel = false; + playerInfo->m_pVehicleEx = this; + playerInfo->m_ColModel = *normalColModel; + bUsingSpecialColModel = true; + specialColModel = &playerInfo->m_ColModel; + + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else{ + m_hydraulicState = 0; + normalUpperLimit += -0.12f; + normalSpringLength = normalUpperLimit - (normalLowerLimit+0.14f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + + // Setup suspension + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = normalColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + // Adjust col model + mi->GetWheelPosn(0, pos); + float minz = pos.z + extendedLowerLimit - wheelRadius; + if(minz < specialColModel->boundingBox.min.z) + specialColModel->boundingBox.min.z = minz; + float radius = max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); + if(specialColModel->boundingSphere.radius < radius) + specialColModel->boundingSphere.radius = radius; + + } + + if(playerInfo->m_WBState != WBSTATE_PLAYING) + return; + + bool setPrevRatio = false; + if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){ + if(m_hydraulicState == 0){ + m_hydraulicState = 20; + for(i = 0; i < 4; i++) + m_aWheelPosition[i] -= 0.06f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + setPrevRatio = true; + }else{ + m_hydraulicState++; + } + }else if(m_hydraulicState != 0){ // must always be true + if(m_hydraulicState < 21 && m_fVelocityChangeForAudio < 0.1f){ + m_hydraulicState--; + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + } + + if(CPad::GetPad(0)->HornJustDown()){ + // Switch between normal and extended + + if(m_hydraulicState < 100) + m_hydraulicState = 100; + else{ + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else + m_hydraulicState = 0; + } + + if(m_hydraulicState < 100){ + if(m_hydraulicState == 0){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Reset suspension to normal + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + }else{ + // Reset suspension to extended + float extendedLineLength = extendedSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += extendedUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= extendedLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = extendedSpringLength; + m_aSuspensionLineLength[i] = extendedLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + setPrevRatio = true; + m_aWheelPosition[i] -= 0.05f; + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + }else{ + float suspChange[4]; + float maxDelta = 0.0f; + float rear = CPad::GetPad(0)->GetCarGunUpDown()/128.0f; + float front = -rear; + float right = CPad::GetPad(0)->GetCarGunLeftRight()/128.0f; + float left = -right; + suspChange[CARWHEEL_FRONT_LEFT] = max(front+left, 0.0f); + suspChange[CARWHEEL_REAR_LEFT] = max(rear+left, 0.0f); + suspChange[CARWHEEL_FRONT_RIGHT] = max(front+right, 0.0f); + suspChange[CARWHEEL_REAR_RIGHT] = max(rear+right, 0.0f); + + if(m_hydraulicState < 100){ + // Lowered, move wheels up + + if(m_hydraulicState == 0){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float oldZ = specialColModel->lines[i].p1.z; + float upperLimit = suspChange[i]*(extendedUpperLimit-normalUpperLimit) + normalUpperLimit; + float springLength = suspChange[i]*(extendedSpringLength-normalSpringLength) + normalSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelPosition[i] -= (oldZ - specialColModel->lines[i].p1.z)*0.3f; + } + } + }else{ + if(m_hydraulicState < 104){ + m_hydraulicState++; + for(i = 0; i < 4; i++) + m_aWheelPosition[i] -= 0.1f; + } + + if(m_fVelocityChangeForAudio < 0.1f){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float upperLimit = suspChange[i]*(normalUpperLimit-extendedUpperLimit) + extendedUpperLimit; + float springLength = suspChange[i]*(normalSpringLength-extendedSpringLength) + extendedSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + } + + float limitDiff = extendedLowerLimit - normalLowerLimit; + if(limitDiff != 0.0f && Abs(maxDelta/limitDiff) > 0.01f){ + float f = (maxDelta + limitDiff)/2.0f/limitDiff; + f = clamp(f, 0.0f, 1.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_3, f); + if(f < 0.4f || f > 0.6f) + setPrevRatio = true; + if(f < 0.25f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + else if(f > 0.75f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + } + } + + if(setPrevRatio) + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + m_aSuspensionSpringRatioPrev[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } } -WRAPPER void -CAutomobile::ProcessBuoyancy(void) -{ EAXJMP(0x5308D0); +void +CAutomobile::ProcessBuoyancy(void) +{ + int i; + CVector impulse, point; + + if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ + m_flagD8 = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + + CVector initialSpeed = m_vecMoveSpeed; + float timeStep = max(CTimer::GetTimeStep(), 0.01f); + float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep); + float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep()); + m_vecMoveSpeed *= waterResistance; + m_vecTurnSpeed *= waterResistance; + + if(impulseRatio > 0.5f){ + bIsInWater = true; + if(m_vecMoveSpeed.z < -0.1f) + m_vecMoveSpeed.z = -0.1f; + + if(pDriver){ + pDriver->bIsInWater = true; + if(pDriver->IsPlayer() || !bWaterTight) + pDriver->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]){ + pPassengers[i]->bIsInWater = true; + if(pPassengers[i]->IsPlayer() || !bWaterTight) + pPassengers[i]->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + }else + bIsInWater = false; + + static uint32 nGenerateRaindrops = 0; + static uint32 nGenerateWaterCircles = 0; + + if(initialSpeed.z < -0.3f && impulse.z > 0.3f){ + RwRGBA color; + color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed())*0.45f*255; + color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen())*0.45f*255; + color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue())*0.45f*255; + color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128; + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.3f)), + 0.0f, 75, color, true); + + nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300; + nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60; + if(m_vecMoveSpeed.z < -0.2f) + m_vecMoveSpeed.z = -0.2f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f); + } + + if(nGenerateWaterCircles > 0 && nGenerateWaterCircles < CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z != 0.0f){ + nGenerateWaterCircles = 0; + pos.z += 1.0f; + for(i = 0; i < 4; i++){ + CVector p = pos; + p.x += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + p.y += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW, + p, CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, black, 0, 0, 0, 0); + } + } + } + + if(nGenerateRaindrops > 0 && nGenerateRaindrops < CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z >= 0.0f){ + nGenerateRaindrops = 0; + pos.z += 0.5; + CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, + pos, CVector(0.0f, 0.0f, 0.0f), 6.5f, 2500, black, true); + } + } + }else{ + bIsInWater = false; + m_flagD8 = false; + + static RwRGBA splashCol = {155, 155, 185, 196}; + static RwRGBA smokeCol = {255, 255, 255, 255}; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].surfaceB == SURFACE_PUDDLE){ + CVector pos = m_aWheelColPoints[i].point + 0.3f*GetUp() - GetPosition(); + CVector vSpeed = GetSpeed(pos); + vSpeed.z = 0.0f; + float fSpeed = vSpeed.MagnitudeSqr(); + if(fSpeed > sq(0.05f)){ + fSpeed = Sqrt(fSpeed); + float size = min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f); + CVector right = 0.2f*fSpeed*GetRight() + 0.2f*vSpeed; + + CParticle::AddParticle(PARTICLE_PED_SPLASH, + pos + GetPosition(), -0.5f*right, + nil, size, splashCol, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, 0); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + pos + GetPosition(), -0.6f*right, + nil, size, smokeCol, 0, 0, 0, 0); + + if((CTimer::GetFrameCounter() & 0xF) == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed); + } + } + } + } } void @@ -1896,6 +3378,112 @@ CAutomobile::dmgDrawCarCollidingParticles(const CVector &pos, float amount) } void +CAutomobile::AddDamagedVehicleParticles(void) +{ + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + return; + + uint8 engineStatus = Damage.GetEngineStatus(); + if(engineStatus < ENGINE_STATUS_STEAM1) + return; + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()) * 180.0f; + CVector direction = 0.5f*m_vecMoveSpeed; + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; + + switch(Damage.GetDoorStatus(DOOR_BONNET)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + // Bonnet is still there, smoke comes out at the edge + damagePos += vecDAMAGE_ENGINE_POS_SMALL; + break; + case DOOR_STATUS_SWINGING: + case DOOR_STATUS_MISSING: + // Bonnet is gone, smoke comes out at the engine + damagePos += vecDAMAGE_ENGINE_POS_BIG; + break; + } + + if(GetModelIndex() == MI_BFINJECT) + damagePos = CVector(0.3f, -1.5f, -0.1f); + + damagePos = GetMatrix()*damagePos; + damagePos.z += 0.15f; + + if(engineStatus < ENGINE_STATUS_STEAM2){ + if(fwdSpeed < 90.0f){ + direction.z += 0.05f; + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.1f); + } + }else if(engineStatus < ENGINE_STATUS_SMOKE){ + if(fwdSpeed < 90.0f) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f); + }else if(engineStatus < ENGINE_STATUS_ON_FIRE){ + if(fwdSpeed < 90.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.3f*direction, nil, 0.0f); + } + }else if(m_fHealth > 250.0f){ + if(fwdSpeed < 90.0f) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.2f*direction, nil, 0.0f); + } +} + +int32 +CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) +{ + int i; + CVector dir; + static RwRGBA grassCol = { 8, 24, 8, 255 }; + static RwRGBA dirtCol = { 64, 64, 64, 255 }; + static RwRGBA dirttrackCol = { 64, 32, 16, 255 }; + static RwRGBA waterCol = { 48, 48, 64, 0 }; + + if(!belowEffectSpeed) + return 0; + + switch(colpoint->surfaceB){ + case SURFACE_GRASS: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.1f), grassCol); + } + return 0; + case SURFACE_DIRT: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirtCol); + } + return 1; + case SURFACE_DIRTTRACK: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirttrackCol); + } + return 0; + default: + // Is this even visible? + if(CWeather::WetRoads > 0.01f && CTimer::GetFrameCounter() & 1){ + CParticle::AddParticle(PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, 1.0f), nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + return 0; + } + return 1; + } +} + +void CAutomobile::GetComponentWorldPosition(int32 component, CVector &pos) { if(m_aCarNodes[component] == nil){ @@ -2192,9 +3780,9 @@ CAutomobile::BlowUpCar(CEntity *culprit) gFireManager.StartFire(this, culprit, 0.8f, 1); // TODO CDarkel::RegisterCarBlownUpByPlayer(this); if(GetModelIndex() == MI_RCBANDIT) - CExplosion::AddExplosion(this, culprit, EXPLOSION_4, GetPosition(), 0); // TODO + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR_QUICK, GetPosition(), 0); else - CExplosion::AddExplosion(this, culprit, EXPLOSION_3, GetPosition(), 0); // TODO + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); } bool @@ -2253,7 +3841,64 @@ CAutomobile::BurstTyre(uint8 wheel) } } -WRAPPER bool CAutomobile::IsRoomForPedToLeaveCar(uint32, CVector *) { EAXJMP(0x53C5B0); } +bool +CAutomobile::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) +{ + CColPoint colpoint; + CEntity *ent; + colpoint.point = CVector(0.0f, 0.0f, 0.0f); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector seatPos; + switch(component){ + case CAR_DOOR_RF: + seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT]; + break; + case CAR_DOOR_LF: + seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT]; + seatPos.x = -seatPos.x; + break; + case CAR_DOOR_RR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + break; + case CAR_DOOR_LR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + seatPos.x = -seatPos.x; + break; + } + seatPos = GetMatrix() * seatPos; + + CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component); + if(doorOffset){ + CVector off = *doorOffset; + if(component == CAR_DOOR_RF || component == CAR_DOOR_RR) + off.x = -off.x; + doorPos += Multiply3x3(GetMatrix(), off); + } + + if(GetUp().z < 0.0f){ + seatPos.z += 0.5f; + doorPos.z += 0.5f; + } + + CVector dist = doorPos - seatPos; + float length = dist.Magnitude(); + CVector pedPos = seatPos + dist*((length+0.6f)/length); + + if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false)) + return false; + if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false)) + return false; + if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f) + return false; + float upperZ = colpoint.point.z; + if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + return false; + if(upperZ != 0.0f && upperZ < colpoint.point.z) + return false; + return true; +} float CAutomobile::GetHeightAboveRoad(void) @@ -2300,7 +3945,7 @@ CAutomobile::ResetSuspension(void) m_aSuspensionSpringRatio[i] = 1.0f; m_aWheelTimer[i] = 0.0f; m_aWheelRotation[i] = 0.0f; - m_aWheelState[i] = WHEEL_STATE_0; + m_aWheelState[i] = WHEEL_STATE_NORMAL; } } @@ -2867,6 +4512,8 @@ STARTPATCHES InjectHook(0x52D190, &CAutomobile_::SetModelIndex_, PATCH_JUMP); InjectHook(0x531470, &CAutomobile_::ProcessControl_, PATCH_JUMP); InjectHook(0x535180, &CAutomobile_::Teleport_, PATCH_JUMP); + InjectHook(0x539EA0, &CAutomobile_::Render_, PATCH_JUMP); + InjectHook(0x535B40, &CAutomobile_::PreRender_, PATCH_JUMP); InjectHook(0x53B270, &CAutomobile_::ProcessEntityCollision_, PATCH_JUMP); InjectHook(0x53B660, &CAutomobile_::ProcessControlInputs_, PATCH_JUMP); InjectHook(0x52E5F0, &CAutomobile_::GetComponentWorldPosition_, PATCH_JUMP); @@ -2881,10 +4528,13 @@ STARTPATCHES InjectHook(0x53BC60, &CAutomobile_::BlowUpCar_, PATCH_JUMP); InjectHook(0x53BF70, &CAutomobile_::SetUpWheelColModel_, PATCH_JUMP); InjectHook(0x53C0E0, &CAutomobile_::BurstTyre_, PATCH_JUMP); + InjectHook(0x53C5B0, &CAutomobile_::IsRoomForPedToLeaveCar_, PATCH_JUMP); InjectHook(0x437690, &CAutomobile_::GetHeightAboveRoad_, PATCH_JUMP); InjectHook(0x53C450, &CAutomobile_::PlayCarHorn_, PATCH_JUMP); InjectHook(0x53E090, &CAutomobile::PlaceOnRoadProperly, PATCH_JUMP); InjectHook(0x52F030, &CAutomobile::dmgDrawCarCollidingParticles, PATCH_JUMP); + InjectHook(0x535450, &CAutomobile::AddDamagedVehicleParticles, PATCH_JUMP); + InjectHook(0x5357D0, &CAutomobile::AddWheelDirtAndWater, PATCH_JUMP); InjectHook(0x5353A0, &CAutomobile::ResetSuspension, PATCH_JUMP); InjectHook(0x52D210, &CAutomobile::SetupSuspensionLines, PATCH_JUMP); InjectHook(0x53E000, &CAutomobile::BlowUpCarsInPath, PATCH_JUMP); diff --git a/src/vehicles/Automobile.h b/src/vehicles/Automobile.h index 1a103777..6226b555 100644 --- a/src/vehicles/Automobile.h +++ b/src/vehicles/Automobile.h @@ -48,13 +48,13 @@ public: uint8 bHadDriver : 1; // for bombs uint8 m_auto_flagA20 : 1; uint8 m_auto_flagA40 : 1; - uint8 m_auto_flagA80 : 1; + uint8 bWaterTight : 1; // no damage for non-player peds uint8 bNotDamagedUpsideDown : 1; uint8 bMoreResistantToDamage : 1; uint8 field_4DB; CEntity *m_pBombRigger; int16 field_4E0; - int16 field_4E2; + uint16 m_hydraulicState; uint32 m_nBusDoorTimerEnd; uint32 m_nBusDoorTimerStart; float m_aSuspensionSpringLength[4]; @@ -71,7 +71,7 @@ public: float m_weaponDoorTimerRight; float m_fCarGunLR; float m_fCarGunUD; - float m_fWindScreenRotation; + float m_fPropellerRotation; uint8 stuff4[4]; uint8 m_nWheelsOnGround; uint8 m_nDriveWheelsOnGround; @@ -108,7 +108,7 @@ public: void BlowUpCar(CEntity *ent); bool SetUpWheelColModel(CColModel *colModel); void BurstTyre(uint8 tyre); - bool IsRoomForPedToLeaveCar(uint32, CVector *); + bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); float GetHeightAboveRoad(void); void PlayCarHorn(void); @@ -122,6 +122,8 @@ public: int32 RcbanditCheck1CarWheels(CPtrList &list); void PlaceOnRoadProperly(void); void dmgDrawCarCollidingParticles(const CVector &pos, float amount); + void AddDamagedVehicleParticles(void); + int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); void PlayHornIfNecessary(void); void ResetSuspension(void); void SetupSuspensionLines(void); diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index c18bad25..f614b78f 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -5,7 +5,7 @@ float &fShapeLength = *(float*)0x600E78; float &fShapeTime = *(float*)0x600E7C; float &fRangeMult = *(float*)0x600E80; //0.6f; // 0.75f gta 3 -float &fTimeMult = *(float*)0xA0FCF4; +float &fTimeMult = *(float*)0x943008; float MAX_WAKE_LENGTH = 50.0f; float MIN_WAKE_INTERVAL = 1.0f; diff --git a/src/vehicles/DamageManager.h b/src/vehicles/DamageManager.h index adcd7430..bf0c59a1 100644 --- a/src/vehicles/DamageManager.h +++ b/src/vehicles/DamageManager.h @@ -6,6 +6,9 @@ enum eEngineStatus { + ENGINE_STATUS_STEAM1 = 100, + ENGINE_STATUS_STEAM2 = 150, + ENGINE_STATUS_SMOKE = 200, ENGINE_STATUS_ON_FIRE = 225 }; @@ -32,6 +35,12 @@ enum eWheelStatus WHEEL_STATUS_MISSING }; +enum eLightStatus +{ + LIGHT_STATUS_OK, + LIGHT_STATUS_BROKEN +}; + enum tComponent { COMPONENT_DEFAULT, diff --git a/src/vehicles/Floater.cpp b/src/vehicles/Floater.cpp index cabe00c3..6b8bf755 100644 --- a/src/vehicles/Floater.cpp +++ b/src/vehicles/Floater.cpp @@ -22,7 +22,7 @@ static float fBoatVolumeDistribution[9] = { }; bool -cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point) +cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse) { m_numSteps = 2.0f; @@ -32,7 +32,7 @@ cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CV PreCalcSetup(phys, buoyancy); SimpleCalcBuoyancy(); - float f = CalcBuoyancyForce(phys, impulse, point); + float f = CalcBuoyancyForce(phys, point, impulse); if(m_isBoat) return true; return f != 0.0f; @@ -82,7 +82,7 @@ cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) m_haveVolume = false; m_numPartialVolumes = 1.0f; m_volumeUnderWater = 0.0f; - m_impulse = CVector(0.0f, 0.0f, 0.0f); + m_impulsePoint = CVector(0.0f, 0.0f, 0.0f); m_position = phys->GetPosition(); m_positionZ = CVector(0.0f, 0.0f, m_position.z); m_buoyancy = buoyancy; @@ -148,7 +148,7 @@ cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition) fFraction = 1.0f/m_numPartialVolumes; fRemainingSlice = 1.0f - fFraction; - m_impulse = m_impulse*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction; + m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction; m_numPartialVolumes += 1.0f; m_haveVolume = true; return fThisVolume; @@ -175,13 +175,13 @@ cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel } bool -cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point) +cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse) { if(!m_haveVolume) return false; - *impulse = Multiply3x3(m_matrix, m_impulse); - *point = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep()); + *point = Multiply3x3(m_matrix, m_impulsePoint); + *impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep()); return true; } diff --git a/src/vehicles/Floater.h b/src/vehicles/Floater.h index ede2b9d0..0a2ae809 100644 --- a/src/vehicles/Floater.h +++ b/src/vehicles/Floater.h @@ -1,6 +1,6 @@ #pragma once -class Physical; +class CPhysical; enum tWaterLevel { @@ -33,7 +33,7 @@ public: char m_field_B9; bool m_isBoat; float m_volumeUnderWater; - CVector m_impulse; + CVector m_impulsePoint; bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point); void PreCalcSetup(CPhysical *phys, float buoyancy); diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index cc98bbe9..36675c8b 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -59,8 +59,8 @@ CVehicle::CVehicle(uint8 CreatedBy) m_pBlowUpEntity = nil; field_1FB = 0; bComedyControls = false; - m_veh_flagB40 = false; - m_veh_flagB80 = false; + bCraneMessageDone = false; + bExtendedRange = false; bTakeLessDamage = false; bIsDamaged = false; bFadeOut = false; @@ -70,7 +70,7 @@ CVehicle::CVehicle(uint8 CreatedBy) bHasBeenOwnedByPlayer = false; m_veh_flagC20 = false; bCanBeDamaged = true; - m_veh_flagC80 = false; + bUsingSpecialColModel = false; m_veh_flagD1 = false; m_veh_flagD2 = false; m_nGunFiringTime = 0; @@ -255,9 +255,9 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); float contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); - if(*wheelState != WHEEL_STATE_0) + if(*wheelState != WHEEL_STATE_NORMAL) bAlreadySkidding = true; - *wheelState = WHEEL_STATE_0; + *wheelState = WHEEL_STATE_NORMAL; adhesion *= CTimer::GetTimeStep(); if(bAlreadySkidding) @@ -299,7 +299,7 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon if(brake > adhesion){ if(Abs(contactSpeedFwd) > 0.005f) - *wheelState = WHEEL_STATE_STATIC; + *wheelState = WHEEL_STATE_FIXED; }else { if(fwd > 0.0f){ if(fwd > brake) @@ -312,11 +312,11 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon } if(sq(adhesion) < sq(right) + sq(fwd)){ - if(*wheelState != WHEEL_STATE_STATIC){ + if(*wheelState != WHEEL_STATE_FIXED){ if(bDriving && contactSpeedFwd < 0.2f) - *wheelState = WHEEL_STATE_1; + *wheelState = WHEEL_STATE_SPINNING; else - *wheelState = WHEEL_STATE_2; + *wheelState = WHEEL_STATE_SKIDDING; } float l = Sqrt(sq(right) + sq(fwd)); @@ -343,10 +343,10 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec { float angularVelocity; switch(state){ - case WHEEL_STATE_1: + case WHEEL_STATE_SPINNING: angularVelocity = -1.1f; // constant speed forward break; - case WHEEL_STATE_STATIC: + case WHEEL_STATE_FIXED: angularVelocity = 0.0f; // not moving break; default: @@ -377,12 +377,13 @@ CVehicle::ProcessDelayedExplosion(void) return; int tick = CTimer::GetTimeStep()/60.0f*1000.0f; + int16 prev = m_nBombTimer; if(tick > m_nBombTimer) m_nBombTimer = 0; else m_nBombTimer -= tick; - if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != 0xFE00) + if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != (prev & 0xFE00)) DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_BOMB_TICK, 0.0f); if (m_nBombTimer != 0) diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h index 38d411cd..262bd62c 100644 --- a/src/vehicles/Vehicle.h +++ b/src/vehicles/Vehicle.h @@ -59,6 +59,11 @@ enum CAR_POS_EXHAUST = 9, }; +enum +{ + BOAT_POS_FRONTSEAT +}; + enum eDoors { DOOR_BONNET = 0, @@ -119,10 +124,10 @@ enum enum tWheelState { - WHEEL_STATE_0 = 0, - WHEEL_STATE_1 = 1, // constant velocity - WHEEL_STATE_2 = 2, // normal - WHEEL_STATE_STATIC = 3, // not moving + WHEEL_STATE_NORMAL, // standing still or rolling normally + WHEEL_STATE_SPINNING, // rotating but not moving + WHEEL_STATE_SKIDDING, + WHEEL_STATE_FIXED, // not rotating }; enum eFlightModel @@ -176,8 +181,8 @@ public: uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims uint8 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way) uint8 bWarnedPeds : 1; // Has scan and warn peds of danger been processed? - uint8 m_veh_flagB40 : 1; - uint8 m_veh_flagB80 : 1; + uint8 bCraneMessageDone : 1; // A crane message has been printed for this car allready + uint8 bExtendedRange : 1; // This vehicle needs to be a bit further away to get deleted uint8 bTakeLessDamage : 1; // This vehicle is stronger (takes about 1/4 of damage) uint8 bIsDamaged : 1; // This vehicle has been damaged and is displaying all its components @@ -186,7 +191,7 @@ public: uint8 m_veh_flagC10 : 1; uint8 m_veh_flagC20 : 1; uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions - uint8 m_veh_flagC80 : 1; + uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure uint8 m_veh_flagD1 : 1; uint8 m_veh_flagD2 : 1; @@ -253,7 +258,7 @@ public: 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 bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false;} virtual float GetHeightAboveRoad(void); virtual void PlayCarHorn(void) {} diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp index 0fc89637..f83f2271 100644 --- a/src/weapons/Weapon.cpp +++ b/src/weapons/Weapon.cpp @@ -3,11 +3,14 @@ #include "Weapon.h" #include "Timer.h" #include "WeaponInfo.h" +#include "Ped.h" +#include "World.h" WRAPPER bool CWeapon::Fire(CEntity*, CVector*) { EAXJMP(0x55C380); } WRAPPER void CWeapon::FireFromCar(CAutomobile *car, bool left) { EAXJMP(0x55C940); } WRAPPER void CWeapon::AddGunshell(CEntity*, CVector const&, CVector2D const&, float) { EAXJMP(0x55F770); } WRAPPER void CWeapon::Update(int32 audioEntity) { EAXJMP(0x563A10); } +WRAPPER void CWeapon::DoTankDoomAiming(CEntity *playerVehicle, CEntity *playerPed, CVector *start, CVector *end) { EAXJMP(0x563200); } void CWeapon::Initialise(eWeaponType type, int ammo) @@ -49,7 +52,42 @@ CWeapon::IsTypeMelee(void) return m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BASEBALLBAT; } +bool +CWeapon::HitsGround(CEntity *holder, CVector *firePos, CEntity *aimingTo) +{ + if (!holder->IsPed() || !((CPed*)holder)->m_pSeekTarget) + return false; + + CWeaponInfo *ourType = CWeaponInfo::GetWeaponInfo(m_eWeaponType); + CVector adjustedOffset = ourType->m_vecFireOffset; + adjustedOffset.z += 0.6f; + + CVector point1, point2; + CEntity *foundEnt = nil; + CColPoint foundCol; + + if (firePos) + point1 = *firePos; + else + point1 = holder->GetMatrix() * adjustedOffset; + + CEntity *aimEntity = aimingTo ? aimingTo : ((CPed*)holder)->m_pSeekTarget; + point2 = aimEntity->GetPosition(); + point2.z += 0.6f; + + CWorld::ProcessLineOfSight(point1, point2, foundCol, foundEnt, true, false, false, false, false, false, false); + if (foundEnt && foundEnt->IsBuilding()) { + // That was supposed to be Magnitude, according to leftover code in assembly + float diff = (foundCol.point.z - point1.z); + if (diff < 0.0f && diff > -3.0f) + return true; + } + + return false; +} + STARTPATCHES InjectHook(0x55C330, &CWeapon::Initialise, PATCH_JUMP); InjectHook(0x5639D0, &CWeapon::Reload, PATCH_JUMP); + InjectHook(0x564890, &CWeapon::HitsGround, PATCH_JUMP); ENDPATCHES
\ No newline at end of file diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h index 71fe1f45..4916284f 100644 --- a/src/weapons/Weapon.h +++ b/src/weapons/Weapon.h @@ -70,5 +70,7 @@ public: void AddGunshell(CEntity*, CVector const&, CVector2D const&, float); bool IsTypeMelee(void); bool IsType2Handed(void); + static void DoTankDoomAiming(CEntity *playerVehicle, CEntity *playerPed, CVector *start, CVector *end); + bool HitsGround(CEntity* holder, CVector* firePos, CEntity* aimingTo); }; static_assert(sizeof(CWeapon) == 0x18, "CWeapon: error"); |