diff --git a/src/Collision.h b/src/Collision.h index 9d23524f..53a314bd 100644 --- a/src/Collision.h +++ b/src/Collision.h @@ -22,6 +22,7 @@ struct CColBox uint8 piece; void Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece); + CVector GetSize(void) { return max - min; } }; struct CColLine diff --git a/src/World.cpp b/src/World.cpp index 761d8bf6..c657be78 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -9,6 +9,11 @@ CSector (*CWorld::ms_aSectors)[NUMSECTORS_X] = (CSector (*)[NUMSECTORS_Y])0x6656 uint16 &CWorld::ms_nCurrentScanCode = *(uint16*)0x95CC64; bool &CWorld::bNoMoreCollisionTorque = *(bool*)0x95CDCC; +CEntity *&CWorld::pIgnoreEntity = *(CEntity**)0x8F6494; +bool &CWorld::bSecondShift = *(bool*)0x95CD54; +bool &CWorld::bForceProcessControl = *(bool*)0x95CD6C; +bool &CWorld::bProcessCutsceneOnly = *(bool*)0x95CD8B; + void CWorld::ClearScanCodes(void) diff --git a/src/World.h b/src/World.h index ecd8feb3..309f407c 100644 --- a/src/World.h +++ b/src/World.h @@ -32,6 +32,8 @@ public: }; static_assert(sizeof(CSector) == 0x28, "CSector: error"); +class CEntity; + class CWorld { static CPtrList *ms_bigBuildingsList; // [4]; @@ -40,7 +42,12 @@ class CWorld static uint16 &ms_nCurrentScanCode; public: + static CEntity *&pIgnoreEntity; static bool &bNoMoreCollisionTorque; + static bool &bSecondShift; + static bool &bForceProcessControl; + static bool &bProcessCutsceneOnly; + static CSector *GetSector(int x, int y) { return &ms_aSectors[y][x]; } static CPtrList &GetBigBuildingList(eLevelName i) { return ms_bigBuildingsList[i]; } diff --git a/src/entities/Object.h b/src/entities/Object.h index 6992b92d..5d648e07 100644 --- a/src/entities/Object.h +++ b/src/entities/Object.h @@ -8,6 +8,8 @@ enum { TEMP_OBJECT = 3, }; +class CVehicle; + class CObject : public CPhysical { public: @@ -39,7 +41,7 @@ public: int8 field_186; int8 field_187; CEntity *m_pCurSurface; - CEntity *field_18C; + CVehicle *m_pCollidingVehicle; int8 m_colour1, m_colour2; static void *operator new(size_t); diff --git a/src/entities/Ped.h b/src/entities/Ped.h index fd71b616..fe448a94 100644 --- a/src/entities/Ped.h +++ b/src/entities/Ped.h @@ -24,7 +24,10 @@ public: bool bInVehicle; uint8 stuff4[23]; int32 m_nPedType; - uint8 stuff5[528]; + + uint8 stuff5[28]; + CVehicle *m_pCollidingVehicle; + uint8 stuff6[496]; bool IsPlayer(void) { return m_nPedType == 0 || m_nPedType== 1 || m_nPedType == 2 || m_nPedType == 3; } }; @@ -32,4 +35,5 @@ static_assert(offsetof(CPed, m_nPedState) == 0x224, "CPed: error"); static_assert(offsetof(CPed, m_pCurSurface) == 0x2FC, "CPed: error"); static_assert(offsetof(CPed, m_pMyVehicle) == 0x310, "CPed: error"); static_assert(offsetof(CPed, m_nPedType) == 0x32C, "CPed: error"); +static_assert(offsetof(CPed, m_pCollidingVehicle) == 0x34C, "CPed: error"); static_assert(sizeof(CPed) == 0x540, "CPed: error"); diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp index f235cb42..c528444e 100644 --- a/src/entities/Physical.cpp +++ b/src/entities/Physical.cpp @@ -787,6 +787,17 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint) } +// ProcessCollision calls +// CheckCollision +// CheckCollision_SimpleCar +// CheckCollision calls +// ProcessCollisionSectorList +// CheckCollision_SimpleCar +// ProcessCollisionSectorList_SimpleCar +// ProcessShift calls +// ProcessCollisionSectorList +// ProcessShiftSectorList + void CPhysical::AddCollisionRecord(CEntity *ent) { @@ -839,6 +850,168 @@ CPhysical::GetHasCollidedWith(CEntity *ent) return false; } +bool +CPhysical::ProcessShiftSectorList(CPtrList *lists) +{ + int i, j; + CPtrList *list; + CPtrNode *node; + CPhysical *A, *B; + CObject *Bobj; + bool canshift; + CVector center; + float radius; + + int numCollisions; + int mostColliding; + CColPoint colpoints[32]; + CVector shift = { 0.0f, 0.0f, 0.0f }; + bool doShift = false; + CEntity *boat = nil; + + bool skipShift; + + A = this; + + A->GetBoundCentre(center); + radius = A->GetBoundRadius(); + for(i = 0; i <= ENTITYLIST_PEDS_OVERLAP; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + B = (CPhysical*)node->item; + Bobj = (CObject*)B; + skipShift = false; + + if(B->IsBuilding() || + B->IsObject() && B->bInfiniteMass) + canshift = true; + else + canshift = A->IsPed() && + B->IsObject() && B->bInfiniteMass && !Bobj->bHasBeenDamaged; + if(B == A || + B->m_scanCode == CWorld::GetCurrentScanCode() || + !B->bUsesCollision || + (A->bHasHitWall && !canshift) || + !B->GetIsTouching(center, radius)) + continue; + + // This could perhaps be done a bit nicer + + if(B->IsBuilding()) + skipShift = false; + else if(IsTrafficLight(A->GetModelIndex()) && + (B->IsVehicle() || B->IsPed()) && + A->GetUp().z < 0.66f) + skipShift = true; + else if((A->IsVehicle() || A->IsPed()) && + B->GetUp().z < 0.66f && + IsTrafficLight(B->GetModelIndex())) + skipShift = true; + else if(A->IsObject() && B->IsVehicle()){ + CObject *Aobj = (CObject*)A; + if(Aobj->ObjectCreatedBy != TEMP_OBJECT && + !Aobj->bHasBeenDamaged && + Aobj->bIsStatic){ + if(Aobj->m_pCollidingVehicle == B) + Aobj->m_pCollidingVehicle = nil; + }else if(Aobj->m_pCollidingVehicle != B){ + CMatrix inv; + CVector size = CModelInfo::GetModelInfo(A->GetModelIndex())->GetColModel()->boundingBox.GetSize(); + size = A->GetMatrix() * size; + if(size.z < B->GetPosition().z || + (Invert(B->GetMatrix(), inv) * size).z < 0.0f){ + skipShift = true; + Aobj->m_pCollidingVehicle = (CVehicle*)B; + } + } + }else if(B->IsObject() && A->IsVehicle()){ + CObject *Bobj = (CObject*)B; + if(Bobj->ObjectCreatedBy != TEMP_OBJECT && + !Bobj->bHasBeenDamaged && + Bobj->bIsStatic){ + if(Bobj->m_pCollidingVehicle == A) + Bobj->m_pCollidingVehicle = nil; + }else if(Bobj->m_pCollidingVehicle != A){ + CMatrix inv; + CVector size = CModelInfo::GetModelInfo(B->GetModelIndex())->GetColModel()->boundingBox.GetSize(); + size = B->GetMatrix() * size; + if(size.z < A->GetPosition().z || + (Invert(A->GetMatrix(), inv) * size).z < 0.0f){ + skipShift = true; + Bobj->m_pCollidingVehicle = (CVehicle*)A; + } + } + }else if(IsBodyPart(A->GetModelIndex()) && B->IsPed()) + skipShift = true; + else if(A->IsPed() && IsBodyPart(B->GetModelIndex())) + skipShift = true; + else if(A->IsPed() && ((CPed*)A)->m_pCollidingVehicle == B || + B->IsPed() && ((CPed*)B)->m_pCollidingVehicle == A || + A->GetModelIndex() == MI_RCBANDIT && B->IsVehicle() || + B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())) + skipShift = true; + + if(skipShift) + continue; + + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, colpoints); + if(numCollisions <= 0) + continue; + + mostColliding = 0; + for(j = 1; j < numCollisions; j++) + if(colpoints[j].depth > colpoints[mostColliding].depth) + mostColliding = j; + + if(CWorld::bSecondShift) + for(j = 0; j < numCollisions; j++) + shift += colpoints[j].normal * colpoints[j].depth * 1.5f/numCollisions; + else + for(j = 0; j < numCollisions; j++) + shift += colpoints[j].normal * colpoints[j].depth * 1.2f/numCollisions; + + if(A->IsVehicle() && B->IsVehicle()){ + CVector dir = A->GetPosition() - B->GetPosition(); + dir.Normalise(); + if(dir.z < 0.0f && dir.z < A->GetForward().z && dir.z < A->GetRight().z) + dir.z = min(0.0f, min(A->GetForward().z, A->GetRight().z)); + shift += dir * colpoints[mostColliding].depth * 0.5f; + }else if(A->IsPed() && B->IsVehicle() && ((CVehicle*)B)->IsBoat()){ + CVector dir = colpoints[mostColliding].normal; + float f = min(fabs(dir.z), 0.9f); + dir.z = 0.0f; + dir.Normalise(); + shift += dir * colpoints[mostColliding].depth / (1.0f - f); + boat = B; + }else if(B->IsPed() && A->IsVehicle() && ((CVehicle*)A)->IsBoat()){ + CVector dir = colpoints[mostColliding].normal * -1.0f; + float f = min(fabs(dir.z), 0.9f); + dir.z = 0.0f; + dir.Normalise(); + B->GetPosition() += dir * colpoints[mostColliding].depth / (1.0f - f); + // BUG? how can that ever happen? A is a Ped + if(B->IsVehicle()) + B->ProcessEntityCollision(A, colpoints); + }else{ + if(CWorld::bSecondShift) + shift += colpoints[mostColliding].normal * colpoints[mostColliding].depth * 0.4f; + else + shift += colpoints[mostColliding].normal * colpoints[mostColliding].depth * 0.2f; + } + + doShift = true; + } + } + + if(!doShift) + return false; + GetPosition() += shift; + if(boat) + ProcessEntityCollision(boat, colpoints); + return true; +} + void CPhysical::ProcessControl(void) { @@ -897,6 +1070,8 @@ STARTPATCHES InjectHook(0x4970C0, &CPhysical::AddCollisionRecord_Treadable, PATCH_JUMP); InjectHook(0x497240, &CPhysical::GetHasCollidedWith, PATCH_JUMP); + InjectHook(0x49DA10, &CPhysical::ProcessShiftSectorList, PATCH_JUMP); + #define F3 float, float, float InjectHook(0x495B10, &CPhysical::ApplyMoveSpeed, PATCH_JUMP); InjectHook(0x497280, &CPhysical::ApplyTurnSpeed, PATCH_JUMP); diff --git a/src/entities/Physical.h b/src/entities/Physical.h index 681ab5c8..9867c33c 100644 --- a/src/entities/Physical.h +++ b/src/entities/Physical.h @@ -77,6 +77,8 @@ public: CRect GetBoundRect(void); void ProcessControl(void); + virtual int32 ProcessEntityCollision(CEntity *ent, CColPoint *point); + void RemoveAndAdd(void); void AddToMovingList(void); void RemoveFromMovingList(void); @@ -128,6 +130,8 @@ public: void AddCollisionRecord_Treadable(CEntity *ent); bool GetHasCollidedWith(CEntity *ent); + bool ProcessShiftSectorList(CPtrList *ptrlists); + // to make patching virtual functions possible void Add_(void) { CPhysical::Add(); } void Remove_(void) { CPhysical::Remove(); } diff --git a/src/entities/Vehicle.h b/src/entities/Vehicle.h index 598b4a57..1a43e075 100644 --- a/src/entities/Vehicle.h +++ b/src/entities/Vehicle.h @@ -16,6 +16,12 @@ public: CEntity *m_pCurSurface; uint8 stuff3[160]; int32 m_vehType; + + bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; } + bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; } + bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } + bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } + bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } }; static_assert(sizeof(CVehicle) == 0x288, "CVehicle: error"); static_assert(offsetof(CVehicle, m_pCurSurface) == 0x1E0, "CVehicle: error"); diff --git a/src/modelinfo/ModelIndices.h b/src/modelinfo/ModelIndices.h index 5de10558..f008543e 100644 --- a/src/modelinfo/ModelIndices.h +++ b/src/modelinfo/ModelIndices.h @@ -205,6 +205,16 @@ enum MI_RHINO = 122, MI_COACH = 127, + MI_RCBANDIT = 131, + + MI_CAR_DOOR = 190, + MI_CAR_BUMPER, + MI_CAR_PANEL, + MI_CAR_BONNET, + MI_CAR_BOOT, + MI_CAR_WEEL, + MI_BODYPARTA, + MI_BODYPARTB, }; void InitModelIndices(void); @@ -222,3 +232,19 @@ IsGlass(int16 id) id == MI_GLASS7 || id == MI_GLASS8; } + +inline bool +IsTrafficLight(int16 id) +{ + return id == MI_TRAFFICLIGHTS || + id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_DOUBLESTREETLIGHTS; +} + +inline bool +IsBodyPart(int16 id) +{ + return id == MI_BODYPARTA || id == MI_BODYPARTB; +} diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index fd949cee..5dc5959a 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -195,7 +195,7 @@ CRenderer::RenderEverythingBarRoads(void) if(e->IsVehicle() || e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){ - if(e->IsVehicle() && ((CVehicle*)e)->m_vehType == VEHICLE_TYPE_BOAT){ + if(e->IsVehicle() && ((CVehicle*)e)->IsBoat()){ dist = ms_vecCameraPosition - e->GetPosition(); if(!CVisibilityPlugins::InsertEntityIntoSortedList(e, dist.Magnitude())){ printf("Ran out of space in alpha entity list"); @@ -221,7 +221,7 @@ CRenderer::RenderVehiclesButNotBoats(void) node != &gSortedVehiclesAndPeds.head; node = node->prev){ CVehicle *v = (CVehicle*)node->item.ent; - if(v->IsVehicle() && v->m_vehType == VEHICLE_TYPE_BOAT) // BUG: missing in III + if(v->IsVehicle() && v->IsBoat()) // BUG: IsVehicle missing in III continue; RenderOneNonRoad(v); } @@ -236,9 +236,7 @@ CRenderer::RenderBoats(void) node != &gSortedVehiclesAndPeds.head; node = node->prev){ CVehicle *v = (CVehicle*)node->item.ent; - if(!v->IsVehicle()) // BUG: missing in III - continue; - if(v->m_vehType == VEHICLE_TYPE_BOAT) + if(v->IsVehicle() && v->IsBoat()) // BUG: IsVehicle missing in III RenderOneNonRoad(v); } }