summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/audio/AudioManager.cpp19
-rw-r--r--src/control/Darkel.cpp12
-rw-r--r--src/control/Pickups.cpp5
-rw-r--r--src/control/Pickups.h4
-rw-r--r--src/core/Collision.cpp4
-rw-r--r--src/core/FileLoader.cpp2
-rw-r--r--src/core/Wanted.h3
-rw-r--r--src/core/ZoneCull.cpp2
-rw-r--r--src/core/config.h1
-rw-r--r--src/core/main.cpp1
-rw-r--r--src/core/main.h1
-rw-r--r--src/entities/Physical.h2
-rw-r--r--src/modelinfo/VehicleModelInfo.cpp23
-rw-r--r--src/modelinfo/VehicleModelInfo.h5
-rw-r--r--src/peds/Ped.cpp487
-rw-r--r--src/peds/Ped.h39
-rw-r--r--src/peds/PlayerPed.cpp41
-rw-r--r--src/peds/PlayerPed.h17
-rw-r--r--src/vehicles/Automobile.cpp4
-rw-r--r--src/vehicles/Automobile.h50
-rw-r--r--src/vehicles/Door.cpp60
-rw-r--r--src/vehicles/Door.h31
-rw-r--r--src/vehicles/Plane.cpp967
-rw-r--r--src/vehicles/Plane.h71
-rw-r--r--src/vehicles/Train.cpp705
-rw-r--r--src/vehicles/Train.h90
-rw-r--r--src/vehicles/Vehicle.cpp2
-rw-r--r--src/vehicles/Vehicle.h50
28 files changed, 2527 insertions, 171 deletions
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index ebf6a153..bd99628e 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -17,6 +17,7 @@
#include "sampman.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/control/Darkel.cpp b/src/control/Darkel.cpp
index ad8d1176..670c10fb 100644
--- a/src/control/Darkel.cpp
+++ b/src/control/Darkel.cpp
@@ -168,7 +168,7 @@ void CDarkel::ResetOnPlayerDeath()
WeaponType = WEAPONTYPE_UZI;
if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
- FindPlayerPed()->m_bWeaponSlot = InterruptedWeapon;
+ FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
}
@@ -187,7 +187,7 @@ void CDarkel::ResetOnPlayerDeath()
WeaponType = WEAPONTYPE_UZI;
if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
- FindPlayerPed()->m_bWeaponSlot = InterruptedWeapon;
+ FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
}
@@ -234,8 +234,8 @@ void CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, int16 kill, int32
FindPlayerPed()->GiveWeapon(weaponType, 0);
AmmoInterruptedWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal;
FindPlayerPed()->GiveWeapon(weaponType, 30000);
- FindPlayerPed()->m_bWeaponSlot = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
- FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_bWeaponSlot);
+ FindPlayerPed()->m_nSelectedWepSlot = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
+ FindPlayerPed()->MakeChangesForNewWeapon(FindPlayerPed()->m_nSelectedWepSlot);
if (FindPlayerVehicle()) {
FindPlayerPed()->m_currentWeapon = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_eWeaponType;
@@ -277,7 +277,7 @@ void CDarkel::Update()
WeaponType = WEAPONTYPE_UZI;
if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
- FindPlayerPed()->m_bWeaponSlot = InterruptedWeapon;
+ FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
}
@@ -306,7 +306,7 @@ void CDarkel::Update()
WeaponType = WEAPONTYPE_UZI;
if (WeaponType < WEAPONTYPE_TOTALWEAPONS) {
- FindPlayerPed()->m_bWeaponSlot = InterruptedWeapon;
+ FindPlayerPed()->m_nSelectedWepSlot = InterruptedWeapon;
CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal = AmmoInterruptedWeapon;
}
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index c93798fe..2770a948 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -4,11 +4,16 @@
CPickup(&CPickups::aPickUps)[NUMPICKUPS] = *(CPickup(*)[NUMPICKUPS])*(uintptr*)0x878C98;
+// 20 ?! Some Miami leftover? (Originally at 0x5ED8D4)
+uint16 CPickups::ms_maxAmmosForWeapons[20] = { 0, 1, 45, 125, 25, 150, 300, 25, 5, 250, 5, 5, 0, 500, 0, 100, 0, 0, 0, 0 };
+
WRAPPER void CPickups::RenderPickUpText(void) { EAXJMP(0x432440); }
WRAPPER void CPickups::DoCollectableEffects(CEntity *ent) { EAXJMP(0x431C30); }
WRAPPER void CPickups::DoMoneyEffects(CEntity *ent) { EAXJMP(0x431F40); }
WRAPPER void CPickups::DoMineEffects(CEntity *ent) { EAXJMP(0x4321C0); }
WRAPPER void CPickups::DoPickUpEffects(CEntity *ent) { EAXJMP(0x431520); }
WRAPPER void CPickups::RemoveAllFloatingPickups() { EAXJMP(0x430800); }
+WRAPPER int32 CPickups::GenerateNewOne(CVector, uint32, uint8, uint32) { EAXJMP(0x4304B0); }
+WRAPPER int32 CPickups::GenerateNewOne_WeaponType(CVector, eWeaponType, uint8, uint32) { EAXJMP(0x430660); }
WRAPPER void CPacManPickups::Render(void) { EAXJMP(0x432F60); }
diff --git a/src/control/Pickups.h b/src/control/Pickups.h
index b740e72e..20a779a8 100644
--- a/src/control/Pickups.h
+++ b/src/control/Pickups.h
@@ -1,4 +1,5 @@
#pragma once
+#include "Weapon.h"
enum ePickupType
{
@@ -42,8 +43,11 @@ public:
static void DoMineEffects(CEntity *ent);
static void DoPickUpEffects(CEntity *ent);
static void RemoveAllFloatingPickups();
+ static int32 GenerateNewOne(CVector, uint32, uint8, uint32);
+ static int32 GenerateNewOne_WeaponType(CVector, eWeaponType, uint8, uint32);
static CPickup (&aPickUps)[NUMPICKUPS];
+ static uint16 ms_maxAmmosForWeapons[20];
};
class CPacManPickups
diff --git a/src/core/Collision.cpp b/src/core/Collision.cpp
index cc360f79..df1dcd63 100644
--- a/src/core/Collision.cpp
+++ b/src/core/Collision.cpp
@@ -142,8 +142,8 @@ CCollision::LoadCollisionWhenINeedIt(bool forceChange)
veh = FindPlayerVehicle();
if(veh && veh->IsTrain()){
- if(((CTrain*)veh)->m_doorState != TRAIN_DOOR_STATE2)
- return ;
+ if(((CTrain*)veh)->m_nDoorState != TRAIN_DOOR_OPEN)
+ return;
}else if(playerCoors.z < 4.0f && !CCullZones::DoINeedToLoadCollision())
return;
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/Wanted.h b/src/core/Wanted.h
index 1a72f839..7cd89b7e 100644
--- a/src/core/Wanted.h
+++ b/src/core/Wanted.h
@@ -33,6 +33,9 @@ public:
CVector m_vecPosn;
bool m_bReported;
bool m_bPoliceDoesntCare;
+
+ CCrimeBeingQd() { };
+ ~CCrimeBeingQd() { };
};
class CWanted
diff --git a/src/core/ZoneCull.cpp b/src/core/ZoneCull.cpp
index 90155bcf..dc162147 100644
--- a/src/core/ZoneCull.cpp
+++ b/src/core/ZoneCull.cpp
@@ -186,7 +186,7 @@ CCullZones::MarkSubwayAsInvisible(bool visible)
n = CPools::GetVehiclePool()->GetSize();
for(i = 0; i < n; i++){
v = CPools::GetVehiclePool()->GetSlot(i);
- if(v && v->IsTrain() && ((CTrain*)v)->m_trackId != 0)
+ if(v && v->IsTrain() && ((CTrain*)v)->m_nTrackId != TRACK_ELTRAIN)
v->bIsVisible = visible;
}
}
diff --git a/src/core/config.h b/src/core/config.h
index 07c86c0d..52d1dab8 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -123,3 +123,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/core/main.cpp b/src/core/main.cpp
index 04fee197..a4c4de7b 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -71,6 +71,7 @@ char version_name[64];
float FramesPerSecond = 30.0f;
bool gbPrintShite = false;
+bool gbModelViewer;
bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha);
void DoRWStuffEndOfFrame(void);
diff --git a/src/core/main.h b/src/core/main.h
index 9b3c27f5..6d393066 100644
--- a/src/core/main.h
+++ b/src/core/main.h
@@ -18,6 +18,7 @@ extern wchar *gUString;
extern wchar *gUString2;
extern bool &b_FoundRecentSavedGameWantToLoad;
extern bool gbPrintShite;
+extern bool gbModelViewer;
class CSprite2d;
diff --git a/src/entities/Physical.h b/src/entities/Physical.h
index 5bd98815..26ef0086 100644
--- a/src/entities/Physical.h
+++ b/src/entities/Physical.h
@@ -58,7 +58,7 @@ public:
uint8 bIsInWater : 1;
uint8 m_phy_flagA10 : 1;
uint8 m_phy_flagA20 : 1;
- uint8 bHitByTrain : 1; // from nick
+ uint8 bHitByTrain : 1;
uint8 m_phy_flagA80 : 1;
uint8 m_nSurfaceTouched;
diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp
index 33e7195b..c031b76e 100644
--- a/src/modelinfo/VehicleModelInfo.cpp
+++ b/src/modelinfo/VehicleModelInfo.cpp
@@ -11,6 +11,9 @@
#include "FileMgr.h"
#include "World.h"
#include "Vehicle.h"
+#include "Automobile.h"
+#include "Train.h"
+#include "Plane.h"
#include "ModelIndices.h"
#include "ModelInfo.h"
@@ -84,13 +87,13 @@ RwObjectNameIdAssocation boatIds[] = {
};
RwObjectNameIdAssocation trainIds[] = {
- { "door_lhs_dummy", 1, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
- { "door_rhs_dummy", 2, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
- { "light_front", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "light_rear", 1, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "ped_left_entry", 2, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "ped_mid_entry", 3, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
- { "ped_right_entry", 4, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "door_lhs_dummy", TRAIN_DOOR_LHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
+ { "door_rhs_dummy", TRAIN_DOOR_RHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
+ { "light_front", TRAIN_POS_LIGHT_FRONT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "light_rear", TRAIN_POS_LIGHT_REAR, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "ped_left_entry", TRAIN_POS_LEFT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "ped_mid_entry", TRAIN_POS_MID_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
+ { "ped_right_entry", TRAIN_POS_RIGHT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
@@ -108,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/peds/Ped.cpp b/src/peds/Ped.cpp
index f8b44301..d05568f6 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -30,9 +30,9 @@
#include "ModelIndices.h"
#include "FileMgr.h"
#include "TempColModels.h"
+#include "Pickups.h"
WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC430); }
-WRAPPER void CPed::SetDead(void) { EAXJMP(0x4D3970); }
WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); }
WRAPPER void CPed::SetPedPositionInCar(void) { EAXJMP(0x4D4970); }
WRAPPER void CPed::ProcessControl(void) { EAXJMP(0x4C8910); }
@@ -43,13 +43,13 @@ WRAPPER void CPed::SetMoveAnim(void) { EAXJMP(0x4C5A40); }
WRAPPER void CPed::SetFollowRoute(int16, int16) { EAXJMP(0x4DD690); }
WRAPPER void CPed::SetDuck(uint32) { EAXJMP(0x4E4920); }
WRAPPER void CPed::RegisterThreatWithGangPeds(CEntity*) { EAXJMP(0x4E3870); }
-WRAPPER void CPed::MakeChangesForNewWeapon(int8) { EAXJMP(0x4F2560); }
-WRAPPER void CPed::SetSeek(CVector, float) { EAXJMP(0x4D14B0); }
WRAPPER bool CPed::Seek(void) { EAXJMP(0x4D1640); }
WRAPPER void CPed::SetFollowPath(CVector) { EAXJMP(0x4D2EA0); }
WRAPPER void CPed::RemoveInCarAnims(void) { EAXJMP(0x4E4E20); }
WRAPPER void CPed::StartFightDefend(uint8, uint8, uint8) { EAXJMP(0x4E7780); }
WRAPPER void CPed::SetDirectionToWalkAroundObject(CEntity*) { EAXJMP(0x4CCEB0); }
+WRAPPER void CPed::SetRadioStation(void) { EAXJMP(0x4D7BC0); }
+WRAPPER void CPed::MakeTyresMuddySectorList(CPtrList&) { EAXJMP(0x53CFD0); }
bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44;
bool &CPed::bPedCheat2 = *(bool*)0x95CD5A;
@@ -79,7 +79,7 @@ CPed::~CPed(void)
CWorld::Remove(this);
CRadar::ClearBlipForEntity(BLIP_CHAR, CPools::GetPedPool()->GetIndex(this));
if (bInVehicle && m_pMyVehicle){
- uint8 door_flag = GetVehDoorFlag(m_vehEnterType);
+ uint8 door_flag = GetCarDoorFlag(m_vehEnterType);
if (m_pMyVehicle->pDriver == this)
m_pMyVehicle->pDriver = nil;
else {
@@ -283,6 +283,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_type = ENTITY_TYPE_PED;
bPedPhysics = true;
bUseCollisionRecords = true;
+// m_status = STATUS_SIMPLE;
m_vecAnimMoveDelta.x = 0.0f;
m_vecAnimMoveDelta.y = 0.0f;
@@ -320,7 +321,7 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_standardTimer = 0;
m_lastHitTime = 0;
m_hitRecoverTimer = 0;
- field_4E8 = 0;
+ m_duckAndCoverTimer = 0;
m_moved = CVector2D(0.0f, 0.0f);
m_fRotationCur = 0.0f;
m_headingRate = 15.0f;
@@ -420,10 +421,10 @@ CPed::CPed(uint32 pedType) : m_pedIK(this)
m_ped_flagF2 = false;
m_ped_flagF4 = false;
m_ped_flagF8 = false;
- m_ped_flagF10 = false;
+ bWillBeQuickJacked = false;
m_ped_flagF20 = false;
m_ped_flagF40 = false;
- m_ped_flagF80 = false;
+ bDuckAndCover = false;
m_ped_flagG1 = false;
m_ped_flagG2 = true;
@@ -1354,7 +1355,7 @@ CPed::BeingDraggedFromCar(void)
animAssoc->blendDelta = -1000.0f;
if (m_vehEnterType == CAR_DOOR_LF || m_vehEnterType == CAR_DOOR_LR) {
- if (m_ped_flagF10) {
+ if (bWillBeQuickJacked) {
enterAnim = ANIM_CAR_QJACKED;
} else if (m_pMyVehicle->bLowVehicle) {
enterAnim = ANIM_CAR_LJACKED_LHS;
@@ -1415,7 +1416,7 @@ CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg)
ped->m_pSeekTarget = nil;
vehicle = ped->m_pMyVehicle;
- vehicle->m_nGettingOutFlags &= ~GetVehDoorFlag(ped->m_vehEnterType);
+ vehicle->m_nGettingOutFlags &= ~GetCarDoorFlag(ped->m_vehEnterType);
if (vehicle->pDriver == ped) {
vehicle->RemoveDriver();
@@ -2263,7 +2264,6 @@ CPed::CanPedJumpThis(int32 unused)
{
CVector2D forward(-Sin(m_fRotationCur), Cos(m_fRotationCur));
CVector pos = GetPosition();
- // wat?
CVector forwardPos(
forward.x + pos.x,
forward.y + pos.y,
@@ -2288,19 +2288,19 @@ CPed::CanSeeEntity(CEntity *entity, float threshold)
GetPosition().y);
if (neededAngle < 0.0f)
- neededAngle += 2 * PI;
- else if (neededAngle > 2 * PI)
- neededAngle -= 2 * PI;
+ neededAngle += TWOPI;
+ else if (neededAngle > TWOPI)
+ neededAngle -= TWOPI;
float ourAngle = m_fRotationCur;
if (ourAngle < 0.0f)
- ourAngle += 2 * PI;
- else if (ourAngle > 2 * PI)
- ourAngle -= 2 * PI;
+ ourAngle += TWOPI;
+ else if (ourAngle > TWOPI)
+ ourAngle -= TWOPI;
float neededTurn = Abs(neededAngle - ourAngle);
- return neededTurn < threshold || 2 * PI - threshold < neededTurn;
+ return neededTurn < threshold || TWOPI - threshold < neededTurn;
}
bool
@@ -2725,7 +2725,7 @@ CPed::QuitEnteringCar(void)
if (veh->m_nNumGettingIn != 0)
veh->m_nNumGettingIn--;
- veh->m_nGettingInFlags &= ~GetVehDoorFlag(m_vehEnterType);
+ veh->m_nGettingInFlags &= ~GetCarDoorFlag(m_vehEnterType);
}
bUsesCollision = true;
@@ -2776,7 +2776,7 @@ CPed::ReactToAttack(CEntity *attacker)
CPed *attackerPed = (CPed*)attacker;
if (bNotAllowedToDuck) {
if (!attackerPed->GetWeapon()->IsTypeMelee()) {
- field_4E8 = CTimer::GetTimeInMilliseconds();
+ m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds();
return;
}
} else if (bCrouchWhenShooting || bKindaStayInSamePlace) {
@@ -3659,7 +3659,6 @@ CPed::InflictDamage(CEntity* damagedBy, eWeaponType method, float damage, ePedPi
} else {
CDarkel::RegisterKillNotByPlayer(this, method);
}
- // WAT?
if (method == WEAPONTYPE_WATER)
bIsInTheAir = false;
@@ -3892,8 +3891,7 @@ CPed::ClearWeapons(void)
currentWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
AddWeaponModel(currentWeapon->m_nModelId);
- for(int i = 0; i < WEAPONTYPE_TOTAL_INVENTORY_WEAPONS; i++)
- {
+ for(int i = 0; i < WEAPONTYPE_TOTAL_INVENTORY_WEAPONS; i++) {
CWeapon &weapon = GetWeapon(i);
weapon.m_eWeaponType = WEAPONTYPE_UNARMED;
weapon.m_eWeaponState = WEAPONSTATE_READY;
@@ -4558,7 +4556,7 @@ CPed::GetLocalDirection(CVector2D &posOffset)
{
float direction;
- for (direction = posOffset.Heading() - m_fRotationCur + DEGTORAD(45.0f); direction < 0.0f; direction += 2 * PI);
+ for (direction = posOffset.Heading() - m_fRotationCur + DEGTORAD(45.0f); direction < 0.0f; direction += TWOPI);
for (direction = (int)RADTODEG(direction) / 90; direction > 3; direction -= 4);
@@ -4961,7 +4959,21 @@ CPed::PlayHitSound(CPed *hitTo)
{
// That was very complicated to reverse for me...
// First index is our fight move ID (from 1 to 12, total 12), second is the one of we fight with (from 13 to 22, total 10).
-
+ enum {
+ S33 = SOUND_FIGHT_PUNCH_33,
+ S34 = SOUND_FIGHT_KICK_34,
+ S35 = SOUND_FIGHT_HEADBUTT_35,
+ S36 = SOUND_FIGHT_PUNCH_36,
+ S37 = SOUND_FIGHT_PUNCH_37,
+ S38 = SOUND_FIGHT_CLOSE_PUNCH_38,
+ S39 = SOUND_FIGHT_PUNCH_39,
+ S40 = SOUND_FIGHT_PUNCH_OR_KICK_BELOW_40 ,
+ S41 = SOUND_FIGHT_PUNCH_41,
+ S42 = SOUND_FIGHT_PUNCH_FROM_BEHIND_42,
+ S43 = SOUND_FIGHT_KNEE_OR_KICK_43,
+ S44 = SOUND_FIGHT_KICK_44,
+ NO_SND = SOUND_TOTAL_PED_SOUNDS
+ };
uint16 hitSoundsByFightMoves[12][10] = {
{S39,S42,S43,S43,S39,S39,S39,S39,S39,S42},
{NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND},
@@ -5267,6 +5279,419 @@ CPed::CollideWithPed(CPed *collideWith)
}
}
+void
+CPed::CreateDeadPedMoney(void)
+{
+ if (!CGame::nastyGame)
+ return;
+
+ int skin = m_modelIndex;
+ if ((skin >= MI_COP && skin <= MI_FIREMAN) || CharCreatedBy == MISSION_CHAR || bInVehicle)
+ return;
+
+ int money = CGeneral::GetRandomNumber() % 60;
+ if (money < 10)
+ return;
+
+ if (money == 43)
+ money = 700;
+
+ int pickupCount = money / 40 + 1;
+ int moneyPerPickup = money / pickupCount;
+
+ for(int i = 0; i < pickupCount; i++) {
+ float pickupX = 1.5f * Sin((CGeneral::GetRandomNumber() % 256) * PI / 128) + GetPosition().x;
+ float pickupY = 1.5f * Cos((CGeneral::GetRandomNumber() % 256) * PI / 128) + GetPosition().y;
+ bool found = false;
+ float groundZ = CWorld::FindGroundZFor3DCoord(pickupX, pickupY, GetPosition().z, &found) + 0.5f;
+ if (found) {
+ CPickups::GenerateNewOne(CVector(pickupX, pickupY, groundZ), MI_MONEY, PICKUP_MONEY, moneyPerPickup + (CGeneral::GetRandomNumber() & 7));
+ }
+ }
+}
+
+void
+CPed::CreateDeadPedWeaponPickups(void)
+{
+ bool found = false;
+ float angleToPed;
+ CVector pickupPos;
+
+ if (bInVehicle)
+ return;
+
+ for(int i = 0; i < WEAPONTYPE_TOTAL_INVENTORY_WEAPONS; i++) {
+
+ eWeaponType weapon = GetWeapon(i).m_eWeaponType;
+ int weaponAmmo = GetWeapon(i).m_nAmmoTotal;
+ if (weapon == WEAPONTYPE_UNARMED || weapon == WEAPONTYPE_DETONATOR || weaponAmmo == 0)
+ continue;
+
+ angleToPed = i * 1.75f;
+ pickupPos = GetPosition();
+ pickupPos.x += 1.5f * Sin(angleToPed);
+ pickupPos.y += 1.5f * Cos(angleToPed);
+ pickupPos.z = CWorld::FindGroundZFor3DCoord(pickupPos.x, pickupPos.y, pickupPos.z, &found) + 0.5f;
+
+ CVector pedPos = GetPosition();
+ pedPos.z += 0.3f;
+
+ CVector pedToPickup = pickupPos - pedPos;
+ float distance = pedToPickup.Magnitude();
+
+ // outer edge of pickup
+ distance = (distance + 0.3f) / distance;
+ CVector pickupPos2 = pedPos;
+ pickupPos2 += distance * pedToPickup;
+
+ // pickup must be on ground and line to its edge must be clear
+ if (!found || CWorld::GetIsLineOfSightClear(pickupPos2, pedPos, true, false, false, false, false, false, false)) {
+ // otherwise try another position (but disregard second check apparently)
+ angleToPed += 3.14f;
+ pickupPos = GetPosition();
+ pickupPos.x = 1.5f * Sin(angleToPed);
+ pickupPos.y = 1.5f * Cos(angleToPed);
+ pickupPos.z = CWorld::FindGroundZFor3DCoord(pickupPos.x, pickupPos.y, pickupPos.z, &found) + 0.5f;
+ }
+ if (found)
+ CPickups::GenerateNewOne_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, min(weaponAmmo, CPickups::ms_maxAmmosForWeapons[weapon]));
+ }
+ ClearWeapons();
+}
+
+void
+CPed::SetAttackTimer(uint32 time)
+{
+ if (CTimer::GetTimeInMilliseconds() > m_attackTimer)
+ m_attackTimer = max(m_lastHitTime, CTimer::GetTimeInMilliseconds()) + time;
+}
+
+void
+CPed::SetBeingDraggedFromCar(CVehicle *veh, uint32 vehEnterType, bool quickJack)
+{
+ if (m_nPedState == PED_DRAG_FROM_CAR)
+ return;
+
+ bUsesCollision = false;
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ m_nLastPedState = PED_IDLE;
+ SetMoveState(PEDMOVE_STILL);
+ m_pSeekTarget = veh;
+ m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget);
+ m_vehEnterType = vehEnterType;
+ if (m_vehEnterType == CAR_DOOR_LF) {
+ if (veh->pDriver && veh->pDriver->IsPlayer())
+ veh->m_status = STATUS_PLAYER_DISABLED;
+ else
+ veh->m_status = STATUS_ABANDONED;
+ }
+ RemoveInCarAnims();
+ SetMoveState(PEDMOVE_NONE);
+ LineUpPedWithCar(LINE_UP_TO_CAR_START);
+ m_pVehicleAnim = nil;
+ m_nPedState = PED_DRAG_FROM_CAR;
+ bChangedSeat = false;
+ bWillBeQuickJacked = quickJack;
+
+ CVector pos = GetPosition();
+ GetMatrix().SetRotate(0.0f, 0.0f, m_fRotationCur);
+ GetPosition() += pos;
+
+ Say(SOUND_PED_CAR_JACKED);
+ SetRadioStation();
+ veh->m_nGettingOutFlags |= GetCarDoorFlag(m_vehEnterType);
+}
+
+void
+CPed::SetBuyIceCream(void)
+{
+ if (m_nPedState == PED_BUY_ICECREAM || !IsPedInControl())
+ return;
+
+ if (!m_carInObjective)
+ return;
+
+ // Side of the Ice Cream van
+ m_fRotationDest = m_carInObjective->GetForward().Heading() - HALFPI;
+
+ if (Abs(m_fRotationDest - m_fRotationCur) < HALFPI) {
+ m_standardTimer = CTimer::GetTimeInMilliseconds() + 3000;
+ m_nPedState = PED_BUY_ICECREAM;
+ }
+}
+
+void
+CPed::SetChat(CEntity* chatWith, uint32 time)
+{
+ if(m_nPedState != PED_CHAT)
+ SetStoredState();
+
+ m_nPedState = PED_CHAT;
+ SetMoveState(PEDMOVE_STILL);
+ SetLookFlag(chatWith, 1);
+ m_standardTimer = CTimer::GetTimeInMilliseconds() + time;
+ m_lookTimer = CTimer::GetTimeInMilliseconds() + 3000;
+}
+
+void
+CPed::SetDead(void)
+{
+ bUsesCollision = false;
+ m_fHealth = 0.0f;
+ if (m_nPedState == PED_DRIVING)
+ bIsVisible = false;
+
+ m_nPedState = PED_DEAD;
+ m_pVehicleAnim = nil;
+ m_pCollidingEntity = nil;
+
+ CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
+ RemoveWeaponModel(weapon->m_nModelId);
+
+ m_currentWeapon = WEAPONTYPE_UNARMED;
+ CEventList::RegisterEvent(EVENT_INJURED_PED, EVENT_ENTITY_PED, this, nil, 250);
+ if (this != FindPlayerPed()) {
+ CreateDeadPedWeaponPickups();
+ CreateDeadPedMoney();
+ }
+
+ // BUG: Is this count or timer?!
+ m_bloodyFootprintCount = CTimer::GetTimeInMilliseconds();
+ m_deadBleeding = 0;
+ bDoBloodyFootprints = false;
+ m_ped_flagI4 = false;
+ CEventList::RegisterEvent(EVENT_DEAD_PED, EVENT_ENTITY_PED, this, nil, 1000);
+}
+
+void
+CPed::SetSeek(CEntity *seeking, float unk)
+{
+ if (!IsPedInControl())
+ return;
+
+ if (m_nPedState == PED_SEEK_ENTITY && m_pSeekTarget == seeking)
+ return;
+
+ if (!seeking)
+ return;
+
+ if (m_nPedState != PED_SEEK_ENTITY)
+ SetStoredState();
+
+ m_nPedState = PED_SEEK_ENTITY;
+ field_318 = unk;
+ m_pSeekTarget = seeking;
+ m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget);
+ SetMoveState(PEDMOVE_STILL);
+}
+
+void
+CPed::SetSeek(CVector pos, float unk)
+{
+ if (!IsPedInControl()
+ || (m_nPedState == PED_SEEK_POS && m_vecSeekVehicle.x != pos.x && m_vecSeekVehicle.y != pos.y))
+ return;
+
+ if (GetWeapon()->m_eWeaponType == WEAPONTYPE_M16
+ || GetWeapon()->m_eWeaponType == WEAPONTYPE_AK47
+ || GetWeapon()->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE
+ || GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER
+ || GetWeapon()->m_eWeaponType == WEAPONTYPE_SHOTGUN) {
+ ClearPointGunAt();
+ }
+
+ if (m_nPedState != PED_SEEK_POS)
+ SetStoredState();
+
+ m_nPedState = PED_SEEK_POS;
+ field_318 = unk;
+ m_vecSeekVehicle = pos;
+}
+
+void
+CPed::DeadPedMakesTyresBloody(void)
+{
+ int minX = CWorld::GetSectorIndexX(GetPosition().x - 2.0f);
+ if (minX < 0) minX = 0;
+ int minY = CWorld::GetSectorIndexY(GetPosition().y - 2.0f);
+ if (minY < 0) minY = 0;
+ int maxX = CWorld::GetSectorIndexX(GetPosition().x + 2.0f);
+ if (maxX > NUMSECTORS_X-1) maxX = NUMSECTORS_X-1;
+ int maxY = CWorld::GetSectorIndexY(GetPosition().y + 2.0f);
+ if (maxY > NUMSECTORS_Y-1) maxY = NUMSECTORS_Y-1;
+
+ CWorld::AdvanceCurrentScanCode();
+
+ for (int curY = minY; curY <= maxY; curY++) {
+ for (int curX = minX; curX <= maxX; curX++) {
+ CSector* sector = CWorld::GetSector(curX, curY);
+ MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES]);
+ MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP]);
+ }
+ }
+}
+
+void
+CPed::Die(void)
+{
+ // UNUSED: This is a perfectly empty function.
+}
+
+uint8
+CPed::DoesLOSBulletHitPed(CColPoint &colPoint)
+{
+ RwMatrix mat;
+
+ CPedIK::GetWorldMatrix(GetNodeFrame(PED_HEAD), &mat);
+ float headZ = RwMatrixGetPos(&mat)->z;
+
+ if (m_nPedState == PED_FALL)
+ return 1;
+
+ float colZ = colPoint.point.z;
+ if (colZ < headZ)
+ return 1;
+
+ if (headZ + 0.2f <= colZ)
+ return 0;
+
+ return 2;
+}
+
+bool
+CPed::DuckAndCover(void)
+{
+ if (!m_pedInObjective || CTimer::GetTimeInMilliseconds() <= m_duckAndCoverTimer)
+ return false;
+
+ if (bKindaStayInSamePlace){
+
+ if (CTimer::GetTimeInMilliseconds() <= m_leaveCarTimer) {
+ if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) {
+ m_pLookTarget = m_pedInObjective;
+ m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget);
+ }
+ if (!bIsAimingGun)
+ SetAimFlag(m_pedInObjective);
+
+ } else {
+ bCrouchWhenShooting = false;
+ bKindaStayInSamePlace = false;
+ bIsDucking = false;
+ bDuckAndCover = false;
+ m_headingRate = 10.0f;
+ m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(20000,30000);
+ if (m_pSeekTarget && m_pSeekTarget->IsVehicle())
+ ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover--;
+ }
+ return false;
+ }
+
+ bool justDucked = false;
+ CVehicle *foundVeh = nil;
+ float maxDist = 225.0f;
+ bIsDucking = false;
+ bCrouchWhenShooting = false;
+ if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) {
+ CVector pos = GetPosition();
+ int16 lastVehicle;
+ CEntity* vehicles[8];
+ CWorld::FindObjectsInRange(pos, 15.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false);
+
+ for (int i = 0; i < lastVehicle; i++) {
+ CVehicle *veh = (CVehicle*) vehicles[i];
+ if (veh->m_vecMoveSpeed.Magnitude() <= 0.02f
+ && !veh->bIsBus
+ && !veh->bIsVan
+ && !veh->bIsBig
+ && veh->m_numPedsUseItAsCover < 3) {
+ float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr();
+ if (dist < maxDist) {
+ maxDist = dist;
+ foundVeh = veh;
+ }
+ }
+ }
+ if (foundVeh) {
+ // Unused.
+ // CVector lfWheelPos, rfWheelPos;
+ // foundVeh->GetComponentWorldPosition(CAR_WHEEL_RF, rfWheelPos);
+ // foundVeh->GetComponentWorldPosition(CAR_WHEEL_LF, lfWheelPos);
+ CVector rightSide, leftSide;
+
+ // 3 persons can use the car as cover. Found the correct position for us.
+ if (foundVeh->m_numPedsUseItAsCover == 2) {
+ rightSide = CVector(1.5f, -0.5f, 0.0f);
+ leftSide = CVector(-1.5f, -0.5f, 0.0f);
+ } else if (foundVeh->m_numPedsUseItAsCover == 1) {
+ rightSide = CVector(1.5f, 0.5f, 0.0f);
+ leftSide = CVector(-1.5f, 0.5f, 0.0f);
+ } else if (foundVeh->m_numPedsUseItAsCover == 0) {
+ rightSide = CVector(1.5f, 0.0f, 0.0f);
+ leftSide = CVector(-1.5f, 0.0f, 0.0f);
+ }
+
+ CMatrix vehMatrix(foundVeh->GetMatrix());
+ CVector duckAtRightSide = Multiply3x3(vehMatrix, rightSide) + foundVeh->GetPosition();
+
+ CVector duckAtLeftSide = Multiply3x3(vehMatrix, leftSide) + foundVeh->GetPosition();
+
+ CVector distWithPedRightSide = m_pedInObjective->GetPosition() - duckAtRightSide;
+ CVector distWithPedLeftSide = m_pedInObjective->GetPosition() - duckAtLeftSide;
+
+ CVector duckPos;
+ if (distWithPedRightSide.MagnitudeSqr() <= distWithPedLeftSide.MagnitudeSqr())
+ duckPos = duckAtLeftSide;
+ else
+ duckPos = duckAtRightSide;
+
+ if (CWorld::TestSphereAgainstWorld(duckPos, 0.5f, nil, true, true, true, false, false, false)
+ && CWorld::GetIsLineOfSightClear(GetPosition(), duckPos, 1, 0, 0, 1, 0, 0, 0)) {
+ SetSeek(duckPos, 1.0f);
+ m_headingRate = 15.0f;
+ m_ped_flagB20 = true;
+ bDuckAndCover = true;
+ justDucked = true;
+ m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500;
+ if (foundVeh->bIsLawEnforcer)
+ m_carInObjective = foundVeh;
+
+ // BUG? Shouldn't we register the reference?
+ m_pSeekTarget = foundVeh;
+ ClearPointGunAt();
+ } else {
+ m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(10000, 15000);
+ bDuckAndCover = false;
+ }
+ } else {
+ bDuckAndCover = false;
+ }
+ }
+
+ if (!justDucked && !bDuckAndCover)
+ return false;
+
+ if (!Seek())
+ return true;
+
+ bKindaStayInSamePlace = true;
+ bDuckAndCover = false;
+ m_vecSeekVehicle = CVector(0.0f, 0.0f, 0.0f);
+ if (m_pSeekTarget && m_pSeekTarget->IsVehicle())
+ ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover++;
+
+ SetIdle();
+ SetMoveState(PEDMOVE_STILL);
+ SetMoveAnim();
+ if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) {
+ m_pLookTarget = m_pedInObjective;
+ m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget);
+ }
+
+ m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(3000, 6000);
+ return false;
+}
+
WRAPPER void CPed::PedGetupCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE810); }
WRAPPER void CPed::PedStaggerCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8D0); }
WRAPPER void CPed::PedEvadeCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D36E0); }
@@ -5409,4 +5834,16 @@ STARTPATCHES
InjectHook(0x4D1D70, (void (CPed::*)(CEntity*, int)) &CPed::SetFlee, PATCH_JUMP);
InjectHook(0x4D1C40, (void (CPed::*)(CVector2D&, int)) &CPed::SetFlee, PATCH_JUMP);
InjectHook(0x4EB9A0, &CPed::CollideWithPed, PATCH_JUMP);
-ENDPATCHES \ No newline at end of file
+ InjectHook(0x433490, &CPed::CreateDeadPedMoney, PATCH_JUMP);
+ InjectHook(0x433660, &CPed::CreateDeadPedWeaponPickups, PATCH_JUMP);
+ InjectHook(0x4D3970, &CPed::SetDead, PATCH_JUMP);
+ InjectHook(0x53CDF0, &CPed::DeadPedMakesTyresBloody, PATCH_JUMP);
+ InjectHook(0x4E0640, &CPed::SetBeingDraggedFromCar, PATCH_JUMP);
+ InjectHook(0x4D1300, &CPed::SetAttackTimer, PATCH_JUMP);
+ InjectHook(0x4D6950, &CPed::SetBuyIceCream, PATCH_JUMP);
+ InjectHook(0x4D3A60, &CPed::SetChat, PATCH_JUMP);
+ InjectHook(0x4D14B0, (void (CPed::*)(CVector, float)) &CPed::SetSeek, PATCH_JUMP);
+ InjectHook(0x4D15A0, (void (CPed::*)(CEntity*, float)) &CPed::SetSeek, PATCH_JUMP);
+ InjectHook(0x4EB5C0, &CPed::DoesLOSBulletHitPed, PATCH_JUMP);
+ InjectHook(0x4E3EC0, &CPed::DuckAndCover, PATCH_JUMP);
+ENDPATCHES
diff --git a/src/peds/Ped.h b/src/peds/Ped.h
index dcbe247a..db19619b 100644
--- a/src/peds/Ped.h
+++ b/src/peds/Ped.h
@@ -22,23 +22,6 @@ struct CPedAudioData
int m_nMaxRandomDelayTime;
};
-// For hit sounds in fight
-enum {
- S33 = SOUND_FIGHT_PUNCH_33,
- S34 = SOUND_FIGHT_KICK_34,
- S35 = SOUND_FIGHT_HEADBUTT_35,
- S36 = SOUND_FIGHT_PUNCH_36,
- S37 = SOUND_FIGHT_PUNCH_37,
- S38 = SOUND_FIGHT_CLOSE_PUNCH_38,
- S39 = SOUND_FIGHT_PUNCH_39,
- S40 = SOUND_FIGHT_PUNCH_OR_KICK_BELOW_40 ,
- S41 = SOUND_FIGHT_PUNCH_41,
- S42 = SOUND_FIGHT_PUNCH_FROM_BEHIND_42,
- S43 = SOUND_FIGHT_KNEE_OR_KICK_43,
- S44 = SOUND_FIGHT_KICK_44,
- NO_SND = SOUND_TOTAL_PED_SOUNDS
-};
-
struct FightMove
{
AnimationId animId;
@@ -305,10 +288,10 @@ public:
uint8 m_ped_flagF2 : 1;
uint8 m_ped_flagF4 : 1;
uint8 m_ped_flagF8 : 1;
- uint8 m_ped_flagF10 : 1; // set before "quickjack"
+ uint8 bWillBeQuickJacked : 1;
uint8 m_ped_flagF20 : 1;
uint8 m_ped_flagF40 : 1;
- uint8 m_ped_flagF80 : 1;
+ uint8 bDuckAndCover : 1;
uint8 m_ped_flagG1 : 1;
uint8 m_ped_flagG2 : 1;
@@ -394,7 +377,7 @@ public:
float m_fRotationCur;
float m_fRotationDest;
float m_headingRate;
- uint16 m_vehEnterType;
+ uint16 m_vehEnterType; // TODO: this is more like a door, not a type
uint16 m_walkAroundType;
CEntity *m_pCurrentPhysSurface;
CVector m_vecOffsetFromPhysSurface;
@@ -450,7 +433,7 @@ public:
uint32 m_hitRecoverTimer;
uint32 m_objectiveTimer;
uint32 m_duckTimer;
- uint32 field_4E8;
+ uint32 m_duckAndCoverTimer;
int32 m_bloodyFootprintCount;
uint8 m_panicCounter;
uint8 m_deadBleeding;
@@ -538,9 +521,9 @@ public:
void RegisterThreatWithGangPeds(CEntity*);
bool TurnBody(void);
void Chat(void);
- void MakeChangesForNewWeapon(int8);
void CheckAroundForPossibleCollisions(void);
void SetSeek(CVector, float);
+ void SetSeek(CEntity*, float);
bool MakePhonecall(void);
bool FacePhone(void);
CPed *CheckForDeadPeds(void);
@@ -589,6 +572,17 @@ public:
void RemoveInCarAnims(void);
void CollideWithPed(CPed*);
void SetDirectionToWalkAroundObject(CEntity*);
+ void CreateDeadPedMoney(void);
+ void CreateDeadPedWeaponPickups(void);
+ void SetAttackTimer(uint32);
+ void SetBeingDraggedFromCar(CVehicle*, uint32, bool);
+ void SetRadioStation(void);
+ void SetBuyIceCream(void);
+ void SetChat(CEntity*, uint32);
+ void DeadPedMakesTyresBloody(void);
+ void MakeTyresMuddySectorList(CPtrList&);
+ uint8 DoesLOSBulletHitPed(CColPoint &point);
+ bool DuckAndCover(void);
// Static methods
static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset);
@@ -645,6 +639,7 @@ public:
void SetLeader(CEntity* leader);
void SetPedStats(ePedStats);
bool IsGangMember(void);
+ void Die(void);
bool HasWeapon(uint8 weaponType) { return m_weapons[weaponType].m_eWeaponType == weaponType; }
CWeapon &GetWeapon(uint8 weaponType) { return m_weapons[weaponType]; }
diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp
index 24eb4a35..361a9098 100644
--- a/src/peds/PlayerPed.cpp
+++ b/src/peds/PlayerPed.cpp
@@ -3,6 +3,7 @@
#include "PlayerPed.h"
#include "Camera.h"
#include "WeaponEffects.h"
+#include "ModelIndices.h"
CPlayerPed::~CPlayerPed()
{
@@ -14,7 +15,45 @@ WRAPPER void CPlayerPed::SetupPlayerPed(int32) { EAXJMP(0x4EFB60); }
WRAPPER void CPlayerPed::DeactivatePlayerPed(int32) { EAXJMP(0x4EFC00); }
WRAPPER void CPlayerPed::ReactivatePlayerPed(int32) { EAXJMP(0x4EFC20); }
WRAPPER void CPlayerPed::KeepAreaAroundPlayerClear(void) { EAXJMP(0x4F3460); }
+WRAPPER void CPlayerPed::MakeChangesForNewWeapon(int8) { EAXJMP(0x4F2560); }
+WRAPPER void CPlayerPed::SetInitialState(void) { EAXJMP(0x4EFC40); }
+WRAPPER void CPlayerPed::SetMoveAnim(void) { EAXJMP(0x4F3760); }
+WRAPPER void CPlayerPed::ProcessControl(void) { EAXJMP(0x4EFD90); }
+CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1)
+{
+ m_fMoveSpeed = 0.0f;
+ SetModelIndex(MI_PLAYER);
+ SetInitialState();
+
+ m_pWanted = new CWanted();
+ m_pWanted->Initialise();
+ m_pArrestingCop = nil;
+ m_currentWeapon = WEAPONTYPE_UNARMED;
+ m_nSelectedWepSlot = 0;
+ m_nSpeedTimer = 0;
+ m_bSpeedTimerFlag = 0;
+ m_pPointGunAt = nil;
+ m_nPedState = PED_IDLE;
+ m_fMaxStamina = 150.0f;
+ m_fCurrentStamina = m_fMaxStamina;
+ m_fStaminaProgress = 0.0f;
+ m_bShouldEvade = 0;
+ field_1367 = 0;
+ m_nShotDelay = 0;
+ field_1376 = 0.0f;
+ field_1380 = 0;
+ m_bHasLockOnTarget = false;
+ m_bCanBeDamaged = true;
+ m_fWalkAngle = 0.0f;
+ m_fFPSMoveHeading = 0.0f;
+ m_nTargettableObjects[0] = m_nTargettableObjects[1] = m_nTargettableObjects[2] = m_nTargettableObjects[3] = -1;
+ field_1413 = 0;
+ for (int i = 0; i < 6; i++) {
+ m_vecSafePos[i] = CVector(0.0f, 0.0f, 0.0f);
+ field_1488[i] = 0;
+ }
+}
void CPlayerPed::ClearWeaponTarget()
{
@@ -58,10 +97,12 @@ CPlayerPed::AnnoyPlayerPed(bool itsPolice)
class CPlayerPed_ : public CPlayerPed
{
public:
+ CPlayerPed* ctor(void) { return ::new (this) CPlayerPed(); }
void dtor(void) { CPlayerPed::~CPlayerPed(); }
};
STARTPATCHES
+ InjectHook(0x4EF7E0, &CPlayerPed_::ctor, PATCH_JUMP);
InjectHook(0x4EFB30, &CPlayerPed_::dtor, PATCH_JUMP);
InjectHook(0x4F28A0, &CPlayerPed::ClearWeaponTarget, PATCH_JUMP);
InjectHook(0x4F3700, &CPlayerPed::AnnoyPlayerPed, PATCH_JUMP);
diff --git a/src/peds/PlayerPed.h b/src/peds/PlayerPed.h
index fa6d9d43..ecef9bef 100644
--- a/src/peds/PlayerPed.h
+++ b/src/peds/PlayerPed.h
@@ -12,14 +12,14 @@ public:
float m_fCurrentStamina;
float m_fMaxStamina;
float m_fStaminaProgress;
- bool m_bWeaponSlot;
+ uint8 m_nSelectedWepSlot;
bool m_bSpeedTimerFlag;
bool m_bShouldEvade;
int8 field_1367;
int32 m_nSpeedTimer;
int32 m_nShotDelay;
- float field_1376;
- int8 field_1380; // set if can't attack, why?
+ float field_1376; // m_fAttackButtonCounter?
+ int8 field_1380; // bHaveTargetSelected?
int8 field_1381;
int8 field_1382;
int8 field_1383;
@@ -34,19 +34,24 @@ public:
int8 field_1413;
int8 field_1414;
int8 field_1415;
- CVector field_1416[6];
- int32 field_1488[6];
+ CVector m_vecSafePos[6]; // safe places from the player, for example behind a tree
+ int32 field_1488[6]; // m_pPedAtSafePos?
float m_fWalkAngle;
float m_fFPSMoveHeading;
+ CPlayerPed();
~CPlayerPed();
void ReApplyMoveAnims(void);
- void ClearWeaponTarget();
+ void ClearWeaponTarget(void);
void SetWantedLevel(int32 level);
void SetWantedLevelNoDrop(int32 level);
void KeepAreaAroundPlayerClear(void);
void AnnoyPlayerPed(bool);
+ void MakeChangesForNewWeapon(int8);
+ void SetInitialState(void);
+ void SetMoveAnim(void);
+ void ProcessControl(void);
static void SetupPlayerPed(int32);
static void DeactivatePlayerPed(int32);
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/Automobile.h b/src/vehicles/Automobile.h
index 6226b555..e6b64e6e 100644
--- a/src/vehicles/Automobile.h
+++ b/src/vehicles/Automobile.h
@@ -6,6 +6,39 @@
class CObject;
+enum eCarNodes
+{
+ CAR_WHEEL_RF = 1,
+ CAR_WHEEL_RM,
+ CAR_WHEEL_RB,
+ CAR_WHEEL_LF,
+ CAR_WHEEL_LM,
+ CAR_WHEEL_LB,
+ CAR_BUMP_FRONT,
+ CAR_BUMP_REAR,
+ CAR_WING_RF,
+ CAR_WING_RR,
+ CAR_DOOR_RF,
+ CAR_DOOR_RR,
+ CAR_WING_LF,
+ CAR_WING_LR,
+ CAR_DOOR_LF,
+ CAR_DOOR_LR,
+ CAR_BONNET,
+ CAR_BOOT,
+ CAR_WINDSCREEN,
+ NUM_CAR_NODES,
+};
+
+enum eCarPositions
+{
+ CAR_POS_HEADLIGHTS,
+ CAR_POS_TAILLIGHTS,
+ CAR_POS_FRONTSEAT,
+ CAR_POS_BACKSEAT,
+ CAR_POS_EXHAUST = 9,
+};
+
// These are used for all the wheel arrays
// DON'T confuse with VEHWHEEL, which are vehicle components
enum {
@@ -81,7 +114,7 @@ public:
static bool &m_sAllTaxiLights;
- CAutomobile(int32, uint8);
+ CAutomobile(int32 id, uint8 CreatedBy);
// from CEntity
void SetModelIndex(uint32 id);
@@ -152,3 +185,18 @@ public:
static void SetAllTaxiLights(bool set);
};
static_assert(sizeof(CAutomobile) == 0x5A8, "CAutomobile: error");
+
+inline uint8 GetCarDoorFlag(int32 carnode) {
+ switch (carnode) {
+ case CAR_DOOR_LF:
+ return 1;
+ case CAR_DOOR_LR:
+ return 2;
+ case CAR_DOOR_RF:
+ return 4;
+ case CAR_DOOR_RR:
+ return 8;
+ default:
+ return 0;
+ }
+}
diff --git a/src/vehicles/Door.cpp b/src/vehicles/Door.cpp
index 25e87504..2d47b3e9 100644
--- a/src/vehicles/Door.cpp
+++ b/src/vehicles/Door.cpp
@@ -115,6 +115,60 @@ CDoor::IsClosed(void)
return m_fAngle == RetAngleWhenClosed();
}
+
+CTrainDoor::CTrainDoor(void)
+{
+ memset(this, 0, sizeof(*this));
+}
+
+void
+CTrainDoor::Open(float ratio)
+{
+ float open;
+
+ m_fPrevPosn = m_fPosn;
+ open = RetTranslationWhenOpen();
+ if(ratio < 1.0f){
+ m_fPosn = open*ratio;
+ }else{
+ m_nDoorState = DOORST_OPEN;
+ m_fPosn = open;
+ }
+}
+
+float
+CTrainDoor::RetTranslationWhenClosed(void)
+{
+ if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn))
+ return m_fClosedPosn;
+ else
+ return m_fOpenPosn;
+}
+
+float
+CTrainDoor::RetTranslationWhenOpen(void)
+{
+ if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn))
+ return m_fOpenPosn;
+ else
+ return m_fClosedPosn;
+}
+
+bool
+CTrainDoor::IsFullyOpen(void)
+{
+ // 0.5f again...
+ if(Abs(m_fPosn) < Abs(RetTranslationWhenOpen()) - 0.5f)
+ return false;
+ return true;
+}
+
+bool
+CTrainDoor::IsClosed(void)
+{
+ return m_fPosn == RetTranslationWhenClosed();
+}
+
STARTPATCHES
InjectHook(0x545EF0, &CDoor::Open, PATCH_JUMP);
InjectHook(0x545BD0, &CDoor::Process, PATCH_JUMP);
@@ -123,4 +177,10 @@ STARTPATCHES
InjectHook(0x545F80, &CDoor::GetAngleOpenRatio, PATCH_JUMP);
InjectHook(0x546090, &CDoor::IsFullyOpen, PATCH_JUMP);
InjectHook(0x546060, &CDoor::IsClosed, PATCH_JUMP);
+
+ InjectHook(0x546200, &CTrainDoor::Open, PATCH_JUMP);
+ InjectHook(0x546180, &CTrainDoor::RetTranslationWhenClosed, PATCH_JUMP);
+ InjectHook(0x5461C0, &CTrainDoor::RetTranslationWhenOpen, PATCH_JUMP);
+ InjectHook(0x546120, &CTrainDoor::IsFullyOpen, PATCH_JUMP);
+ InjectHook(0x5460F0, &CTrainDoor::IsClosed, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Door.h b/src/vehicles/Door.h
index 7bb7bba3..567d3263 100644
--- a/src/vehicles/Door.h
+++ b/src/vehicles/Door.h
@@ -11,8 +11,9 @@ enum eDoorState
DOORST_CLOSED
};
-struct CDoor
+class CDoor
{
+public:
float m_fMaxAngle;
float m_fMinAngle;
// direction of rotation for air resistance
@@ -34,9 +35,35 @@ struct CDoor
}
void Open(float ratio);
void Process(CVehicle *veh);
- float RetAngleWhenClosed(void);
+ float RetAngleWhenClosed(void); // dead
float RetAngleWhenOpen(void);
float GetAngleOpenRatio(void);
bool IsFullyOpen(void);
+ bool IsClosed(void); // dead
+};
+
+class CTrainDoor
+{
+public:
+ float m_fClosedPosn;
+ float m_fOpenPosn;
+ int8 m_nDirn;
+ int8 m_nDoorState; // same enum as above?
+ int8 m_nAxis;
+ float m_fPosn;
+ float m_fPrevPosn;
+ int field_14; // unused?
+
+ CTrainDoor(void);
+ void Init(float open, float closed, int8 dir, int8 axis) {
+ m_fOpenPosn = open;
+ m_fClosedPosn = closed;
+ m_nDirn = dir;
+ m_nAxis = axis;
+ }
bool IsClosed(void);
+ bool IsFullyOpen(void);
+ float RetTranslationWhenClosed(void);
+ float RetTranslationWhenOpen(void);
+ void Open(float ratio);
};
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 b7fd6ca1..dada1b21 100644
--- a/src/vehicles/Train.cpp
+++ b/src/vehicles/Train.cpp
@@ -1,20 +1,719 @@
#include "common.h"
+#include "main.h"
#include "patcher.h"
+#include "Timer.h"
+#include "ModelIndices.h"
+#include "FileMgr.h"
+#include "Streaming.h"
+#include "Pad.h"
+#include "Camera.h"
+#include "Coronas.h"
+#include "World.h"
+#include "Ped.h"
+#include "HandlingMgr.h"
#include "Train.h"
-CTrain::CTrain(int mi, uint8 owner)
+static CTrainNode *&pTrackNodes = *(CTrainNode**)0x8F4338;
+static int16 &NumTrackNodes = *(int16*)0x95CC5C;
+static float StationDist[3] = { 873.0f, 1522.0f, 2481.0f };
+static float &TotalLengthOfTrack = *(float*)0x64D000;
+static float &TotalDurationOfTrack = *(float*)0x64D004;
+static CTrainInterpolationLine *aLineBits = (CTrainInterpolationLine*)0x70D838; // [17]
+static float *EngineTrackPosition = (float*)0x64D008; //[2]
+static float *EngineTrackSpeed = (float*)0x880848; //[2]
+
+static CTrainNode *&pTrackNodes_S = *(CTrainNode**)0x8F2560;
+static int16 &NumTrackNodes_S = *(int16*)0x95CC6A;
+static float StationDist_S[4] = { 55.0f, 1388.0f, 2337.0f, 3989.0f };
+static float &TotalLengthOfTrack_S = *(float*)0x64D010;
+static float &TotalDurationOfTrack_S = *(float*)0x64D014;
+static CTrainInterpolationLine *aLineBits_S = (CTrainInterpolationLine*)0x726600; // [18]
+static float *EngineTrackPosition_S = (float*)0x64D018; //[4]
+static float *EngineTrackSpeed_S = (float*)0x87C7C8; //[4]
+
+CVector CTrain::aStationCoors[3];
+CVector CTrain::aStationCoors_S[4];
+
+CTrain::CTrain(int32 id, uint8 CreatedBy)
+ : CVehicle(CreatedBy)
+{
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
+ m_vehType = VEHICLE_TYPE_TRAIN;
+ pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId);
+ SetModelIndex(id);
+
+ Doors[0].Init(0.8f, 0.0f, 1, 0);
+ Doors[1].Init(-0.8f, 0.0f, 0, 0);
+
+ m_fMass = 100000000.0f;
+ m_fTurnMass = 100000000.0f;
+ m_fAirResistance = 0.9994f;
+ m_fElasticity = 0.05f;
+
+ m_bProcessDoor = true;
+ m_bTrainStopping = false;
+
+ m_nNumMaxPassengers = 5;
+ m_nDoorTimer = CTimer::GetTimeInMilliseconds();
+ m_nDoorState = TRAIN_DOOR_CLOSED;
+
+ bUsesCollision = true;
+ m_status = STATUS_TRAIN_MOVING;
+}
+
+void
+CTrain::SetModelIndex(uint32 id)
+{
+ int i;
+
+ CVehicle::SetModelIndex(id);
+ for(i = 0; i < 3; i++)
+ m_aTrainNodes[i] = nil;
+ CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes);
+}
+
+void
+CTrain::ProcessControl(void)
+{
+ if(gbModelViewer || m_isFarAway && (CTimer::GetFrameCounter() + m_nWagonId) & 0xF)
+ return;
+
+ CTrainNode *trackNodes;
+ int16 numTrackNodes;
+ float totalLengthOfTrack;
+ float *engineTrackPosition;
+ float *engineTrackSpeed;
+
+ if(m_nTrackId == TRACK_SUBWAY){
+ trackNodes = pTrackNodes_S;
+ numTrackNodes = NumTrackNodes_S;
+ totalLengthOfTrack = TotalLengthOfTrack_S;
+ engineTrackPosition = EngineTrackPosition_S;
+ engineTrackSpeed = EngineTrackSpeed_S;
+ }else{
+ trackNodes = pTrackNodes;
+ numTrackNodes = NumTrackNodes;
+ totalLengthOfTrack = TotalLengthOfTrack;
+ engineTrackPosition = EngineTrackPosition;
+ engineTrackSpeed = EngineTrackSpeed;
+ }
+
+ float trackPositionRear = engineTrackPosition[m_nWagonGroup] - m_fWagonPosition;
+ if(trackPositionRear < 0.0f)
+ trackPositionRear += totalLengthOfTrack;
+
+ // Advance current node to appropriate position
+ float pos1, pos2;
+ int nextTrackNode = m_nCurTrackNode + 1;
+ pos1 = trackNodes[m_nCurTrackNode].t;
+ if(nextTrackNode < numTrackNodes)
+ pos2 = trackNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = totalLengthOfTrack;
+ }
+ while(trackPositionRear < pos1 || trackPositionRear > pos2){
+ m_nCurTrackNode = (m_nCurTrackNode+1) % numTrackNodes;
+ nextTrackNode = m_nCurTrackNode + 1;
+ pos1 = trackNodes[m_nCurTrackNode].t;
+ if(nextTrackNode < numTrackNodes)
+ pos2 = trackNodes[nextTrackNode].t;
+ else{
+ nextTrackNode = 0;
+ pos2 = totalLengthOfTrack;
+ }
+ }
+ float dist = trackNodes[nextTrackNode].t - trackNodes[m_nCurTrackNode].t;
+ if(dist < 0.0f)
+ dist += totalLengthOfTrack;
+ float f = (trackPositionRear - trackNodes[m_nCurTrackNode].t)/dist;
+ CVector posRear = (1.0f - f)*trackNodes[m_nCurTrackNode].p + f*trackNodes[nextTrackNode].p;
+
+ // Now same again for the front
+ float trackPositionFront = trackPositionRear + 20.0f;
+ if(trackPositionFront > totalLengthOfTrack)
+ trackPositionFront -= totalLengthOfTrack;
+ int curTrackNodeFront = m_nCurTrackNode;
+ int nextTrackNodeFront = curTrackNodeFront + 1;
+ pos1 = trackNodes[curTrackNodeFront].t;
+ if(nextTrackNodeFront < numTrackNodes)
+ pos2 = trackNodes[nextTrackNodeFront].t;
+ else{
+ nextTrackNodeFront = 0;
+ pos2 = totalLengthOfTrack;
+ }
+ while(trackPositionFront < pos1 || trackPositionFront > pos2){
+ curTrackNodeFront = (curTrackNodeFront+1) % numTrackNodes;
+ nextTrackNodeFront = curTrackNodeFront + 1;
+ pos1 = trackNodes[curTrackNodeFront].t;
+ if(nextTrackNodeFront < numTrackNodes)
+ pos2 = trackNodes[nextTrackNodeFront].t;
+ else{
+ nextTrackNodeFront = 0;
+ pos2 = totalLengthOfTrack;
+ }
+ }
+ dist = trackNodes[nextTrackNodeFront].t - trackNodes[curTrackNodeFront].t;
+ if(dist < 0.0f)
+ dist += totalLengthOfTrack;
+ f = (trackPositionFront - trackNodes[curTrackNodeFront].t)/dist;
+ CVector posFront = (1.0f - f)*trackNodes[curTrackNodeFront].p + f*trackNodes[nextTrackNodeFront].p;
+
+ // Now set matrix
+ GetPosition() = (posRear + posFront)/2.0f;
+ CVector fwd = posFront - posRear;
+ fwd.Normalise();
+ CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f));
+ right.Normalise();
+ CVector up = CrossProduct(right, fwd);
+ GetRight() = right;
+ GetUp() = up;
+ GetForward() = fwd;
+
+ // Set speed
+ m_vecMoveSpeed = fwd*engineTrackSpeed[m_nWagonGroup]/60.0f;
+ m_fSpeed = engineTrackSpeed[m_nWagonGroup]/60.0f;
+ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+
+ if(engineTrackSpeed[m_nWagonGroup] > 0.001f){
+ m_status = STATUS_TRAIN_MOVING;
+ m_bTrainStopping = false;
+ m_bProcessDoor = true;
+ }else{
+ m_status = STATUS_TRAIN_NOT_MOVING;
+ m_bTrainStopping = true;
+ }
+
+ m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(250.0f));
+
+ if(m_fWagonPosition == 20.0f && m_fSpeed > 0.0001f)
+ if(Abs(TheCamera.GetPosition().z - GetPosition().z) < 15.0f)
+ CPad::GetPad(0)->StartShake_Train(GetPosition().x, GetPosition().y);
+
+ if(m_bProcessDoor)
+ switch(m_nDoorState){
+ case TRAIN_DOOR_CLOSED:
+ if(m_bTrainStopping){
+ m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000;
+ m_nDoorState = TRAIN_DOOR_OPENING;
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_18, 0.0f);
+ }
+ break;
+
+ case TRAIN_DOOR_OPENING:
+ if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){
+ OpenTrainDoor(1.0f - (m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f);
+ }else{
+ OpenTrainDoor(1.0f);
+ m_nDoorState = TRAIN_DOOR_OPEN;
+ }
+ break;
+
+ case TRAIN_DOOR_OPEN:
+ if(!m_bTrainStopping){
+ m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000;
+ m_nDoorState = TRAIN_DOOR_CLOSING;
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_19, 0.0f);
+ }
+ break;
+
+ case TRAIN_DOOR_CLOSING:
+ if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){
+ OpenTrainDoor((m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f);
+ }else{
+ OpenTrainDoor(0.0f);
+ m_nDoorState = TRAIN_DOOR_CLOSED;
+ m_bProcessDoor = false;
+ }
+ break;
+ }
+
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+ RemoveAndAdd();
+
+ bIsStuck = false;
+ bIsInSafePosition = true;
+ bWasPostponed = false;
+
+ // request/remove model
+ if(m_isFarAway){
+ if(m_rwObject)
+ DeleteRwObject();
+ }else if(CStreaming::HasModelLoaded(MI_TRAIN)){
+ if(m_rwObject == nil){
+ m_modelIndex = -1;
+ SetModelIndex(MI_TRAIN);
+ }
+ }else{
+ if(FindPlayerCoors().z * GetPosition().z >= 0.0f)
+ CStreaming::RequestModel(MI_TRAIN, STREAMFLAGS_DEPENDENCY);
+ }
+
+ // Hit stuff
+ if(m_bIsFirstWagon && m_status == STATUS_TRAIN_MOVING){
+ CVector front = GetPosition() + GetForward()*GetColModel()->boundingBox.max.y + m_vecMoveSpeed*CTimer::GetTimeStep();
+
+ int x, xmin, xmax;
+ int y, ymin, ymax;
+
+ xmin = CWorld::GetSectorIndexX(front.x - 3.0f);
+ if(xmin < 0) xmin = 0;
+ xmax = CWorld::GetSectorIndexX(front.x + 3.0f);
+ if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1;
+ ymin = CWorld::GetSectorIndexX(front.y - 3.0f);
+ if(ymin < 0) ymin = 0;
+ ymax = CWorld::GetSectorIndexX(front.y + 3.0f);
+ if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1;
+
+ CWorld::AdvanceCurrentScanCode();
+
+ for(y = ymin; y <= ymax; y++)
+ for(x = xmin; x <= xmax; x++){
+ CSector *s = CWorld::GetSector(x, y);
+ TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES]);
+ TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]);
+ TrainHitStuff(s->m_lists[ENTITYLIST_PEDS]);
+ TrainHitStuff(s->m_lists[ENTITYLIST_PEDS_OVERLAP]);
+ }
+ }
+}
+
+void
+CTrain::PreRender(void)
{
- ctor(mi, owner);
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex());
+
+ if(m_bIsFirstWagon){
+ 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());
+
+ if(behindness < 0.0f){
+ // In front of train
+ CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_FRONT];
+ CVector lightR = GetMatrix() * lightPos;
+ CVector lightL = lightR;
+ lightL -= GetRight()*2.0f*lightPos.x;
+
+ float intensity = -0.4f*behindness + 0.2f;
+ float size = 1.0f - behindness;
+
+ if(behindness < -0.9f && camDist < 35.0f){
+ // directly in front
+ CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255,
+ lightL, size, 80.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, 80.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, 80.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, 80.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ }
+ }
+ }
+
+ if(m_bIsLastWagon){
+ CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_REAR];
+ CVector lightR = GetMatrix() * lightPos;
+ CVector lightL = lightR;
+ lightL -= GetRight()*2.0f*lightPos.x;
+
+ CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255,
+ lightL, 1.0f, 80.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ CCoronas::RegisterCorona((uintptr)this + 13, 255, 0, 0, 255,
+ lightR, 1.0f, 80.0f,
+ CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
+ CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f);
+ }
}
-WRAPPER CTrain* CTrain::ctor(int, uint8) { EAXJMP(0x54E2A0); }
+void
+CTrain::Render(void)
+{
+ CEntity::Render();
+}
+
+void
+CTrain::TrainHitStuff(CPtrList &list)
+{
+ CPtrNode *node;
+ CPhysical *phys;
+
+ for(node = list.first; node; node = node->next){
+ phys = (CPhysical*)node->item;
+ if(phys != this && Abs(this->GetPosition().z - phys->GetPosition().z) < 1.5f)
+ phys->bHitByTrain = true;
+ }
+}
+
+void
+CTrain::AddPassenger(CPed *ped)
+{
+ int i = ped->m_vehEnterType;
+ if((i == TRAIN_POS_LEFT_ENTRY || i == TRAIN_POS_MID_ENTRY || i == TRAIN_POS_RIGHT_ENTRY) && pPassengers[i] == nil){
+ pPassengers[i] = ped;
+ m_nNumPassengers++;
+ }else{
+ for(i = 0; i < 6; i++)
+ if(pPassengers[i] == nil){
+ pPassengers[i] = ped;
+ m_nNumPassengers++;
+ return;
+ }
+ }
+}
+
+void
+CTrain::OpenTrainDoor(float ratio)
+{
+ if(m_rwObject == nil)
+ return;
+
+ CMatrix doorL(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_LHS]));
+ CMatrix doorR(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_RHS]));
+ CVector posL = doorL.GetPosition();
+ CVector posR = doorR.GetPosition();
+
+ bool isClosed = Doors[0].IsClosed(); // useless
+
+ Doors[0].Open(ratio);
+ Doors[1].Open(ratio);
+
+ if(isClosed)
+ Doors[0].RetTranslationWhenClosed(); // useless
+
+ posL.y = Doors[0].m_fPosn;
+ posR.y = Doors[1].m_fPosn;
+
+ doorL.SetTranslate(posL);
+ doorR.SetTranslate(posR);
+
+ doorL.UpdateRW();
+ doorR.UpdateRW();
+}
+
+
+
+void
+CTrain::InitTrains(void)
+{
+ int i, j;
+ CTrain *train;
+
+ // El train
+ if(pTrackNodes == nil)
+ ReadAndInterpretTrackFile("data\\paths\\tracks.dat", &pTrackNodes, &NumTrackNodes, 3, StationDist,
+ &TotalLengthOfTrack, &TotalDurationOfTrack, aLineBits, false);
+ // Subway
+ if(pTrackNodes_S == nil)
+ ReadAndInterpretTrackFile("data\\paths\\tracks2.dat", &pTrackNodes_S, &NumTrackNodes_S, 4, StationDist_S,
+ &TotalLengthOfTrack_S, &TotalDurationOfTrack_S, aLineBits_S, true);
+
+ int trainId;
+ CStreaming::LoadAllRequestedModels(false);
+ if(CModelInfo::GetModelInfo("train", &trainId))
+ CStreaming::RequestModel(trainId, 0);
+ CStreaming::LoadAllRequestedModels(false);
+
+ // El-Train wagons
+ float wagonPositions[] = { 0.0f, 20.0f, 40.0f, 0.0f, 20.0f };
+ int8 firstWagon[] = { 1, 0, 0, 1, 0 };
+ int8 lastWagon[] = { 0, 0, 1, 0, 1 };
+ int16 wagonGroup[] = { 0, 0, 0, 1, 1 };
+ for(i = 0; i < 5; i++){
+ train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE);
+ train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ train->m_status = STATUS_ABANDONED;
+ train->bIsLocked = true;
+ train->m_fWagonPosition = wagonPositions[i];
+ train->m_bIsFirstWagon = firstWagon[i];
+ train->m_bIsLastWagon = lastWagon[i];
+ train->m_nWagonGroup = wagonGroup[i];
+ train->m_nWagonId = i;
+ train->m_nCurTrackNode = 0;
+ CWorld::Add(train);
+ }
+
+ // Subway wagons
+ float wagonPositions_S[] = { 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f };
+ int8 firstWagon_S[] = { 1, 0, 1, 0, 1, 0, 1, 0 };
+ int8 lastWagon_S[] = { 0, 1, 0, 1, 0, 1, 0, 1 };
+ int16 wagonGroup_S[] = { 0, 0, 1, 1, 2, 2, 3, 3 };
+ for(i = 0; i < 8; i++){
+ train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE);
+ train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f);
+ train->m_status = STATUS_ABANDONED;
+ train->bIsLocked = true;
+ train->m_fWagonPosition = wagonPositions_S[i];
+ train->m_bIsFirstWagon = firstWagon_S[i];
+ train->m_bIsLastWagon = lastWagon_S[i];
+ train->m_nWagonGroup = wagonGroup_S[i];
+ train->m_nWagonId = i;
+ train->m_nCurTrackNode = 0;
+ train->m_nTrackId = TRACK_SUBWAY;
+ CWorld::Add(train);
+ }
+
+ // This code is actually useless, it seems it was used for announcements once
+ for(i = 0; i < 3; i++){
+ for(j = 0; pTrackNodes[j].t < StationDist[i]; j++);
+ aStationCoors[i] = pTrackNodes[j].p;
+ }
+ for(i = 0; i < 4; i++){
+ for(j = 0; pTrackNodes_S[j].t < StationDist_S[i]; j++);
+ aStationCoors_S[i] = pTrackNodes_S[j].p;
+ }
+}
+
+void
+CTrain::Shutdown(void)
+{
+ delete[] pTrackNodes;
+ delete[] pTrackNodes_S;
+ pTrackNodes = nil;
+ pTrackNodes_S = nil;
+}
+
+void
+CTrain::ReadAndInterpretTrackFile(char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists,
+ float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail)
+{
+ bool readingFile = false;
+ int bp, lp;
+ int i, tmp;
+
+ if(*nodes == nil){
+ readingFile = true;
+
+ 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++;
+ // BUG: game doesn't terminate string and uses numNodes in sscanf directly
+ gString[lp] = '\0';
+ sscanf(gString, "%d", &tmp);
+ *numNodes = tmp;
+ *nodes = new CTrainNode[*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);
+ }
+
+ // Coordinates are of one of the rails, but we want the center
+ float toCenter = rightRail ? 0.9f : -0.9f;
+ CVector fwd;
+ for(i = 0; i < *numNodes; i++){
+ if(i == *numNodes-1)
+ fwd = (*nodes)[0].p - (*nodes)[i].p;
+ else
+ fwd = (*nodes)[i+1].p - (*nodes)[i].p;
+ CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f));
+ right.Normalise();
+ (*nodes)[i].p -= right*toCenter;
+ }
+ }
+
+ // Calculate length of segments and track
+ float t = 0.0f;
+ for(i = 0; i < *numNodes; i++){
+ (*nodes)[i].t = t;
+ t += ((*nodes)[(i+1) % (*numNodes)].p - (*nodes)[i].p).Magnitude2D();
+ }
+ *totalLength = t;
+
+ // Find correct z values
+ if(readingFile){
+ CColPoint colpoint;
+ CEntity *entity;
+ for(i = 0; i < *numNodes; i++){
+ CVector p = (*nodes)[i].p;
+ p.z += 1.0f;
+ if(CWorld::ProcessVerticalLine(p, p.z-0.5f, colpoint, entity, true, false, false, false, true, false, nil))
+ (*nodes)[i].p.z = colpoint.point.z;
+ (*nodes)[i].p.z += 0.2f;
+ }
+ }
+
+ // Create animation for stopping at stations
+ // TODO: figure out magic numbers?
+ float position = 0.0f;
+ float time = 0.0f;
+ int j = 0;
+ for(i = 0; i < numStations; i++){
+ // Start at full speed
+ interpLines[j].type = 1;
+ interpLines[j].time = time;
+ interpLines[j].position = position;
+ interpLines[j].speed = 15.0f;
+ interpLines[j].acceleration = 0.0f;
+ j++;
+ // distance to next keyframe
+ float dist = (stationDists[i]-40.0f) - position;
+ time += dist/15.0f;
+ position += dist;
+
+ // Now slow down 40 units before stop
+ interpLines[j].type = 2;
+ interpLines[j].time = time;
+ interpLines[j].position = position;
+ interpLines[j].speed = 15.0f;
+ interpLines[j].acceleration = -45.0f/32.0f;
+ j++;
+ time += 80.0f/15.0f;
+ position += 40.0f; // at station
+
+ // stopping
+ interpLines[j].type = 0;
+ interpLines[j].time = time;
+ interpLines[j].position = position;
+ interpLines[j].speed = 0.0f;
+ interpLines[j].acceleration = 0.0f;
+ j++;
+ time += 25.0f;
+
+ // accelerate again
+ interpLines[j].type = 2;
+ interpLines[j].time = time;
+ interpLines[j].position = position;
+ interpLines[j].speed = 0.0f;
+ interpLines[j].acceleration = 45.0f/32.0f;
+ j++;
+ time += 80.0f/15.0f;
+ position += 40.0f; // after station
+ }
+ // last keyframe
+ interpLines[j].type = 1;
+ interpLines[j].time = time;
+ interpLines[j].position = position;
+ interpLines[j].speed = 15.0f;
+ interpLines[j].acceleration = 0.0f;
+ j++;
+ *totalDuration = time + (*totalLength - position)/15.0f;
+
+ // end
+ interpLines[j].time = *totalDuration;
+}
+
+void
+ProcessTrainAnnouncements(void)
+{
+ // TODO but unused
+}
+
+void
+CTrain::UpdateTrains(void)
+{
+ int i, j;
+ uint32 time;
+ float t, deltaT;
+
+ if(TheCamera.GetPosition().x > 200.0f && TheCamera.GetPosition().x < 1600.0f &&
+ TheCamera.GetPosition().y > -1000.0f && TheCamera.GetPosition().y < 500.0f){
+ // Update El-Train
+
+ time = CTimer::GetTimeInMilliseconds();
+ for(i = 0; i < 2; i++){
+ t = TotalDurationOfTrack * (float)(time & 0x1FFFF)/0x20000;
+ // find current frame
+ for(j = 0; t > aLineBits[j+1].time; j++);
+
+ deltaT = t - aLineBits[j].time;
+ switch(aLineBits[j].type){
+ case 0: // standing still
+ EngineTrackPosition[i] = aLineBits[j].position;
+ EngineTrackSpeed[i] = 0.0f;
+ break;
+ case 1: // moving with constant speed
+ EngineTrackPosition[i] = aLineBits[j].position + aLineBits[j].speed*deltaT;
+ EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000) * aLineBits[j].speed;
+ break;
+ case 2: // accelerating/braking
+ EngineTrackPosition[i] = aLineBits[j].position + (aLineBits[j].speed + aLineBits[j].acceleration*deltaT)*deltaT;
+ EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000)*aLineBits[j].speed + 2.0f*aLineBits[j].acceleration*deltaT;
+ break;
+ }
+
+ // time offset for each train
+ time += 0x20000/2;
+ }
+
+ ProcessTrainAnnouncements();
+ }
+
+ // Update Subway
+ time = CTimer::GetTimeInMilliseconds();
+ for(i = 0; i < 4; i++){
+ t = TotalDurationOfTrack_S * (float)(time & 0x3FFFF)/0x40000;
+ // find current frame
+ for(j = 0; t > aLineBits_S[j+1].time; j++);
+
+ deltaT = t - aLineBits_S[j].time;
+ switch(aLineBits_S[j].type){
+ case 0: // standing still
+ EngineTrackPosition_S[i] = aLineBits_S[j].position;
+ EngineTrackSpeed_S[i] = 0.0f;
+ break;
+ case 1: // moving with constant speed
+ EngineTrackPosition_S[i] = aLineBits_S[j].position + aLineBits_S[j].speed*deltaT;
+ EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000) * aLineBits_S[j].speed;
+ break;
+ case 2: // accelerating/braking
+ EngineTrackPosition_S[i] = aLineBits_S[j].position + (aLineBits_S[j].speed + aLineBits_S[j].acceleration*deltaT)*deltaT;
+ EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000)*aLineBits_S[j].speed + 2.0f*aLineBits_S[j].acceleration*deltaT;
+ break;
+ }
+
+ // time offset for each train
+ time += 0x40000/4;
+ }
+}
class CTrain_ : public CTrain
{
public:
+ void ctor(int32 id, uint8 CreatedBy) { ::new (this) CTrain(id, CreatedBy); }
+ void SetModelIndex_(uint32 id) { CTrain::SetModelIndex(id); }
+ void ProcessControl_(void) { CTrain::ProcessControl(); }
+ void PreRender_(void) { CTrain::PreRender(); }
+ void Render_(void) { CTrain::Render(); }
void dtor(void) { CTrain::~CTrain(); }
};
STARTPATCHES
+ InjectHook(0x54E470, &CTrain_::SetModelIndex_, PATCH_JUMP);
+ InjectHook(0x54E4C0, &CTrain_::PreRender_, PATCH_JUMP);
+ InjectHook(0x54EAA0, &CTrain_::Render_, PATCH_JUMP);
InjectHook(0x54E450, &CTrain_::dtor, PATCH_JUMP);
+ InjectHook(0x54E2A0, &CTrain_::ctor, PATCH_JUMP);
+ InjectHook(0x550300, &CTrain::TrainHitStuff, PATCH_JUMP);
+ InjectHook(0x5504A0, &CTrain::AddPassenger, PATCH_JUMP);
+ InjectHook(0x550360, &CTrain::OpenTrainDoor, PATCH_JUMP);
+
+ InjectHook(0x54F000, CTrain::InitTrains, PATCH_JUMP);
+ InjectHook(0x54F360, CTrain::Shutdown, PATCH_JUMP);
+ InjectHook(0x54EAB0, CTrain::ReadAndInterpretTrackFile, PATCH_JUMP);
+ InjectHook(0x54F3A0, CTrain::UpdateTrains, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Train.h b/src/vehicles/Train.h
index 5e1e2e35..84b53537 100644
--- a/src/vehicles/Train.h
+++ b/src/vehicles/Train.h
@@ -1,25 +1,93 @@
#pragma once
-#include "common.h"
-#include "patcher.h"
#include "Vehicle.h"
+#include "Door.h"
enum
{
- TRAIN_DOOR_STATE2 = 2
+ TRACK_ELTRAIN,
+ TRACK_SUBWAY
+};
+
+enum
+{
+ TRAIN_DOOR_CLOSED,
+ TRAIN_DOOR_OPENING,
+ TRAIN_DOOR_OPEN,
+ TRAIN_DOOR_CLOSING
+};
+
+enum eTrainNodes
+{
+ TRAIN_DOOR_LHS = 1,
+ TRAIN_DOOR_RHS
+};
+
+enum eTrainPositions
+{
+ TRAIN_POS_LIGHT_FRONT,
+ TRAIN_POS_LIGHT_REAR,
+ TRAIN_POS_LEFT_ENTRY,
+ TRAIN_POS_MID_ENTRY,
+ TRAIN_POS_RIGHT_ENTRY
+};
+
+struct CTrainNode
+{
+ CVector p; // position
+ float t; // xy-distance from start on track
+};
+
+struct CTrainInterpolationLine
+{
+ uint8 type;
+ float time; // when does this keyframe start
+ // initial values at start of frame
+ float position;
+ float speed;
+ float acceleration;
};
class CTrain : public CVehicle
{
public:
// 0x288
- uint8 stuff1[20];
- uint8 m_trackId;
- uint8 stuff2[7];
- int16 m_doorState;
- uint8 stuff3[62];
-
- CTrain(int, uint8);
- CTrain* ctor(int, uint8);
+ float m_fWagonPosition;
+ int16 m_nWagonId;
+ int16 m_isFarAway; // don't update so often?
+ int16 m_nCurTrackNode;
+ int16 m_nWagonGroup;
+ float m_fSpeed;
+ bool m_bProcessDoor;
+ bool m_bTrainStopping;
+ bool m_bIsFirstWagon;
+ bool m_bIsLastWagon;
+ uint8 m_nTrackId; // or m_bUsesSubwayTracks?
+ uint32 m_nDoorTimer;
+ int16 m_nDoorState;
+ CTrainDoor Doors[2];
+ RwFrame *m_aTrainNodes[3];
+
+ // unused
+ static CVector aStationCoors[3];
+ static CVector aStationCoors_S[4];
+
+ CTrain(int32 id, uint8 CreatedBy);
+
+ // from CEntity
+ void SetModelIndex(uint32 id);
+ void ProcessControl(void);
+ void PreRender(void);
+ void Render(void);
+
+ void AddPassenger(CPed *ped);
+ void OpenTrainDoor(float ratio);
+ void TrainHitStuff(CPtrList &list);
+
+ static void InitTrains(void);
+ static void Shutdown(void);
+ static void ReadAndInterpretTrackFile(char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists,
+ float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail);
+ static void UpdateTrains(void);
};
static_assert(sizeof(CTrain) == 0x2E4, "CTrain: error");
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 36675c8b..aaf5926f 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -77,7 +77,7 @@ CVehicle::CVehicle(uint8 CreatedBy)
field_214 = 0;
bLightsOn = false;
bVehicleColProcessed = false;
- field_1F9 = 0;
+ m_numPedsUseItAsCover = 0;
bIsCarParkVehicle = false;
bHasAlreadyBeenRecorded = false;
m_bSirenOrAlarm = 0;
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 262bd62c..07893782 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -26,39 +26,6 @@ enum eCarLock {
};
-enum eCarNodes
-{
- CAR_WHEEL_RF = 1,
- CAR_WHEEL_RM,
- CAR_WHEEL_RB,
- CAR_WHEEL_LF,
- CAR_WHEEL_LM,
- CAR_WHEEL_LB,
- CAR_BUMP_FRONT,
- CAR_BUMP_REAR,
- CAR_WING_RF,
- CAR_WING_RR,
- CAR_DOOR_RF,
- CAR_DOOR_RR,
- CAR_WING_LF,
- CAR_WING_LR,
- CAR_DOOR_LF,
- CAR_DOOR_LR,
- CAR_BONNET,
- CAR_BOOT,
- CAR_WINDSCREEN,
- NUM_CAR_NODES,
-};
-
-enum
-{
- CAR_POS_HEADLIGHTS,
- CAR_POS_TAILLIGHTS,
- CAR_POS_FRONTSEAT,
- CAR_POS_BACKSEAT,
- CAR_POS_EXHAUST = 9,
-};
-
enum
{
BOAT_POS_FRONTSEAT
@@ -202,7 +169,7 @@ public:
uint8 m_veh_flagD40 : 1;
uint8 m_veh_flagD80 : 1;
- int8 field_1F9;
+ int8 m_numPedsUseItAsCover;
uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default)
int8 field_1FB;
int8 field_1FC[4];
@@ -311,21 +278,6 @@ static_assert(offsetof(CVehicle, m_pCurGroundEntity) == 0x1E0, "CVehicle: error"
static_assert(offsetof(CVehicle, m_nAlarmState) == 0x1A0, "CVehicle: error");
static_assert(offsetof(CVehicle, m_nLastWeaponDamage) == 0x228, "CVehicle: error");
-inline uint8 GetVehDoorFlag(int32 carnode) {
- switch (carnode) {
- case CAR_DOOR_LF:
- return 1;
- case CAR_DOOR_LR:
- return 2;
- case CAR_DOOR_RF:
- return 4;
- case CAR_DOOR_RR:
- return 8;
- default:
- return 0;
- }
-}
-
class cTransmission;
class cVehicleParams