From a19fa8764f63b4521873bbee8d2549c23d4be70a Mon Sep 17 00:00:00 2001 From: Sergeanur Date: Tue, 14 Apr 2020 13:08:03 +0300 Subject: CarGen, Cranes, Gangs: cleanup, fixes, r* visioned placement --- src/control/CarGen.cpp | 312 ------------------ src/control/CarGen.h | 56 ---- src/control/Cranes.cpp | 679 ---------------------------------------- src/control/Cranes.h | 98 ------ src/control/Gangs.cpp | 89 ------ src/control/Gangs.h | 44 --- src/peds/Gangs.cpp | 87 +++++ src/peds/Gangs.h | 44 +++ src/save/GenericGameStorage.cpp | 2 +- src/vehicles/CarGen.cpp | 258 +++++++++++++++ src/vehicles/CarGen.h | 54 ++++ src/vehicles/Cranes.cpp | 671 +++++++++++++++++++++++++++++++++++++++ src/vehicles/Cranes.h | 97 ++++++ 13 files changed, 1212 insertions(+), 1279 deletions(-) delete mode 100644 src/control/CarGen.cpp delete mode 100644 src/control/CarGen.h delete mode 100644 src/control/Cranes.cpp delete mode 100644 src/control/Cranes.h delete mode 100644 src/control/Gangs.cpp delete mode 100644 src/control/Gangs.h create mode 100644 src/peds/Gangs.cpp create mode 100644 src/peds/Gangs.h create mode 100644 src/vehicles/CarGen.cpp create mode 100644 src/vehicles/CarGen.h create mode 100644 src/vehicles/Cranes.cpp create mode 100644 src/vehicles/Cranes.h diff --git a/src/control/CarGen.cpp b/src/control/CarGen.cpp deleted file mode 100644 index 721ac35a..00000000 --- a/src/control/CarGen.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#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) -{ - WriteSaveBuf(buffer, m_nModelIndex); - WriteSaveBuf(buffer, m_vecPos); - WriteSaveBuf(buffer, m_fAngle); - WriteSaveBuf(buffer, m_nColor1); - WriteSaveBuf(buffer, m_nColor2); - WriteSaveBuf(buffer, m_bForceSpawn); - WriteSaveBuf(buffer, m_nAlarm); - WriteSaveBuf(buffer, m_nDoorlock); - WriteSaveBuf(buffer, (uint8)0); - WriteSaveBuf(buffer, m_nMinDelay); - WriteSaveBuf(buffer, m_nMaxDelay); - WriteSaveBuf(buffer, m_nTimer); - WriteSaveBuf(buffer, m_nVehicleHandle); - WriteSaveBuf(buffer, m_nUsesRemaining); - WriteSaveBuf(buffer, m_bIsBlocking); - WriteSaveBuf(buffer, (uint8)0); - WriteSaveBuf(buffer, m_vecInf); - WriteSaveBuf(buffer, m_vecSup); - WriteSaveBuf(buffer, m_fSize); - - // or - //WriteSaveBuf(buffer, *this); - -} - -void CCarGenerator::Load(uint8 *&buffer) -{ - m_nModelIndex = ReadSaveBuf(buffer); - m_vecPos = ReadSaveBuf(buffer); - m_fAngle = ReadSaveBuf(buffer); - m_nColor1 = ReadSaveBuf(buffer); - m_nColor2 = ReadSaveBuf(buffer); - m_bForceSpawn = ReadSaveBuf(buffer); - m_nAlarm = ReadSaveBuf(buffer); - m_nDoorlock = ReadSaveBuf(buffer); - ReadSaveBuf(buffer); - m_nMinDelay = ReadSaveBuf(buffer); - m_nMaxDelay = ReadSaveBuf(buffer); - m_nTimer = ReadSaveBuf(buffer); - m_nVehicleHandle = ReadSaveBuf(buffer); - m_nUsesRemaining = ReadSaveBuf(buffer); - m_bIsBlocking = ReadSaveBuf(buffer); - ReadSaveBuf(buffer); - m_vecInf = ReadSaveBuf(buffer); - m_vecSup = ReadSaveBuf(buffer); - m_fSize = ReadSaveBuf(buffer); - - // or - //*this = ReadSaveBuf(buffer); -} - -void CTheCarGenerators::Process() -{ - if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing()) - 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 = 20 + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE; -INITSAVEBUF - WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE); - - WriteSaveBuf(buffer, 12); /* what is this? */ - WriteSaveBuf(buffer, NumOfCarGenerators); - WriteSaveBuf(buffer, CurrentActiveCount); - WriteSaveBuf(buffer, ProcessCounter); - WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter); - WriteSaveBuf(buffer, (int16)0); - WriteSaveBuf(buffer, sizeof(CarGeneratorArray)); - for (int i = 0; i < NUM_CARGENS; i++){ - CarGeneratorArray[i].Save(buffer); - } -VALIDATESAVEBUF(*size) -} - -void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) -{ - Init(); -INITSAVEBUF - assert(size == 20 + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE); - CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE); - ReadSaveBuf(buffer); - NumOfCarGenerators = ReadSaveBuf(buffer); - CurrentActiveCount = ReadSaveBuf(buffer); - ProcessCounter = ReadSaveBuf(buffer); - GenerateEvenIfPlayerIsCloseCounter = ReadSaveBuf(buffer); - ReadSaveBuf(buffer); - assert(ReadSaveBuf(buffer) == sizeof(CarGeneratorArray)); - for (int i = 0; i < NUM_CARGENS; i++) { - CarGeneratorArray[i].Load(buffer); - } -VALIDATESAVEBUF(size) -} - -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 deleted file mode 100644 index 75acdd56..00000000 --- a/src/control/CarGen.h +++ /dev/null @@ -1,56 +0,0 @@ -#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/Cranes.cpp b/src/control/Cranes.cpp deleted file mode 100644 index 33385dae..00000000 --- a/src/control/Cranes.cpp +++ /dev/null @@ -1,679 +0,0 @@ -#include "common.h" -#include "patcher.h" -#include "Cranes.h" - -#include "Camera.h" -#include "DMAudio.h" -#include "Garages.h" -#include "General.h" -#include "Entity.h" -#include "ModelIndices.h" -#include "Replay.h" -#include "Object.h" -#include "World.h" - -#define MAX_DISTANCE_TO_FIND_CRANE (10.0f) -#define CRANE_UPDATE_RADIUS (300.0f) -#define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f) -#define CRUSHER_Z (-0.951f) -#define MILITARY_Z (10.7862f) -#define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f) -#define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f) -#define CAR_REWARD_MILITARY_CRANE (1500) -#define CAR_MOVING_SPEED_THRESHOLD (0.01f) -#define CRANE_SLOWDOWN_MULTIPLIER (0.3f) - -#define OSCILLATION_SPEED (0.002f) -#define CAR_ROTATION_SPEED (0.0035f) -#define CRANE_MOVEMENT_SPEED (0.001f) -#define HOOK_ANGLE_MOVEMENT_SPEED (0.004f) -#define HOOK_OFFSET_MOVEMENT_SPEED (0.1f) -#define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f) - -#define MESSAGE_SHOW_DURATION (4000) - -#define MAX_DISTANCE (99999.9f) -#define MIN_VALID_POSITION (-10000.0f) -#define DEFAULT_OFFSET (20.0f) - -uint32 TimerForCamInterpolation; - -uint32& CCranes::CarsCollectedMilitaryCrane = *(uint32*)0x8F6248; -int32& CCranes::NumCranes = *(int32*)0x8E28AC; -CCrane(&CCranes::aCranes)[NUM_CRANES] = *(CCrane(*)[NUM_CRANES])*(uintptr*)0x6FA4E0; - -void CCranes::InitCranes(void) -{ - CarsCollectedMilitaryCrane = 0; - NumCranes = 0; - for (int i = 0; i < NUMSECTORS_X; i++) { - for (int j = 0; j < NUMSECTORS_Y; j++) { - for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { - CEntity* pEntity = (CEntity*)pNode->item; - if (MODELID_CRANE_1 == pEntity->GetModelIndex() || - MODELID_CRANE_2 == pEntity->GetModelIndex() || - MODELID_CRANE_3 == pEntity->GetModelIndex()) - AddThisOneCrane(pEntity); - } - } - } - for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_INDUSTRIAL).first; pNode; pNode = pNode->next) { - CEntity* pEntity = (CEntity*)pNode->item; - if (MODELID_CRANE_1 == pEntity->GetModelIndex() || - MODELID_CRANE_2 == pEntity->GetModelIndex() || - MODELID_CRANE_3 == pEntity->GetModelIndex()) - AddThisOneCrane(pEntity); - } -} - -void CCranes::AddThisOneCrane(CEntity* pEntity) -{ - pEntity->GetMatrix().ResetOrientation(); - if (NumCranes >= NUM_CRANES) - return; - CCrane* pCrane = &aCranes[NumCranes]; - pCrane->Init(); - pCrane->m_pCraneEntity = (CBuilding*)pEntity; - pCrane->m_nCraneStatus = CCrane::NONE; - pCrane->m_fHookAngle = NumCranes; // lol wtf - while (pCrane->m_fHookAngle > TWOPI) - pCrane->m_fHookAngle -= TWOPI; - pCrane->m_fHookOffset = DEFAULT_OFFSET; - pCrane->m_fHookHeight = DEFAULT_OFFSET; - pCrane->m_nTimeForNextCheck = 0; - pCrane->m_nCraneState = CCrane::IDLE; - pCrane->m_bWasMilitaryCrane = false; - pCrane->m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[NumCranes]); - if (pCrane->m_nAudioEntity >= 0) - DMAudio.SetEntityStatus(pCrane->m_nAudioEntity, 1); - pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex()); - // Is this used to avoid military crane? - if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) { - CObject* pHook = new CObject(MI_MAGNET, false); - pHook->ObjectCreatedBy = MISSION_OBJECT; - pHook->bUsesCollision = false; - pHook->bExplosionProof = true; - pHook->bAffectedByGravity = false; - pCrane->m_pHook = pHook; - pCrane->CalcHookCoordinates(&pCrane->m_vecHookCurPos.x, &pCrane->m_vecHookCurPos.y, &pCrane->m_vecHookCurPos.z); - pCrane->SetHookMatrix(); - } - else - pCrane->m_pHook = nil; - NumCranes++; -} - -void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY) -{ - float fMinDistance = MAX_DISTANCE; - float X = fPosX, Y = fPosY; - if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) { - X = fDropOffX; - Y = fDropOffY; - } - int index = 0; - for (int i = 0; i < NumCranes; i++) { - float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); - if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { - fMinDistance = distance; - index = i; - } - } -#ifdef FIX_BUGS // classic - if (fMinDistance == MAX_DISTANCE) - return; -#endif - CCrane* pCrane = &aCranes[index]; - pCrane->m_fPickupX1 = fInfX; - pCrane->m_fPickupX2 = fSupX; - pCrane->m_fPickupY1 = fInfY; - pCrane->m_fPickupY2 = fSupY; - pCrane->m_vecDropoffTarget.x = fDropOffX; - pCrane->m_vecDropoffTarget.y = fDropOffY; - pCrane->m_vecDropoffTarget.z = fDropOffZ; - pCrane->m_nCraneStatus = CCrane::ACTIVATED; - pCrane->m_pVehiclePickedUp = nil; - pCrane->m_nVehiclesCollected = 0; - pCrane->m_fDropoffHeading = fHeading; - pCrane->m_bIsCrusher = bIsCrusher; - pCrane->m_bIsMilitaryCrane = bIsMilitary; - bool military = true; - if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) - military = false; - pCrane->m_bWasMilitaryCrane = military; - pCrane->m_nTimeForNextCheck = 0; - pCrane->m_nCraneState = CCrane::IDLE; - float Z; - if (bIsCrusher) - Z = CRUSHER_Z; - else if (bIsMilitary) - Z = MILITARY_Z; - else - Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2); - pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight); - pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight); -} - -void CCranes::DeActivateCrane(float X, float Y) -{ - float fMinDistance = MAX_DISTANCE; - int index = 0; - for (int i = 0; i < NumCranes; i++) { - float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); - if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { - fMinDistance = distance; - index = i; - } - } -#ifdef FIX_BUGS // classic - if (fMinDistance == MAX_DISTANCE) - return; -#endif - aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED; - aCranes[index].m_nCraneState = CCrane::IDLE; -} - -bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle) -{ - int index = 0; - bool result = false; - for (int i = 0; i < NumCranes; i++) { - float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); - if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { - if (aCranes[i].m_nCraneStatus == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneStatus == CCrane::ROTATING_TARGET) - result = true; - } - } - return true; -} - -void CCranes::UpdateCranes(void) -{ - for (int i = 0; i < NumCranes; i++) { - if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher || - (TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x && - TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x && - TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y && - TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y)) - aCranes[i].Update(); - } -} - -void CCrane::Update(void) -{ - if (CReplay::IsPlayingBack()) - return; - if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) && - Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && - Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) || - m_nCraneState != IDLE) { - switch (m_nCraneState) { - case IDLE: - if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) && - CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) { - CWorld::AdvanceCurrentScanCode(); -#ifdef FIX_BUGS - int xstart = max(0, CWorld::GetSectorIndexX(m_fPickupX1)); - int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2)); - int ystart = max(0, CWorld::GetSectorIndexY(m_fPickupY1)); - int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2)); -#else - int xstart = CWorld::GetSectorIndexX(m_fPickupX1); - int xend = CWorld::GetSectorIndexX(m_fPickupX2); - int ystart = CWorld::GetSectorIndexY(m_fPickupY1); - int yend = CWorld::GetSectorIndexY(m_fPickupY1); -#endif - assert(xstart <= xend); - assert(ystart <= yend); - for (int i = xstart; i <= xend; i++) { - for (int j = ystart; j <= yend; j++) { - FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]); - FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); - } - } - } - break; - case GOING_TOWARDS_TARGET: - if (m_pVehiclePickedUp){ - if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 || - m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 || - m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 || - m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 || - m_pVehiclePickedUp->pDriver || - Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD || - Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD || - Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD || - FindPlayerPed()->GetPedState() == PED_ENTER_CAR && // TODO: fix carjack bug - FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { - m_pVehiclePickedUp = nil; - m_nCraneState = IDLE; - } - else { - float fAngle, fOffset, fHeight; - FindParametersForTarget( - m_pVehiclePickedUp->GetPosition().x, - m_pVehiclePickedUp->GetPosition().y, - m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z, - &fAngle, &fOffset, &fHeight); - if (GoTowardsTarget(fAngle, fOffset, fHeight)) { - CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos; - distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z; - if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) { - m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT; - m_vecHookVelocity *= 0.4f; - m_pVehiclePickedUp->bLightsOn = false; - m_pVehiclePickedUp->bUsesCollision = false; - if (m_bIsCrusher) - m_pVehiclePickedUp->bCollisionProof = true; - DMAudio.PlayOneShot(m_nAudioEntity, SOUND_CRANE_PICKUP, 0.0f); - } - } - } - } - else - m_nCraneState = IDLE; - break; - case LIFTING_TARGET: - RotateCarriedCarProperly(); - if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER)) - m_nCraneState = ROTATING_TARGET; - if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { - m_pVehiclePickedUp = nil; - m_nCraneState = IDLE; - } - break; - case GOING_TOWARDS_TARGET_ONLY_HEIGHT: - RotateCarriedCarProperly(); - if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER)) - m_nCraneState = LIFTING_TARGET; - TimerForCamInterpolation = CTimer::GetTimeInMilliseconds(); - if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { - m_pVehiclePickedUp = nil; - m_nCraneState = IDLE; - } - break; - case ROTATING_TARGET: - { - bool bRotateFinished = RotateCarriedCarProperly(); - bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f); - if (bMovementFinished && bRotateFinished) { - float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f; - if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) { - m_nCraneState = DROPPING_TARGET; - if (m_pVehiclePickedUp) { - m_pVehiclePickedUp->bUsesCollision = true; - m_pVehiclePickedUp->m_nStaticFrames = 0; - ++m_nVehiclesCollected; - if (m_bIsMilitaryCrane) { - CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex()); - if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) { - CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE; - CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1); - } - CWorld::Remove(m_pVehiclePickedUp); - delete m_pVehiclePickedUp; - } - } - m_pVehiclePickedUp = nil; - } - } - break; - } - case DROPPING_TARGET: - if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) { - m_nCraneState = IDLE; - m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000; - } - break; - default: - break; - } - CVector vecHook; - CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z); - m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED; - m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep()); - m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep(); - m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep(); - m_vecHookCurPos.z = vecHook.z; - switch (m_nCraneState) { - case LIFTING_TARGET: - case GOING_TOWARDS_TARGET_ONLY_HEIGHT: - case ROTATING_TARGET: - if (m_pVehiclePickedUp) { - m_pVehiclePickedUp->GetPosition() = CVector(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z); - m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f); - CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f); - up.Normalise(); - m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up); - m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight()); - m_pVehiclePickedUp->GetUp() = up; - } - break; - default: - break; - } - } - else { - int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF; - // 16 options, lasting 2048 ms each - // a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug? - if (rnd < 4) { - m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep(); - if (m_fHookAngle < 0.0f) - m_fHookAngle += TWOPI; - } - else if (rnd > 5 && rnd < 12) { - m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep(); - if (m_fHookAngle > TWOPI) - m_fHookAngle -= TWOPI; - } - CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z); - m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f; - } - float fCos = Cos(m_fHookAngle); - float fSin = Sin(m_fHookAngle); - m_pCraneEntity->GetRight().x = fCos; - m_pCraneEntity->GetForward().y = fCos; - m_pCraneEntity->GetRight().y = fSin; - m_pCraneEntity->GetForward().x = -fSin; - m_pCraneEntity->GetMatrix().UpdateRW(); - m_pCraneEntity->UpdateRwFrame(); - SetHookMatrix(); -} - -bool CCrane::RotateCarriedCarProperly() -{ - if (m_fDropoffHeading <= 0.0f) - return true; - if (!m_pVehiclePickedUp) - return true; - float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y); - while (fAngleDelta < -HALFPI) - fAngleDelta += PI; - while (fAngleDelta > HALFPI) - fAngleDelta -= PI; - float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep(); - if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied? - return true; - m_pVehiclePickedUp->GetMatrix().RotateZ(Abs(fDeltaThisFrame)); - return false; -} - -void CCrane::FindCarInSectorList(CPtrList* pList) -{ - CPtrNode* node; - for (node = pList->first; node; node = node->next) { - CVehicle* pVehicle = (CVehicle*)node->item; - if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode()) - continue; - pVehicle->m_scanCode = CWorld::GetCurrentScanCode(); - if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 || - pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2) - continue; - if (pVehicle->pDriver) - continue; - if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD || - Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD || - Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD) - continue; - if (!pVehicle->IsCar() || pVehicle->m_status == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f) - continue; - if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) || - m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) { - if (!pVehicle->bCraneMessageDone) { - pVehicle->bCraneMessageDone = true; - if (!m_bIsMilitaryCrane) - CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle. - else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex())) - CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal. - else - CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model. - } - } - else { - m_pVehiclePickedUp = pVehicle; - pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp); - m_nCraneState = GOING_TOWARDS_TARGET; - } - } -} - -bool CCrane::DoesCranePickUpThisCarType(uint32 mi) -{ - if (m_bIsCrusher) { - return mi != MI_FIRETRUCK && - mi != MI_TRASH && -#ifndef FIX_BUGS // why - mi != MI_BLISTA && -#endif - mi != MI_SECURICA && - mi != MI_BUS && - mi != MI_DODO && - mi != MI_RHINO; - } - if (m_bIsMilitaryCrane) { - return mi == MI_FIRETRUCK || - mi == MI_AMBULAN || - mi == MI_ENFORCER || - mi == MI_FBICAR || - mi == MI_RHINO || - mi == MI_BARRACKS || - mi == MI_POLICE; - } - return true; -} - -bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) -{ - switch (mi) { - case MI_FIRETRUCK: return (CCranes::CarsCollectedMilitaryCrane & 1); - case MI_AMBULAN: return (CCranes::CarsCollectedMilitaryCrane & 2); - case MI_ENFORCER: return (CCranes::CarsCollectedMilitaryCrane & 4); - case MI_FBICAR: return (CCranes::CarsCollectedMilitaryCrane & 8); - case MI_RHINO: return (CCranes::CarsCollectedMilitaryCrane & 0x10); - case MI_BARRACKS: return (CCranes::CarsCollectedMilitaryCrane & 0x20); - case MI_POLICE: return (CCranes::CarsCollectedMilitaryCrane & 0x40); - default: break; - } - return false; -} - -void CCranes::RegisterCarForMilitaryCrane(uint32 mi) -{ - switch (mi) { - case MI_FIRETRUCK: CCranes::CarsCollectedMilitaryCrane |= 1; break; - case MI_AMBULAN: CCranes::CarsCollectedMilitaryCrane |= 2; break; - case MI_ENFORCER: CCranes::CarsCollectedMilitaryCrane |= 4; break; - case MI_FBICAR: CCranes::CarsCollectedMilitaryCrane |= 8; break; - case MI_RHINO: CCranes::CarsCollectedMilitaryCrane |= 0x10; break; - case MI_BARRACKS: CCranes::CarsCollectedMilitaryCrane |= 0x20; break; - case MI_POLICE: CCranes::CarsCollectedMilitaryCrane |= 0x40; break; - default: break; - } -} - -bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() -{ - return (CCranes::CarsCollectedMilitaryCrane & 0x7F) == 0x7F; -} - -bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier) -{ - bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished; - float fHookAngleDelta = fAngleToTarget - m_fHookAngle; - while (fHookAngleDelta > PI) - fHookAngleDelta -= TWOPI; - while (fHookAngleDelta < -PI) - fHookAngleDelta += TWOPI; - float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED; - if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) { - m_fHookAngle = fAngleToTarget; - bAngleMovementFinished = true; - } - else { - if (fHookAngleDelta < 0.0f) { - m_fHookAngle -= fHookAngleChangeThisFrame; - if (m_fHookAngle < 0.0f) - m_fHookAngle += TWOPI; - } - else { - m_fHookAngle += fHookAngleChangeThisFrame; - if (m_fHookAngle > TWOPI) - m_fHookAngle -= TWOPI; - } - bAngleMovementFinished = false; - } - float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset; - float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED; - if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) { - m_fHookOffset = fDistanceToTarget; - bOffsetMovementFinished = true; - } - else { - if (fHookOffsetDelta < 0.0f) - m_fHookOffset -= fHookOffsetChangeThisFrame; - else - m_fHookOffset += fHookOffsetChangeThisFrame; - bOffsetMovementFinished = false; - } - float fHookHeightDelta = fTargetHeight - m_fHookHeight; - float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; - if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { - m_fHookHeight = fTargetHeight; - bHeightMovementFinished = true; - } - else { - if (fHookHeightDelta < 0.0f) - m_fHookHeight -= fHookHeightChangeThisFrame; - else - m_fHookHeight += fHookHeightChangeThisFrame; - bHeightMovementFinished = false; - } - return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished; -} - -bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier) -{ - bool bHeightMovementFinished; - float fHookHeightDelta = fTargetHeight - m_fHookHeight; - float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; - if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { - m_fHookHeight = fTargetHeight; - bHeightMovementFinished = true; - } - else { - if (fHookHeightDelta < 0.0f) - m_fHookHeight -= fHookHeightChangeThisFrame; - else - m_fHookHeight += fHookHeightChangeThisFrame; - bHeightMovementFinished = false; - } - return bHeightMovementFinished; -} - -void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight) -{ - *pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y); - *pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude(); - *pHeight = Z; -} - -void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ) -{ - *pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x; - *pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y; - *pZ = m_fHookHeight; -} - -void CCrane::SetHookMatrix() -{ - if (!m_pHook) - return; - m_pHook->GetPosition() = m_vecHookCurPos; - CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f); - up.Normalise(); - m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up); - m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight()); - m_pHook->GetUp() = up; - m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI); - m_pHook->GetMatrix().UpdateRW(); - m_pHook->UpdateRwFrame(); - CWorld::Remove(m_pHook); - CWorld::Add(m_pHook); -} - -bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle) -{ - for (int i = 0; i < NumCranes; i++) { - if (pVehicle == aCranes[i].m_pVehiclePickedUp) { - switch (aCranes[i].m_nCraneState) { - case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT: - case CCrane::LIFTING_TARGET: - case CCrane::ROTATING_TARGET: - return true; - default: - break; - } - } - } - return false; -} - -bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle) -{ - for (int i = 0; i < NumCranes; i++) { - if (pVehicle == aCranes[i].m_pVehiclePickedUp) - return true; - } - return false; -} - -void CCranes::Save(uint8* buf, uint32* size) -{ - INITSAVEBUF - - *size = 2 * sizeof(uint32) + NUM_CRANES * sizeof(CCrane); - WriteSaveBuf(buf, NumCranes); - WriteSaveBuf(buf, CarsCollectedMilitaryCrane); - for (int i = 0; i < NUM_CRANES; i++) { - CCrane* pCrane = WriteSaveBuf(buf, aCranes[i]); - if (pCrane->m_pCraneEntity) - pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex((CBuilding*)pCrane->m_pCraneEntity) + 1); - if (pCrane->m_pHook) - pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex((CObject*)pCrane->m_pHook) + 1); - if (pCrane->m_pVehiclePickedUp) - pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex((CVehicle*)pCrane->m_pVehiclePickedUp) + 1); - } - - VALIDATESAVEBUF(*size); -} - -void CranesLoad(uint8* buf, uint32 size) -{ - INITSAVEBUF - - CCranes::NumCranes = ReadSaveBuf(buf); - CCranes::CarsCollectedMilitaryCrane = ReadSaveBuf(buf); - for (int i = 0; i < NUM_CRANES; i++) - CCranes::aCranes[i] = ReadSaveBuf(buf); - for (int i = 0; i < NUM_CRANES; i++) { - CCrane* pCrane = &CCranes::aCranes[i]; - if (pCrane->m_pCraneEntity) - pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uint32)pCrane->m_pCraneEntity - 1); - if (pCrane->m_pHook) - pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uint32)pCrane->m_pHook - 1); - if (pCrane->m_pVehiclePickedUp) - pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uint32)pCrane->m_pVehiclePickedUp + 1); - } - for (int i = 0; i < NUM_CRANES; i++) { - CCranes::aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &CCranes::aCranes[i]); - if (CCranes::aCranes[i].m_nAudioEntity) - DMAudio.SetEntityStatus(CCranes::aCranes[i].m_nAudioEntity, 1); - } - - VALIDATESAVEBUF(size); -} - -STARTPATCHES - InjectHook(0x5454D0, CranesLoad, PATCH_JUMP); // GenericLoad -ENDPATCHES diff --git a/src/control/Cranes.h b/src/control/Cranes.h deleted file mode 100644 index d9817282..00000000 --- a/src/control/Cranes.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include "common.h" - -#include "World.h" - -class CVehicle; -class CEntity; -class CObject; -class CBuilding; - -class CCrane -{ -public: - enum CraneState : uint8 { - IDLE = 0, - GOING_TOWARDS_TARGET = 1, - LIFTING_TARGET = 2, - GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3, - ROTATING_TARGET = 4, - DROPPING_TARGET = 5 - }; - enum CraneStatus : uint8 { - NONE = 0, - ACTIVATED = 1, - DEACTIVATED = 2 - }; - CBuilding *m_pCraneEntity; - CObject *m_pHook; - int32 m_nAudioEntity; - float m_fPickupX1; - float m_fPickupX2; - float m_fPickupY1; - float m_fPickupY2; - CVector m_vecDropoffTarget; - float m_fDropoffHeading; - float m_fPickupAngle; - float m_fDropoffAngle; - float m_fPickupDistance; - float m_fDropoffDistance; - float m_fPickupHeight; - float m_fDropoffHeight; - float m_fHookAngle; - float m_fHookOffset; - float m_fHookHeight; - CVector m_vecHookInitPos; - CVector m_vecHookCurPos; - CVector2D m_vecHookVelocity; - CVehicle *m_pVehiclePickedUp; - uint32 m_nTimeForNextCheck; - CraneStatus m_nCraneStatus; - CraneState m_nCraneState; - uint8 m_nVehiclesCollected; - bool m_bIsCrusher; - bool m_bIsMilitaryCrane; - bool m_bWasMilitaryCrane; - bool m_bIsTop; - - void Init(void) { memset(this, 0, sizeof(*this)); } - void Update(void); - bool RotateCarriedCarProperly(void); - void FindCarInSectorList(CPtrList* pList); - bool DoesCranePickUpThisCarType(uint32 mi); - bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f); - bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f); - void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight); - void CalcHookCoordinates(float* pX, float* pY, float* pZ); - void SetHookMatrix(void); - - float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); }; - float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); } - float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); } - float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); } -}; - -static_assert(sizeof(CCrane) == 128, "CCrane: error"); - -class CCranes -{ -public: - static void InitCranes(void); - static void AddThisOneCrane(CEntity* pCraneEntity); - static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY); - static void DeActivateCrane(float fX, float fY); - static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle); - static void UpdateCranes(void); - static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi); - static void RegisterCarForMilitaryCrane(uint32 mi); - static bool HaveAllCarsBeenCollectedByMilitaryCrane(void); - static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle); - static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle); - static void Save(uint8* buf, uint32* size); - - static uint32& CarsCollectedMilitaryCrane; - static int32& NumCranes; - static CCrane(&aCranes)[NUM_CRANES]; -}; - -void CranesLoad(uint8*, uint32); // is this really outside CCranes? diff --git a/src/control/Gangs.cpp b/src/control/Gangs.cpp deleted file mode 100644 index 57d9c67e..00000000 --- a/src/control/Gangs.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "common.h" -#include "patcher.h" -#include "ModelIndices.h" -#include "Gangs.h" -#include "Weapon.h" - -//CGangInfo(&CGangs::Gang)[NUM_GANGS] = *(CGangInfo(*)[NUM_GANGS])*(uintptr*)0x6EDF78; -CGangInfo CGangs::Gang[NUM_GANGS]; - -CGangInfo::CGangInfo() : - m_nVehicleMI(MI_BUS), - m_nPedModelOverride(-1), - m_Weapon1(WEAPONTYPE_UNARMED), - m_Weapon2(WEAPONTYPE_UNARMED) -{} - -void CGangs::Initialise(void) -{ - Gang[GANG_MAFIA].m_nVehicleMI = MI_MAFIA; - Gang[GANG_TRIAD].m_nVehicleMI = MI_BELLYUP; - Gang[GANG_DIABLOS].m_nVehicleMI = MI_DIABLOS; - Gang[GANG_YAKUZA].m_nVehicleMI = MI_YAKUZA; - Gang[GANG_YARDIE].m_nVehicleMI = MI_YARDIE; - Gang[GANG_COLUMB].m_nVehicleMI = MI_COLUMB; - Gang[GANG_HOODS].m_nVehicleMI = MI_HOODS; - Gang[GANG_7].m_nVehicleMI = -1; - Gang[GANG_8].m_nVehicleMI = -1; -#ifdef FIX_BUGS - for (int i = 0; i < NUM_GANGS; i++) - Gang[i].m_nPedModelOverride = -1; -#endif -} - -void CGangs::SetGangVehicleModel(int16 gang, int32 model) -{ - GetGangInfo(gang)->m_nVehicleMI = model; -} - -void CGangs::SetGangWeapons(int16 gang, int32 weapon1, int32 weapon2) -{ - CGangInfo *gi = GetGangInfo(gang); - gi->m_Weapon1 = weapon1; - gi->m_Weapon2 = weapon2; -} - -void CGangs::SetGangPedModelOverride(int16 gang, int8 ovrd) -{ - GetGangInfo(gang)->m_nPedModelOverride = ovrd; -} - -int8 CGangs::GetGangPedModelOverride(int16 gang) -{ - return GetGangInfo(gang)->m_nPedModelOverride; -} - -void CGangs::SaveAllGangData(uint8 *buf, uint32 *size) -{ -INITSAVEBUF - - *size = SAVE_HEADER_SIZE + sizeof(Gang); - WriteSaveHeader(buf, 'G','N','G','\0', *size - SAVE_HEADER_SIZE); - for (int i = 0; i < NUM_GANGS; i++) - WriteSaveBuf(buf, Gang[i]); - -VALIDATESAVEBUF(*size); -} - -void CGangs::LoadAllGangData(uint8 *buf, uint32 size) -{ - Initialise(); - -INITSAVEBUF - // original: SkipSaveBuf(buf, SAVE_HEADER_SIZE); - CheckSaveHeader(buf, 'G','N','G','\0', size - SAVE_HEADER_SIZE); - - for (int i = 0; i < NUM_GANGS; i++) - Gang[i] = ReadSaveBuf(buf); -VALIDATESAVEBUF(size); -} - -STARTPATCHES - InjectHook(0x4C3FB0, CGangs::Initialise, PATCH_JUMP); - InjectHook(0x4C4010, CGangs::SetGangVehicleModel, PATCH_JUMP); - InjectHook(0x4C4030, CGangs::SetGangWeapons, PATCH_JUMP); - InjectHook(0x4C4050, CGangs::SetGangPedModelOverride, PATCH_JUMP); - InjectHook(0x4C4070, CGangs::GetGangPedModelOverride, PATCH_JUMP); - InjectHook(0x4C4080, CGangs::SaveAllGangData, PATCH_JUMP); - InjectHook(0x4C4100, CGangs::LoadAllGangData, PATCH_JUMP); -ENDPATCHES diff --git a/src/control/Gangs.h b/src/control/Gangs.h deleted file mode 100644 index dd7a7f93..00000000 --- a/src/control/Gangs.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -struct CGangInfo -{ - int32 m_nVehicleMI; - int8 m_nPedModelOverride; - int32 m_Weapon1; - int32 m_Weapon2; - - CGangInfo(); -}; - -static_assert(sizeof(CGangInfo) == 0x10, "CGangInfo: error"); - -enum { - GANG_MAFIA = 0, - GANG_TRIAD, - GANG_DIABLOS, - GANG_YAKUZA, - GANG_YARDIE, - GANG_COLUMB, - GANG_HOODS, - GANG_7, - GANG_8, - NUM_GANGS -}; - -class CGangs -{ -public: - static void Initialise(void); - static void SetGangVehicleModel(int16, int32); - static void SetGangWeapons(int16, int32, int32); - static void SetGangPedModelOverride(int16, int8); - static int8 GetGangPedModelOverride(int16); - static void SaveAllGangData(uint8 *, uint32 *); - static void LoadAllGangData(uint8 *, uint32); - - static int32 GetGangVehicleModel(int16 gang) { return Gang[gang].m_nVehicleMI; } - static CGangInfo *GetGangInfo(int16 gang) { return &Gang[gang]; } - -private: - static CGangInfo Gang[NUM_GANGS]; -}; diff --git a/src/peds/Gangs.cpp b/src/peds/Gangs.cpp new file mode 100644 index 00000000..c67fe599 --- /dev/null +++ b/src/peds/Gangs.cpp @@ -0,0 +1,87 @@ +#include "common.h" +#include "patcher.h" +#include "ModelIndices.h" +#include "Gangs.h" +#include "Weapon.h" + +CGangInfo CGangs::Gang[NUM_GANGS]; + +CGangInfo::CGangInfo() : + m_nVehicleMI(MI_BUS), + m_nPedModelOverride(-1), + m_Weapon1(WEAPONTYPE_UNARMED), + m_Weapon2(WEAPONTYPE_UNARMED) +{} + +void CGangs::Initialise(void) +{ + Gang[GANG_MAFIA].m_nVehicleMI = MI_MAFIA; + Gang[GANG_TRIAD].m_nVehicleMI = MI_BELLYUP; + Gang[GANG_DIABLOS].m_nVehicleMI = MI_DIABLOS; + Gang[GANG_YAKUZA].m_nVehicleMI = MI_YAKUZA; + Gang[GANG_YARDIE].m_nVehicleMI = MI_YARDIE; + Gang[GANG_COLUMB].m_nVehicleMI = MI_COLUMB; + Gang[GANG_HOODS].m_nVehicleMI = MI_HOODS; + Gang[GANG_7].m_nVehicleMI = -1; + Gang[GANG_8].m_nVehicleMI = -1; +#ifdef FIX_BUGS + for (int i = 0; i < NUM_GANGS; i++) + Gang[i].m_nPedModelOverride = -1; +#endif +} + +void CGangs::SetGangVehicleModel(int16 gang, int32 model) +{ + GetGangInfo(gang)->m_nVehicleMI = model; +} + +void CGangs::SetGangWeapons(int16 gang, int32 weapon1, int32 weapon2) +{ + CGangInfo *gi = GetGangInfo(gang); + gi->m_Weapon1 = weapon1; + gi->m_Weapon2 = weapon2; +} + +void CGangs::SetGangPedModelOverride(int16 gang, int8 ovrd) +{ + GetGangInfo(gang)->m_nPedModelOverride = ovrd; +} + +int8 CGangs::GetGangPedModelOverride(int16 gang) +{ + return GetGangInfo(gang)->m_nPedModelOverride; +} + +void CGangs::SaveAllGangData(uint8 *buf, uint32 *size) +{ +INITSAVEBUF + + *size = SAVE_HEADER_SIZE + sizeof(Gang); + WriteSaveHeader(buf, 'G','N','G','\0', *size - SAVE_HEADER_SIZE); + for (int i = 0; i < NUM_GANGS; i++) + WriteSaveBuf(buf, Gang[i]); + +VALIDATESAVEBUF(*size); +} + +void CGangs::LoadAllGangData(uint8 *buf, uint32 size) +{ + Initialise(); + +INITSAVEBUF + CheckSaveHeader(buf, 'G','N','G','\0', size - SAVE_HEADER_SIZE); + + for (int i = 0; i < NUM_GANGS; i++) + Gang[i] = ReadSaveBuf(buf); +VALIDATESAVEBUF(size); +} + +STARTPATCHES + InjectHook(0x4C3FB0, CGangs::Initialise, PATCH_JUMP); + InjectHook(0x4C4010, CGangs::SetGangVehicleModel, PATCH_JUMP); + InjectHook(0x4C4030, CGangs::SetGangWeapons, PATCH_JUMP); + InjectHook(0x4C4050, CGangs::SetGangPedModelOverride, PATCH_JUMP); + InjectHook(0x4C4070, CGangs::GetGangPedModelOverride, PATCH_JUMP); + InjectHook(0x4C4080, CGangs::SaveAllGangData, PATCH_JUMP); + InjectHook(0x4C4100, CGangs::LoadAllGangData, PATCH_JUMP); +ENDPATCHES diff --git a/src/peds/Gangs.h b/src/peds/Gangs.h new file mode 100644 index 00000000..dd7a7f93 --- /dev/null +++ b/src/peds/Gangs.h @@ -0,0 +1,44 @@ +#pragma once + +struct CGangInfo +{ + int32 m_nVehicleMI; + int8 m_nPedModelOverride; + int32 m_Weapon1; + int32 m_Weapon2; + + CGangInfo(); +}; + +static_assert(sizeof(CGangInfo) == 0x10, "CGangInfo: error"); + +enum { + GANG_MAFIA = 0, + GANG_TRIAD, + GANG_DIABLOS, + GANG_YAKUZA, + GANG_YARDIE, + GANG_COLUMB, + GANG_HOODS, + GANG_7, + GANG_8, + NUM_GANGS +}; + +class CGangs +{ +public: + static void Initialise(void); + static void SetGangVehicleModel(int16, int32); + static void SetGangWeapons(int16, int32, int32); + static void SetGangPedModelOverride(int16, int8); + static int8 GetGangPedModelOverride(int16); + static void SaveAllGangData(uint8 *, uint32 *); + static void LoadAllGangData(uint8 *, uint32); + + static int32 GetGangVehicleModel(int16 gang) { return Gang[gang].m_nVehicleMI; } + static CGangInfo *GetGangInfo(int16 gang) { return &Gang[gang]; } + +private: + static CGangInfo Gang[NUM_GANGS]; +}; diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp index 4cfa75b3..0ec0b117 100644 --- a/src/save/GenericGameStorage.cpp +++ b/src/save/GenericGameStorage.cpp @@ -283,7 +283,7 @@ GenericLoad() LoadSaveDataBlock(); ReadDataFromBlock("Loading Paths \n", ThePaths.Load); LoadSaveDataBlock(); - ReadDataFromBlock("Loading Cranes \n", CranesLoad); + ReadDataFromBlock("Loading Cranes \n", CCranes::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Pickups \n", CPickups::Load); LoadSaveDataBlock(); diff --git a/src/vehicles/CarGen.cpp b/src/vehicles/CarGen.cpp new file mode 100644 index 00000000..43f77fb7 --- /dev/null +++ b/src/vehicles/CarGen.cpp @@ -0,0 +1,258 @@ +#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; +uint32 CTheCarGenerators::NumOfCarGenerators; +CCarGenerator CTheCarGenerators::CarGeneratorArray[NUM_CARGENS]; +uint8 CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter; +uint32 CTheCarGenerators::CurrentActiveCount; + +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 CTheCarGenerators::Process() +{ + if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing()) + 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) +{ + const uint32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + *size = sizeof(int) + nGeneralDataSize + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE; +INITSAVEBUF + WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE); + + WriteSaveBuf(buffer, nGeneralDataSize); + WriteSaveBuf(buffer, NumOfCarGenerators); + WriteSaveBuf(buffer, CurrentActiveCount); + WriteSaveBuf(buffer, ProcessCounter); + WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter); + WriteSaveBuf(buffer, (int16)0); // alignment + WriteSaveBuf(buffer, sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + WriteSaveBuf(buffer, CarGeneratorArray[i]); +VALIDATESAVEBUF(*size) +} + +void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) +{ + const int32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + Init(); +INITSAVEBUF + CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE); + assert(ReadSaveBuf(buffer) == nGeneralDataSize); + NumOfCarGenerators = ReadSaveBuf(buffer); + CurrentActiveCount = ReadSaveBuf(buffer); + ProcessCounter = ReadSaveBuf(buffer); + GenerateEvenIfPlayerIsCloseCounter = ReadSaveBuf(buffer); + ReadSaveBuf(buffer); // alignment + assert(ReadSaveBuf(buffer) == sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + CarGeneratorArray[i] = ReadSaveBuf(buffer); +VALIDATESAVEBUF(size) +} + +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/vehicles/CarGen.h b/src/vehicles/CarGen.h new file mode 100644 index 00000000..9d645318 --- /dev/null +++ b/src/vehicles/CarGen.h @@ -0,0 +1,54 @@ +#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 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/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp new file mode 100644 index 00000000..dbc3c340 --- /dev/null +++ b/src/vehicles/Cranes.cpp @@ -0,0 +1,671 @@ +#include "common.h" +#include "patcher.h" +#include "Cranes.h" + +#include "Camera.h" +#include "DMAudio.h" +#include "Garages.h" +#include "General.h" +#include "Entity.h" +#include "ModelIndices.h" +#include "Replay.h" +#include "Object.h" +#include "World.h" + +#define MAX_DISTANCE_TO_FIND_CRANE (10.0f) +#define CRANE_UPDATE_RADIUS (300.0f) +#define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f) +#define CRUSHER_Z (-0.951f) +#define MILITARY_Z (10.7862f) +#define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f) +#define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f) +#define CAR_REWARD_MILITARY_CRANE (1500) +#define CAR_MOVING_SPEED_THRESHOLD (0.01f) +#define CRANE_SLOWDOWN_MULTIPLIER (0.3f) + +#define OSCILLATION_SPEED (0.002f) +#define CAR_ROTATION_SPEED (0.0035f) +#define CRANE_MOVEMENT_SPEED (0.001f) +#define HOOK_ANGLE_MOVEMENT_SPEED (0.004f) +#define HOOK_OFFSET_MOVEMENT_SPEED (0.1f) +#define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f) + +#define MESSAGE_SHOW_DURATION (4000) + +#define MAX_DISTANCE (99999.9f) +#define MIN_VALID_POSITION (-10000.0f) +#define DEFAULT_OFFSET (20.0f) + +uint32 TimerForCamInterpolation; + +uint32 CCranes::CarsCollectedMilitaryCrane; +int32 CCranes::NumCranes; +CCrane CCranes::aCranes[NUM_CRANES]; + +void CCranes::InitCranes(void) +{ + CarsCollectedMilitaryCrane = 0; + NumCranes = 0; + for (int i = 0; i < NUMSECTORS_X; i++) { + for (int j = 0; j < NUMSECTORS_Y; j++) { + for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } + } + } + for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_INDUSTRIAL).first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } +} + +void CCranes::AddThisOneCrane(CEntity* pEntity) +{ + pEntity->GetMatrix().ResetOrientation(); + if (NumCranes >= NUM_CRANES) + return; + CCrane* pCrane = &aCranes[NumCranes]; + pCrane->Init(); + pCrane->m_pCraneEntity = (CBuilding*)pEntity; + pCrane->m_nCraneStatus = CCrane::NONE; + pCrane->m_fHookAngle = NumCranes; // lol wtf + while (pCrane->m_fHookAngle > TWOPI) + pCrane->m_fHookAngle -= TWOPI; + pCrane->m_fHookOffset = DEFAULT_OFFSET; + pCrane->m_fHookHeight = DEFAULT_OFFSET; + pCrane->m_nTimeForNextCheck = 0; + pCrane->m_nCraneState = CCrane::IDLE; + pCrane->m_bWasMilitaryCrane = false; + pCrane->m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[NumCranes]); + if (pCrane->m_nAudioEntity >= 0) + DMAudio.SetEntityStatus(pCrane->m_nAudioEntity, 1); + pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex()); + // Is this used to avoid military crane? + if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) { + CObject* pHook = new CObject(MI_MAGNET, false); + pHook->ObjectCreatedBy = MISSION_OBJECT; + pHook->bUsesCollision = false; + pHook->bExplosionProof = true; + pHook->bAffectedByGravity = false; + pCrane->m_pHook = pHook; + pCrane->CalcHookCoordinates(&pCrane->m_vecHookCurPos.x, &pCrane->m_vecHookCurPos.y, &pCrane->m_vecHookCurPos.z); + pCrane->SetHookMatrix(); + } + else + pCrane->m_pHook = nil; + NumCranes++; +} + +void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY) +{ + float fMinDistance = MAX_DISTANCE; + float X = fPosX, Y = fPosY; + if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) { + X = fDropOffX; + Y = fDropOffY; + } + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + CCrane* pCrane = &aCranes[index]; + pCrane->m_fPickupX1 = fInfX; + pCrane->m_fPickupX2 = fSupX; + pCrane->m_fPickupY1 = fInfY; + pCrane->m_fPickupY2 = fSupY; + pCrane->m_vecDropoffTarget.x = fDropOffX; + pCrane->m_vecDropoffTarget.y = fDropOffY; + pCrane->m_vecDropoffTarget.z = fDropOffZ; + pCrane->m_nCraneStatus = CCrane::ACTIVATED; + pCrane->m_pVehiclePickedUp = nil; + pCrane->m_nVehiclesCollected = 0; + pCrane->m_fDropoffHeading = fHeading; + pCrane->m_bIsCrusher = bIsCrusher; + pCrane->m_bIsMilitaryCrane = bIsMilitary; + bool military = true; + if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) + military = false; + pCrane->m_bWasMilitaryCrane = military; + pCrane->m_nTimeForNextCheck = 0; + pCrane->m_nCraneState = CCrane::IDLE; + float Z; + if (bIsCrusher) + Z = CRUSHER_Z; + else if (bIsMilitary) + Z = MILITARY_Z; + else + Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2); + pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight); + pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight); +} + +void CCranes::DeActivateCrane(float X, float Y) +{ + float fMinDistance = MAX_DISTANCE; + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED; + aCranes[index].m_nCraneState = CCrane::IDLE; +} + +bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle) +{ + int index = 0; + bool result = false; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { + if (aCranes[i].m_nCraneStatus == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneStatus == CCrane::ROTATING_TARGET) + result = true; + } + } + return true; +} + +void CCranes::UpdateCranes(void) +{ + for (int i = 0; i < NumCranes; i++) { + if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher || + (TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y)) + aCranes[i].Update(); + } +} + +void CCrane::Update(void) +{ + if (CReplay::IsPlayingBack()) + return; + if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) && + Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && + Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) || + m_nCraneState != IDLE) { + switch (m_nCraneState) { + case IDLE: + if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) && + CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) { + CWorld::AdvanceCurrentScanCode(); +#ifdef FIX_BUGS + int xstart = max(0, CWorld::GetSectorIndexX(m_fPickupX1)); + int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2)); + int ystart = max(0, CWorld::GetSectorIndexY(m_fPickupY1)); + int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2)); +#else + int xstart = CWorld::GetSectorIndexX(m_fPickupX1); + int xend = CWorld::GetSectorIndexX(m_fPickupX2); + int ystart = CWorld::GetSectorIndexY(m_fPickupY1); + int yend = CWorld::GetSectorIndexY(m_fPickupY1); +#endif + assert(xstart <= xend); + assert(ystart <= yend); + for (int i = xstart; i <= xend; i++) { + for (int j = ystart; j <= yend; j++) { + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]); + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + } + } + } + break; + case GOING_TOWARDS_TARGET: + if (m_pVehiclePickedUp){ + if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 || + m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 || + m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 || + m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 || + m_pVehiclePickedUp->pDriver || + Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD || + FindPlayerPed()->GetPedState() == PED_ENTER_CAR && // TODO: fix carjack bug + FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + else { + float fAngle, fOffset, fHeight; + FindParametersForTarget( + m_pVehiclePickedUp->GetPosition().x, + m_pVehiclePickedUp->GetPosition().y, + m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z, + &fAngle, &fOffset, &fHeight); + if (GoTowardsTarget(fAngle, fOffset, fHeight)) { + CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos; + distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z; + if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) { + m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT; + m_vecHookVelocity *= 0.4f; + m_pVehiclePickedUp->bLightsOn = false; + m_pVehiclePickedUp->bUsesCollision = false; + if (m_bIsCrusher) + m_pVehiclePickedUp->bCollisionProof = true; + DMAudio.PlayOneShot(m_nAudioEntity, SOUND_CRANE_PICKUP, 0.0f); + } + } + } + } + else + m_nCraneState = IDLE; + break; + case LIFTING_TARGET: + RotateCarriedCarProperly(); + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = ROTATING_TARGET; + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + RotateCarriedCarProperly(); + if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = LIFTING_TARGET; + TimerForCamInterpolation = CTimer::GetTimeInMilliseconds(); + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case ROTATING_TARGET: + { + bool bRotateFinished = RotateCarriedCarProperly(); + bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f); + if (bMovementFinished && bRotateFinished) { + float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f; + if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) { + m_nCraneState = DROPPING_TARGET; + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->bUsesCollision = true; + m_pVehiclePickedUp->m_nStaticFrames = 0; + ++m_nVehiclesCollected; + if (m_bIsMilitaryCrane) { + CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex()); + if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE; + CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1); + } + CWorld::Remove(m_pVehiclePickedUp); + delete m_pVehiclePickedUp; + } + } + m_pVehiclePickedUp = nil; + } + } + break; + } + case DROPPING_TARGET: + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) { + m_nCraneState = IDLE; + m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000; + } + break; + default: + break; + } + CVector vecHook; + CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z); + m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED; + m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep()); + m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep(); + m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep(); + m_vecHookCurPos.z = vecHook.z; + switch (m_nCraneState) { + case LIFTING_TARGET: + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case ROTATING_TARGET: + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->GetPosition() = CVector(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z); + m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f); + CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up); + m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight()); + m_pVehiclePickedUp->GetUp() = up; + } + break; + default: + break; + } + } + else { + int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF; + // 16 options, lasting 2048 ms each + // a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug? + if (rnd < 4) { + m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else if (rnd > 5 && rnd < 12) { + m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z); + m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f; + } + float fCos = Cos(m_fHookAngle); + float fSin = Sin(m_fHookAngle); + m_pCraneEntity->GetRight().x = fCos; + m_pCraneEntity->GetForward().y = fCos; + m_pCraneEntity->GetRight().y = fSin; + m_pCraneEntity->GetForward().x = -fSin; + m_pCraneEntity->GetMatrix().UpdateRW(); + m_pCraneEntity->UpdateRwFrame(); + SetHookMatrix(); +} + +bool CCrane::RotateCarriedCarProperly() +{ + if (m_fDropoffHeading <= 0.0f) + return true; + if (!m_pVehiclePickedUp) + return true; + float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y); + while (fAngleDelta < -HALFPI) + fAngleDelta += PI; + while (fAngleDelta > HALFPI) + fAngleDelta -= PI; + float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep(); + if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied? + return true; + m_pVehiclePickedUp->GetMatrix().RotateZ(Abs(fDeltaThisFrame)); + return false; +} + +void CCrane::FindCarInSectorList(CPtrList* pList) +{ + CPtrNode* node; + for (node = pList->first; node; node = node->next) { + CVehicle* pVehicle = (CVehicle*)node->item; + if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + pVehicle->m_scanCode = CWorld::GetCurrentScanCode(); + if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 || + pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2) + continue; + if (pVehicle->pDriver) + continue; + if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD) + continue; + if (!pVehicle->IsCar() || pVehicle->m_status == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f) + continue; + if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) || + m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) { + if (!pVehicle->bCraneMessageDone) { + pVehicle->bCraneMessageDone = true; + if (!m_bIsMilitaryCrane) + CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle. + else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex())) + CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal. + else + CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model. + } + } + else { + m_pVehiclePickedUp = pVehicle; + pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp); + m_nCraneState = GOING_TOWARDS_TARGET; + } + } +} + +bool CCrane::DoesCranePickUpThisCarType(uint32 mi) +{ + if (m_bIsCrusher) { + return mi != MI_FIRETRUCK && + mi != MI_TRASH && +#ifndef FIX_BUGS // why + mi != MI_BLISTA && +#endif + mi != MI_SECURICA && + mi != MI_BUS && + mi != MI_DODO && + mi != MI_RHINO; + } + if (m_bIsMilitaryCrane) { + return mi == MI_FIRETRUCK || + mi == MI_AMBULAN || + mi == MI_ENFORCER || + mi == MI_FBICAR || + mi == MI_RHINO || + mi == MI_BARRACKS || + mi == MI_POLICE; + } + return true; +} + +bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) +{ + switch (mi) { + case MI_FIRETRUCK: return (CarsCollectedMilitaryCrane & 1); + case MI_AMBULAN: return (CarsCollectedMilitaryCrane & 2); + case MI_ENFORCER: return (CarsCollectedMilitaryCrane & 4); + case MI_FBICAR: return (CarsCollectedMilitaryCrane & 8); + case MI_RHINO: return (CarsCollectedMilitaryCrane & 0x10); + case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20); + case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40); + default: break; + } + return false; +} + +void CCranes::RegisterCarForMilitaryCrane(uint32 mi) +{ + switch (mi) { + case MI_FIRETRUCK: CarsCollectedMilitaryCrane |= 1; break; + case MI_AMBULAN: CarsCollectedMilitaryCrane |= 2; break; + case MI_ENFORCER: CarsCollectedMilitaryCrane |= 4; break; + case MI_FBICAR: CarsCollectedMilitaryCrane |= 8; break; + case MI_RHINO: CarsCollectedMilitaryCrane |= 0x10; break; + case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break; + case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break; + default: break; + } +} + +bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() +{ + return (CarsCollectedMilitaryCrane & 0x7F) == 0x7F; +} + +bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier) +{ + bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished; + float fHookAngleDelta = fAngleToTarget - m_fHookAngle; + while (fHookAngleDelta > PI) + fHookAngleDelta -= TWOPI; + while (fHookAngleDelta < -PI) + fHookAngleDelta += TWOPI; + float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED; + if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) { + m_fHookAngle = fAngleToTarget; + bAngleMovementFinished = true; + } else { + if (fHookAngleDelta < 0.0f) { + m_fHookAngle -= fHookAngleChangeThisFrame; + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else { + m_fHookAngle += fHookAngleChangeThisFrame; + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + bAngleMovementFinished = false; + } + float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset; + float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED; + if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) { + m_fHookOffset = fDistanceToTarget; + bOffsetMovementFinished = true; + } else { + if (fHookOffsetDelta < 0.0f) + m_fHookOffset -= fHookOffsetChangeThisFrame; + else + m_fHookOffset += fHookOffsetChangeThisFrame; + bOffsetMovementFinished = false; + } + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished; +} + +bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier) +{ + bool bHeightMovementFinished; + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bHeightMovementFinished; +} + +void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight) +{ + *pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y); + *pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude(); + *pHeight = Z; +} + +void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ) +{ + *pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x; + *pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y; + *pZ = m_fHookHeight; +} + +void CCrane::SetHookMatrix() +{ + if (m_pHook == nil) + return; + m_pHook->GetPosition() = m_vecHookCurPos; + CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up); + m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight()); + m_pHook->GetUp() = up; + m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI); + m_pHook->GetMatrix().UpdateRW(); + m_pHook->UpdateRwFrame(); + CWorld::Remove(m_pHook); + CWorld::Add(m_pHook); +} + +bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) { + switch (aCranes[i].m_nCraneState) { + case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case CCrane::LIFTING_TARGET: + case CCrane::ROTATING_TARGET: + return true; + default: + break; + } + } + } + return false; +} + +bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) + return true; + } + return false; +} + +void CCranes::Save(uint8* buf, uint32* size) +{ + INITSAVEBUF + + *size = 2 * sizeof(uint32) + sizeof(aCranes); + WriteSaveBuf(buf, NumCranes); + WriteSaveBuf(buf, CarsCollectedMilitaryCrane); + for (int i = 0; i < NUM_CRANES; i++) { + CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]); + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex(pCrane->m_pCraneEntity) + 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex(pCrane->m_pHook) + 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex(pCrane->m_pVehiclePickedUp) + 1); + } + + VALIDATESAVEBUF(*size); +} + +void CCranes::Load(uint8* buf, uint32 size) +{ + INITSAVEBUF + + NumCranes = ReadSaveBuf(buf); + CarsCollectedMilitaryCrane = ReadSaveBuf(buf); + for (int i = 0; i < NUM_CRANES; i++) + aCranes[i] = ReadSaveBuf(buf); + for (int i = 0; i < NUM_CRANES; i++) { + CCrane *pCrane = &aCranes[i]; + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uint32)pCrane->m_pCraneEntity - 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uint32)pCrane->m_pHook - 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uint32)pCrane->m_pVehiclePickedUp - 1); + } + for (int i = 0; i < NUM_CRANES; i++) { + aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]); + if (aCranes[i].m_nAudioEntity != 0) + DMAudio.SetEntityStatus(aCranes[i].m_nAudioEntity, 1); + } + + VALIDATESAVEBUF(size); +} diff --git a/src/vehicles/Cranes.h b/src/vehicles/Cranes.h new file mode 100644 index 00000000..c0502638 --- /dev/null +++ b/src/vehicles/Cranes.h @@ -0,0 +1,97 @@ +#pragma once +#include "common.h" + +#include "World.h" + +class CVehicle; +class CEntity; +class CObject; +class CBuilding; + +class CCrane +{ +public: + enum CraneState : uint8 { + IDLE = 0, + GOING_TOWARDS_TARGET = 1, + LIFTING_TARGET = 2, + GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3, + ROTATING_TARGET = 4, + DROPPING_TARGET = 5 + }; + enum CraneStatus : uint8 { + NONE = 0, + ACTIVATED = 1, + DEACTIVATED = 2 + }; + CBuilding *m_pCraneEntity; + CObject *m_pHook; + int32 m_nAudioEntity; + float m_fPickupX1; + float m_fPickupX2; + float m_fPickupY1; + float m_fPickupY2; + CVector m_vecDropoffTarget; + float m_fDropoffHeading; + float m_fPickupAngle; + float m_fDropoffAngle; + float m_fPickupDistance; + float m_fDropoffDistance; + float m_fPickupHeight; + float m_fDropoffHeight; + float m_fHookAngle; + float m_fHookOffset; + float m_fHookHeight; + CVector m_vecHookInitPos; + CVector m_vecHookCurPos; + CVector2D m_vecHookVelocity; + CVehicle *m_pVehiclePickedUp; + uint32 m_nTimeForNextCheck; + CraneStatus m_nCraneStatus; + CraneState m_nCraneState; + uint8 m_nVehiclesCollected; + bool m_bIsCrusher; + bool m_bIsMilitaryCrane; + bool m_bWasMilitaryCrane; + bool m_bIsTop; + + void Init(void) { memset(this, 0, sizeof(*this)); } + void Update(void); + bool RotateCarriedCarProperly(void); + void FindCarInSectorList(CPtrList* pList); + bool DoesCranePickUpThisCarType(uint32 mi); + bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f); + bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f); + void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight); + void CalcHookCoordinates(float* pX, float* pY, float* pZ); + void SetHookMatrix(void); + + float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); }; + float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); } + float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); } + float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); } +}; + +static_assert(sizeof(CCrane) == 128, "CCrane: error"); + +class CCranes +{ +public: + static void InitCranes(void); + static void AddThisOneCrane(CEntity* pCraneEntity); + static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY); + static void DeActivateCrane(float fX, float fY); + static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle); + static void UpdateCranes(void); + static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi); + static void RegisterCarForMilitaryCrane(uint32 mi); + static bool HaveAllCarsBeenCollectedByMilitaryCrane(void); + static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle); + static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle); + static void Save(uint8* buf, uint32* size); + static void Load(uint8* buf, uint32 size); // on mobile it's CranesLoad outside of the class + + static uint32 CarsCollectedMilitaryCrane; + static int32 NumCranes; + static CCrane aCranes[NUM_CRANES]; +}; -- cgit v1.2.3