summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/audio/AudioManager.cpp19
-rw-r--r--src/core/FileLoader.cpp2
-rw-r--r--src/core/config.h1
-rw-r--r--src/modelinfo/VehicleModelInfo.cpp7
-rw-r--r--src/modelinfo/VehicleModelInfo.h5
-rw-r--r--src/vehicles/Automobile.cpp4
-rw-r--r--src/vehicles/Plane.cpp967
-rw-r--r--src/vehicles/Plane.h71
-rw-r--r--src/vehicles/Train.cpp12
9 files changed, 1048 insertions, 40 deletions
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index dfecafb2..bffdc27f 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -17,6 +17,7 @@
#include "SampleManager.h"
#include "Stats.h"
#include "Vehicle.h"
+#include "Plane.h"
#include "World.h"
uint32 *audioLogicTimers = (uint32 *)0x6508A0;
@@ -4338,10 +4339,6 @@ cAudioManager::ProcessHomeScriptObject(uint8 sound)
}
}
-float *PlanePathPosition = (float *)0x8F5FC8;
-float &LandingPoint = *(float *)0x8F2C7C;
-float &TakeOffPoint = *(float *)0x8E28A4;
-
void
cAudioManager::ProcessJumbo(cVehicleParams *params)
{
@@ -4352,9 +4349,9 @@ cAudioManager::ProcessJumbo(cVehicleParams *params)
CalculateDistance((bool *)params, params->m_fDistance);
plane = (CPlane *)params->m_pVehicle;
DoJumboVolOffset();
- position = PlanePathPosition[plane->m_wIndex];
+ position = PlanePathPosition[plane->m_nPlaneId];
if(position <= TakeOffPoint) {
- if(plane->field_656 <= 0.10334f) {
+ if(plane->m_fSpeed <= 0.10334f) {
ProcessJumboTaxi();
return;
}
@@ -4366,7 +4363,7 @@ cAudioManager::ProcessJumbo(cVehicleParams *params)
ProcessJumboFlying();
} else {
if(position > LandingPoint) {
- if(plane->field_656 > 0.10334f) {
+ if(plane->m_fSpeed > 0.10334f) {
ProcessJumboDecel(plane);
return;
}
@@ -4387,7 +4384,7 @@ cAudioManager::ProcessJumboAccel(CPlane *plane)
float modificator;
if(SetupJumboFlySound(20u)) {
- modificator = (plane->field_656 - 0.10334f) * 1.676f;
+ modificator = (plane->m_fSpeed - 0.10334f) * 1.676f;
if(modificator > 1.0f) modificator = 1.0f;
if(cAudioManager::SetupJumboRumbleSound(maxVolume * modificator) &&
SetupJumboTaxiSound((1.0f - modificator) * 75.f)) {
@@ -4412,7 +4409,7 @@ cAudioManager::ProcessJumboDecel(CPlane *plane)
float modificator;
if(SetupJumboFlySound(20u) && SetupJumboTaxiSound(75u)) {
- modificator = (plane->field_656 - 0.10334f) * 1.676f;
+ modificator = (plane->m_fSpeed - 0.10334f) * 1.676f;
if(modificator > 1.0f) modificator = 1.0f;
SetupJumboEngineSound(maxVolume * modificator, 6050.f * modificator + 16000);
SetupJumboWhineSound(18u, 29500);
@@ -4428,7 +4425,7 @@ cAudioManager::ProcessJumboFlying()
void
cAudioManager::ProcessJumboLanding(CPlane *plane)
{
- float modificator = (LandingPoint - PlanePathPosition[plane->m_wIndex]) * 0.0028571f;
+ float modificator = (LandingPoint - PlanePathPosition[plane->m_nPlaneId]) * 0.0028571f;
if(SetupJumboFlySound(107.f * modificator + 20)) {
if(SetupJumboTaxiSound(75.f * (1.f - modificator))) {
SetupJumboEngineSound(maxVolume, 22050);
@@ -4441,7 +4438,7 @@ cAudioManager::ProcessJumboLanding(CPlane *plane)
void
cAudioManager::ProcessJumboTakeOff(CPlane *plane)
{
- double modificator = (PlanePathPosition[plane->m_wIndex] - TakeOffPoint) * 0.0033333f;
+ double modificator = (PlanePathPosition[plane->m_nPlaneId] - TakeOffPoint) * 0.0033333f;
if(cAudioManager::SetupJumboFlySound((107.f * modificator) + 20) &&
cAudioManager::SetupJumboRumbleSound(maxVolume * (1.f - modificator))) {
diff --git a/src/core/FileLoader.cpp b/src/core/FileLoader.cpp
index 14dc91cd..e9c06201 100644
--- a/src/core/FileLoader.cpp
+++ b/src/core/FileLoader.cpp
@@ -744,7 +744,7 @@ CFileLoader::LoadVehicleObject(const char *line)
}else if(strncmp(type, "heli", 5) == 0){
mi->m_vehicleType = VEHICLE_TYPE_HELI;
}else if(strncmp(type, "plane", 6) == 0){
- mi->m_wheelId = misc;
+ mi->m_planeLodId = misc;
mi->m_wheelScale = 1.0f;
mi->m_vehicleType = VEHICLE_TYPE_PLANE;
}else if(strncmp(type, "bike", 5) == 0){
diff --git a/src/core/config.h b/src/core/config.h
index 1f684869..85d79a01 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -121,3 +121,4 @@ enum Config {
#define KANGAROO_CHEAT
#define ASPECT_RATIO_SCALE
#define USE_DEBUG_SCRIPT_LOADER
+#define EXPLODING_AIRTRAIN // can blow up jumbo jet with rocket launcher
diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp
index b412adcf..c031b76e 100644
--- a/src/modelinfo/VehicleModelInfo.cpp
+++ b/src/modelinfo/VehicleModelInfo.cpp
@@ -13,6 +13,7 @@
#include "Vehicle.h"
#include "Automobile.h"
#include "Train.h"
+#include "Plane.h"
#include "ModelIndices.h"
#include "ModelInfo.h"
@@ -110,9 +111,9 @@ RwObjectNameIdAssocation heliIds[] = {
RwObjectNameIdAssocation planeIds[] = {
{ "wheel_front_dummy", 2, 0 },
{ "wheel_rear_dummy", 3, 0 },
- { "light_tailplane", 2, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "light_left", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "light_right", 1, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "light_tailplane", PLANE_POS_LIGHT_TAIL, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "light_left", PLANE_POS_LIGHT_LEFT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "light_right", PLANE_POS_LIGHT_RIGHT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
diff --git a/src/modelinfo/VehicleModelInfo.h b/src/modelinfo/VehicleModelInfo.h
index b5399d44..1a6d6a55 100644
--- a/src/modelinfo/VehicleModelInfo.h
+++ b/src/modelinfo/VehicleModelInfo.h
@@ -57,7 +57,10 @@ public:
uint8 m_lastColour2;
char m_gameName[32];
int32 m_vehicleType;
- int32 m_wheelId;
+ union {
+ int32 m_wheelId;
+ int32 m_planeLodId;
+ };
float m_wheelScale;
int32 m_numDoors;
int32 m_handlingId;
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index 28ede4cd..00f53762 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -131,7 +131,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
field_594 = 0;
bNotDamagedUpsideDown = false;
bMoreResistantToDamage = false;
- m_fVelocityChangeForAudio = 0.f;
+ m_fVelocityChangeForAudio = 0.0f;
m_hydraulicState = 0;
for(i = 0; i < 4; i++){
@@ -4479,6 +4479,7 @@ CAutomobile::SetAllTaxiLights(bool set)
class CAutomobile_ : public CAutomobile
{
public:
+ void ctor(int32 id, uint8 CreatedBy) { ::new (this) CAutomobile(id, CreatedBy); }
void dtor() { CAutomobile::~CAutomobile(); }
void SetModelIndex_(uint32 id) { CAutomobile::SetModelIndex(id); }
void ProcessControl_(void) { CAutomobile::ProcessControl(); }
@@ -4508,6 +4509,7 @@ public:
};
STARTPATCHES
+ InjectHook(0x52C6B0, &CAutomobile_::ctor, PATCH_JUMP);
InjectHook(0x52D170, &CAutomobile_::dtor, PATCH_JUMP);
InjectHook(0x52D190, &CAutomobile_::SetModelIndex_, PATCH_JUMP);
InjectHook(0x531470, &CAutomobile_::ProcessControl_, PATCH_JUMP);
diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp
index 3bad1e07..8e0e313d 100644
--- a/src/vehicles/Plane.cpp
+++ b/src/vehicles/Plane.cpp
@@ -1,25 +1,984 @@
#include "common.h"
+#include "main.h"
#include "patcher.h"
+#include "General.h"
+#include "ModelIndices.h"
+#include "FileMgr.h"
+#include "Streaming.h"
+#include "Replay.h"
+#include "Camera.h"
+#include "Coronas.h"
+#include "Particle.h"
+#include "Explosion.h"
+#include "World.h"
+#include "HandlingMgr.h"
#include "Plane.h"
-CPlane::CPlane(int mi, uint8 owner)
+CPlaneNode *&pPathNodes = *(CPlaneNode**)0x8F1B68;
+CPlaneNode *&pPath2Nodes = *(CPlaneNode**)0x885B8C;
+CPlaneNode *&pPath3Nodes = *(CPlaneNode**)0x885B78;
+CPlaneNode *&pPath4Nodes = *(CPlaneNode**)0x885AD8;
+int32 &NumPathNodes = *(int32*)0x8F2BE4;
+int32 &NumPath2Nodes = *(int32*)0x941498;
+int32 &NumPath3Nodes = *(int32*)0x9414D8;
+int32 &NumPath4Nodes = *(int32*)0x9412C8;
+float &TotalLengthOfFlightPath = *(float*)0x8F2C6C;
+float &TotalLengthOfFlightPath2 = *(float*)0x64CFBC;
+float &TotalLengthOfFlightPath3 = *(float*)0x64CFD0;
+float &TotalLengthOfFlightPath4 = *(float*)0x64CFDC;
+float &TotalDurationOfFlightPath = *(float*)0x64CFB8;
+float &TotalDurationOfFlightPath2 = *(float*)0x64CFC0;
+float &TotalDurationOfFlightPath3 = *(float*)0x64CFD4;
+float &TotalDurationOfFlightPath4 = *(float*)0x64CFE0;
+float &LandingPoint = *(float*)0x8F2C7C;
+float &TakeOffPoint = *(float*)0x8E28A4;
+CPlaneInterpolationLine *aPlaneLineBits = (CPlaneInterpolationLine*)0x734168; //[6]
+
+float *PlanePathPosition = (float*)0x8F5FC8; //[3]
+float *OldPlanePathPosition = (float*)0x8F5FBC; //[3]
+float *PlanePathSpeed = (float*)0x941538; //[3]
+float *PlanePath2Position = (float*)0x64CFC4; //[3]
+float &PlanePath3Position = *(float*)0x64CFD8;
+float &PlanePath4Position = *(float*)0x64CFE4;
+float *PlanePath2Speed = (float*)0x8F1A54; //[3]
+float &PlanePath3Speed = *(float*)0x8F1A94;
+float &PlanePath4Speed = *(float*)0x8F1AFC;
+
+
+enum
{
- ctor(mi, owner);
-}
+ CESNA_STATUS_NONE, // doesn't even exist
+ CESNA_STATUS_FLYING,
+ CESNA_STATUS_DESTROYED,
+ CESNA_STATUS_LANDED,
+};
+
+int32 &CesnaMissionStatus = *(int32*)0x64CFE8;
+int32 &CesnaMissionStartTime = *(int32*)0x64CFEC;
+CPlane *&pDrugRunCesna = *(CPlane**)0x8F5F80;
+int32 &DropOffCesnaMissionStatus = *(int32*)0x64CFF0;
+int32 &DropOffCesnaMissionStartTime = *(int32*)0x64CFF4;
+CPlane *&pDropOffCesna = *(CPlane**)0x8E2A38;
-WRAPPER CPlane* CPlane::ctor(int, uint8) { EAXJMP(0x54B170); }
+
+CPlane::CPlane(int32 id, uint8 CreatedBy)
+ : CVehicle(CreatedBy)
+{
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
+ m_vehType = VEHICLE_TYPE_PLANE;
+ pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId);
+ SetModelIndex(id);
+
+ m_fMass = 100000000.0f;
+ m_fTurnMass = 100000000.0f;
+ m_fAirResistance = 0.9994f;
+ m_fElasticity = 0.05f;
+
+ bUsesCollision = false;
+ m_bHasBeenHit = false;
+ m_bIsDrugRunCesna = false;
+ m_bIsDropOffCesna = false;
+
+ m_status = STATUS_PLANE;
+ bIsBIGBuilding = true;
+ m_level = LEVEL_NONE;
+}
CPlane::~CPlane()
{
DeleteRwObject();
}
+void
+CPlane::SetModelIndex(uint32 id)
+{
+ CVehicle::SetModelIndex(id);
+}
+
+void
+CPlane::DeleteRwObject(void)
+{
+ if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){
+ m_matrix.Detach();
+ if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check
+ RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject);
+ RpAtomicDestroy((RpAtomic*)m_rwObject);
+ RwFrameDestroy(f);
+ }
+ m_rwObject = nil;
+ }
+ CEntity::DeleteRwObject();
+}
+
+// There's a LOT of copy and paste in here. Maybe this could be refactored somehow
+void
+CPlane::ProcessControl(void)
+{
+ int i;
+ CVector pos;
+
+ // Explosion
+ if(m_bHasBeenHit){
+ // BUG: since this is all based on frames, you can skip the explosion processing when you go into the menu
+ if(GetModelIndex() == MI_AIRTRAIN){
+ int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit;
+ if(frm == 20){
+ static int nFrameGen;
+ CRGBA colors[8];
+
+ CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0);
+
+ colors[0] = CRGBA(0, 0, 0, 255);
+ colors[1] = CRGBA(224, 230, 238, 255);
+ colors[2] = CRGBA(224, 230, 238, 255);
+ colors[3] = CRGBA(0, 0, 0, 255);
+ colors[4] = CRGBA(224, 230, 238, 255);
+ colors[5] = CRGBA(0, 0, 0, 255);
+ colors[6] = CRGBA(0, 0, 0, 255);
+ colors[7] = CRGBA(224, 230, 238, 255);
+
+ for(i = 0; i < 40; i++){
+ int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f);
+ if(CGeneral::GetRandomNumber() & 1)
+ rotSpeed = -rotSpeed;
+ int f = ++nFrameGen & 3;
+ CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f),
+ CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
+ CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
+ CGeneral::GetRandomNumberInRange(0.0f, 2.0f)),
+ nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
+ colors[nFrameGen], rotSpeed, 0, f, 0);
+ }
+ }
+ if(frm >= 40 && frm <= 80 && frm & 1){
+ if(frm & 1){
+ pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f;
+ pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f;
+ pos.y = frm - 40;
+ pos = GetMatrix() * pos;
+ }else{
+ pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f;
+ pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f;
+ pos.y = 40 - frm;
+ pos = GetMatrix() * pos;
+ }
+ CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0);
+ }
+ if(frm == 60)
+ bRenderScorched = true;
+ if(frm == 82){
+ TheCamera.SetFadeColour(255, 255, 255);
+ TheCamera.Fade(0.0f, FADE_OUT);
+ TheCamera.ProcessFade();
+ TheCamera.Fade(1.0f, FADE_IN);
+ FlagToDestroyWhenNextProcessed();
+ }
+ }else{
+ int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit;
+ if(frm == 20){
+ static int nFrameGen;
+ CRGBA colors[8];
+
+ CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0);
+
+ colors[0] = CRGBA(0, 0, 0, 255);
+ colors[1] = CRGBA(224, 230, 238, 255);
+ colors[2] = CRGBA(224, 230, 238, 255);
+ colors[3] = CRGBA(0, 0, 0, 255);
+ colors[4] = CRGBA(252, 66, 66, 255);
+ colors[5] = CRGBA(0, 0, 0, 255);
+ colors[6] = CRGBA(0, 0, 0, 255);
+ colors[7] = CRGBA(252, 66, 66, 255);
+
+ for(i = 0; i < 40; i++){
+ int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f);
+ if(CGeneral::GetRandomNumber() & 1)
+ rotSpeed = -rotSpeed;
+ int f = ++nFrameGen & 3;
+ CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f),
+ CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
+ CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
+ CGeneral::GetRandomNumberInRange(0.0f, 2.0f)),
+ nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
+ colors[nFrameGen], rotSpeed, 0, f, 0);
+ }
+ }
+ if(frm >= 40 && frm <= 60 && frm & 1){
+ if(frm & 1){
+ pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f;
+ pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f;
+ pos.y = (frm - 40)*0.3f;
+ pos = GetMatrix() * pos;
+ }else{
+ pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f;
+ pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f;
+ pos.y = (40 - frm)*0.3f;
+ pos = GetMatrix() * pos;
+ }
+ CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0);
+ }
+ if(frm == 30)
+ bRenderScorched = true;
+ if(frm == 61){
+ TheCamera.SetFadeColour(200, 200, 200);
+ TheCamera.Fade(0.0f, FADE_OUT);
+ TheCamera.ProcessFade();
+ TheCamera.Fade(1.0f, FADE_IN);
+ if(m_bIsDrugRunCesna){
+ CesnaMissionStatus = CESNA_STATUS_DESTROYED;
+ pDrugRunCesna = nil;
+ }
+ if(m_bIsDropOffCesna){
+ DropOffCesnaMissionStatus = CESNA_STATUS_DESTROYED;
+ pDropOffCesna = nil;
+ }
+ FlagToDestroyWhenNextProcessed();
+ }
+ }
+ }
+
+ // Update plane position and speed
+ if(GetModelIndex() == MI_AIRTRAIN || !m_isFarAway || ((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0){
+ if(GetModelIndex() == MI_AIRTRAIN){
+ float pathPositionRear = PlanePathPosition[m_nPlaneId] - 30.0f;
+ if(pathPositionRear < 0.0f)
+ pathPositionRear += TotalLengthOfFlightPath;
+ float pathPosition = pathPositionRear + 30.0f;
+
+ float pitch = 0.0f;
+ float distSinceTakeOff = pathPosition - TakeOffPoint;
+ if(distSinceTakeOff <= 0.0f && distSinceTakeOff > -70.0f){
+ // shortly before take off
+ pitch = 1.0f - distSinceTakeOff/-70.0f;
+ }else if(distSinceTakeOff >= 0.0f && distSinceTakeOff < 100.0f){
+ // shortly after take off
+ pitch = 1.0f - distSinceTakeOff/100.0f;
+ }
+
+ float distSinceLanding = pathPosition - LandingPoint;
+ if(distSinceLanding <= 0.0f && distSinceLanding > -200.0f){
+ // shortly before landing
+ pitch = 1.0f - distSinceLanding/-200.0f;
+ }else if(distSinceLanding >= 0.0f && distSinceLanding < 70.0f){
+ // shortly after landing
+ pitch = 1.0f - distSinceLanding/70.0f;
+ }
+
+
+ // Advance current node to appropriate position
+ float pos1, pos2;
+ int nextTrackNode = m_nCurPathNode + 1;
+ pos1 = pPathNodes[m_nCurPathNode].t;
+ if(nextTrackNode < NumPathNodes)
+ pos2 = pPathNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ while(pathPositionRear < pos1 || pathPositionRear > pos2){
+ m_nCurPathNode = (m_nCurPathNode+1) % NumPathNodes;
+ nextTrackNode = m_nCurPathNode + 1;
+ pos1 = pPathNodes[m_nCurPathNode].t;
+ if(nextTrackNode < NumPathNodes)
+ pos2 = pPathNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ }
+ bool bothOnGround = pPathNodes[m_nCurPathNode].bOnGround && pPathNodes[nextTrackNode].bOnGround;
+ if(PlanePathPosition[m_nPlaneId] >= LandingPoint && OldPlanePathPosition[m_nPlaneId] < LandingPoint)
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_PLANE_ON_GROUND, 0.0f);
+ float dist = pPathNodes[nextTrackNode].t - pPathNodes[m_nCurPathNode].t;
+ if(dist < 0.0f)
+ dist += TotalLengthOfFlightPath;
+ float f = (pathPositionRear - pPathNodes[m_nCurPathNode].t)/dist;
+ CVector posRear = (1.0f - f)*pPathNodes[m_nCurPathNode].p + f*pPathNodes[nextTrackNode].p;
+
+ // Same for the front
+ float pathPositionFront = pathPositionRear + 60.0f;
+ if(pathPositionFront > TotalLengthOfFlightPath)
+ pathPositionFront -= TotalLengthOfFlightPath;
+ int curPathNodeFront = m_nCurPathNode;
+ int nextPathNodeFront = curPathNodeFront + 1;
+ pos1 = pPathNodes[curPathNodeFront].t;
+ if(nextPathNodeFront < NumPathNodes)
+ pos2 = pPathNodes[nextPathNodeFront].t;
+ else{
+ nextPathNodeFront = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ while(pathPositionFront < pos1 || pathPositionFront > pos2){
+ curPathNodeFront = (curPathNodeFront+1) % NumPathNodes;
+ nextPathNodeFront = curPathNodeFront + 1;
+ pos1 = pPathNodes[curPathNodeFront].t;
+ if(nextPathNodeFront < NumPathNodes)
+ pos2 = pPathNodes[nextPathNodeFront].t;
+ else{
+ nextPathNodeFront = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ }
+ dist = pPathNodes[nextPathNodeFront].t - pPathNodes[curPathNodeFront].t;
+ if(dist < 0.0f)
+ dist += TotalLengthOfFlightPath;
+ f = (pathPositionFront - pPathNodes[curPathNodeFront].t)/dist;
+ CVector posFront = (1.0f - f)*pPathNodes[curPathNodeFront].p + f*pPathNodes[nextPathNodeFront].p;
+
+ // And for another point 60 units in front of the plane, used to calculate roll
+ float pathPositionFront2 = pathPositionFront + 60.0f;
+ if(pathPositionFront2 > TotalLengthOfFlightPath)
+ pathPositionFront2 -= TotalLengthOfFlightPath;
+ int curPathNodeFront2 = m_nCurPathNode;
+ int nextPathNodeFront2 = curPathNodeFront2 + 1;
+ pos1 = pPathNodes[curPathNodeFront2].t;
+ if(nextPathNodeFront2 < NumPathNodes)
+ pos2 = pPathNodes[nextPathNodeFront2].t;
+ else{
+ nextPathNodeFront2 = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){
+ curPathNodeFront2 = (curPathNodeFront2+1) % NumPathNodes;
+ nextPathNodeFront2 = curPathNodeFront2 + 1;
+ pos1 = pPathNodes[curPathNodeFront2].t;
+ if(nextPathNodeFront2 < NumPathNodes)
+ pos2 = pPathNodes[nextPathNodeFront2].t;
+ else{
+ nextPathNodeFront2 = 0;
+ pos2 = TotalLengthOfFlightPath;
+ }
+ }
+ dist = pPathNodes[nextPathNodeFront2].t - pPathNodes[curPathNodeFront2].t;
+ if(dist < 0.0f)
+ dist += TotalLengthOfFlightPath;
+ f = (pathPositionFront2 - pPathNodes[curPathNodeFront2].t)/dist;
+ CVector posFront2 = (1.0f - f)*pPathNodes[curPathNodeFront2].p + f*pPathNodes[nextPathNodeFront2].p;
+
+ // Now set matrix
+ GetPosition() = (posRear + posFront)/2.0f;
+ GetPosition().z += 4.3f;
+ CVector fwd = posFront - posRear;
+ fwd.Normalise();
+ if(pitch != 0.0f){
+ fwd.z += 0.4f*pitch;
+ fwd.Normalise();
+ }
+ CVector fwd2 = posFront2 - posRear;
+ fwd2.Normalise();
+ CVector roll = CrossProduct(fwd, fwd2);
+ CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f));
+ if(!bothOnGround)
+ right.z += 3.0f*roll.z;
+ right.Normalise();
+ CVector up = CrossProduct(right, fwd);
+ GetRight() = right;
+ GetUp() = up;
+ GetForward() = fwd;
+
+ // Set speed
+ m_vecMoveSpeed = fwd*PlanePathSpeed[m_nPlaneId]/60.0f;
+ m_fSpeed = PlanePathSpeed[m_nPlaneId]/60.0f;
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+
+ m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f));
+ }else{
+ float planePathPosition;
+ float totalLengthOfFlightPath;
+ CPlaneNode *pathNodes;
+ float planePathSpeed;
+ int numPathNodes;
+
+ if(m_bIsDrugRunCesna){
+ planePathPosition = PlanePath3Position;
+ totalLengthOfFlightPath = TotalLengthOfFlightPath3;
+ pathNodes = pPath3Nodes;
+ planePathSpeed = PlanePath3Speed;
+ numPathNodes = NumPath3Nodes;
+ if(CesnaMissionStatus == CESNA_STATUS_LANDED){
+ pDrugRunCesna = false;
+ FlagToDestroyWhenNextProcessed();
+ }
+ }else if(m_bIsDropOffCesna){
+ planePathPosition = PlanePath4Position;
+ totalLengthOfFlightPath = TotalLengthOfFlightPath4;
+ pathNodes = pPath4Nodes;
+ planePathSpeed = PlanePath4Speed;
+ numPathNodes = NumPath4Nodes;
+ if(DropOffCesnaMissionStatus == CESNA_STATUS_LANDED){
+ pDropOffCesna = false;
+ FlagToDestroyWhenNextProcessed();
+ }
+ }else{
+ planePathPosition = PlanePath2Position[m_nPlaneId];
+ totalLengthOfFlightPath = TotalLengthOfFlightPath2;
+ pathNodes = pPath2Nodes;
+ planePathSpeed = PlanePath2Speed[m_nPlaneId];
+ numPathNodes = NumPath2Nodes;
+ }
+
+ // Advance current node to appropriate position
+ float pathPositionRear = planePathPosition - 10.0f;
+ if(pathPositionRear < 0.0f)
+ pathPositionRear += totalLengthOfFlightPath;
+ float pos1, pos2;
+ int nextTrackNode = m_nCurPathNode + 1;
+ pos1 = pathNodes[m_nCurPathNode].t;
+ if(nextTrackNode < numPathNodes)
+ pos2 = pathNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ while(pathPositionRear < pos1 || pathPositionRear > pos2){
+ m_nCurPathNode = (m_nCurPathNode+1) % numPathNodes;
+ nextTrackNode = m_nCurPathNode + 1;
+ pos1 = pathNodes[m_nCurPathNode].t;
+ if(nextTrackNode < numPathNodes)
+ pos2 = pathNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ }
+ float dist = pathNodes[nextTrackNode].t - pathNodes[m_nCurPathNode].t;
+ if(dist < 0.0f)
+ dist += totalLengthOfFlightPath;
+ float f = (pathPositionRear - pathNodes[m_nCurPathNode].t)/dist;
+ CVector posRear = (1.0f - f)*pathNodes[m_nCurPathNode].p + f*pathNodes[nextTrackNode].p;
+
+ // Same for the front
+ float pathPositionFront = pathPositionRear + 20.0f;
+ if(pathPositionFront > totalLengthOfFlightPath)
+ pathPositionFront -= totalLengthOfFlightPath;
+ int curPathNodeFront = m_nCurPathNode;
+ int nextPathNodeFront = curPathNodeFront + 1;
+ pos1 = pathNodes[curPathNodeFront].t;
+ if(nextPathNodeFront < numPathNodes)
+ pos2 = pathNodes[nextPathNodeFront].t;
+ else{
+ nextPathNodeFront = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ while(pathPositionFront < pos1 || pathPositionFront > pos2){
+ curPathNodeFront = (curPathNodeFront+1) % numPathNodes;
+ nextPathNodeFront = curPathNodeFront + 1;
+ pos1 = pathNodes[curPathNodeFront].t;
+ if(nextPathNodeFront < numPathNodes)
+ pos2 = pathNodes[nextPathNodeFront].t;
+ else{
+ nextPathNodeFront = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ }
+ dist = pathNodes[nextPathNodeFront].t - pathNodes[curPathNodeFront].t;
+ if(dist < 0.0f)
+ dist += totalLengthOfFlightPath;
+ f = (pathPositionFront - pathNodes[curPathNodeFront].t)/dist;
+ CVector posFront = (1.0f - f)*pathNodes[curPathNodeFront].p + f*pathNodes[nextPathNodeFront].p;
+
+ // And for another point 60 units in front of the plane, used to calculate roll
+ float pathPositionFront2 = pathPositionFront + 30.0f;
+ if(pathPositionFront2 > totalLengthOfFlightPath)
+ pathPositionFront2 -= totalLengthOfFlightPath;
+ int curPathNodeFront2 = m_nCurPathNode;
+ int nextPathNodeFront2 = curPathNodeFront2 + 1;
+ pos1 = pathNodes[curPathNodeFront2].t;
+ if(nextPathNodeFront2 < numPathNodes)
+ pos2 = pathNodes[nextPathNodeFront2].t;
+ else{
+ nextPathNodeFront2 = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){
+ curPathNodeFront2 = (curPathNodeFront2+1) % numPathNodes;
+ nextPathNodeFront2 = curPathNodeFront2 + 1;
+ pos1 = pathNodes[curPathNodeFront2].t;
+ if(nextPathNodeFront2 < numPathNodes)
+ pos2 = pathNodes[nextPathNodeFront2].t;
+ else{
+ nextPathNodeFront2 = 0;
+ pos2 = totalLengthOfFlightPath;
+ }
+ }
+ dist = pathNodes[nextPathNodeFront2].t - pathNodes[curPathNodeFront2].t;
+ if(dist < 0.0f)
+ dist += totalLengthOfFlightPath;
+ f = (pathPositionFront2 - pathNodes[curPathNodeFront2].t)/dist;
+ CVector posFront2 = (1.0f - f)*pathNodes[curPathNodeFront2].p + f*pathNodes[nextPathNodeFront2].p;
+
+ // Now set matrix
+ GetPosition() = (posRear + posFront)/2.0f;
+ GetPosition().z += 1.0f;
+ CVector fwd = posFront - posRear;
+ fwd.Normalise();
+ CVector fwd2 = posFront2 - posRear;
+ fwd2.Normalise();
+ CVector roll = CrossProduct(fwd, fwd2);
+ CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f));
+ right.z += 3.0f*roll.z;
+ right.Normalise();
+ CVector up = CrossProduct(right, fwd);
+ GetRight() = right;
+ GetUp() = up;
+ GetForward() = fwd;
+
+ // Set speed
+ m_vecMoveSpeed = fwd*planePathSpeed/60.0f;
+ m_fSpeed = planePathSpeed/60.0f;
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+
+ m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f));
+ }
+ }
+
+ bIsInSafePosition = true;
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+
+ // Handle streaming and such
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+ if(m_isFarAway){
+ // Switch to LOD model
+ if(m_rwObject && RwObjectGetType(m_rwObject) == rpCLUMP){
+ DeleteRwObject();
+ if(mi->m_planeLodId != -1){
+ m_rwObject = CModelInfo::GetModelInfo(mi->m_planeLodId)->CreateInstance();
+ if(m_rwObject)
+ m_matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)));
+ }
+ }
+ }else if(CStreaming::HasModelLoaded(GetModelIndex())){
+ if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){
+ // Get rid of LOD model
+ m_matrix.Detach();
+ if(m_rwObject){ // useless check
+ if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check
+ RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject);
+ RpAtomicDestroy((RpAtomic*)m_rwObject);
+ RwFrameDestroy(f);
+ }
+ m_rwObject = nil;
+ }
+ }
+ // Set high detail model
+ if(m_rwObject == nil){
+ int id = GetModelIndex();
+ m_modelIndex = -1;
+ SetModelIndex(id);
+ }
+ }else{
+ CStreaming::RequestModel(GetModelIndex(), STREAMFLAGS_DEPENDENCY);
+ }
+}
+
+void
+CPlane::PreRender(void)
+{
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+
+ CVector lookVector = GetPosition() - TheCamera.GetPosition();
+ float camDist = lookVector.Magnitude();
+ if(camDist != 0.0f)
+ lookVector *= 1.0f/camDist;
+ else
+ lookVector = CVector(1.0f, 0.0f, 0.0f);
+ float behindness = DotProduct(lookVector, GetForward());
+
+ // Wing lights
+ if(behindness < 0.0f){
+ // in front of plane
+ CVector lightPos = mi->m_positions[PLANE_POS_LIGHT_RIGHT];
+ CVector lightR = GetMatrix() * lightPos;
+ CVector lightL = lightR;
+ lightL -= GetRight()*2.0f*lightPos.x;
+
+ float intensity = -0.6f*behindness + 0.4f;
+ float size = 1.0f - behindness;
+
+ if(behindness < -0.9f && camDist < 50.0f){
+ // directly in front
+ CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255,
+ lightL, size, 240.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255,
+ lightR, size, 240.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ }else{
+ CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255,
+ lightL, size, 240.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255,
+ lightR, size, 240.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ }
+ }
+
+ // Tail light
+ if(CTimer::GetTimeInMilliseconds() & 0x200){
+ CVector pos = GetMatrix() * mi->m_positions[PLANE_POS_LIGHT_TAIL];
+
+ CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255,
+ pos, 1.0f, 120.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ }
+}
+
+void
+CPlane::Render(void)
+{
+ CEntity::Render();
+}
+
+#define CRUISE_SPEED (50.0f)
+#define TAXI_SPEED (5.0f)
+
+void
+CPlane::InitPlanes(void)
+{
+ int i;
+
+ CesnaMissionStatus = CESNA_STATUS_NONE;
+
+ // Jumbo
+ if(pPathNodes == nil){
+ pPathNodes = LoadPath("data\\paths\\flight.dat", NumPathNodes, TotalLengthOfFlightPath, true);
+
+ // Figure out which nodes are on ground
+ CColPoint colpoint;
+ CEntity *entity;
+ for(i = 0; i < NumPathNodes; i++){
+ if(CWorld::ProcessVerticalLine(pPathNodes[i].p, 1000.0f, colpoint, entity, true, false, false, false, true, false, nil)){
+ pPathNodes[i].p.z = colpoint.point.z;
+ pPathNodes[i].bOnGround = true;
+ }else
+ pPathNodes[i].bOnGround = false;
+ }
+
+ // Find lading and takeoff points
+ LandingPoint = -1.0f;
+ TakeOffPoint = -1.0f;
+ bool lastOnGround = pPathNodes[NumPathNodes-1].bOnGround;
+ for(i = 0; i < NumPathNodes; i++){
+ if(pPathNodes[i].bOnGround && !lastOnGround)
+ LandingPoint = pPathNodes[i].t;
+ else if(!pPathNodes[i].bOnGround && lastOnGround)
+ TakeOffPoint = pPathNodes[i].t;
+ lastOnGround = pPathNodes[i].bOnGround;
+ }
+
+ // Animation
+ float time = 0.0f;
+ float position = 0.0f;
+ // Start on ground with slow speed
+ aPlaneLineBits[0].type = 1;
+ aPlaneLineBits[0].time = time;
+ aPlaneLineBits[0].position = position;
+ aPlaneLineBits[0].speed = TAXI_SPEED;
+ aPlaneLineBits[0].acceleration = 0.0f;
+ float dist = (TakeOffPoint-600.0f) - position;
+ time += dist/TAXI_SPEED;
+ position += dist;
+
+ // Accelerate to take off
+ aPlaneLineBits[1].type = 2;
+ aPlaneLineBits[1].time = time;
+ aPlaneLineBits[1].position = position;
+ aPlaneLineBits[1].speed = TAXI_SPEED;
+ aPlaneLineBits[1].acceleration = 33.0f/32.0f;
+ time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
+ position += 600.0f;
+
+ // Fly at cruise speed
+ aPlaneLineBits[2].type = 1;
+ aPlaneLineBits[2].time = time;
+ aPlaneLineBits[2].position = position;
+ aPlaneLineBits[2].speed = CRUISE_SPEED;
+ aPlaneLineBits[2].acceleration = 0.0f;
+ dist = LandingPoint - TakeOffPoint;
+ time += dist/CRUISE_SPEED;
+ position += dist;
+
+ // Brake after landing
+ aPlaneLineBits[3].type = 2;
+ aPlaneLineBits[3].time = time;
+ aPlaneLineBits[3].position = position;
+ aPlaneLineBits[3].speed = CRUISE_SPEED;
+ aPlaneLineBits[3].acceleration = -33.0f/32.0f;
+ time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f);
+ position += 600.0f;
+
+ // Taxi
+ aPlaneLineBits[4].type = 1;
+ aPlaneLineBits[4].time = time;
+ aPlaneLineBits[4].position = position;
+ aPlaneLineBits[4].speed = TAXI_SPEED;
+ aPlaneLineBits[4].acceleration = 0.0f;
+ time += (TotalLengthOfFlightPath - position)/TAXI_SPEED;
+
+ // end
+ aPlaneLineBits[5].time = time;
+ TotalDurationOfFlightPath = time;
+ }
+
+ // Dodo
+ if(pPath2Nodes == nil){
+ pPath2Nodes = LoadPath("data\\paths\\flight2.dat", NumPath2Nodes, TotalLengthOfFlightPath2, true);
+ TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED;
+ }
+
+ // Mission Cesna
+ if(pPath3Nodes == nil){
+ pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false);
+ TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED;
+ }
+
+ // Mission Cesna
+ if(pPath4Nodes == nil){
+ pPath4Nodes = LoadPath("data\\paths\\flight4.dat", NumPath4Nodes, TotalLengthOfFlightPath4, false);
+ TotalDurationOfFlightPath4 = TotalLengthOfFlightPath4/CRUISE_SPEED;
+ }
+
+ CStreaming::LoadAllRequestedModels(false);
+ CStreaming::RequestModel(MI_AIRTRAIN, 0);
+ CStreaming::LoadAllRequestedModels(false);
+
+ for(i = 0; i < 3; i++){
+ CPlane *plane = new CPlane(MI_AIRTRAIN, PERMANENT_VEHICLE);
+ plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ plane->m_status = STATUS_ABANDONED;
+ plane->bIsLocked = true;
+ plane->m_nPlaneId = i;
+ plane->m_nCurPathNode = 0;
+ CWorld::Add(plane);
+ }
+
+
+ CStreaming::RequestModel(MI_DEADDODO, 0);
+ CStreaming::LoadAllRequestedModels(false);
+
+ for(i = 0; i < 3; i++){
+ CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE);
+ plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ plane->m_status = STATUS_ABANDONED;
+ plane->bIsLocked = true;
+ plane->m_nPlaneId = i;
+ plane->m_nCurPathNode = 0;
+ CWorld::Add(plane);
+ }
+}
+
+void
+CPlane::Shutdown(void)
+{
+ delete[] pPathNodes;
+ delete[] pPath2Nodes;
+ delete[] pPath3Nodes;
+ delete[] pPath4Nodes;
+ pPathNodes = nil;
+ pPath2Nodes = nil;
+ pPath3Nodes = nil;
+ pPath4Nodes = nil;
+}
+
+CPlaneNode*
+CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop)
+{
+ int bp, lp;
+ int i;
+
+ CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r");
+ *gString = '\0';
+ for(bp = 0, lp = 0; work_buff[bp] != '\n'; bp++, lp++)
+ gString[lp] = work_buff[bp];
+ bp++;
+ gString[lp] = '\0';
+ sscanf(gString, "%d", &numNodes);
+ CPlaneNode *nodes = new CPlaneNode[numNodes];
+
+ for(i = 0; i < numNodes; i++){
+ *gString = '\0';
+ for(lp = 0; work_buff[bp] != '\n'; bp++, lp++)
+ gString[lp] = work_buff[bp];
+ bp++;
+ // BUG: game doesn't terminate string
+ gString[lp] = '\0';
+ sscanf(gString, "%f %f %f", &nodes[i].p.x, &nodes[i].p.y, &nodes[i].p.z);
+ }
+
+ // Calculate length of segments and path
+ totalLength = 0.0f;
+ for(i = 0; i < numNodes; i++){
+ nodes[i].t = totalLength;
+ float l = (nodes[(i+1) % numNodes].p - nodes[i].p).Magnitude2D();
+ if(!loop && i == numNodes-1)
+ l = 0.0f;
+ totalLength += l;
+ }
+
+ return nodes;
+}
+
+void
+CPlane::UpdatePlanes(void)
+{
+ int i, j;
+ uint32 time;
+ float t, deltaT;
+
+ if(CReplay::IsPlayingBack())
+ return;
+
+ // Jumbo jets
+ time = CTimer::GetTimeInMilliseconds();
+ for(i = 0; i < 3; i++){
+ t = TotalDurationOfFlightPath * (float)(time & 0x7FFFF)/0x80000;
+ // find current frame
+ for(j = 0; t > aPlaneLineBits[j+1].time; j++);
+
+ OldPlanePathPosition[i] = PlanePathPosition[i];
+ deltaT = t - aPlaneLineBits[j].time;
+ switch(aPlaneLineBits[j].type){
+ case 0: // standing still
+ PlanePathPosition[i] = aPlaneLineBits[j].position;
+ PlanePathSpeed[i] = 0.0f;
+ break;
+ case 1: // moving with constant speed
+ PlanePathPosition[i] = aPlaneLineBits[j].position + aPlaneLineBits[j].speed*deltaT;
+ PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000) * aPlaneLineBits[j].speed;
+ break;
+ case 2: // accelerating/braking
+ PlanePathPosition[i] = aPlaneLineBits[j].position + (aPlaneLineBits[j].speed + aPlaneLineBits[j].acceleration*deltaT)*deltaT;
+ PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000)*aPlaneLineBits[j].speed + 2.0f*aPlaneLineBits[j].acceleration*deltaT;
+ break;
+ }
+
+ // time offset for each plane
+ time += 0x80000/3;
+ }
+
+ time = CTimer::GetTimeInMilliseconds();
+
+ t = TotalDurationOfFlightPath2/0x80000;
+ PlanePath2Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t;
+ PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/3) & 0x7FFFF)*t;
+ PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/3*2) & 0x7FFFF)*t;
+ PlanePath2Speed[0] = CRUISE_SPEED*t;
+ PlanePath2Speed[1] = CRUISE_SPEED*t;
+ PlanePath2Speed[2] = CRUISE_SPEED*t;
+
+ if(CesnaMissionStatus == CESNA_STATUS_FLYING){
+ PlanePath3Speed = CRUISE_SPEED*TotalDurationOfFlightPath3/0x20000;
+ PlanePath3Position = PlanePath3Speed * ((time - CesnaMissionStartTime) & 0x1FFFF);
+ if(time - CesnaMissionStartTime >= 128072)
+ CesnaMissionStatus = CESNA_STATUS_LANDED;
+ }
+
+ if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){
+ PlanePath4Speed = CRUISE_SPEED*TotalDurationOfFlightPath4/0x80000;
+ PlanePath4Position = PlanePath4Speed * ((time - DropOffCesnaMissionStartTime) & 0x7FFFF);
+ if(time - DropOffCesnaMissionStartTime >= 521288)
+ DropOffCesnaMissionStatus = CESNA_STATUS_LANDED;
+ }
+}
+
+bool
+CPlane::TestRocketCollision(CVector *rocketPos)
+{
+ int i;
+
+ i = CPools::GetVehiclePool()->GetSize();
+ while(--i >= 0){
+ CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i);
+ if(plane &&
+#ifdef EXPLODING_AIRTRAIN
+ (plane->GetModelIndex() == MI_AIRTRAIN || plane->GetModelIndex() == MI_DODO) &&
+#else
+ plane->GetModelIndex() != MI_AIRTRAIN && plane->GetModelIndex() == MI_DODO && // strange check
+#endif
+ !plane->m_bHasBeenHit && (*rocketPos - plane->GetPosition()).Magnitude() < 25.0f){
+ plane->m_nFrameWhenHit = CTimer::GetFrameCounter();
+ plane->m_bHasBeenHit = true;
+ CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_DESTROYED_CESSNA,
+ plane->GetPosition(), i+1983, false);
+ return true;
+ }
+ }
+ return false;
+}
+
+// BUG: not in CPlane in the game
+void
+CPlane::CreateIncomingCesna(void)
+{
+ if(CesnaMissionStatus == CESNA_STATUS_FLYING){
+ CWorld::Remove(pDrugRunCesna);
+ delete pDrugRunCesna;
+ pDrugRunCesna = nil;
+ }
+ pDrugRunCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE);
+ pDrugRunCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ pDrugRunCesna->m_status = STATUS_ABANDONED;
+ pDrugRunCesna->bIsLocked = true;
+ pDrugRunCesna->m_nPlaneId = 0;
+ pDrugRunCesna->m_nCurPathNode = 0;
+ pDrugRunCesna->m_bIsDrugRunCesna = true;
+ CWorld::Add(pDrugRunCesna);
+
+ CesnaMissionStatus = CESNA_STATUS_FLYING;
+ CesnaMissionStartTime = CTimer::GetTimeInMilliseconds();
+ printf("CPlane::CreateIncomingCesna(void)\n");
+}
+
+void
+CPlane::CreateDropOffCesna(void)
+{
+ if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){
+ CWorld::Remove(pDropOffCesna);
+ delete pDropOffCesna;
+ pDropOffCesna = nil;
+ }
+ pDropOffCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE);
+ pDropOffCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ pDropOffCesna->m_status = STATUS_ABANDONED;
+ pDropOffCesna->bIsLocked = true;
+ pDropOffCesna->m_nPlaneId = 0;
+ pDropOffCesna->m_nCurPathNode = 0;
+ pDropOffCesna->m_bIsDropOffCesna = true;
+ CWorld::Add(pDropOffCesna);
+
+ DropOffCesnaMissionStatus = CESNA_STATUS_FLYING;
+ DropOffCesnaMissionStartTime = CTimer::GetTimeInMilliseconds();
+ printf("CPlane::CreateDropOffCesna(void)\n");
+}
+
+CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); }
+CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDrugRunCesna->GetPosition(); }
+bool CPlane::HasCesnaLanded(void) { return CesnaMissionStatus == CESNA_STATUS_LANDED; }
+bool CPlane::HasCesnaBeenDestroyed(void) { return CesnaMissionStatus == CESNA_STATUS_DESTROYED; }
+bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatus == CESNA_STATUS_DESTROYED; }
+
+
class CPlane_ : public CPlane
{
public:
+ void ctor(int32 id, uint8 CreatedBy) { ::new (this) CPlane(id, CreatedBy); }
void dtor(void) { CPlane::~CPlane(); }
};
STARTPATCHES
+ InjectHook(0x54B170, &CPlane_::ctor, PATCH_JUMP);
InjectHook(0x54B270, &CPlane_::dtor, PATCH_JUMP);
+ InjectHook(0x54B820, CPlane::InitPlanes, PATCH_JUMP);
+ InjectHook(0x54BCD0, CPlane::Shutdown, PATCH_JUMP);
+ InjectHook(0x54BD50, CPlane::LoadPath, PATCH_JUMP);
+ InjectHook(0x54BEC0, CPlane::UpdatePlanes, PATCH_JUMP);
+ InjectHook(0x54DE90, CPlane::TestRocketCollision, PATCH_JUMP);
+ InjectHook(0x54E000, CPlane::CreateIncomingCesna, PATCH_JUMP);
+ InjectHook(0x54E160, CPlane::CreateDropOffCesna, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Plane.h b/src/vehicles/Plane.h
index e263766e..eddbc2cd 100644
--- a/src/vehicles/Plane.h
+++ b/src/vehicles/Plane.h
@@ -1,27 +1,72 @@
#pragma once
-#include "common.h"
#include "Vehicle.h"
+enum ePlanePositions
+{
+ PLANE_POS_LIGHT_LEFT,
+ PLANE_POS_LIGHT_RIGHT,
+ PLANE_POS_LIGHT_TAIL,
+};
+
+struct CPlaneNode
+{
+ CVector p; // position
+ float t; // xy-distance from start on path
+ bool bOnGround; // i.e. not flying
+};
+
+struct CPlaneInterpolationLine
+{
+ uint8 type;
+ float time; // when does this keyframe start
+ // initial values at start of frame
+ float position;
+ float speed;
+ float acceleration;
+};
+
class CPlane : public CVehicle
{
public:
// 0x288
- int16 m_wIndex;
- int16 field_650;
- int16 m_wNextPathNode;
+ int16 m_nPlaneId;
+ int16 m_isFarAway;
+ int16 m_nCurPathNode;
char field_654;
char field_655;
- float field_656;
- int m_nFrameWhenHit;
- char m_bHasBeenHit;
- char m_bIsIncomingCesna;
- char m_bIsDropoffCesna;
- char field_667;
-
- CPlane(int, uint8);
+ float m_fSpeed;
+ uint32 m_nFrameWhenHit;
+ bool m_bHasBeenHit;
+ bool m_bIsDrugRunCesna;
+ bool m_bIsDropOffCesna;
+
+ CPlane(int32 id, uint8 CreatedBy);
~CPlane(void);
- CPlane* ctor(int, uint8);
+
+ // from CEntity
+ void SetModelIndex(uint32 id);
+ void DeleteRwObject(void);
+ void ProcessControl(void);
+ void PreRender(void);
+ void Render(void);
void FlagToDestroyWhenNextProcessed() { bRemoveFromWorld = true; }
+
+ static void InitPlanes(void);
+ static void Shutdown(void);
+ static CPlaneNode *LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop);
+ static void UpdatePlanes(void);
+ static bool TestRocketCollision(CVector *rocketPos);
+ static void CreateIncomingCesna(void);
+ static void CreateDropOffCesna(void);
+ static CVector FindDrugPlaneCoordinates(void);
+ static CVector FindDropOffCesnaCoordinates(void);
+ static bool HasCesnaLanded(void);
+ static bool HasCesnaBeenDestroyed(void);
+ static bool HasDropOffCesnaBeenShotDown(void);
};
static_assert(sizeof(CPlane) == 0x29C, "CPlane: error");
+
+extern float &LandingPoint;
+extern float &TakeOffPoint;
+extern float *PlanePathPosition; //[3]
diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp
index cf00cc63..dada1b21 100644
--- a/src/vehicles/Train.cpp
+++ b/src/vehicles/Train.cpp
@@ -484,8 +484,8 @@ CTrain::InitTrains(void)
void
CTrain::Shutdown(void)
{
- if(pTrackNodes) delete[] pTrackNodes;
- if(pTrackNodes_S) delete[] pTrackNodes_S;
+ delete[] pTrackNodes;
+ delete[] pTrackNodes_S;
pTrackNodes = nil;
pTrackNodes_S = nil;
}
@@ -582,7 +582,7 @@ CTrain::ReadAndInterpretTrackFile(char *filename, CTrainNode **nodes, int16 *num
interpLines[j].speed = 15.0f;
interpLines[j].acceleration = -45.0f/32.0f;
j++;
- time += 16.0f/3.0f;
+ time += 80.0f/15.0f;
position += 40.0f; // at station
// stopping
@@ -601,7 +601,7 @@ CTrain::ReadAndInterpretTrackFile(char *filename, CTrainNode **nodes, int16 *num
interpLines[j].speed = 0.0f;
interpLines[j].acceleration = 45.0f/32.0f;
j++;
- time += 16.0f/3.0f;
+ time += 80.0f/15.0f;
position += 40.0f; // after station
}
// last keyframe
@@ -657,7 +657,7 @@ CTrain::UpdateTrains(void)
}
// time offset for each train
- time += 0x10000;
+ time += 0x20000/2;
}
ProcessTrainAnnouncements();
@@ -687,7 +687,7 @@ CTrain::UpdateTrains(void)
}
// time offset for each train
- time += 0x10000;
+ time += 0x40000/4;
}
}