diff --git a/src/control/Cranes.cpp b/src/control/Cranes.cpp index 4c1bf2c8..96c41f45 100644 --- a/src/control/Cranes.cpp +++ b/src/control/Cranes.cpp @@ -2,13 +2,238 @@ #include "patcher.h" #include "Cranes.h" +#include "Camera.h" +#include "DMAudio.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) + +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->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_pObject = pEntity; + pCrane->m_bCraneStatus = CCrane::NONE; + pCrane->m_fHeight = NumCranes; // lol wtf + while (pCrane->m_fHeight > TWOPI) + pCrane->m_fHeight -= TWOPI; + pCrane->m_fHookOffset = 20.0f; + pCrane->m_fHookHeight = 20.0f; + pCrane->m_nUpdateTimer = 0; + pCrane->m_bCraneState = CCrane::IDLE; + pCrane->m_bWasMilitaryCrane = 0; + 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()); + // W T F ? ? ? + // Is this used to avoid military crane? + if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) { + CObject* pMagnet = new CObject(MI_MAGNET, false); + pMagnet->ObjectCreatedBy = MISSION_OBJECT; + pMagnet->bUsesCollision = false; + pMagnet->bExplosionProof = true; + pMagnet->bAffectedByGravity = false; + pCrane->m_pMagnet = pMagnet; + pCrane->CalcHookCoordinates(&pCrane->m_vecHookCurPos.x, &pCrane->m_vecHookCurPos.y, &pCrane->m_vecHookCurPos.z); + pCrane->SetHookMatrix(); + } + else + pCrane->m_pMagnet = 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 = 99999.9f; + float X = fPosX, Y = fPosY; + if (X <= -10000.0f || Y <= -10000.0f) { + X = fDropOffX; + Y = fDropOffY; + } + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pObject->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == 99999.9f) + return; +#endif + CCrane* pCrane = &aCranes[NumCranes]; + 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_bCraneStatus = CCrane::ACTIVATED; + pCrane->m_pVehiclePickedUp = nil; + pCrane->m_bVehiclesCollected = 0; + pCrane->m_bIsCrusher = bIsCrusher; + pCrane->m_bIsMilitaryCrane = bIsMilitary; + bool military = true; + if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) + military = false; + pCrane->m_bWasMilitaryCrane = military; + pCrane->m_nUpdateTimer = 0; + pCrane->m_bCraneState = CCrane::IDLE; + float Z; + if (bIsCrusher) + Z = CRUSHER_Z; + else if (bIsMilitary) + Z = MILITARY_Z; + else + Z = CWorld::FindGroundZForCoord((fInfX + fSupY) / 2, (fInfY + fSupY) / 2); + pCrane->FindParametersForTarget((fInfX + fSupY) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fAngle); + pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDistance); +} + +void CCranes::DeActivateCrane(float X, float Y) +{ + float fMinDistance = 99999.9f; + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pObject->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == 99999.9f) + return; +#endif + aCranes[index].m_bCraneStatus = CCrane::DEACTIVATED; + aCranes[index].m_bCraneState = 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_pObject->GetPosition()).Magnitude(); + if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { + if (aCranes[i].m_bCraneStatus == CCrane::LIFTING_TARGET || aCranes[i].m_bCraneStatus == 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_pObject->GetPosition().x && + TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pObject->GetPosition().x && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pObject->GetPosition().y && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pObject->GetPosition().y)) + aCranes[i].Update(); + } +} + +void CCrane::Update(void) +{ + if (CReplay::IsPlayingBack()) + return; + if ((m_bCraneStatus == ACTIVATED || m_bCraneStatus == DEACTIVATED) && + Abs(TheCamera.GetGameCamPosition().x - m_pObject->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && + Abs(TheCamera.GetGameCamPosition().y - m_pObject->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) { + switch (m_bCraneState) { + case IDLE: + if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, 4.0f + m_fAngle + m_bIsCrusher ? 4.5f : 0.0f, 1.0f) && + CTimer::GetTimeInMilliseconds() > m_nUpdateTimer) { + 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) { + m_bCraneState = IDLE; + break; + } + 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) > 0.01f || + Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > 0.01f || + Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > 0.01f || + FindPlayerPed()->GetPedState() == PED_ENTER_CAR && + FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { + m_pVehiclePickedUp = nil; + m_bCraneState = IDLE; + break; + } + + } + } +} + WRAPPER bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle*) { EAXJMP(0x5451E0); } WRAPPER bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle*) { EAXJMP(0x545190); } -WRAPPER bool CCranes::IsThisCarPickedUp(float, float, CVehicle*) { EAXJMP(0x543940); } WRAPPER bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() { EAXJMP(0x544BE0); } -WRAPPER void CCranes::ActivateCrane(float, float, float, float, float, float, float, float, bool, bool, float, float) { EAXJMP(0x543650); } -WRAPPER void CCranes::DeActivateCrane(float, float) { EAXJMP(0x543890); } -WRAPPER void CCranes::InitCranes(void) { EAXJMP(0x543360); } -WRAPPER void CCranes::UpdateCranes(void) { EAXJMP(0x5439E0); } + + WRAPPER void CCranes::Save(uint8*, uint32*) { EAXJMP(0x545210); } WRAPPER void CranesLoad(uint8*, uint32) { EAXJMP(0x5454d0); } diff --git a/src/control/Cranes.h b/src/control/Cranes.h index b40454ea..c121378b 100644 --- a/src/control/Cranes.h +++ b/src/control/Cranes.h @@ -1,6 +1,8 @@ #pragma once #include "common.h" +#include "World.h" + class CVehicle; class CEntity; class CObject; @@ -8,9 +10,22 @@ class CObject; class CCrane { public: + enum CraneState : uint8 { + IDLE = 0, + GOING_TOWARDS_TARGET = 1, + LIFTING_TARGET = 2, + GOING_TOWARDS_HEIGHT_TARGET = 3, + ROTATING_TARGET = 4, + DROPPING_TARGET = 5 + }; + enum CraneStatus : uint8 { + NONE = 0, + ACTIVATED = 1, + DEACTIVATED = 2 + }; CEntity *m_pObject; CObject *m_pMagnet; - int m_nAudioEntity; + int32 m_nAudioEntity; float m_fPickupX1; float m_fPickupX2; float m_fPickupY1; @@ -31,15 +46,25 @@ public: float m_fHookVelocityX; float m_fHookVelocityY; CVehicle *m_pVehiclePickedUp; - int m_nUpdateTimer; - char m_bCraneActive; - char m_bCraneStatus; - char m_bVehiclesCollected; - char m_bIsCrusher; - char m_bIsMilitaryCrane; - char field_125; - char m_bNotMilitaryCrane; - char gap_127[1]; + uint32 m_nUpdateTimer; + CraneStatus m_bCraneStatus; + CraneState m_bCraneState; + uint8 m_bVehiclesCollected; + 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 FindCarInSectorList(CPtrList*); + bool DoesCranePickUpThisCarType(uint32); + bool GoTowardsTarget(float, float, float, float); + bool GoTowardsHeightTarget(float, float); + void FindParametersForTarget(float, float, float, float*, float*, float*); + void CalcHookCoordinates(float*, float*, float*); + void SetHookMatrix(); }; static_assert(sizeof(CCrane) == 128, "CCrane: error"); @@ -47,15 +72,22 @@ static_assert(sizeof(CCrane) == 128, "CCrane: error"); class CCranes { public: - static bool IsThisCarBeingTargettedByAnyCrane(CVehicle*); - static bool IsThisCarBeingCarriedByAnyCrane(CVehicle*); - static bool IsThisCarPickedUp(float, float, CVehicle*); - static bool HaveAllCarsBeenCollectedByMilitaryCrane(); + static void InitCranes(void); + static void AddThisOneCrane(CEntity*); static void ActivateCrane(float, float, float, float, float, float, float, float, bool, bool, float, float); static void DeActivateCrane(float, float); - static void InitCranes(void); + static bool IsThisCarPickedUp(float, float, CVehicle*); static void UpdateCranes(void); + static bool DoesMilitaryCraneHaveThisOneAlready(uint32); + static void RegisterCarForMilitaryCrane(uint32); + static bool HaveAllCarsBeenCollectedByMilitaryCrane(); + static bool IsThisCarBeingCarriedByAnyCrane(CVehicle*); + static bool IsThisCarBeingTargettedByAnyCrane(CVehicle*); static void Save(uint8*, uint32*); + + static int32& CarsCollectedMilitaryCrane; + static int32& NumCranes; + static CCrane(&aCranes)[NUM_CRANES]; }; void CranesLoad(uint8*, uint32); // is this really outside CCranes? diff --git a/src/core/config.h b/src/core/config.h index 58885e57..784a9b95 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -118,7 +118,9 @@ enum Config { NUM_AUDIO_REFLECTIONS = 5, NUM_SCRIPT_MAX_ENTITIES = 40, - NUM_GARAGE_STORED_CARS = 6 + NUM_GARAGE_STORED_CARS = 6, + + NUM_CRANES = 8 }; // We'll use this once we're ready to become independent of the game diff --git a/src/math/Matrix.h b/src/math/Matrix.h index da38cb1d..35972e7f 100644 --- a/src/math/Matrix.h +++ b/src/math/Matrix.h @@ -243,6 +243,17 @@ public: m_matrix.pos.y = 0.0f; m_matrix.pos.z = 0.0f; } + void ResetOrientation(void) { + m_matrix.right.x = 1.0f; + m_matrix.right.y = 0.0f; + m_matrix.right.z = 0.0f; + m_matrix.up.x = 0.0f; + m_matrix.up.y = 1.0f; + m_matrix.up.z = 0.0f; + m_matrix.at.x = 0.0f; + m_matrix.at.y = 0.0f; + m_matrix.at.z = 1.0f; + } };