summaryrefslogtreecommitdiff
path: root/src/entities
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
committerclaude-bot <[email protected]>2026-07-13 12:27:07 +0000
commit9f61c9e6ac6b1ac5692cf6352d2ebbd47a31a686 (patch)
treea84756b82513739a2672db3a1f0ec579db6d18ff /src/entities
downloadre3-miami.tar.gz
re3-miami.zip
Import Cai1Hsu/re3 @ miami (reVC / GTA:VC decompilation)HEADmiami
Snapshot import (no upstream history) into git.ancap.in.ua/claude, per @lzcnt. Source: https://github.com/Cai1Hsu/re3 branch miami.
Diffstat (limited to 'src/entities')
-rw-r--r--src/entities/Dummy.cpp67
-rw-r--r--src/entities/Dummy.h19
-rw-r--r--src/entities/Entity.cpp901
-rw-r--r--src/entities/Entity.h184
-rw-r--r--src/entities/Physical.cpp2310
-rw-r--r--src/entities/Physical.h177
6 files changed, 3658 insertions, 0 deletions
diff --git a/src/entities/Dummy.cpp b/src/entities/Dummy.cpp
new file mode 100644
index 0000000..9878b59
--- /dev/null
+++ b/src/entities/Dummy.cpp
@@ -0,0 +1,67 @@
+#include "common.h"
+
+#include "Pools.h"
+#include "World.h"
+#include "Dummy.h"
+
+void *CDummy::operator new(size_t sz) { return CPools::GetDummyPool()->New(); }
+void CDummy::operator delete(void *p, size_t sz) { CPools::GetDummyPool()->Delete((CDummy*)p); }
+
+void
+CDummy::Add(void)
+{
+ int x, xstart, xmid, xend;
+ int y, ystart, ymid, yend;
+ CSector *s;
+ CPtrList *list;
+
+ CRect bounds = GetBoundRect();
+ xstart = CWorld::GetSectorIndexX(bounds.left);
+ xend = CWorld::GetSectorIndexX(bounds.right);
+ xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
+ ystart = CWorld::GetSectorIndexY(bounds.top);
+ yend = CWorld::GetSectorIndexY(bounds.bottom);
+ ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
+ assert(xstart >= 0);
+ assert(xend < NUMSECTORS_X);
+ assert(ystart >= 0);
+ assert(yend < NUMSECTORS_Y);
+
+ for(y = ystart; y <= yend; y++)
+ for(x = xstart; x <= xend; x++){
+ s = CWorld::GetSector(x, y);
+ if(x == xmid && y == ymid)
+ list = &s->m_lists[ENTITYLIST_DUMMIES];
+ else
+ list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
+ CPtrNode *node = list->InsertItem(this);
+ assert(node);
+ m_entryInfoList.InsertItem(list, node, s);
+ }
+}
+
+void
+CDummy::Remove(void)
+{
+ CEntryInfoNode *node, *next;
+ for(node = m_entryInfoList.first; node; node = next){
+ next = node->next;
+ node->list->DeleteNode(node->listnode);
+ m_entryInfoList.DeleteNode(node);
+ }
+}
+
+bool
+IsDummyPointerValid(CDummy* pDummy)
+{
+ if (!pDummy)
+ return false;
+ int index = CPools::GetDummyPool()->GetJustIndex_NoFreeAssert(pDummy);
+#ifdef FIX_BUGS
+ if (index < 0 || index >= CPools::GetDummyPool()->GetSize())
+#else
+ if (index < 0 || index > CPools::GetDummyPool()->GetSize())
+#endif
+ return false;
+ return pDummy->m_entryInfoList.first;
+}
diff --git a/src/entities/Dummy.h b/src/entities/Dummy.h
new file mode 100644
index 0000000..84b1ce1
--- /dev/null
+++ b/src/entities/Dummy.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Lists.h"
+#include "Entity.h"
+
+class CDummy : public CEntity
+{
+public:
+ CEntryInfoList m_entryInfoList;
+
+ CDummy(void) { m_type = ENTITY_TYPE_DUMMY; }
+ void Add(void);
+ void Remove(void);
+
+ static void *operator new(size_t);
+ static void operator delete(void*, size_t);
+};
+
+bool IsDummyPointerValid(CDummy* pDummy);
diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp
new file mode 100644
index 0000000..6204b74
--- /dev/null
+++ b/src/entities/Entity.cpp
@@ -0,0 +1,901 @@
+#include "common.h"
+
+#include "General.h"
+#include "RwHelper.h"
+#include "ModelIndices.h"
+#include "Timer.h"
+#include "Entity.h"
+#include "Object.h"
+#include "World.h"
+#include "Camera.h"
+#include "Glass.h"
+#include "Weather.h"
+#include "Timecycle.h"
+#include "TrafficLights.h"
+#include "Coronas.h"
+#include "PointLights.h"
+#include "Shadows.h"
+#include "Pickups.h"
+#include "SpecialFX.h"
+#include "TxdStore.h"
+#include "Zones.h"
+#include "MemoryHeap.h"
+#include "Bones.h"
+#include "Debug.h"
+#include "Ped.h"
+#include "Dummy.h"
+#include "WindModifiers.h"
+
+int gBuildings;
+
+CEntity::CEntity(void)
+{
+ m_type = ENTITY_TYPE_NOTHING;
+ m_status = STATUS_ABANDONED;
+
+ bUsesCollision = false;
+ bCollisionProcessed = false;
+ bIsStatic = false;
+ bHasContacted = false;
+ bPedPhysics = false;
+ bIsStuck = false;
+ bIsInSafePosition = false;
+ bUseCollisionRecords = false;
+
+ bWasPostponed = false;
+ bExplosionProof = false;
+ bIsVisible = true;
+ bHasCollided = false;
+ bRenderScorched = false;
+ bHasBlip = false;
+ bIsBIGBuilding = false;
+ bStreamBIGBuilding = false;
+
+ bRenderDamaged = false;
+ bBulletProof = false;
+ bFireProof = false;
+ bCollisionProof = false;
+ bMeleeProof = false;
+ bOnlyDamagedByPlayer = false;
+ bStreamingDontDelete = false;
+ bRemoveFromWorld = false;
+
+ bHasHitWall = false;
+ bImBeingRendered = false;
+ bTouchingWater = false;
+ bIsSubway = false;
+ bDrawLast = false;
+ bNoBrightHeadLights = false;
+ bDoNotRender = false;
+ bDistanceFade = false;
+
+ m_flagE1 = false;
+ m_flagE2 = false;
+ bOffscreen = false;
+ bIsStaticWaitingForCollision = false;
+ bDontStream = false;
+ bUnderwater = false;
+ bHasPreRenderEffects = false;
+
+ m_scanCode = 0;
+ m_modelIndex = -1;
+ m_rwObject = nil;
+ m_area = AREA_MAIN_MAP;
+ m_randomSeed = CGeneral::GetRandomNumber();
+ m_pFirstReference = nil;
+}
+
+CEntity::~CEntity(void)
+{
+ DeleteRwObject();
+ ResolveReferences();
+}
+
+void
+CEntity::SetModelIndex(uint32 id)
+{
+ m_modelIndex = id;
+ bHasPreRenderEffects = HasPreRenderEffects();
+ CreateRwObject();
+}
+
+void
+CEntity::SetModelIndexNoCreate(uint32 id)
+{
+ m_modelIndex = id;
+ bHasPreRenderEffects = HasPreRenderEffects();
+}
+
+void
+CEntity::CreateRwObject(void)
+{
+ CBaseModelInfo *mi;
+
+ mi = CModelInfo::GetModelInfo(m_modelIndex);
+
+ PUSH_MEMID(MEMID_WORLD);
+ m_rwObject = mi->CreateInstance();
+ POP_MEMID();
+
+ if(m_rwObject){
+ if(IsBuilding())
+ gBuildings++;
+ if(RwObjectGetType(m_rwObject) == rpATOMIC)
+ m_matrix.AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)), false);
+ else if(RwObjectGetType(m_rwObject) == rpCLUMP)
+ m_matrix.AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)m_rwObject)), false);
+
+ mi->AddRef();
+ }
+}
+
+void
+CEntity::AttachToRwObject(RwObject *obj)
+{
+ m_rwObject = obj;
+ if(m_rwObject){
+ if(RwObjectGetType(m_rwObject) == rpATOMIC)
+ m_matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)), false);
+ else if(RwObjectGetType(m_rwObject) == rpCLUMP)
+ m_matrix.Attach(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)m_rwObject)), false);
+
+ CModelInfo::GetModelInfo(m_modelIndex)->AddRef();
+ }
+}
+
+void
+CEntity::DetachFromRwObject(void)
+{
+ if(m_rwObject)
+ CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef();
+ m_rwObject = nil;
+ m_matrix.Detach();
+}
+
+RpAtomic*
+AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data)
+{
+ if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))){
+ RpHAnimHierarchy *hier = RpSkinAtomicGetHAnimHierarchy(atomic);
+#ifdef LIBRW
+ if(hier && hier->interpolator->currentAnim){
+ RpHAnimAnimationDestroy(hier->interpolator->currentAnim);
+ hier->interpolator->currentAnim = nil;
+ }
+#else
+ if(hier && hier->currentAnim){
+ RpHAnimAnimationDestroy(hier->currentAnim->pCurrentAnim);
+ hier->currentAnim = nil;
+ }
+#endif
+ }
+ return atomic;
+}
+
+void
+CEntity::DeleteRwObject(void)
+{
+ RwFrame *f;
+
+ m_matrix.Detach();
+ if(m_rwObject){
+ if(RwObjectGetType(m_rwObject) == rpATOMIC){
+ f = RpAtomicGetFrame((RpAtomic*)m_rwObject);
+ RpAtomicDestroy((RpAtomic*)m_rwObject);
+ RwFrameDestroy(f);
+ }else if(RwObjectGetType(m_rwObject) == rpCLUMP){
+ if(IsClumpSkinned((RpClump*)m_rwObject))
+ RpClumpForAllAtomics((RpClump*)m_rwObject, AtomicRemoveAnimFromSkinCB, nil);
+ RpClumpDestroy((RpClump*)m_rwObject);
+ }
+ m_rwObject = nil;
+ CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef();
+ if(IsBuilding())
+ gBuildings--;
+ }
+}
+
+CRect
+CEntity::GetBoundRect(void)
+{
+ CRect rect;
+ CVector v;
+ CColModel *col = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel();
+
+ rect.ContainPoint(m_matrix * col->boundingBox.min);
+ rect.ContainPoint(m_matrix * col->boundingBox.max);
+
+ v = col->boundingBox.min;
+ v.x = col->boundingBox.max.x;
+ rect.ContainPoint(m_matrix * v);
+
+ v = col->boundingBox.max;
+ v.x = col->boundingBox.min.x;
+ rect.ContainPoint(m_matrix * v);
+
+ return rect;
+}
+
+CVector
+CEntity::GetBoundCentre(void)
+{
+ CVector v;
+ GetBoundCentre(v);
+ return v;
+}
+
+void
+CEntity::GetBoundCentre(CVector &out)
+{
+ out = m_matrix * CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.center;
+}
+
+float
+CEntity::GetBoundRadius(void)
+{
+ return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.radius;
+}
+
+void
+CEntity::UpdateRwFrame(void)
+{
+ if(m_rwObject)
+ RwFrameUpdateObjects((RwFrame*)rwObjectGetParent(m_rwObject));
+}
+
+void
+CEntity::UpdateRpHAnim(void)
+{
+ if(IsClumpSkinned(GetClump())){
+ RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump());
+ RpHAnimHierarchyUpdateMatrices(hier);
+#if 0
+ int i;
+ char buf[256];
+ if(this == (CEntity*)FindPlayerPed())
+ for(i = 0; i < hier->numNodes; i++){
+ RpHAnimStdInterpFrame *kf = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i);
+ sprintf(buf, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %d %s",
+ kf->q.imag.x, kf->q.imag.y, kf->q.imag.z, kf->q.real,
+ kf->t.x, kf->t.y, kf->t.z,
+ HIERNODEID(hier, i),
+ ConvertBoneTag2BoneName(HIERNODEID(hier, i)));
+ CDebug::PrintAt(buf, 10, 1+i*3);
+
+ RwMatrix *m = &RpHAnimHierarchyGetMatrixArray(hier)[i];
+ sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
+ m->right.x, m->up.x, m->at.x, m->pos.x);
+ CDebug::PrintAt(buf, 80, 1+i*3+0);
+ sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
+ m->right.y, m->up.y, m->at.y, m->pos.y);
+ CDebug::PrintAt(buf, 80, 1+i*3+1);
+ sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
+ m->right.z, m->up.z, m->at.z, m->pos.z);
+ CDebug::PrintAt(buf, 80, 1+i*3+2);
+ }
+
+ void RenderSkeleton(RpHAnimHierarchy *hier);
+ RenderSkeleton(hier);
+#endif
+ }
+}
+
+bool
+CEntity::HasPreRenderEffects(void)
+{
+ return IsTreeModel(GetModelIndex()) ||
+ GetModelIndex() == MI_COLLECTABLE1 ||
+ GetModelIndex() == MI_MONEY ||
+ GetModelIndex() == MI_CARMINE ||
+ GetModelIndex() == MI_NAUTICALMINE ||
+ GetModelIndex() == MI_BRIEFCASE ||
+ GetModelIndex() == MI_GRENADE ||
+ GetModelIndex() == MI_MOLOTOV ||
+ GetModelIndex() == MI_MISSILE ||
+ GetModelIndex() == MI_BEACHBALL ||
+ IsGlass(GetModelIndex()) ||
+ IsObject() && ((CObject*)this)->bIsPickup ||
+ IsLightWithPreRenderEffects(GetModelIndex());
+}
+
+void
+CEntity::PreRender(void)
+{
+ if (CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects() != 0)
+ ProcessLightsForEntity();
+
+ if(!bHasPreRenderEffects)
+ return;
+
+ switch(m_type){
+ case ENTITY_TYPE_BUILDING:
+ if(IsTreeModel(GetModelIndex())){
+ float dist = (TheCamera.GetPosition() - GetPosition()).Magnitude2D();
+ CObject::fDistToNearestTree = Min(CObject::fDistToNearestTree, dist);
+ ModifyMatrixForTreeInWind();
+ }
+ break;
+ case ENTITY_TYPE_OBJECT:
+ if(GetModelIndex() == MI_COLLECTABLE1){
+ CPickups::DoCollectableEffects(this);
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+ }else if(GetModelIndex() == MI_MONEY){
+ CPickups::DoMoneyEffects(this);
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+ }else if(GetModelIndex() == MI_NAUTICALMINE ||
+ GetModelIndex() == MI_CARMINE ||
+ GetModelIndex() == MI_BRIEFCASE){
+ if(((CObject*)this)->bIsPickup){
+ CPickups::DoMineEffects(this);
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+ }
+ }else if(GetModelIndex() == MI_MISSILE){
+ CVector pos = GetPosition();
+ float flicker = (CGeneral::GetRandomNumber() & 0xF)/(float)0x10;
+ CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE,
+ gpShadowExplosionTex, &pos,
+ 8.0f, 0.0f, 0.0f, -8.0f,
+ 255, 200.0f*flicker, 160.0f*flicker, 120.0f*flicker,
+ 20.0f, false, 1.0f, nil, false);
+ CPointLights::AddLight(CPointLights::LIGHT_POINT,
+ pos, CVector(0.0f, 0.0f, 0.0f),
+ 8.0f,
+ 1.0f*flicker,
+ 0.8f*flicker,
+ 0.6f*flicker,
+ CPointLights::FOG_NONE, true);
+ CCoronas::RegisterCorona((uintptr)this,
+ 255.0f*flicker, 220.0f*flicker, 190.0f*flicker, 255,
+ pos, 6.0f*flicker, 80.0f, gpCoronaTexture[CCoronas::TYPE_STAR],
+ CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ }else if(IsGlass(GetModelIndex())){
+ PreRenderForGlassWindow();
+ }else if (((CObject*)this)->bIsPickup) {
+ CPickups::DoPickUpEffects(this);
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+ } else if (GetModelIndex() == MI_GRENADE) {
+ CMotionBlurStreaks::RegisterStreak((uintptr)this,
+ 100, 100, 100,
+ GetPosition() - 0.07f * TheCamera.GetRight(),
+ GetPosition() + 0.07f * TheCamera.GetRight());
+ } else if (GetModelIndex() == MI_MOLOTOV) {
+ CMotionBlurStreaks::RegisterStreak((uintptr)this,
+ 0, 100, 0,
+ GetPosition() - 0.07f * TheCamera.GetRight(),
+ GetPosition() + 0.07f * TheCamera.GetRight());
+ }else if(GetModelIndex() == MI_BEACHBALL){
+ CVector pos = GetPosition();
+ CShadows::StoreShadowToBeRendered(SHADOWTYPE_DARK,
+ gpShadowPedTex, &pos,
+ 0.4f, 0.0f, 0.0f, -0.4f,
+ CTimeCycle::GetShadowStrength(),
+ CTimeCycle::GetShadowStrength(),
+ CTimeCycle::GetShadowStrength(),
+ CTimeCycle::GetShadowStrength(),
+ 20.0f, false, 1.0f, nil, false);
+ }
+ // fall through
+ case ENTITY_TYPE_DUMMY:
+ if(GetModelIndex() == MI_TRAFFICLIGHTS){
+ CTrafficLights::DisplayActualLight(this);
+ CShadows::StoreShadowForPole(this, 2.957f, 0.147f, 0.0f, 16.0f, 0.4f, 0);
+ }else if(GetModelIndex() == MI_TRAFFICLIGHTS_VERTICAL){
+ CTrafficLights::DisplayActualLight(this);
+ }else if(GetModelIndex() == MI_TRAFFICLIGHTS_MIAMI){
+ CTrafficLights::DisplayActualLight(this);
+ CShadows::StoreShadowForPole(this, 4.819f, 1.315f, 0.0f, 16.0f, 0.4f, 0);
+ }else if(GetModelIndex() == MI_TRAFFICLIGHTS_TWOVERTICAL){
+ CTrafficLights::DisplayActualLight(this);
+ CShadows::StoreShadowForPole(this, 7.503f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
+ }else if(GetModelIndex() == MI_SINGLESTREETLIGHTS1)
+ CShadows::StoreShadowForPole(this, 0.744f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
+ else if(GetModelIndex() == MI_SINGLESTREETLIGHTS2)
+ CShadows::StoreShadowForPole(this, 0.043f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
+ else if(GetModelIndex() == MI_SINGLESTREETLIGHTS3)
+ CShadows::StoreShadowForPole(this, 1.143f, 0.145f, 0.0f, 16.0f, 0.4f, 0);
+ else if(GetModelIndex() == MI_DOUBLESTREETLIGHTS)
+ CShadows::StoreShadowForPole(this, 0.0f, -0.048f, 0.0f, 16.0f, 0.4f, 0);
+ break;
+ }
+}
+
+void
+CEntity::Render(void)
+{
+ if(m_rwObject){
+ bImBeingRendered = true;
+ if(RwObjectGetType(m_rwObject) == rpATOMIC)
+ RpAtomicRender((RpAtomic*)m_rwObject);
+ else
+ RpClumpRender((RpClump*)m_rwObject);
+ bImBeingRendered = false;
+ }
+}
+
+bool
+CEntity::GetIsTouching(CVector const &center, float radius)
+{
+ return sq(GetBoundRadius()+radius) > (GetBoundCentre()-center).MagnitudeSqr();
+}
+
+bool
+CEntity::IsVisible(void)
+{
+ return m_rwObject && bIsVisible && GetIsOnScreen();
+}
+
+bool
+CEntity::IsVisibleComplex(void)
+{
+ return m_rwObject && bIsVisible && GetIsOnScreenComplex();
+}
+
+bool
+CEntity::GetIsOnScreen(void)
+{
+ return TheCamera.IsSphereVisible(GetBoundCentre(), GetBoundRadius(),
+ &TheCamera.GetCameraMatrix());
+}
+
+bool
+CEntity::GetIsOnScreenComplex(void)
+{
+#ifdef GTA_PS2
+ CVuVector boundBox[8];
+#else
+ CVector boundBox[8];
+#endif
+
+ if(TheCamera.IsPointVisible(GetBoundCentre(), &TheCamera.GetCameraMatrix()))
+ return true;
+
+ CRect rect = GetBoundRect();
+ CColModel *colmodel = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel();
+ float z = GetPosition().z;
+ float minz = z + colmodel->boundingBox.min.z;
+ float maxz = z + colmodel->boundingBox.max.z;
+ boundBox[0].x = rect.left;
+ boundBox[0].y = rect.bottom;
+ boundBox[0].z = minz;
+ boundBox[1].x = rect.left;
+ boundBox[1].y = rect.top;
+ boundBox[1].z = minz;
+ boundBox[2].x = rect.right;
+ boundBox[2].y = rect.bottom;
+ boundBox[2].z = minz;
+ boundBox[3].x = rect.right;
+ boundBox[3].y = rect.top;
+ boundBox[3].z = minz;
+ boundBox[4].x = rect.left;
+ boundBox[4].y = rect.bottom;
+ boundBox[4].z = maxz;
+ boundBox[5].x = rect.left;
+ boundBox[5].y = rect.top;
+ boundBox[5].z = maxz;
+ boundBox[6].x = rect.right;
+ boundBox[6].y = rect.bottom;
+ boundBox[6].z = maxz;
+ boundBox[7].x = rect.right;
+ boundBox[7].y = rect.top;
+ boundBox[7].z = maxz;
+
+ return TheCamera.IsBoxVisible(boundBox, &TheCamera.GetCameraMatrix());
+}
+
+void
+CEntity::Add(void)
+{
+ int x, xstart, xmid, xend;
+ int y, ystart, ymid, yend;
+ CSector *s;
+ CPtrList *list;
+
+ CRect bounds = GetBoundRect();
+ xstart = CWorld::GetSectorIndexX(bounds.left);
+ xend = CWorld::GetSectorIndexX(bounds.right);
+ xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
+ ystart = CWorld::GetSectorIndexY(bounds.top);
+ yend = CWorld::GetSectorIndexY(bounds.bottom);
+ ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
+ assert(xstart >= 0);
+ assert(xend < NUMSECTORS_X);
+ assert(ystart >= 0);
+ assert(yend < NUMSECTORS_Y);
+
+ for(y = ystart; y <= yend; y++)
+ for(x = xstart; x <= xend; x++){
+ s = CWorld::GetSector(x, y);
+ if(x == xmid && y == ymid) switch(m_type){
+ case ENTITY_TYPE_BUILDING:
+ list = &s->m_lists[ENTITYLIST_BUILDINGS];
+ break;
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS];
+ break;
+ case ENTITY_TYPE_DUMMY:
+ list = &s->m_lists[ENTITYLIST_DUMMIES];
+ break;
+ }else switch(m_type){
+ case ENTITY_TYPE_BUILDING:
+ list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
+ break;
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
+ break;
+ case ENTITY_TYPE_DUMMY:
+ list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
+ break;
+ }
+ list->InsertItem(this);
+ }
+}
+
+void
+CEntity::Remove(void)
+{
+ int x, xstart, xmid, xend;
+ int y, ystart, ymid, yend;
+ CSector *s;
+ CPtrList *list;
+
+ CRect bounds = GetBoundRect();
+ xstart = CWorld::GetSectorIndexX(bounds.left);
+ xend = CWorld::GetSectorIndexX(bounds.right);
+ xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
+ ystart = CWorld::GetSectorIndexY(bounds.top);
+ yend = CWorld::GetSectorIndexY(bounds.bottom);
+ ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
+ assert(xstart >= 0);
+ assert(xend < NUMSECTORS_X);
+ assert(ystart >= 0);
+ assert(yend < NUMSECTORS_Y);
+
+ for(y = ystart; y <= yend; y++)
+ for(x = xstart; x <= xend; x++){
+ s = CWorld::GetSector(x, y);
+ if(x == xmid && y == ymid) switch(m_type){
+ case ENTITY_TYPE_BUILDING:
+ list = &s->m_lists[ENTITYLIST_BUILDINGS];
+ break;
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS];
+ break;
+ case ENTITY_TYPE_DUMMY:
+ list = &s->m_lists[ENTITYLIST_DUMMIES];
+ break;
+ }else switch(m_type){
+ case ENTITY_TYPE_BUILDING:
+ list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
+ break;
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
+ break;
+ case ENTITY_TYPE_DUMMY:
+ list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
+ break;
+ }
+ list->RemoveItem(this);
+ }
+}
+
+float
+CEntity::GetDistanceFromCentreOfMassToBaseOfModel(void)
+{
+ return -CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingBox.min.z;
+}
+
+void
+CEntity::SetupBigBuilding(void)
+{
+ CSimpleModelInfo *mi;
+
+ mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex);
+ bIsBIGBuilding = true;
+ bStreamingDontDelete = true;
+ bUsesCollision = false;
+ m_level = CTheZones::GetLevelFromPosition(&GetPosition());
+ if(mi->m_lodDistances[0] <= 2000.0f)
+ bStreamBIGBuilding = true;
+ if(mi->m_lodDistances[0] > 2500.0f || mi->m_ignoreDrawDist)
+ m_level = LEVEL_GENERIC;
+ else if(m_level == LEVEL_GENERIC)
+ printf("%s isn't in a level\n", mi->GetModelName());
+}
+
+float WindTabel[] = {
+ 1.0f, 0.5f, 0.2f, 0.7f, 0.4f, 1.0f, 0.5f, 0.3f,
+ 0.2f, 0.1f, 0.7f, 0.6f, 0.3f, 1.0f, 0.5f, 0.2f,
+};
+
+void
+CEntity::ModifyMatrixForTreeInWind(void)
+{
+ uint16 t;
+ float f;
+ float strength, flutter;
+
+ if(CTimer::GetIsPaused())
+ return;
+
+ CMatrix mat(GetMatrix().m_attachment);
+
+ if(CWeather::Wind >= 0.5){
+ t = m_randomSeed + 8*CTimer::GetTimeInMilliseconds();
+ f = (t & 0xFFF)/(float)0x1000;
+ flutter = f * WindTabel[(t>>12)+1 & 0xF] +
+ (1.0f - f) * WindTabel[(t>>12) & 0xF] +
+ 1.0f;
+ strength = -0.015f*CWeather::Wind;
+ }else if(CWeather::Wind >= 0.2){
+ t = (uintptr)this + CTimer::GetTimeInMilliseconds();
+ f = (t & 0xFFF)/(float)0x1000;
+ flutter = Sin(f * 6.28f);
+ strength = -0.008f;
+ }else{
+ t = (uintptr)this + CTimer::GetTimeInMilliseconds();
+ f = (t & 0xFFF)/(float)0x1000;
+ flutter = Sin(f * 6.28f);
+ strength = -0.005f;
+ }
+
+ mat.GetUp().x = strength * flutter;
+ if(IsPalmTreeModel(GetModelIndex()))
+ mat.GetUp().x += -0.07f*CWeather::Wind;
+ mat.GetUp().y = mat.GetUp().x;
+
+ CWindModifiers::FindWindModifier(GetPosition(), &mat.GetUp().x, &mat.GetUp().y);
+
+ mat.UpdateRW();
+ UpdateRwFrame();
+}
+
+float BannerWindTabel[] = {
+ 0.0f, 0.3f, 0.6f, 0.85f, 0.99f, 0.97f, 0.65f, 0.15f,
+ -0.1f, 0.0f, 0.35f, 0.57f, 0.55f, 0.35f, 0.45f, 0.67f,
+ 0.73f, 0.45f, 0.25f, 0.35f, 0.35f, 0.11f, 0.13f, 0.21f,
+ 0.28f, 0.28f, 0.22f, 0.1f, 0.0f, -0.1f, -0.17f, -0.12f
+};
+
+// unused
+void
+CEntity::ModifyMatrixForBannerInWind(void)
+{
+ uint16 t;
+ float f;
+ float strength, flutter;
+ CVector right, up;
+
+ if(CTimer::GetIsPaused())
+ return;
+
+ if(CWeather::Wind < 0.1f)
+ strength = 0.2f;
+ else if(CWeather::Wind < 0.8f)
+ strength = 0.43f;
+ else
+ strength = 0.66f;
+
+ t = ((int)(GetMatrix().GetPosition().x + GetMatrix().GetPosition().y) << 10) + 16*CTimer::GetTimeInMilliseconds();
+ f = (t & 0x7FF)/(float)0x800;
+ flutter = f * BannerWindTabel[(t>>11)+1 & 0x1F] +
+ (1.0f - f) * BannerWindTabel[(t>>11) & 0x1F];
+ flutter *= strength;
+
+ right = CrossProduct(GetForward(), GetUp());
+ right.z = 0.0f;
+ right.Normalise();
+ up = right * flutter;
+ up.z = Sqrt(sq(1.0f) - sq(flutter));
+ GetRight() = CrossProduct(GetForward(), up);
+ GetUp() = up;
+
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+}
+
+void
+CEntity::PreRenderForGlassWindow(void)
+{
+ if(((CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_isArtistGlass)
+ return;
+ CGlass::AskForObjectToBeRenderedInGlass(this);
+ bIsVisible = false;
+}
+
+/*
+0x487A10 - SetAtomicAlphaCB
+0x4879E0 - SetClumpAlphaCB
+*/
+
+RpMaterial*
+SetAtomicAlphaCB(RpMaterial *material, void *data)
+{
+ ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data;
+ return material;
+}
+
+RpAtomic*
+SetClumpAlphaCB(RpAtomic *atomic, void *data)
+{
+ RpGeometry *geometry = RpAtomicGetGeometry(atomic);
+ RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR);
+ RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)data);
+ return atomic;
+}
+
+void
+CEntity::SetRwObjectAlpha(int32 alpha)
+{
+ if (m_rwObject != nil) {
+ switch (RwObjectGetType(m_rwObject)) {
+ case rpATOMIC: {
+ RpGeometry *geometry = RpAtomicGetGeometry((RpAtomic*)m_rwObject);
+ RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR);
+ RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)alpha);
+ break;
+ }
+ case rpCLUMP:
+ RpClumpForAllAtomics((RpClump*)m_rwObject, SetClumpAlphaCB, (void*)alpha);
+ break;
+ }
+ }
+}
+
+bool IsEntityPointerValid(CEntity* pEntity)
+{
+ if (!pEntity)
+ return false;
+ switch (pEntity->GetType()) {
+ case ENTITY_TYPE_NOTHING: return false;
+ case ENTITY_TYPE_BUILDING: return IsBuildingPointerValid((CBuilding*)pEntity);
+ case ENTITY_TYPE_VEHICLE: return IsVehiclePointerValid((CVehicle*)pEntity);
+ case ENTITY_TYPE_PED: return IsPedPointerValid((CPed*)pEntity);
+ case ENTITY_TYPE_OBJECT: return IsObjectPointerValid((CObject*)pEntity);
+ case ENTITY_TYPE_DUMMY: return IsDummyPointerValid((CDummy*)pEntity);
+ }
+ return false;
+}
+
+#ifdef COMPATIBLE_SAVES
+void
+CEntity::SaveEntityFlags(uint8*& buf)
+{
+ uint32 tmp = 0;
+ tmp |= (m_type & (BIT(3) - 1));
+ tmp |= (m_status & (BIT(5) - 1)) << 3;
+
+ if (bUsesCollision) tmp |= BIT(8);
+ if (bCollisionProcessed) tmp |= BIT(9);
+ if (bIsStatic) tmp |= BIT(10);
+ if (bHasContacted) tmp |= BIT(11);
+ if (bPedPhysics) tmp |= BIT(12);
+ if (bIsStuck) tmp |= BIT(13);
+ if (bIsInSafePosition) tmp |= BIT(14);
+ if (bUseCollisionRecords) tmp |= BIT(15);
+
+ if (bWasPostponed) tmp |= BIT(16);
+ if (bExplosionProof) tmp |= BIT(17);
+ if (bIsVisible) tmp |= BIT(18);
+ if (bHasCollided) tmp |= BIT(19);
+ if (bRenderScorched) tmp |= BIT(20);
+ if (bHasBlip) tmp |= BIT(21);
+ if (bIsBIGBuilding) tmp |= BIT(22);
+ if (bStreamBIGBuilding) tmp |= BIT(23);
+
+ if (bRenderDamaged) tmp |= BIT(24);
+ if (bBulletProof) tmp |= BIT(25);
+ if (bFireProof) tmp |= BIT(26);
+ if (bCollisionProof) tmp |= BIT(27);
+ if (bMeleeProof) tmp |= BIT(28);
+ if (bOnlyDamagedByPlayer) tmp |= BIT(29);
+ if (bStreamingDontDelete) tmp |= BIT(30);
+ if (bRemoveFromWorld) tmp |= BIT(31);
+
+ WriteSaveBuf<uint32>(buf, tmp);
+
+ tmp = 0;
+
+ if (bHasHitWall) tmp |= BIT(0);
+ if (bImBeingRendered) tmp |= BIT(1);
+ if (bTouchingWater) tmp |= BIT(2);
+ if (bIsSubway) tmp |= BIT(3);
+ if (bDrawLast) tmp |= BIT(4);
+ if (bNoBrightHeadLights) tmp |= BIT(5);
+ if (bDoNotRender) tmp |= BIT(6);
+ if (bDistanceFade) tmp |= BIT(7);
+
+ if (m_flagE1) tmp |= BIT(8);
+ if (m_flagE2) tmp |= BIT(9);
+ if (bOffscreen) tmp |= BIT(10);
+ if (bIsStaticWaitingForCollision) tmp |= BIT(11);
+ if (bDontStream) tmp |= BIT(12);
+ if (bUnderwater) tmp |= BIT(13);
+ if (bHasPreRenderEffects) tmp |= BIT(14);
+
+ WriteSaveBuf<uint32>(buf, tmp);
+}
+
+void
+CEntity::LoadEntityFlags(uint8*& buf)
+{
+ uint32 tmp = ReadSaveBuf<uint32>(buf);
+ m_type = (tmp & ((BIT(3) - 1)));
+ m_status = ((tmp >> 3) & (BIT(5) - 1));
+
+ bUsesCollision = !!(tmp & BIT(8));
+ bCollisionProcessed = !!(tmp & BIT(9));
+ bIsStatic = !!(tmp & BIT(10));
+ bHasContacted = !!(tmp & BIT(11));
+ bPedPhysics = !!(tmp & BIT(12));
+ bIsStuck = !!(tmp & BIT(13));
+ bIsInSafePosition = !!(tmp & BIT(14));
+ bUseCollisionRecords = !!(tmp & BIT(15));
+
+ bWasPostponed = !!(tmp & BIT(16));
+ bExplosionProof = !!(tmp & BIT(17));
+ bIsVisible = !!(tmp & BIT(18));
+ bHasCollided = !!(tmp & BIT(19));
+ bRenderScorched = !!(tmp & BIT(20));
+ bHasBlip = !!(tmp & BIT(21));
+ bIsBIGBuilding = !!(tmp & BIT(22));
+ bStreamBIGBuilding = !!(tmp & BIT(23));
+
+ bRenderDamaged = !!(tmp & BIT(24));
+ bBulletProof = !!(tmp & BIT(25));
+ bFireProof = !!(tmp & BIT(26));
+ bCollisionProof = !!(tmp & BIT(27));
+ bMeleeProof = !!(tmp & BIT(28));
+ bOnlyDamagedByPlayer = !!(tmp & BIT(29));
+ bStreamingDontDelete = !!(tmp & BIT(30));
+ bRemoveFromWorld = !!(tmp & BIT(31));
+
+ tmp = ReadSaveBuf<uint32>(buf);
+
+ bHasHitWall = !!(tmp & BIT(0));
+ bImBeingRendered = !!(tmp & BIT(1));
+ bTouchingWater = !!(tmp & BIT(2));
+ bIsSubway = !!(tmp & BIT(3));
+ bDrawLast = !!(tmp & BIT(4));
+ bNoBrightHeadLights = !!(tmp & BIT(5));
+ bDoNotRender = !!(tmp & BIT(6));
+ bDistanceFade = !!(tmp & BIT(7));
+
+ m_flagE1 = !!(tmp & BIT(8));
+ m_flagE2 = !!(tmp & BIT(9));
+ bOffscreen = !!(tmp & BIT(10));
+ bIsStaticWaitingForCollision = !!(tmp & BIT(11));
+ bDontStream = !!(tmp & BIT(12));
+ bUnderwater = !!(tmp & BIT(13));
+ bHasPreRenderEffects = !!(tmp & BIT(14));
+}
+
+#endif
diff --git a/src/entities/Entity.h b/src/entities/Entity.h
new file mode 100644
index 0000000..2749e3f
--- /dev/null
+++ b/src/entities/Entity.h
@@ -0,0 +1,184 @@
+#pragma once
+
+#include "ModelInfo.h"
+#include "Placeable.h"
+
+struct CReference;
+class CPtrList;
+
+enum eEntityType
+{
+ ENTITY_TYPE_NOTHING = 0,
+ ENTITY_TYPE_BUILDING,
+ ENTITY_TYPE_VEHICLE,
+ ENTITY_TYPE_PED,
+ ENTITY_TYPE_OBJECT,
+ ENTITY_TYPE_DUMMY,
+};
+
+enum eEntityStatus
+{
+ STATUS_PLAYER,
+ STATUS_PLAYER_PLAYBACKFROMBUFFER,
+ STATUS_SIMPLE,
+ STATUS_PHYSICS,
+ STATUS_ABANDONED,
+ STATUS_WRECKED,
+ STATUS_TRAIN_MOVING,
+ STATUS_TRAIN_NOT_MOVING,
+ STATUS_HELI,
+ STATUS_PLANE,
+ STATUS_PLAYER_REMOTE,
+ STATUS_PLAYER_DISABLED,
+ STATUS_GHOST
+};
+
+class CEntity : public CPlaceable
+{
+public:
+ RwObject *m_rwObject;
+protected:
+ uint32 m_type : 3;
+private:
+ uint32 m_status : 5;
+public:
+ // flagsA
+ uint32 bUsesCollision : 1; // does entity use collision
+ uint32 bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function
+ uint32 bIsStatic : 1; // is entity static
+ uint32 bHasContacted : 1; // has entity processed some contact forces
+ uint32 bPedPhysics : 1;
+ uint32 bIsStuck : 1; // is entity stuck
+ uint32 bIsInSafePosition : 1; // is entity in a collision free safe position
+ uint32 bUseCollisionRecords : 1;
+
+ // flagsB
+ uint32 bWasPostponed : 1; // was entity control processing postponed
+ uint32 bExplosionProof : 1;
+ uint32 bIsVisible : 1; //is the entity visible
+ uint32 bHasCollided : 1;
+ uint32 bRenderScorched : 1;
+ uint32 bHasBlip : 1;
+ uint32 bIsBIGBuilding : 1; // Set if this entity is a big building
+ uint32 bStreamBIGBuilding : 1; // set when draw dist <= 2000
+
+ // flagsC
+ uint32 bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage
+ uint32 bBulletProof : 1;
+ uint32 bFireProof : 1;
+ uint32 bCollisionProof : 1;
+ uint32 bMeleeProof : 1;
+ uint32 bOnlyDamagedByPlayer : 1;
+ uint32 bStreamingDontDelete : 1; // Dont let the streaming remove this
+ uint32 bRemoveFromWorld : 1; // remove this entity next time it should be processed
+
+ // flagsD
+ uint32 bHasHitWall : 1; // has collided with a building (changes subsequent collisions)
+ uint32 bImBeingRendered : 1; // don't delete me because I'm being rendered
+ uint32 bTouchingWater : 1; // used by cBuoyancy::ProcessBuoyancy
+ uint32 bIsSubway : 1; // set when subway, but maybe different meaning?
+ uint32 bDrawLast : 1; // draw object last
+ uint32 bNoBrightHeadLights : 1;
+ uint32 bDoNotRender : 1; //-- only applies to CObjects apparently
+ uint32 bDistanceFade : 1; // Fade entity because it is far away
+
+ // flagsE
+ uint32 m_flagE1 : 1;
+ uint32 m_flagE2 : 1;
+ uint32 bOffscreen : 1; // offscreen flag. This can only be trusted when it is set to true
+ uint32 bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them
+ uint32 bDontStream : 1; // tell the streaming not to stream me
+ uint32 bUnderwater : 1; // this object is underwater change drawing order
+ uint32 bHasPreRenderEffects : 1; // Object has a prerender effects attached to it
+
+ uint16 m_scanCode;
+ uint16 m_randomSeed;
+ int16 m_modelIndex;
+ int8 m_level;
+ int8 m_area;
+ CReference *m_pFirstReference;
+
+public:
+ uint8 GetType() const { return m_type; }
+ void SetType(uint8 type) { m_type = type; }
+ uint8 GetStatus() const { return m_status; }
+ void SetStatus(uint8 status) { m_status = status; }
+ CColModel *GetColModel(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); }
+ bool GetIsStatic(void) const { return bIsStatic || bIsStaticWaitingForCollision; }
+ void SetIsStatic(bool state) { bIsStatic = state; }
+#ifdef COMPATIBLE_SAVES
+ void SaveEntityFlags(uint8*& buf);
+ void LoadEntityFlags(uint8*& buf);
+#else
+ uint32* GetAddressOfEntityProperties() { /* AWFUL */ return (uint32*)((char*)&m_rwObject + sizeof(m_rwObject)); }
+#endif
+
+ CEntity(void);
+ virtual ~CEntity(void);
+
+ virtual void Add(void);
+ virtual void Remove(void);
+ virtual void SetModelIndex(uint32 id);
+ virtual void SetModelIndexNoCreate(uint32 id);
+ virtual void CreateRwObject(void);
+ virtual void DeleteRwObject(void);
+ virtual CRect GetBoundRect(void);
+ virtual void ProcessControl(void) {}
+ virtual void ProcessCollision(void) {}
+ virtual void ProcessShift(void) {}
+ virtual void Teleport(CVector v) {}
+ virtual void PreRender(void);
+ virtual void Render(void);
+ virtual bool SetupLighting(void);
+ virtual void RemoveLighting(bool);
+ virtual void FlagToDestroyWhenNextProcessed(void) {}
+
+ bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; }
+ bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; }
+ bool IsPed(void) { return m_type == ENTITY_TYPE_PED; }
+ bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; }
+ bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; }
+
+ RpAtomic *GetAtomic(void) {
+ assert(RwObjectGetType(m_rwObject) == rpATOMIC);
+ return (RpAtomic*)m_rwObject;
+ }
+ RpClump *GetClump(void) {
+ assert(RwObjectGetType(m_rwObject) == rpCLUMP);
+ return (RpClump*)m_rwObject;
+ }
+
+ void GetBoundCentre(CVector &out);
+ CVector GetBoundCentre(void);
+ float GetBoundRadius(void);
+ float GetDistanceFromCentreOfMassToBaseOfModel(void);
+ bool GetIsTouching(CVector const &center, float r);
+ bool GetIsOnScreen(void);
+ bool GetIsOnScreenComplex(void);
+ bool IsVisible(void);
+ bool IsVisibleComplex(void);
+ bool IsEntityOccluded(void);
+ int16 GetModelIndex(void) const { return m_modelIndex; }
+ void UpdateRwFrame(void);
+ void SetupBigBuilding(void);
+ bool HasPreRenderEffects(void);
+
+ void AttachToRwObject(RwObject *obj);
+ void DetachFromRwObject(void);
+
+ void RegisterReference(CEntity **pent);
+ void ResolveReferences(void);
+ void PruneReferences(void);
+ void CleanUpOldReference(CEntity **pent);
+
+ void UpdateRpHAnim(void);
+
+ void PreRenderForGlassWindow(void);
+ void AddSteamsFromGround(CVector *unused);
+ void ModifyMatrixForTreeInWind(void);
+ void ModifyMatrixForBannerInWind(void);
+ void ProcessLightsForEntity(void);
+ void SetRwObjectAlpha(int32 alpha);
+};
+
+bool IsEntityPointerValid(CEntity*);
diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp
new file mode 100644
index 0000000..b897a6e
--- /dev/null
+++ b/src/entities/Physical.cpp
@@ -0,0 +1,2310 @@
+#include "common.h"
+
+#include "World.h"
+#include "General.h"
+#include "Timer.h"
+#include "Stats.h"
+#include "ModelIndices.h"
+#include "Treadable.h"
+#include "Vehicle.h"
+#include "Ped.h"
+#include "Object.h"
+#include "Glass.h"
+#include "ParticleObject.h"
+#include "Particle.h"
+#include "SurfaceTable.h"
+#include "PathFind.h"
+#include "CarCtrl.h"
+#include "DMAudio.h"
+#include "Automobile.h"
+#include "Bike.h"
+#include "Pickups.h"
+#include "Physical.h"
+
+#ifdef WALLCLIMB_CHEAT
+bool gGravityCheat;
+#endif
+
+
+CPhysical::CPhysical(void)
+{
+ int i;
+
+#ifdef FIX_BUGS
+ m_nLastTimeCollided = 0;
+#endif
+
+ m_fForceMultiplier = 1.0f;
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+ m_vecMoveSpeedAvg = CVector(0.0f, 0.0f, 0.0f);
+ m_vecTurnSpeedAvg = CVector(0.0f, 0.0f, 0.0f);
+
+ m_movingListNode = nil;
+ m_nStaticFrames = 0;
+
+ m_nCollisionRecords = 0;
+ for(i = 0; i < 6; i++)
+ m_aCollisionRecords[i] = nil;
+
+ m_bIsVehicleBeingShifted = false;
+ bJustCheckCollision = false;
+
+ m_nDamagePieceType = 0;
+ m_fDamageImpulse = 0.0f;
+ m_pDamageEntity = nil;
+ m_vecDamageNormal = CVector(0.0f, 0.0f, 0.0f);
+
+ bUsesCollision = true;
+ m_audioEntityId = -5;
+ m_phys_unused1 = 100.0f;
+ m_vecCentreOfMass = CVector(0.0f, 0.0f, 0.0f);
+ m_phys_unused2 = 0;
+
+ bIsHeavy = false;
+ bAffectedByGravity = true;
+ bInfiniteMass = false;
+ m_phy_flagA08 = false;
+ bIsInWater = false;
+ bHitByTrain = false;
+ bSkipLineCol = false;
+
+ m_fDistanceTravelled = 0.0f;
+
+ m_phy_flagA20 = false;
+
+#ifdef FIX_BUGS
+ m_nSurfaceTouched = SURFACE_DEFAULT;
+#endif
+ m_nZoneLevel = LEVEL_GENERIC;
+
+ bIsFrozen = false;
+ bDontLoadCollision = false;
+}
+
+CPhysical::~CPhysical(void)
+{
+ m_entryInfoList.Flush();
+}
+
+void
+CPhysical::Add(void)
+{
+ int x, xstart, xmid, xend;
+ int y, ystart, ymid, yend;
+ CSector *s;
+ CPtrList *list;
+
+ CRect bounds = GetBoundRect();
+ xstart = CWorld::GetSectorIndexX(bounds.left);
+ xend = CWorld::GetSectorIndexX(bounds.right);
+ xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
+ ystart = CWorld::GetSectorIndexY(bounds.top);
+ yend = CWorld::GetSectorIndexY(bounds.bottom);
+ ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
+ assert(xstart >= 0);
+ assert(xend < NUMSECTORS_X);
+ assert(ystart >= 0);
+ assert(yend < NUMSECTORS_Y);
+
+ for(y = ystart; y <= yend; y++)
+ for(x = xstart; x <= xend; x++){
+ s = CWorld::GetSector(x, y);
+ if(x == xmid && y == ymid) switch(m_type){
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS];
+ break;
+ default:
+ assert(0);
+ }else switch(m_type){
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
+ break;
+ default:
+ assert(0);
+ }
+ CPtrNode *node = list->InsertItem(this);
+ assert(node);
+ m_entryInfoList.InsertItem(list, node, s);
+ }
+}
+
+void
+CPhysical::Remove(void)
+{
+ CEntryInfoNode *node, *next;
+ for(node = m_entryInfoList.first; node; node = next){
+ next = node->next;
+ node->list->DeleteNode(node->listnode);
+ m_entryInfoList.DeleteNode(node);
+ }
+}
+
+void
+CPhysical::RemoveAndAdd(void)
+{
+ int x, xstart, xmid, xend;
+ int y, ystart, ymid, yend;
+ CSector *s;
+ CPtrList *list;
+
+ CRect bounds = GetBoundRect();
+ xstart = CWorld::GetSectorIndexX(bounds.left);
+ xend = CWorld::GetSectorIndexX(bounds.right);
+ xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
+ ystart = CWorld::GetSectorIndexY(bounds.top);
+ yend = CWorld::GetSectorIndexY(bounds.bottom);
+ ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
+ assert(xstart >= 0);
+ assert(xend < NUMSECTORS_X);
+ assert(ystart >= 0);
+ assert(yend < NUMSECTORS_Y);
+
+ // we'll try to recycle nodes from here
+ CEntryInfoNode *next = m_entryInfoList.first;
+
+ for(y = ystart; y <= yend; y++)
+ for(x = xstart; x <= xend; x++){
+ s = CWorld::GetSector(x, y);
+ if(x == xmid && y == ymid) switch(m_type){
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS];
+ break;
+ }else switch(m_type){
+ case ENTITY_TYPE_VEHICLE:
+ list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
+ break;
+ case ENTITY_TYPE_PED:
+ list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
+ break;
+ case ENTITY_TYPE_OBJECT:
+ list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
+ break;
+ }
+ if(next){
+ // If we still have old nodes, use them
+ next->list->RemoveNode(next->listnode);
+ list->InsertNode(next->listnode);
+ next->list = list;
+ next->sector = s;
+ next = next->next;
+ }else{
+ CPtrNode *node = list->InsertItem(this);
+ m_entryInfoList.InsertItem(list, node, s);
+ }
+ }
+
+ // Remove old nodes we no longer need
+ CEntryInfoNode *node;
+ for(node = next; node; node = next){
+ next = node->next;
+ node->list->DeleteNode(node->listnode);
+ m_entryInfoList.DeleteNode(node);
+ }
+}
+
+CRect
+CPhysical::GetBoundRect(void)
+{
+ CVector center;
+ float radius;
+ GetBoundCentre(center);
+ radius = GetBoundRadius();
+ return CRect(center.x-radius, center.y-radius, center.x+radius, center.y+radius);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::AddToMovingList(void)
+{
+ if (!bIsStaticWaitingForCollision)
+ m_movingListNode = CWorld::GetMovingEntityList().InsertItem(this);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::RemoveFromMovingList(void)
+{
+ if(m_movingListNode){
+ CWorld::GetMovingEntityList().DeleteNode(m_movingListNode);
+ m_movingListNode = nil;
+ }
+}
+
+void
+CPhysical::SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir)
+{
+ m_nDamagePieceType = piece;
+ m_fDamageImpulse = impulse;
+ m_pDamageEntity = entity;
+ entity->RegisterReference(&m_pDamageEntity);
+ m_vecDamageNormal = dir;
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::AddCollisionRecord(CEntity *ent)
+{
+ AddCollisionRecord_Treadable(ent);
+ this->bHasCollided = true;
+ ent->bHasCollided = true;
+ this->m_nLastTimeCollided = CTimer::GetTimeInMilliseconds();
+ if(IsVehicle() && ent->IsVehicle()){
+ if(((CVehicle*)this)->m_nAlarmState == -1)
+ ((CVehicle*)this)->m_nAlarmState = 15000;
+ if(((CVehicle*)ent)->m_nAlarmState == -1)
+ ((CVehicle*)ent)->m_nAlarmState = 15000;
+ }
+ if(bUseCollisionRecords){
+ int i;
+ for(i = 0; i < m_nCollisionRecords; i++)
+ if(m_aCollisionRecords[i] == ent)
+ return;
+ if(m_nCollisionRecords < PHYSICAL_MAX_COLLISIONRECORDS)
+ m_aCollisionRecords[m_nCollisionRecords++] = ent;
+ }
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::AddCollisionRecord_Treadable(CEntity *ent)
+{
+ if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){
+ }
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::GetHasCollidedWith(CEntity *ent)
+{
+ int i;
+ if(bUseCollisionRecords)
+ for(i = 0; i < m_nCollisionRecords; i++)
+ if(m_aCollisionRecords[i] == ent)
+ return true;
+ return false;
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::RemoveRefsToEntity(CEntity *ent)
+{
+ int i = 0, j;
+
+ while (i < m_nCollisionRecords){
+ if(m_aCollisionRecords[i] == ent){
+ for(j = i; j < m_nCollisionRecords-1; j++)
+ m_aCollisionRecords[j] = m_aCollisionRecords[j+1];
+ m_nCollisionRecords--;
+ } else
+ i++;
+ }
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos)
+{
+ CVector worldPos = other->GetMatrix() * localPos;
+ float step = 0.9f * CTimer::GetTimeStep();
+ CVector pos = other->m_vecMoveSpeed*step + worldPos;
+
+ CWorld::Remove(phys);
+ phys->GetMatrix() = other->GetMatrix();
+ phys->SetPosition(pos);
+ phys->m_vecMoveSpeed = other->m_vecMoveSpeed;
+ phys->GetMatrix().UpdateRW();
+ phys->UpdateRwFrame();
+ CWorld::Add(phys);
+}
+
+// --MIAMI: Proof-read once
+int32
+CPhysical::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints)
+{
+ int32 numSpheres = CCollision::ProcessColModels(
+ GetMatrix(), *GetColModel(),
+ ent->GetMatrix(), *ent->GetColModel(),
+ colpoints,
+ nil, nil); // No Lines allowed!
+ if(numSpheres > 0){
+ AddCollisionRecord(ent);
+ if(!ent->IsBuilding()) // Can't this catch dummies too?
+ ((CPhysical*)ent)->AddCollisionRecord(this);
+ if(ent->IsBuilding() || ent->GetIsStatic())
+ this->bHasHitWall = true;
+ }
+ return numSpheres;
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ProcessControl(void)
+{
+ if(!IsPed())
+ bIsInWater = false;
+ bHasContacted = false;
+ bIsInSafePosition = false;
+ bWasPostponed = false;
+ bHasHitWall = false;
+
+ if(GetStatus() == STATUS_SIMPLE)
+ return;
+
+ m_nCollisionRecords = 0;
+ bHasCollided = false;
+ m_nDamagePieceType = 0;
+ m_fDamageImpulse = 0.0f;
+ m_pDamageEntity = nil;
+
+ if(!bIsStuck){
+ if(IsObject() ||
+ IsPed() && !bPedPhysics){
+ m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f;
+ m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f;
+ float step = CTimer::GetTimeStep() * 0.003f;
+ if(m_vecMoveSpeedAvg.MagnitudeSqr() < step*step &&
+ m_vecTurnSpeedAvg.MagnitudeSqr() < step*step){
+ m_nStaticFrames++;
+ if(m_nStaticFrames > 10){
+ m_nStaticFrames = 10;
+ SetIsStatic(true);
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_vecMoveFriction = m_vecMoveSpeed;
+ m_vecTurnFriction = m_vecTurnSpeed;
+ return;
+ }
+ }else
+ m_nStaticFrames = 0;
+ }
+ }
+ ApplyGravity();
+ ApplyFriction();
+ ApplyAirResistance();
+}
+
+/*
+ * Some quantities (german in parens):
+ *
+ * acceleration: distance/time^2: a
+ * velocity: distance/time: v (GTA: speed)
+ * momentum (impuls): velocity*mass: p
+ * impulse (kraftstoss): delta momentum, force*time: J
+ *
+ * angular equivalents:
+ * velocity -> angular velocity (GTA: turn speed)
+ * momentum -> angular momentum (drehimpuls): L = r cross p
+ * force -> torque (drehmoment): tau = r cross F
+ * mass -> moment of inertia, angular mass (drehmoment, drehmasse): I = L/omega (GTA: turn mass)
+ */
+
+CVector
+CPhysical::GetSpeed(const CVector &r)
+{
+ return m_vecMoveSpeed + m_vecMoveFriction + CrossProduct(m_vecTurnFriction + m_vecTurnSpeed, r);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyMoveSpeed(void)
+{
+ if(bIsFrozen)
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ else
+ GetMatrix().Translate(m_vecMoveSpeed * CTimer::GetTimeStep());
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyTurnSpeed(void)
+{
+ if(bIsFrozen){
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+ }else{
+ // Move the coordinate axes by their speed
+ // Note that this denormalizes the matrix
+ CVector turnvec = m_vecTurnSpeed*CTimer::GetTimeStep();
+ GetRight() += CrossProduct(turnvec, GetRight());
+ GetForward() += CrossProduct(turnvec, GetForward());
+ GetUp() += CrossProduct(turnvec, GetUp());
+ }
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyMoveForce(float jx, float jy, float jz)
+{
+ m_vecMoveSpeed += CVector(jx, jy, jz)*(1.0f/m_fMass);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz)
+{
+ CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
+ CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz));
+ m_vecTurnSpeed += turnimpulse*(1.0f/m_fTurnMass);
+}
+
+void
+CPhysical::ApplyFrictionMoveForce(float jx, float jy, float jz)
+{
+ m_vecMoveFriction += CVector(jx, jy, jz)*(1.0f/m_fMass);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyFrictionTurnForce(float jx, float jy, float jz, float px, float py, float pz)
+{
+ CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
+ CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz));
+ m_vecTurnFriction += turnimpulse*(1.0f/m_fTurnMass);
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias)
+{
+ float compression = 1.0f - springRatio;
+ if(compression > 0.0f){
+ float step = Min(CTimer::GetTimeStep(), 3.0f);
+ float impulse = -GRAVITY*m_fMass*step * springConst * compression * bias*2.0f;
+ ApplyMoveForce(springDir*impulse);
+ ApplyTurnForce(springDir*impulse, point);
+ }
+ return true;
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir)
+{
+ float compression = 1.0f - springRatio;
+ if(compression > 0.0f){
+ if(DotProduct(springDir, forceDir) > 0.0f)
+ forceDir *= -1.0f;
+ float step = Min(CTimer::GetTimeStep(), 3.0f);
+ float impulse = GRAVITY*m_fMass*step * springConst * compression * bias*2.0f;
+ if(bIsHeavy)
+ impulse *= 0.75f;
+ ApplyMoveForce(forceDir*impulse);
+ ApplyTurnForce(forceDir*impulse, point);
+ }
+ return true;
+}
+
+// --MIAMI: Proof-read once
+// What exactly is speed?
+bool
+CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed)
+{
+ float speedA = DotProduct(speed, springDir);
+ float speedB = DotProduct(GetSpeed(point), springDir);
+ float step = Min(CTimer::GetTimeStep(), 3.0f);
+ float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f;
+ if(bIsHeavy)
+ impulse *= 2.0f;
+
+ // what is this?
+ float a = m_fTurnMass / ((point.MagnitudeSqr() + 1.0f) * 2.0f * m_fMass);
+ a = Min(a, 1.0f);
+ float b = Abs(impulse / (speedB * m_fMass));
+ if(a < b)
+ impulse *= a/b;
+
+ ApplyMoveForce(springDir*impulse);
+ ApplyTurnForce(springDir*impulse, point);
+ return true;
+}
+
+void
+CPhysical::ApplyGravity(void)
+{
+ if (!bAffectedByGravity)
+ return;
+#ifdef WALLCLIMB_CHEAT
+ if (gGravityCheat && this == FindPlayerVehicle()) {
+ static CVector gravityUp(0.0f, 0.0f, 1.0f), surfaceUp(0.0f, 0.0f, 1.0f);
+ CVector belowCar = GetPosition() - 2.0f*GetUp();
+ CColPoint point;
+ CEntity* entity;
+ if (CWorld::ProcessLineOfSight(GetPosition(), belowCar, point, entity, true, false, false, false, false, false))
+ surfaceUp = point.normal;
+ else
+ surfaceUp = CVector(0.0f, 0.0f, 1.0f);
+ float t = clamp(CTimer::GetTimeStep() * 0.5f, 0.05f, 0.8f);
+ gravityUp = gravityUp * (1.0f - t) + surfaceUp * t;
+ if (gravityUp.MagnitudeSqr() < 0.1f)
+ gravityUp = CVector(0.0f, 0.0f, 1.0f);
+ else
+ gravityUp.Normalise();
+ m_vecMoveSpeed -= GRAVITY * CTimer::GetTimeStep() * gravityUp;
+ return;
+ }
+#endif
+ m_vecMoveSpeed.z -= GRAVITY * CTimer::GetTimeStep();
+}
+
+void
+CPhysical::ApplyFriction(void)
+{
+ m_vecMoveSpeed += m_vecMoveFriction;
+ m_vecTurnSpeed += m_vecTurnFriction;
+ m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+}
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ApplyAirResistance(void)
+{
+ if(m_fAirResistance > 0.1f){
+ float f = Pow(m_fAirResistance, CTimer::GetTimeStep());
+ m_vecMoveSpeed *= f;
+ m_vecTurnSpeed *= f;
+ }else if(GetStatus() != STATUS_GHOST){
+ float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep());
+ m_vecMoveSpeed *= f;
+ m_vecTurnSpeed *= 0.99f;
+ }
+}
+
+bool
+CPhysical::ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB)
+{
+ float eA, eB;
+ CPhysical *A = this;
+ CObject *Bobj = (CObject*)B;
+
+ bool foo = false; // TODO: what does this mean?
+ bool ispedcontactA = false;
+ bool ispedcontactB = false;
+
+ float massFactorA;
+ if(B->bPedPhysics){
+ massFactorA = 10.0f;
+ if(B->IsPed() && ((CPed*)B)->m_pCurrentPhysSurface == A)
+ ispedcontactA = true;
+ }else
+ massFactorA = A->bIsHeavy ? 2.0f : 1.0f;
+
+ float massFactorB;
+ if(A->bPedPhysics){
+ if(A->IsPed() && ((CPed*)A)->IsPlayer() && B->IsVehicle() &&
+ (B->GetStatus() == STATUS_ABANDONED || B->GetStatus() == STATUS_WRECKED || A->bHasHitWall))
+ massFactorB = 1.0f/(Max(B->m_fMass - 2000.0f, 0.0f)/5000.0f + 1.0f);
+ else
+ massFactorB = 10.0f;
+
+ if(A->IsPed() && ((CPed*)A)->m_pCurrentPhysSurface == B)
+ ispedcontactB = true;
+ }else
+ massFactorB = B->bIsHeavy ? 2.0f : 1.0f;
+
+ if(B->bInfiniteMass && !B->m_phy_flagA08){
+ ispedcontactB = false;
+ foo = true;
+ }
+
+ float speedA, speedB;
+ if(B->GetIsStatic() && !foo){
+ if(A->bPedPhysics){
+ speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
+ if(speedA < 0.0f){
+ if(B->IsObject()){
+ impulseA = -speedA * A->m_fMass;
+ impulseB = impulseA;
+ if(impulseA > Bobj->m_fUprootLimit){
+ if(IsGlass(B->GetModelIndex()))
+ CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false);
+ else if(!B->bInfiniteMass){
+ B->SetIsStatic(false);
+ CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2;
+ CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(30, 60);
+ }
+ }else{
+ if(IsGlass(B->GetModelIndex()))
+ CGlass::WindowRespondsToSoftCollision(B, impulseA);
+ if(!A->bInfiniteMass)
+ A->ApplyMoveForce(colpoint.GetNormal() * (1.0f + A->m_fElasticity) * impulseA);
+ return true;
+ }
+ }else if(!B->bInfiniteMass)
+ B->SetIsStatic(false);
+
+ if(B->bInfiniteMass){
+ impulseA = -speedA * A->m_fMass;
+ impulseB = 0.0f;
+ if(!A->bInfiniteMass)
+ A->ApplyMoveForce(colpoint.normal*(1.0f + A->m_fElasticity)*impulseA);
+ return true;
+ }
+ }
+ }else{
+ CVector pointposA = colpoint.point - A->GetPosition();
+ speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
+ if(speedA < 0.0f){
+ if(B->IsObject()){
+ if(A->bHasHitWall)
+ eA = -1.0f;
+ else
+ eA = -(1.0f + A->m_fElasticity);
+ impulseA = eA * speedA * A->GetMass(pointposA, colpoint.normal);
+ impulseB = impulseA;
+
+ if(Bobj->m_nCollisionDamageEffect && impulseA > 20.0f){
+ Bobj->ObjectDamage(impulseA);
+ if(!B->bUsesCollision){
+ if(!A->bInfiniteMass){
+ A->ApplyMoveForce(colpoint.normal*0.2f*impulseA);
+ A->ApplyTurnForce(colpoint.normal*0.2f*impulseA, pointposA);
+ }
+ return false;
+ }
+ }
+
+ if((impulseA > Bobj->m_fUprootLimit || A->bIsStuck) &&
+ !B->bInfiniteMass){
+ if(IsGlass(B->GetModelIndex()))
+ CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false);
+ else
+ B->SetIsStatic(false);
+ int16 model = B->GetModelIndex();
+ if(model == MI_FIRE_HYDRANT && !Bobj->bHasBeenDamaged){
+ CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, B->GetPosition() - CVector(0.0f, 0.0f, 0.5f), true);
+ Bobj->bHasBeenDamaged = true;
+ }else if(model == MI_PARKINGMETER || model == MI_PARKINGMETER2){
+ CPickups::CreateSomeMoney(GetPosition(), CGeneral::GetRandomNumber()%100);
+ Bobj->bHasBeenDamaged = true;
+ }else if(B->IsObject() && !IsExplosiveThingModel(model))
+ Bobj->bHasBeenDamaged = true;
+ }else{
+ if(IsGlass(B->GetModelIndex()))
+ CGlass::WindowRespondsToSoftCollision(B, impulseA);
+ CVector f = colpoint.GetNormal() * impulseA;
+ if(A->IsVehicle() && colpoint.normal.z < 0.7f)
+ f.z *= 0.3f;
+ if(!A->bInfiniteMass){
+ A->ApplyMoveForce(f);
+ if(!A->IsVehicle() || !CWorld::bNoMoreCollisionTorque)
+ A->ApplyTurnForce(f, pointposA);
+ }
+ return true;
+ }
+ }else if(!B->bInfiniteMass)
+ B->SetIsStatic(false);
+ }
+ }
+
+ if(B->GetIsStatic())
+ return false;
+ if(!B->bInfiniteMass && !B->m_phy_flagA08)
+ B->AddToMovingList();
+ }
+
+ // B is not static
+
+ if(A->bPedPhysics && B->bPedPhysics){
+ // negative if A is moving towards B
+ speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
+ // positive if B is moving towards A
+ float speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
+
+ bool affectB = false;
+ float mA = A->m_fMass;;
+ float mB = B->m_fMass;;
+ float speedSum;
+ if(((CPed*)A)->GetPedState() == PED_FOLLOW_PATH){
+ affectB = true;
+ speedSum = (2.0f*mA*speedA + mB*speedB)/(2.0f*mA + mB);
+ }else{
+ speedSum = Max(speedB, 0.0f);
+ }
+
+ if(speedA < speedSum){
+ if(A->bHasHitWall)
+ eA = speedSum;
+ else
+ eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ impulseA = (eA-speedA) * mA;
+ if(!A->bInfiniteMass)
+ A->ApplyMoveForce(colpoint.normal*impulseA);
+ if(affectB && speedB < speedSum){
+ if(B->bHasHitWall)
+ eB = speedSum;
+ else
+ eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ impulseB = -(eB-speedB) * mB;
+ if(!B->bInfiniteMass)
+ B->ApplyMoveForce(colpoint.normal*-impulseB);
+ }
+ return true;
+ }
+ }else if(A->bPedPhysics){
+ CVector pointposB = colpoint.point - B->GetPosition();
+ speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
+ speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal);
+
+ float mA = A->m_fMass*massFactorA;
+ float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB);
+ float speedSum;
+ if(foo)
+ speedSum = speedB;
+ else
+ speedSum = (mB*speedB + mA*speedA)/(mA + mB);
+ if(speedA < speedSum){
+ if(A->bHasHitWall)
+ eA = speedSum;
+ else
+ eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ if(B->bHasHitWall)
+ eB = speedSum;
+ else
+ eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ impulseA = (eA - speedA) * mA;
+ impulseB = -(eB - speedB) * mB;
+ CVector fA = colpoint.normal*(impulseA/massFactorA);
+ CVector fB = colpoint.normal*(-impulseB/massFactorB);
+ if(!A->bInfiniteMass){
+ if(fA.z < 0.0f) fA.z = 0.0f;
+ if(ispedcontactB){
+ fA.x *= 2.0f;
+ fA.y *= 2.0f;
+ }
+ A->ApplyMoveForce(fA);
+ }
+ if(!B->bInfiniteMass && !ispedcontactB){
+ B->ApplyMoveForce(fB);
+ B->ApplyTurnForce(fB, pointposB);
+ }
+ return true;
+ }
+ }else if(B->bPedPhysics){
+ CVector pointposA = colpoint.point - A->GetPosition();
+ speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
+ speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
+
+ float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA);
+ float mB = B->m_fMass*massFactorB;
+ float speedSum = (mB*speedB + mA*speedA)/(mA + mB);
+ if(speedA < speedSum){
+ if(A->bHasHitWall)
+ eA = speedSum;
+ else
+ eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ if(B->bHasHitWall)
+ eB = speedSum;
+ else
+ eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ impulseA = (eA - speedA) * mA;
+ impulseB = -(eB - speedB) * mB;
+ CVector fA = colpoint.normal*(impulseA/massFactorA);
+ CVector fB = colpoint.normal*(-impulseB/massFactorB);
+ if(!A->bInfiniteMass && !ispedcontactA){
+ if(fA.z < 0.0f) fA.z = 0.0f;
+ A->ApplyMoveForce(fA);
+ A->ApplyTurnForce(fA, pointposA);
+ }
+ if(!B->bInfiniteMass){
+ if(fB.z < 0.0f){
+ fB.z = 0.0f;
+ if(Abs(speedA) < 0.01f)
+ fB *= 0.5f;
+ }
+ if(ispedcontactA){
+ fB.x *= 2.0f;
+ fB.y *= 2.0f;
+ }
+ B->ApplyMoveForce(fB);
+ }
+ return true;
+ }
+ }else{
+ CVector pointposA = colpoint.point - A->GetPosition();
+ CVector pointposB = colpoint.point - B->GetPosition();
+ speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
+ speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal);
+ float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA);
+ float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB);
+ float speedSum = (mB*speedB + mA*speedA)/(mA + mB);
+ if(speedA < speedSum){
+ if(A->bHasHitWall)
+ eA = speedSum;
+ else
+ eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ if(B->bHasHitWall)
+ eB = speedSum;
+ else
+ eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
+ impulseA = (eA - speedA) * mA;
+ impulseB = -(eB - speedB) * mB;
+ CVector fA = colpoint.normal*(impulseA/massFactorA);
+ CVector fB = colpoint.normal*(-impulseB/massFactorB);
+ if(A->IsVehicle() && !A->bHasHitWall){
+ fA.x *= 1.4f;
+ fA.y *= 1.4f;
+ if(colpoint.normal.z < 0.7f)
+ fA.z *= 0.3f;
+ if(A->GetStatus() == STATUS_PLAYER)
+ pointposA *= 0.8f;
+ if(CWorld::bNoMoreCollisionTorque){
+ A->ApplyFrictionMoveForce(fA*-0.3f);
+ A->ApplyFrictionTurnForce(fA*-0.3f, pointposA);
+ }
+ }
+ if(B->IsVehicle() && !B->bHasHitWall){
+ fB.x *= 1.4f;
+ fB.y *= 1.4f;
+ if(-colpoint.normal.z < 0.7f)
+ fB.z *= 0.3f;
+ if(B->GetStatus() == STATUS_PLAYER)
+ pointposB *= 0.8f;
+ if(CWorld::bNoMoreCollisionTorque){
+#ifdef FIX_BUGS
+ B->ApplyFrictionMoveForce(fB*-0.3f);
+ B->ApplyFrictionTurnForce(fB*-0.3f, pointposB);
+#else
+ A->ApplyFrictionMoveForce(fB*-0.3f);
+ A->ApplyFrictionTurnForce(fB*-0.3f, pointposB);
+#endif
+ }
+ }
+ if(!A->bInfiniteMass){
+ A->ApplyMoveForce(fA);
+ A->ApplyTurnForce(fA, pointposA);
+ }
+ if(!B->bInfiniteMass){
+ if(B->bIsInSafePosition)
+ B->UnsetIsInSafePosition();
+ B->ApplyMoveForce(fB);
+ B->ApplyTurnForce(fB, pointposB);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CPhysical::ApplyCollision(CColPoint &colpoint, float &impulse)
+{
+ float speed;
+ if(bPedPhysics){
+ speed = DotProduct(m_vecMoveSpeed, colpoint.normal);
+ if(speed < 0.0f){
+ impulse = -speed * m_fMass;
+ ApplyMoveForce(colpoint.normal*impulse);
+ return true;
+ }
+ }else{
+ CVector pointpos = colpoint.point - GetPosition();
+ speed = DotProduct(GetSpeed(pointpos), colpoint.normal);
+
+ if(speed < 0.0f){
+ float mass = GetMass(pointpos, colpoint.normal);
+ impulse = -(m_fElasticity + 1.0f) * speed * mass;
+ CVector f = colpoint.normal*impulse;
+ if(IsVehicle()){
+ f.x *= 1.4f;
+ f.y *= 1.4f;
+ if(colpoint.normal.z < 0.7f)
+ f.z *= 0.3f;
+ }
+ if(!bInfiniteMass){
+ ApplyMoveForce(f);
+ if(!IsVehicle() || !CWorld::bNoMoreCollisionTorque)
+ ApplyTurnForce(f, pointpos);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+CPhysical::ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed)
+{
+ float normalSpeed;
+ CVector speed;
+ CVector vImpulse;
+
+ if(GetModelIndex() == MI_BEACHBALL && B != (CEntity*)FindPlayerPed())
+ ((CObject*)this)->m_nBeachballBounces = 0;
+
+ if(bPedPhysics){
+ normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal);
+ if(normalSpeed < 0.0f){
+ impulse = -normalSpeed * m_fMass;
+ ApplyMoveForce(colpoint.normal * impulse);
+ return true;
+ }
+ }else{
+ CVector pointpos = colpoint.point - GetPosition();
+ speed = GetSpeed(pointpos);
+ normalSpeed = DotProduct(speed, colpoint.normal);
+ if(normalSpeed < 0.0f){
+ int16 elasticityType = 0;
+ float mass = GetMass(pointpos, colpoint.normal);
+ float minspeed = GRAVITY * CTimer::GetTimeStep();
+
+ if(IsObject())
+ elasticityType = 1;
+ else if(IsVehicle() && !bIsInWater){
+ if(((CVehicle*)this)->IsBike() && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){
+ minspeed *= 1.3f;
+ elasticityType = 3;
+ }else if(((CVehicle*)this)->IsBoat()){
+ minspeed *= 1.2f;
+ elasticityType = 4;
+ }else if(GetUp().z < -0.3f){
+ minspeed *= 1.1f;
+ elasticityType = 2;
+ }
+ }
+
+ if(elasticityType == 1 && !bHasContacted &&
+ Abs(m_vecMoveSpeed.x) < minspeed &&
+ Abs(m_vecMoveSpeed.y) < minspeed &&
+ Abs(m_vecMoveSpeed.z) < minspeed*2.0f)
+ impulse = -0.98f * normalSpeed * mass;
+ if(elasticityType == 3 &&
+ Abs(m_vecMoveSpeed.x) < minspeed &&
+ Abs(m_vecMoveSpeed.y) < minspeed &&
+ Abs(m_vecMoveSpeed.z) < minspeed*2.0f)
+ impulse = -0.8f * normalSpeed * mass;
+ else if(elasticityType == 2 &&
+ Abs(m_vecMoveSpeed.x) < minspeed &&
+ Abs(m_vecMoveSpeed.y) < minspeed &&
+ Abs(m_vecMoveSpeed.z) < minspeed*2.0f)
+ impulse = -0.92f * normalSpeed * mass;
+ else if(elasticityType == 4 &&
+ Abs(m_vecMoveSpeed.x) < minspeed &&
+ Abs(m_vecMoveSpeed.y) < minspeed &&
+ Abs(m_vecMoveSpeed.z) < minspeed*2.0f)
+ impulse = -0.8f * normalSpeed * mass;
+ else if(IsVehicle() && ((CVehicle*)this)->IsBoat() &&
+ colpoint.surfaceB == SURFACE_WOOD_SOLID && colpoint.normal.z < 0.5f)
+ impulse = -(2.0f * m_fElasticity + 1.0f) * normalSpeed * mass;
+ else
+ impulse = -(m_fElasticity + 1.0f) * normalSpeed * mass;
+
+ // ApplyMoveForce
+ vImpulse = colpoint.normal*impulse;
+ if(IsVehicle()){
+ if(!bHasHitWall ||
+ !(m_vecMoveSpeed.MagnitudeSqr() > 0.1 || !(B->IsBuilding() || ((CPhysical*)B)->bInfiniteMass)))
+ moveSpeed += vImpulse * 1.2f * (1.0f/m_fMass);
+ else
+ moveSpeed += vImpulse * (1.0f/m_fMass);
+ vImpulse *= 0.8f;
+ }else
+ moveSpeed += vImpulse * (1.0f/m_fMass);
+
+ // ApplyTurnForce
+ CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
+ CVector turnimpulse = CrossProduct(pointpos-com, vImpulse);
+ turnSpeed += turnimpulse*(1.0f/m_fTurnMass);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
+{
+ CVector speedA, speedB;
+ float normalSpeedA, normalSpeedB;
+ CVector vOtherSpeedA, vOtherSpeedB;
+ float fOtherSpeedA, fOtherSpeedB;
+ float speedSum;
+ CVector frictionDir;
+ float impulseA, impulseB;
+ float impulseLimit;
+ CPhysical *A = this;
+
+ if(A->bPedPhysics && B->bPedPhysics){
+ normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
+ normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
+ vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA;
+ vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB;
+
+ fOtherSpeedA = vOtherSpeedA.Magnitude();
+ fOtherSpeedB = vOtherSpeedB.Magnitude();
+
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeedA;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
+#endif
+
+ speedSum = (B->m_fMass*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(B->m_fMass + A->m_fMass);
+ if(fOtherSpeedA > speedSum){
+ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
+ impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
+ if(impulseA < -impulseLimit) impulseA = -impulseLimit;
+#ifdef FIX_BUGS
+ if(impulseB > impulseLimit) impulseB = impulseLimit;
+#else
+ if(impulseA < -impulseLimit) impulseA = -impulseLimit; // duplicate
+#endif
+ A->ApplyFrictionMoveForce(frictionDir*impulseA);
+ B->ApplyFrictionMoveForce(frictionDir*impulseB);
+ return true;
+ }
+ }else if(A->bPedPhysics){
+ if(B->IsVehicle())
+ return false;
+ CVector pointposB = colpoint.point - B->GetPosition();
+ speedB = B->GetSpeed(pointposB);
+
+ normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
+ normalSpeedB = DotProduct(speedB, colpoint.normal);
+ vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA;
+ vOtherSpeedB = speedB - colpoint.normal*normalSpeedB;
+
+ fOtherSpeedA = vOtherSpeedA.Magnitude();
+ fOtherSpeedB = vOtherSpeedB.Magnitude();
+
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeedA;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
+#endif
+ float massB = B->GetMass(pointposB, frictionDir);
+ speedSum = (massB*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(massB + A->m_fMass);
+ if(fOtherSpeedA > speedSum){
+ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
+ impulseB = (speedSum - fOtherSpeedB) * massB;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
+ if(impulseA < -impulseLimit) impulseA = -impulseLimit;
+ if(impulseB > impulseLimit) impulseB = impulseLimit;
+ A->ApplyFrictionMoveForce(frictionDir*impulseA);
+ B->ApplyFrictionMoveForce(frictionDir*impulseB);
+ B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB);
+ return true;
+ }
+ }else if(B->bPedPhysics){
+ if(A->IsVehicle())
+ return false;
+ CVector pointposA = colpoint.point - A->GetPosition();
+ speedA = A->GetSpeed(pointposA);
+
+ normalSpeedA = DotProduct(speedA, colpoint.normal);
+ normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
+ vOtherSpeedA = speedA - colpoint.normal*normalSpeedA;
+ vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB;
+
+ fOtherSpeedA = vOtherSpeedA.Magnitude();
+ fOtherSpeedB = vOtherSpeedB.Magnitude();
+
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeedA;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
+#endif
+ float massA = A->GetMass(pointposA, frictionDir);
+ speedSum = (B->m_fMass*fOtherSpeedB + massA*fOtherSpeedA)/(B->m_fMass + massA);
+ if(fOtherSpeedA > speedSum){
+ impulseA = (speedSum - fOtherSpeedA) * massA;
+ impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
+ if(impulseA < -impulseLimit) impulseA = -impulseLimit;
+ if(impulseB > impulseLimit) impulseB = impulseLimit;
+ A->ApplyFrictionMoveForce(frictionDir*impulseA);
+ A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA);
+ B->ApplyFrictionMoveForce(frictionDir*impulseB);
+ return true;
+ }
+ }else{
+ CVector pointposA = colpoint.point - A->GetPosition();
+ CVector pointposB = colpoint.point - B->GetPosition();
+ speedA = A->GetSpeed(pointposA);
+ speedB = B->GetSpeed(pointposB);
+
+ normalSpeedA = DotProduct(speedA, colpoint.normal);
+ normalSpeedB = DotProduct(speedB, colpoint.normal);
+ vOtherSpeedA = speedA - colpoint.normal*normalSpeedA;
+ vOtherSpeedB = speedB - colpoint.normal*normalSpeedB;
+
+ fOtherSpeedA = vOtherSpeedA.Magnitude();
+ fOtherSpeedB = vOtherSpeedB.Magnitude();
+
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeedA;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
+#endif
+ float massA = A->GetMass(pointposA, frictionDir);
+ float massB = B->GetMass(pointposB, frictionDir);
+ speedSum = (massB*fOtherSpeedB + massA*fOtherSpeedA)/(massB + massA);
+ if(fOtherSpeedA > speedSum){
+ impulseA = (speedSum - fOtherSpeedA) * massA;
+ impulseB = (speedSum - fOtherSpeedB) * massB;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
+ if(impulseA < -impulseLimit) impulseA = -impulseLimit;
+ if(impulseB > impulseLimit) impulseB = impulseLimit;
+ A->ApplyFrictionMoveForce(frictionDir*impulseA);
+ A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA);
+ B->ApplyFrictionMoveForce(frictionDir*impulseB);
+ B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB);
+ return true;
+ }
+ }
+ return false;
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
+{
+ CVector speed;
+ float normalSpeed;
+ CVector vOtherSpeed;
+ float fOtherSpeed;
+ CVector frictionDir;
+ float fImpulse;
+ float impulseLimit;
+
+ if(bPedPhysics){
+ normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal);
+ vOtherSpeed = m_vecMoveSpeed - colpoint.normal*normalSpeed;
+
+ fOtherSpeed = vOtherSpeed.Magnitude();
+ if(fOtherSpeed > 0.0f){
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeed;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
+#endif
+ // not really impulse but speed
+ // maybe use ApplyFrictionMoveForce instead?
+ fImpulse = -fOtherSpeed;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass;
+ if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
+ CVector vImpulse = frictionDir*fImpulse;
+ m_vecMoveFriction += CVector(vImpulse.x, vImpulse.y, 0.0f);
+ return true;
+ }
+ }else{
+ CVector pointpos = colpoint.point - GetPosition();
+ speed = GetSpeed(pointpos);
+ normalSpeed = DotProduct(speed, colpoint.normal);
+ vOtherSpeed = speed - colpoint.normal*normalSpeed;
+
+ fOtherSpeed = vOtherSpeed.Magnitude();
+ if(fOtherSpeed > 0.0f){
+#ifdef FIX_BUGS // division by 0
+ frictionDir = vOtherSpeed;
+ frictionDir.Normalise();
+#else
+ frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
+#endif
+ fImpulse = -fOtherSpeed * m_fMass;
+ impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5;
+ if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
+ ApplyFrictionMoveForce(frictionDir*fImpulse);
+ ApplyFrictionTurnForce(frictionDir*fImpulse, pointpos);
+
+ if(fOtherSpeed > 0.1f &&
+ colpoint.surfaceB != SURFACE_GRASS && colpoint.surfaceB != SURFACE_MUD_DRY &&
+ CSurfaceTable::GetAdhesionGroup(colpoint.surfaceA) == ADHESIVE_HARD){
+ CVector v = frictionDir * fOtherSpeed * 0.25f;
+ for(int i = 0; i < 4; i++)
+ CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpoint.point, v);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// --MIAMI: Proof-read once
+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[MAX_COLLISION_POINTS];
+ CVector shift = CVector(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->GetIsStatic() && !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(IsLightWithoutShift(A->GetModelIndex()) &&
+ (B->IsVehicle() || B->IsPed()) &&
+ A->GetUp().z < 0.66f)
+ skipShift = true;
+ else if((A->IsVehicle() || A->IsPed()) &&
+ B->GetUp().z < 0.66f &&
+ IsLightWithoutShift(B->GetModelIndex()))
+ skipShift = true;
+ else if(A->IsObject() && B->IsVehicle()){
+ CObject *Aobj = (CObject*)A;
+ if(Aobj->ObjectCreatedBy != TEMP_OBJECT &&
+ !Aobj->bHasBeenDamaged &&
+ Aobj->GetIsStatic()){
+ if(Aobj->m_pCollidingEntity == B)
+ Aobj->m_pCollidingEntity = nil;
+ }else if(Aobj->m_pCollidingEntity != 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_pCollidingEntity = B;
+ }
+ } else
+ skipShift = true;
+ }else if(B->IsObject() && A->IsVehicle()){
+ CObject *Bobj = (CObject*)B;
+ if(Bobj->ObjectCreatedBy != TEMP_OBJECT &&
+ !Bobj->bHasBeenDamaged &&
+ Bobj->GetIsStatic()){
+ if(Bobj->m_pCollidingEntity == A)
+ Bobj->m_pCollidingEntity = nil;
+ }else if(Bobj->m_pCollidingEntity != 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;
+ } else
+ skipShift = true;
+ }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_pCollidingEntity == B ||
+ B->IsPed() && ((CPed*)B)->m_pCollidingEntity == 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].GetDepth() > colpoints[mostColliding].GetDepth())
+ mostColliding = j;
+
+ if(CWorld::bSecondShift)
+ for(j = 0; j < numCollisions; j++)
+ shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 1.5f / numCollisions;
+ else
+ for(j = 0; j < numCollisions; j++)
+ shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 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].GetDepth() * 0.5f;
+ }else if(A->IsPed() && B->IsVehicle() && ((CVehicle*)B)->IsBoat()){
+ CVector dir = colpoints[mostColliding].GetNormal();
+ float f = Min(Abs(dir.z), 0.9f);
+ dir.z = 0.0f;
+ dir.Normalise();
+ shift += dir * colpoints[mostColliding].GetDepth() / (1.0f - f);
+ boat = B;
+ }else if(B->IsPed() && A->IsVehicle() && ((CVehicle*)A)->IsBoat()){
+ CVector dir = colpoints[mostColliding].GetNormal() * -1.0f;
+ float f = Min(Abs(dir.z), 0.9f);
+ dir.z = 0.0f;
+ dir.Normalise();
+ B->GetMatrix().Translate(dir * colpoints[mostColliding].GetDepth() / (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].GetNormal() * colpoints[mostColliding].GetDepth() * 0.4f;
+ else
+ shift += colpoints[mostColliding].GetNormal() * colpoints[mostColliding].GetDepth() * 0.2f;
+ }
+
+ doShift = true;
+ }
+ }
+
+ if(!doShift)
+ return false;
+ GetMatrix().Translate(shift);
+ if(boat)
+ ProcessEntityCollision(boat, colpoints);
+ return true;
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ProcessCollisionSectorList_SimpleCar(CPtrList *lists)
+{
+ static CColPoint aColPoints[MAX_COLLISION_POINTS];
+ float radius;
+ CVector center;
+ int listtype;
+ CPhysical *A, *B;
+ int numCollisions;
+ int i;
+ float impulseA = -1.0f;
+ float impulseB = -1.0f;
+
+ A = (CPhysical*)this;
+
+ if(!A->bUsesCollision)
+ return false;
+
+ radius = A->GetBoundRadius();
+ A->GetBoundCentre(center);
+
+ for(listtype = 3; listtype >= 0; listtype--){
+ // Go through vehicles and objects
+ CPtrList *list;
+ switch(listtype){
+ case 0: list = &lists[ENTITYLIST_VEHICLES]; break;
+ case 1: list = &lists[ENTITYLIST_VEHICLES_OVERLAP]; break;
+ case 2: list = &lists[ENTITYLIST_OBJECTS]; break;
+ case 3: list = &lists[ENTITYLIST_OBJECTS_OVERLAP]; break;
+ }
+
+ // Find first collision in list
+ CPtrNode *listnode;
+ for(listnode = list->first; listnode; listnode = listnode->next){
+ B = (CPhysical*)listnode->item;
+ if(B != A &&
+ !(B->IsObject() && ((CObject*)B)->bIsStreetLight && B->GetUp().z < 0.66f) &&
+ B->m_scanCode != CWorld::GetCurrentScanCode() &&
+ B->bUsesCollision &&
+ B->GetIsTouching(center, radius)){
+ B->m_scanCode = CWorld::GetCurrentScanCode();
+ numCollisions = A->ProcessEntityCollision(B, aColPoints);
+ if(numCollisions > 0)
+ goto collision;
+ }
+ }
+ }
+ // no collision
+ return false;
+
+collision:
+
+ if(A->bHasContacted && B->bHasContacted){
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ }
+ }else if(A->bHasContacted){
+ CVector savedMoveFriction = A->m_vecMoveFriction;
+ CVector savedTurnFriction = A->m_vecTurnFriction;
+ A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+ A->bHasContacted = false;
+
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+
+ if(!A->bHasContacted){
+ A->bHasContacted = true;
+ A->m_vecMoveFriction = savedMoveFriction;
+ A->m_vecTurnFriction = savedTurnFriction;
+ }
+ }else if(B->bHasContacted){
+ CVector savedMoveFriction = B->m_vecMoveFriction;
+ CVector savedTurnFriction = B->m_vecTurnFriction;
+ B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+ B->bHasContacted = false;
+
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+
+ if(!B->bHasContacted){
+ B->bHasContacted = true;
+ B->m_vecMoveFriction = savedMoveFriction;
+ B->m_vecTurnFriction = savedTurnFriction;
+ }
+ }else{
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+ }
+
+ if(B->GetStatus() == STATUS_SIMPLE){
+ B->SetStatus(STATUS_PHYSICS);
+ if(B->IsVehicle())
+ CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B);
+ }
+
+ return true;
+}
+
+// --MIAMI: Proof-read once
+bool
+CPhysical::ProcessCollisionSectorList(CPtrList *lists)
+{
+ static CColPoint aColPoints[MAX_COLLISION_POINTS];
+ float radius;
+ CVector center;
+ CPtrList *list;
+ CPhysical *A, *B;
+ CObject *Aobj, *Bobj;
+ CPed *Aped, *Bped;
+ int numCollisions;
+ int numResponses;
+ int i, j;
+ bool skipCollision, altcollision;
+ bool ret = false;
+ float impulseA = -1.0f;
+ float impulseB = -1.0f;
+
+ A = (CPhysical*)this;
+ Aobj = (CObject*)A;
+ Aped = (CPed*)A;
+
+ radius = A->GetBoundRadius();
+ A->GetBoundCentre(center);
+
+ for(j = 0; j <= ENTITYLIST_PEDS_OVERLAP; j++){
+ list = &lists[j];
+
+ CPtrNode *listnode;
+ for(listnode = list->first; listnode; listnode = listnode->next){
+ B = (CPhysical*)listnode->item;
+ Bobj = (CObject*)B;
+ Bped = (CPed*)B;
+
+ bool isTouching = true;
+ if(!B->bUsesCollision ||
+ B->m_scanCode == CWorld::GetCurrentScanCode() ||
+ B == A ||
+ !(isTouching = B->GetIsTouching(center, radius))){
+ if(!isTouching){
+ if(A->IsObject() && Aobj->m_pCollidingEntity == B)
+ Aobj->m_pCollidingEntity = nil;
+ else if(B->IsObject() && Bobj->m_pCollidingEntity == A)
+ Bobj->m_pCollidingEntity = nil;
+ else if(A->IsPed() && Aped->m_pCollidingEntity == B)
+ Aped->m_pCollidingEntity = nil;
+ else if(B->IsPed() && Bped->m_pCollidingEntity == A)
+ Bped->m_pCollidingEntity = nil;
+ }
+ continue;
+ }
+
+ A->bSkipLineCol = false;
+ skipCollision = false;
+ altcollision = false;
+
+ if(B->IsBuilding())
+ skipCollision = false;
+ else if(A->IsObject() && Aobj->bIsStreetLight &&
+ (B->IsVehicle() || B->IsPed()) &&
+ A->GetUp().z < 0.66f){
+ skipCollision = true;
+ A->bSkipLineCol = true;
+ Aobj->m_pCollidingEntity = B;
+ }else if(B->IsObject() && Bobj->bIsStreetLight &&
+ (A->IsVehicle() || A->IsPed()) &&
+ B->GetUp().z < 0.66f){
+ skipCollision = true;
+ A->bSkipLineCol = true;
+ Bobj->m_pCollidingEntity = A;
+ }else if(A->IsObject() && B->IsVehicle()){
+ if(A->GetModelIndex() == MI_CAR_BUMPER)
+ skipCollision = true;
+ else if(Aobj->ObjectCreatedBy == TEMP_OBJECT ||
+ Aobj->bHasBeenDamaged ||
+ !Aobj->GetIsStatic()){
+ if(Aobj->m_pCollidingEntity == B)
+ skipCollision = true;
+ else if(Aobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){
+ 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){
+ skipCollision = true;
+ Aobj->m_pCollidingEntity = B;
+ }
+ }
+ }
+ }else if(B->IsObject() && A->IsVehicle()){
+ if(B->GetModelIndex() == MI_CAR_BUMPER)
+ skipCollision = true;
+ else if(Bobj->ObjectCreatedBy == TEMP_OBJECT ||
+ Bobj->bHasBeenDamaged ||
+ !Bobj->GetIsStatic()){
+ if(Bobj->m_pCollidingEntity == A)
+ skipCollision = true;
+ else if(Bobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){
+ 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){
+ skipCollision = true;
+ }
+ }
+ }
+ }else if(A->GetModelIndex() == MI_GRENADE && B->IsPed() &&
+ A->GetPosition().z < B->GetPosition().z){
+ skipCollision = true;
+ }else if(B->GetModelIndex() == MI_GRENADE && A->IsPed() &&
+ B->GetPosition().z < A->GetPosition().z){
+ skipCollision = true;
+ A->bSkipLineCol = true;
+ }else if(A->IsPed() && Aped->m_pCollidingEntity == B){
+ skipCollision = true;
+ if(!Aped->bKnockedUpIntoAir || Aped->bKnockedOffBike)
+ A->bSkipLineCol = true;
+ }else if(B->IsPed() && Bped->m_pCollidingEntity == A){
+ skipCollision = true;
+ A->bSkipLineCol = true;
+ }else if(A->GetModelIndex() == MI_RCBANDIT && (B->IsPed() || B->IsVehicle()) ||
+ B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())){
+ skipCollision = true;
+ A->bSkipLineCol = true;
+ }else if(A->IsPed() && B->IsObject() && Bobj->m_fUprootLimit > 0.0f)
+ altcollision = true;
+
+
+ if(!A->bUsesCollision || skipCollision){
+ B->m_scanCode = CWorld::GetCurrentScanCode();
+ numCollisions = A->ProcessEntityCollision(B, aColPoints);
+ if(A->bJustCheckCollision && numCollisions > 0)
+ return true;
+ if(numCollisions == 0 && A == (CEntity*)FindPlayerPed() && Aped->m_pCollidingEntity == B)
+ Aped->m_pCollidingEntity = nil;
+ }else if(B->IsBuilding() || B->bIsStuck || B->m_phy_flagA08 || altcollision){
+ // This is the case where B doesn't move
+
+ B->m_scanCode = CWorld::GetCurrentScanCode();
+ numCollisions = A->ProcessEntityCollision(B, aColPoints);
+ if(numCollisions <= 0)
+ continue;
+
+ CVector moveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ CVector turnSpeed = CVector(0.0f, 0.0f, 0.0f);
+ float maxImpulseA = 0.0f;
+ numResponses = 0;
+ if(A->bHasContacted){
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed))
+ continue;
+
+ numResponses++;
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+
+ if(A->IsVehicle()){
+ if(!(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID) &&
+ impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND)
+ aColPoints[i].surfaceB = SURFACE_SAND;
+
+ float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr();
+ float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr();
+
+ if(A->GetUp().z < -0.6f &&
+ Abs(A->m_vecMoveSpeed.x) < 0.05f &&
+ Abs(A->m_vecMoveSpeed.y) < 0.05f)
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ else
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ }else{
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr();
+ float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ }
+ }
+ }else{
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed))
+ continue;
+
+ numResponses++;
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+ float adhesion = CSurfaceTable::GetAdhesiveLimit(aColPoints[i]) / numCollisions;
+
+ if(A->IsVehicle()){
+ if(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID)
+ adhesion = 0.0f;
+ else if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND)
+ aColPoints[i].surfaceB = SURFACE_SAND;
+
+ float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr();
+ float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr();
+
+ if(A->GetUp().z < -0.6f &&
+ Abs(A->m_vecMoveSpeed.x) < 0.05f &&
+ Abs(A->m_vecMoveSpeed.y) < 0.05f)
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ else
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+
+ if(A->GetModelIndex() == MI_RCBANDIT)
+ adhesion *= 0.2f;
+ else if(((CVehicle*)A)->IsBoat()){
+ if(aColPoints[i].normal.z > 0.6f){
+ if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_LOOSE ||
+ CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND)
+ adhesion *= 3.0f;
+ }else
+ adhesion = 0.0f;
+ }else if(A->GetStatus() == STATUS_WRECKED)
+ adhesion *= 3.0f;
+ else if(A->GetUp().z > 0.3f)
+ adhesion = 0.0f;
+ else
+ adhesion *= Min(5.0f, 0.03f*impulseA + 1.0f);
+ }else{
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr();
+ float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ }
+
+ if(A->ApplyFriction(adhesion, aColPoints[i]))
+ A->bHasContacted = true;
+ }
+ }
+
+ if(numResponses){
+ m_vecMoveSpeed += moveSpeed / numResponses;
+ m_vecTurnSpeed += turnSpeed / numResponses;
+ if(!CWorld::bNoMoreCollisionTorque &&
+ A->GetStatus() == STATUS_PLAYER && A->IsVehicle() &&
+ Abs(A->m_vecMoveSpeed.x) > 0.2f &&
+ Abs(A->m_vecMoveSpeed.y) > 0.2f){
+ A->m_vecMoveFriction.x += moveSpeed.x * -0.3f / numCollisions;
+ A->m_vecMoveFriction.y += moveSpeed.y * -0.3f / numCollisions;
+ A->m_vecTurnFriction += turnSpeed * -0.3f / numCollisions;
+ }
+
+ if(B->IsObject() && Bobj->m_nCollisionDamageEffect && maxImpulseA > 20.0f)
+ Bobj->ObjectDamage(maxImpulseA);
+
+ if(!CWorld::bSecondShift)
+ return true;
+ ret = true;
+ }
+ }else{
+
+ // B can move
+
+ B->m_scanCode = CWorld::GetCurrentScanCode();
+ numCollisions = A->ProcessEntityCollision(B, aColPoints);
+ if(numCollisions <= 0)
+ continue;
+
+ float maxImpulseA = 0.0f;
+ float maxImpulseB = 0.0f;
+ if(A->bHasContacted && B->bHasContacted){
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+ if(impulseB > maxImpulseB) maxImpulseB = impulseB;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+ }
+ }else if(A->bHasContacted){
+ CVector savedMoveFriction = A->m_vecMoveFriction;
+ CVector savedTurnFriction = A->m_vecTurnFriction;
+ A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+ A->bHasContacted = false;
+
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+ if(impulseB > maxImpulseB) maxImpulseB = impulseB;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+
+ if(!A->bHasContacted){
+ A->bHasContacted = true;
+ A->m_vecMoveFriction = savedMoveFriction;
+ A->m_vecTurnFriction = savedTurnFriction;
+ }
+ }else if(B->bHasContacted){
+ CVector savedMoveFriction = B->m_vecMoveFriction;
+ CVector savedTurnFriction = B->m_vecTurnFriction;
+ B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
+ B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
+ B->bHasContacted = false;
+
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+ if(impulseB > maxImpulseB) maxImpulseB = impulseB;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+
+ if(!B->bHasContacted){
+ B->bHasContacted = true;
+ B->m_vecMoveFriction = savedMoveFriction;
+ B->m_vecTurnFriction = savedTurnFriction;
+ }
+ }else{
+ for(i = 0; i < numCollisions; i++){
+ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB))
+ continue;
+
+ if(impulseA > maxImpulseA) maxImpulseA = impulseA;
+ if(impulseB > maxImpulseB) maxImpulseB = impulseB;
+
+ if(impulseA > A->m_fDamageImpulse)
+ A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal);
+
+ if(impulseB > B->m_fDamageImpulse)
+ B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal);
+
+ float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr();
+ float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr();
+
+ DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff));
+
+ if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){
+ A->bHasContacted = true;
+ B->bHasContacted = true;
+ }
+ }
+ }
+
+ if(B->IsPed() && A->IsVehicle() &&
+ (!Bped->IsPlayer() || B->bHasHitWall && A->m_vecMoveSpeed.MagnitudeSqr() > SQR(0.05f)))
+ Bped->KillPedWithCar((CVehicle*)A, maxImpulseB);
+ else if(B->GetModelIndex() == MI_TRAIN && A->IsPed() &&
+ (!Aped->IsPlayer() || A->bHasHitWall))
+ Aped->KillPedWithCar((CVehicle*)B, maxImpulseA*2.0f);
+ else if(B->IsObject() && B->bUsesCollision && A->IsVehicle()){
+ // BUG? not impulseA?
+ if(Bobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f)
+ Bobj->ObjectDamage(maxImpulseB);
+ else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){
+ 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)
+ Bobj->ObjectDamage(50.0f);
+ }
+ }else if(A->IsObject() && A->bUsesCollision && B->IsVehicle()){
+ if(Aobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f)
+ Aobj->ObjectDamage(maxImpulseB);
+#ifdef FIX_BUGS
+ else if(Aobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){
+#else
+ else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){
+#endif
+ 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)
+ Aobj->ObjectDamage(50.0f);
+ }
+ }
+
+ if(B->GetStatus() == STATUS_SIMPLE){
+ B->SetStatus(STATUS_PHYSICS);
+ if(B->IsVehicle())
+ CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B);
+ }
+
+ if(!CWorld::bSecondShift)
+ return true;
+ ret = true;
+ }
+
+ }
+ }
+
+ return ret;
+}
+
+bool
+CPhysical::CheckCollision(void)
+{
+ CEntryInfoNode *node;
+
+ bCollisionProcessed = false;
+ CWorld::AdvanceCurrentScanCode();
+ for(node = m_entryInfoList.first; node; node = node->next)
+ if(ProcessCollisionSectorList(node->sector->m_lists))
+ return true;
+ return false;
+}
+
+bool
+CPhysical::CheckCollision_SimpleCar(void)
+{
+ CEntryInfoNode *node;
+
+ bCollisionProcessed = false;
+ CWorld::AdvanceCurrentScanCode();
+ for(node = m_entryInfoList.first; node; node = node->next)
+ if(ProcessCollisionSectorList_SimpleCar(node->sector->m_lists))
+ return true;
+ return false;
+}
+
+float PHYSICAL_SHIFT_SPEED_DAMP = 0.707f;
+
+// --MIAMI: Proof-read once
+void
+CPhysical::ProcessShift(void)
+{
+ m_fDistanceTravelled = 0.0f;
+ if(GetStatus() == STATUS_SIMPLE){
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ RemoveAndAdd();
+ }else{
+ CPhysical *surf;
+ if(bHasHitWall && (IsPed() && (surf = ((CPed*)this)->m_pCurrentPhysSurface, surf == nil || !surf->bInfiniteMass || surf->m_phy_flagA08) ||
+ CWorld::bSecondShift)){
+ m_vecMoveSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep());
+ m_vecTurnSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep());
+ }
+
+ CMatrix matrix(GetMatrix());
+ ApplyMoveSpeed();
+ ApplyTurnSpeed();
+ GetMatrix().Reorthogonalise();
+
+ CWorld::AdvanceCurrentScanCode();
+
+ if(IsVehicle())
+ m_bIsVehicleBeingShifted = true;
+
+ CEntryInfoNode *node;
+ bool hasshifted = false;
+ for(node = m_entryInfoList.first; node; node = node->next)
+ hasshifted |= ProcessShiftSectorList(node->sector->m_lists);
+ m_bIsVehicleBeingShifted = false;
+ if(hasshifted){
+ CWorld::AdvanceCurrentScanCode();
+ bool hadCollision = false;
+ for(node = m_entryInfoList.first; node; node = node->next)
+ if(ProcessCollisionSectorList(node->sector->m_lists)){
+ if(!CWorld::bSecondShift){
+ GetMatrix() = matrix;
+ return;
+ }
+ hadCollision = true;
+ }
+ if(hadCollision){
+ GetMatrix() = matrix;
+ return;
+ }
+ }
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ m_fDistanceTravelled = (GetPosition() - matrix.GetPosition()).Magnitude();
+ RemoveAndAdd();
+ }
+}
+
+// x is the number of units (m) we would like to step
+#define NUMSTEPS(x) ceil(Sqrt(distSq) * (1.0f/(x)))
+
+float HIGHSPEED_ELASTICITY_MULT_PED = 2.0f;
+float HIGHSPEED_ELASTICITY_MULT_COPCAR = 2.0f;
+
+void
+CPhysical::ProcessCollision(void)
+{
+ int i;
+ CPed *ped = (CPed*)this;
+
+ m_fDistanceTravelled = 0.0f;
+ m_bIsVehicleBeingShifted = false;
+ bSkipLineCol = false;
+
+ if(!bUsesCollision){
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ RemoveAndAdd();
+ return;
+ }
+
+ if(GetStatus() == STATUS_SIMPLE){
+ if(CheckCollision_SimpleCar() && GetStatus() == STATUS_SIMPLE){
+ SetStatus(STATUS_PHYSICS);
+ if(IsVehicle())
+ CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)this);
+ }
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ RemoveAndAdd();
+ return;
+ }
+
+ // Save current state
+ CMatrix savedMatrix(GetMatrix());
+ float savedElasticity = m_fElasticity;
+ CVector savedMoveSpeed = m_vecMoveSpeed;
+ float savedTimeStep = CTimer::GetTimeStep();
+
+ int8 n = 1; // The number of steps we divide the time step into
+ float step = 0.0f; // divided time step
+ float distSq = m_vecMoveSpeed.MagnitudeSqr() * sq(CTimer::GetTimeStep());
+
+ if(IsPed() && (distSq >= sq(0.3f) || ped->IsPlayer())){
+ if(ped->IsPlayer()){
+ if(ped->m_pCurrentPhysSurface)
+ n = Max(NUMSTEPS(0.15f), 4.0f);
+ else
+ n = Max(NUMSTEPS(0.3f), 2.0f);
+ }else
+ n = NUMSTEPS(0.45f);
+ step = savedTimeStep / n;
+ if(!ped->IsPlayer())
+ ped->m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_PED;
+ }else if(IsVehicle() && distSq >= sq(0.4f)){
+ if(GetStatus() == STATUS_PLAYER)
+ n = NUMSTEPS(0.2f);
+ else
+ n = distSq > 0.32f ? NUMSTEPS(0.3f) : NUMSTEPS(0.4f);
+ step = savedTimeStep / n;
+
+ CVector bbox = GetColModel()->boundingBox.GetSize();
+ float relDistX = Abs(DotProduct(m_vecMoveSpeed, GetRight())) * CTimer::GetTimeStep() / bbox.x;
+ float relDistY = Abs(DotProduct(m_vecMoveSpeed, GetForward())) * CTimer::GetTimeStep() / bbox.y;
+ float relDistZ = Abs(DotProduct(m_vecMoveSpeed, GetUp())) * CTimer::GetTimeStep() / bbox.z;
+ if(Max(relDistX, Max(relDistY, relDistZ)) < 1.0f){
+ // check if we can get away with simplified processing
+
+ ApplyMoveSpeed();
+ ApplyTurnSpeed();
+ GetMatrix().Reorthogonalise();
+ bSkipLineCol = false;
+ m_bIsVehicleBeingShifted = false;
+
+ bJustCheckCollision = true;
+ bUsesCollision = false;
+ if(!CheckCollision()){
+ bJustCheckCollision = false;
+ bUsesCollision = true;
+ if(IsVehicle())
+ ((CVehicle*)this)->bVehicleColProcessed = true;
+
+ bHitByTrain = false;
+ m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude();
+ bSkipLineCol = false;
+
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ m_fElasticity = savedElasticity;
+ RemoveAndAdd();
+ return;
+ }
+ bJustCheckCollision = false;
+ bUsesCollision = true;
+ GetMatrix() = savedMatrix;
+ m_vecMoveSpeed = savedMoveSpeed;
+ if(IsVehicle() && ((CVehicle*)this)->bIsLawEnforcer)
+ m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_COPCAR;
+ }
+ }else if(IsObject() && ((CObject*)this)->ObjectCreatedBy != TEMP_OBJECT){
+ int responsecase = ((CObject*)this)->m_nSpecialCollisionResponseCases;
+ if(responsecase == COLLRESPONSE_LAMPOST){
+ CVector speedUp = CVector(0.0f, 0.0f, 0.0f);
+ CVector speedDown = CVector(0.0f, 0.0f, 0.0f);
+ CColModel *colModel = GetColModel();
+ speedUp.z = colModel->boundingBox.max.z;
+ speedDown.z = colModel->boundingBox.min.z;
+ speedUp = Multiply3x3(GetMatrix(), speedUp);
+ speedDown = Multiply3x3(GetMatrix(), speedDown);
+ speedUp = GetSpeed(speedUp);
+ speedDown = GetSpeed(speedDown);
+ distSq = Max(speedUp.MagnitudeSqr(), speedDown.MagnitudeSqr()) * sq(CTimer::GetTimeStep());
+ if(distSq >= sq(0.3f)){
+ n = NUMSTEPS(0.3f);
+ step = savedTimeStep / n;
+ }
+ }else if(responsecase == COLLRESPONSE_UNKNOWN5){
+ if(distSq >= 0.009f){
+ n = NUMSTEPS(0.09f);
+ step = savedTimeStep / n;
+ }
+ }else if(responsecase == COLLRESPONSE_SMALLBOX || responsecase == COLLRESPONSE_FENCEPART){
+ if(distSq >= sq(0.15f)){
+ n = NUMSTEPS(0.15f);
+ step = savedTimeStep / n;
+ }
+ }else{
+ if(distSq >= sq(0.3f)){
+ n = NUMSTEPS(0.3f);
+ step = savedTimeStep / n;
+ }
+ }
+ }
+
+ for(i = 1; i < n; i++){
+ CTimer::SetTimeStep(i * step);
+ ApplyMoveSpeed();
+ ApplyTurnSpeed();
+ // TODO: get rid of copy paste?
+ if(CheckCollision()){
+ if(IsPed() && m_vecMoveSpeed.z == 0.0f &&
+ !ped->bWasStanding &&
+ ped->bIsStanding)
+ savedMatrix.GetPosition().z = GetPosition().z;
+ GetMatrix() = savedMatrix;
+ CTimer::SetTimeStep(savedTimeStep);
+ m_fElasticity = savedElasticity;
+ return;
+ }
+ if(IsPed() && m_vecMoveSpeed.z == 0.0f &&
+ !ped->bWasStanding &&
+ ped->bIsStanding)
+ savedMatrix.GetPosition().z = GetPosition().z;
+ GetMatrix() = savedMatrix;
+ CTimer::SetTimeStep(savedTimeStep);
+ if(IsVehicle()){
+ CVehicle *veh = (CVehicle*)this;
+ if(veh->m_vehType == VEHICLE_TYPE_CAR){
+ CAutomobile *car = (CAutomobile*)this;
+ car->m_aSuspensionSpringRatio[0] = 1.0f;
+ car->m_aSuspensionSpringRatio[1] = 1.0f;
+ car->m_aSuspensionSpringRatio[2] = 1.0f;
+ car->m_aSuspensionSpringRatio[3] = 1.0f;
+ }else if(veh->m_vehType == VEHICLE_TYPE_BIKE){
+ CBike *bike = (CBike*)this;
+ bike->m_aSuspensionSpringRatio[0] = 1.0f;
+ bike->m_aSuspensionSpringRatio[1] = 1.0f;
+ bike->m_aSuspensionSpringRatio[2] = 1.0f;
+ bike->m_aSuspensionSpringRatio[3] = 1.0f;
+ }
+ }
+ }
+
+ ApplyMoveSpeed();
+ ApplyTurnSpeed();
+ GetMatrix().Reorthogonalise();
+ m_bIsVehicleBeingShifted = false;
+ bSkipLineCol = false;
+ if(!m_vecMoveSpeed.IsZero() ||
+ !m_vecTurnSpeed.IsZero() ||
+ bHitByTrain ||
+ GetStatus() == STATUS_PLAYER ||
+ IsVehicle() && ((CVehicle*)this)->bRestingOnPhysical ||
+ IsPed() && ped->IsPlayer()){
+ if(IsVehicle())
+ ((CVehicle*)this)->bVehicleColProcessed = true;
+ if(CheckCollision()){
+ GetMatrix() = savedMatrix;
+ m_fElasticity = savedElasticity;
+ return;
+ }
+ }
+ bHitByTrain = false;
+ m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude();
+ bSkipLineCol = false;
+
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ m_fElasticity = savedElasticity;
+ RemoveAndAdd();
+}
diff --git a/src/entities/Physical.h b/src/entities/Physical.h
new file mode 100644
index 0000000..926b976
--- /dev/null
+++ b/src/entities/Physical.h
@@ -0,0 +1,177 @@
+#pragma once
+
+#include "Lists.h"
+#include "Timer.h"
+#include "Entity.h"
+
+enum {
+ PHYSICAL_MAX_COLLISIONRECORDS = 6
+};
+
+#define GRAVITY (0.008f)
+
+class CTreadable;
+
+class CPhysical : public CEntity
+{
+public:
+ // The not properly indented fields haven't been checked properly yet
+
+ int32 m_audioEntityId;
+ float m_phys_unused1;
+ uint32 m_nLastTimeCollided;
+ CVector m_vecMoveSpeed; // velocity
+ CVector m_vecTurnSpeed; // angular velocity
+ CVector m_vecMoveFriction;
+ CVector m_vecTurnFriction;
+ CVector m_vecMoveSpeedAvg;
+ CVector m_vecTurnSpeedAvg;
+ float m_fMass;
+ float m_fTurnMass; // moment of inertia
+ float m_fForceMultiplier;
+ float m_fAirResistance;
+ float m_fElasticity;
+ float m_fBuoyancy;
+ CVector m_vecCentreOfMass;
+ CEntryInfoList m_entryInfoList;
+ CPtrNode *m_movingListNode;
+
+ int8 m_phys_unused2;
+ uint8 m_nStaticFrames;
+ uint8 m_nCollisionRecords;
+ CEntity *m_aCollisionRecords[PHYSICAL_MAX_COLLISIONRECORDS];
+
+ float m_fDistanceTravelled;
+
+ // damaged piece
+ float m_fDamageImpulse;
+ CEntity *m_pDamageEntity;
+ CVector m_vecDamageNormal;
+ int16 m_nDamagePieceType;
+
+ uint8 bIsHeavy : 1;
+ uint8 bAffectedByGravity : 1;
+ uint8 bInfiniteMass : 1;
+ uint8 m_phy_flagA08 : 1;
+ uint8 bIsInWater : 1;
+ uint8 m_phy_flagA20 : 1; // unused
+ uint8 bHitByTrain : 1;
+ uint8 bSkipLineCol : 1;
+
+ uint8 bIsFrozen : 1;
+ uint8 bDontLoadCollision : 1;
+ uint8 m_bIsVehicleBeingShifted : 1; // wrong name - also used on but never set for peds
+ uint8 bJustCheckCollision : 1; // just see if there is a collision
+
+ uint8 m_nSurfaceTouched;
+ int8 m_nZoneLevel;
+
+ CPhysical(void);
+ ~CPhysical(void);
+
+ // from CEntity
+ void Add(void);
+ void Remove(void);
+ CRect GetBoundRect(void);
+ void ProcessControl(void);
+ void ProcessShift(void);
+ void ProcessCollision(void);
+
+ virtual int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints);
+
+ void RemoveAndAdd(void);
+ void AddToMovingList(void);
+ void RemoveFromMovingList(void);
+ void SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir);
+ void AddCollisionRecord(CEntity *ent);
+ void AddCollisionRecord_Treadable(CEntity *ent);
+ bool GetHasCollidedWith(CEntity *ent);
+ void RemoveRefsToEntity(CEntity *ent);
+ static void PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos);
+
+ // get speed of point p relative to entity center
+ CVector GetSpeed(const CVector &r);
+ CVector GetSpeed(void) { return GetSpeed(CVector(0.0f, 0.0f, 0.0f)); }
+ float GetMass(const CVector &pos, const CVector &dir) {
+ return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/m_fTurnMass +
+ 1.0f/m_fMass);
+ }
+ float GetMassTweak(const CVector &pos, const CVector &dir, float t) {
+ return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/(m_fTurnMass*t) +
+ 1.0f/(m_fMass*t));
+ }
+ void UnsetIsInSafePosition(void) {
+ m_vecMoveSpeed *= -1.0f;
+ m_vecTurnSpeed *= -1.0f;
+ ApplyTurnSpeed();
+ ApplyMoveSpeed();
+ m_vecMoveSpeed *= -1.0f;
+ m_vecTurnSpeed *= -1.0f;
+ bIsInSafePosition = false;
+ }
+
+ const CVector &GetMoveSpeed() { return m_vecMoveSpeed; }
+ void SetMoveSpeed(float x, float y, float z) {
+ m_vecMoveSpeed.x = x;
+ m_vecMoveSpeed.y = y;
+ m_vecMoveSpeed.z = z;
+ }
+ void SetMoveSpeed(const CVector& speed) {
+ m_vecMoveSpeed = speed;
+ }
+ void AddToMoveSpeed(float x, float y, float z) {
+ m_vecMoveSpeed.x += x;
+ m_vecMoveSpeed.y += y;
+ m_vecMoveSpeed.z += z;
+ }
+ void AddToMoveSpeed(const CVector& addition) {
+ m_vecMoveSpeed += addition;
+ }
+ void AddToMoveSpeed(const CVector2D& addition) {
+ m_vecMoveSpeed += CVector(addition.x, addition.y, 0.0f);
+ }
+ const CVector &GetTurnSpeed() { return m_vecTurnSpeed; }
+ void SetTurnSpeed(float x, float y, float z) {
+ m_vecTurnSpeed.x = x;
+ m_vecTurnSpeed.y = y;
+ m_vecTurnSpeed.z = z;
+ }
+ const CVector &GetCenterOfMass() { return m_vecCentreOfMass; }
+ void SetCenterOfMass(float x, float y, float z) {
+ m_vecCentreOfMass.x = x;
+ m_vecCentreOfMass.y = y;
+ m_vecCentreOfMass.z = z;
+ }
+
+ void ApplyMoveSpeed(void);
+ void ApplyTurnSpeed(void);
+ // Force actually means Impulse here
+ void ApplyMoveForce(float jx, float jy, float jz);
+ void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); }
+ // j(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied
+ void ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz);
+ // j is direction of force, p is point relative to model center where force is applied
+ void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
+ void ApplyFrictionMoveForce(float jx, float jy, float jz);
+ void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); }
+ void ApplyFrictionTurnForce(float jx, float jy, float jz, float rx, float ry, float rz);
+ void ApplyFrictionTurnForce(const CVector &j, const CVector &p) { ApplyFrictionTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
+ // springRatio: 1.0 fully extended, 0.0 fully compressed
+ bool ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias);
+ bool ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir);
+ bool ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed);
+ void ApplyGravity(void);
+ void ApplyFriction(void);
+ void ApplyAirResistance(void);
+ bool ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB);
+ bool ApplyCollision(CColPoint &colpoint, float &impulse);
+ bool ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed);
+ bool ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint);
+ bool ApplyFriction(float adhesiveLimit, CColPoint &colpoint);
+
+ bool ProcessShiftSectorList(CPtrList *ptrlists);
+ bool ProcessCollisionSectorList_SimpleCar(CPtrList *lists);
+ bool ProcessCollisionSectorList(CPtrList *lists);
+ bool CheckCollision(void);
+ bool CheckCollision_SimpleCar(void);
+};