mirror of
https://gitlab.com/shinovon/re3-symbian.git
synced 2026-05-23 01:57:21 +03:00
Initial commit
This commit is contained in:
commit
77cdaaf97e
827 changed files with 418745 additions and 0 deletions
4735
src/vehicles/Automobile.cpp
Normal file
4735
src/vehicles/Automobile.cpp
Normal file
File diff suppressed because it is too large
Load diff
204
src/vehicles/Automobile.h
Normal file
204
src/vehicles/Automobile.h
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
#include "DamageManager.h"
|
||||
#include "Door.h"
|
||||
|
||||
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 {
|
||||
CARWHEEL_FRONT_LEFT,
|
||||
CARWHEEL_REAR_LEFT,
|
||||
CARWHEEL_FRONT_RIGHT,
|
||||
CARWHEEL_REAR_RIGHT
|
||||
};
|
||||
|
||||
enum eBombType
|
||||
{
|
||||
CARBOMB_NONE,
|
||||
CARBOMB_TIMED,
|
||||
CARBOMB_ONIGNITION,
|
||||
CARBOMB_REMOTE,
|
||||
CARBOMB_TIMEDACTIVE,
|
||||
CARBOMB_ONIGNITIONACTIVE,
|
||||
};
|
||||
|
||||
enum {
|
||||
CAR_DOOR_FLAG_UNKNOWN = 0x0,
|
||||
CAR_DOOR_FLAG_LF = 0x1,
|
||||
CAR_DOOR_FLAG_LR = 0x2,
|
||||
CAR_DOOR_FLAG_RF = 0x4,
|
||||
CAR_DOOR_FLAG_RR = 0x8
|
||||
};
|
||||
|
||||
class CAutomobile : public CVehicle
|
||||
{
|
||||
public:
|
||||
// 0x288
|
||||
CDamageManager Damage;
|
||||
CDoor Doors[6];
|
||||
RwFrame *m_aCarNodes[NUM_CAR_NODES];
|
||||
CColPoint m_aWheelColPoints[4];
|
||||
float m_aSuspensionSpringRatio[4];
|
||||
float m_aSuspensionSpringRatioPrev[4];
|
||||
float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented
|
||||
float m_auto_unused1;
|
||||
bool m_aWheelSkidmarkMuddy[4];
|
||||
bool m_aWheelSkidmarkBloody[4];
|
||||
float m_aWheelRotation[4];
|
||||
float m_aWheelPosition[4];
|
||||
float m_aWheelSpeed[4];
|
||||
uint8 m_auto_unused2;
|
||||
uint8 m_bombType : 3;
|
||||
uint8 bTaxiLight : 1;
|
||||
uint8 bDriverLastFrame : 1; // for bombs
|
||||
uint8 bFixedColour : 1;
|
||||
uint8 bBigWheels : 1;
|
||||
uint8 bWaterTight : 1; // no damage for non-player peds
|
||||
uint8 bNotDamagedUpsideDown : 1;
|
||||
uint8 bMoreResistantToDamage : 1;
|
||||
CEntity *m_pBombRigger;
|
||||
int16 m_auto_unk1;
|
||||
uint16 m_hydraulicState;
|
||||
uint32 m_nBusDoorTimerEnd;
|
||||
uint32 m_nBusDoorTimerStart;
|
||||
float m_aSuspensionSpringLength[4];
|
||||
float m_aSuspensionLineLength[4];
|
||||
float m_fHeightAboveRoad;
|
||||
float m_fTraction;
|
||||
float m_fVelocityChangeForAudio;
|
||||
float m_randomValues[6]; // used for what?
|
||||
float m_fFireBlowUpTimer;
|
||||
CPhysical *m_aGroundPhysical[4]; // physicals touching wheels
|
||||
CVector m_aGroundOffset[4]; // from ground object to colpoint
|
||||
CEntity *m_pSetOnFireEntity;
|
||||
float m_weaponDoorTimerLeft; // still don't know what exactly this is
|
||||
float m_weaponDoorTimerRight;
|
||||
float m_fCarGunLR;
|
||||
float m_fCarGunUD;
|
||||
float m_fPropellerRotation;
|
||||
uint8 stuff4[4];
|
||||
uint8 m_nWheelsOnGround;
|
||||
uint8 m_nDriveWheelsOnGround;
|
||||
uint8 m_nDriveWheelsOnGroundPrev;
|
||||
float m_fGasPedalAudio;
|
||||
tWheelState m_aWheelState[4];
|
||||
|
||||
static bool m_sAllTaxiLights;
|
||||
|
||||
CAutomobile(int32 id, uint8 CreatedBy);
|
||||
|
||||
// from CEntity
|
||||
void SetModelIndex(uint32 id);
|
||||
void ProcessControl(void);
|
||||
void Teleport(CVector v);
|
||||
void PreRender(void);
|
||||
void Render(void);
|
||||
|
||||
// from CPhysical
|
||||
int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints);
|
||||
|
||||
// from CVehicle
|
||||
void ProcessControlInputs(uint8);
|
||||
void GetComponentWorldPosition(int32 component, CVector &pos);
|
||||
bool IsComponentPresent(int32 component);
|
||||
void SetComponentRotation(int32 component, CVector rotation);
|
||||
void OpenDoor(int32 component, eDoors door, float openRatio);
|
||||
void ProcessOpenDoor(uint32, uint32, float);
|
||||
bool IsDoorReady(eDoors door);
|
||||
bool IsDoorFullyOpen(eDoors door);
|
||||
bool IsDoorClosed(eDoors door);
|
||||
bool IsDoorMissing(eDoors door);
|
||||
void RemoveRefsToVehicle(CEntity *ent);
|
||||
void BlowUpCar(CEntity *ent);
|
||||
bool SetUpWheelColModel(CColModel *colModel);
|
||||
void BurstTyre(uint8 tyre);
|
||||
bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset);
|
||||
float GetHeightAboveRoad(void);
|
||||
void PlayCarHorn(void);
|
||||
|
||||
void FireTruckControl(void);
|
||||
void TankControl(void);
|
||||
void HydraulicControl(void);
|
||||
void VehicleDamage(float impulse, uint16 damagedPiece);
|
||||
void ProcessBuoyancy(void);
|
||||
void DoDriveByShootings(void);
|
||||
int32 RcbanditCheckHitWheels(void);
|
||||
int32 RcbanditCheck1CarWheels(CPtrList &list);
|
||||
void PlaceOnRoadProperly(void);
|
||||
void dmgDrawCarCollidingParticles(const CVector &pos, float amount);
|
||||
void AddDamagedVehicleParticles(void);
|
||||
int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed);
|
||||
void PlayHornIfNecessary(void);
|
||||
void ResetSuspension(void);
|
||||
void SetupSuspensionLines(void);
|
||||
void ScanForCrimes(void);
|
||||
void BlowUpCarsInPath(void);
|
||||
bool HasCarStoppedBecauseOfLight(void);
|
||||
void SetBusDoorTimer(uint32 timer, uint8 type);
|
||||
void ProcessAutoBusDoors(void);
|
||||
void ProcessSwingingDoor(int32 component, eDoors door);
|
||||
void SetupDamageAfterLoad(void);
|
||||
CObject *SpawnFlyingComponent(int32 component, uint32 type);
|
||||
CObject *RemoveBonnetInPedCollision(void);
|
||||
void SetPanelDamage(int32 component, ePanels panel, bool noFlyingComponents = false);
|
||||
void SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents = false);
|
||||
void SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents = false);
|
||||
|
||||
void Fix(void);
|
||||
void SetComponentVisibility(RwFrame *frame, uint32 flags);
|
||||
void SetupModelNodes(void);
|
||||
void SetTaxiLight(bool light);
|
||||
bool GetAllWheelsOffGround(void);
|
||||
void HideAllComps(void);
|
||||
void ShowAllComps(void);
|
||||
void ReduceHornCounter(void);
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
virtual void Save(uint8*& buf);
|
||||
virtual void Load(uint8*& buf);
|
||||
#endif
|
||||
static const uint32 nSaveStructSize;
|
||||
|
||||
static void SetAllTaxiLights(bool set);
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CAutomobile, 0x5A8);
|
||||
|
||||
inline uint8 GetCarDoorFlag(int32 carnode) {
|
||||
switch (carnode) {
|
||||
case CAR_DOOR_LF:
|
||||
return CAR_DOOR_FLAG_LF;
|
||||
case CAR_DOOR_LR:
|
||||
return CAR_DOOR_FLAG_LR;
|
||||
case CAR_DOOR_RF:
|
||||
return CAR_DOOR_FLAG_RF;
|
||||
case CAR_DOOR_RR:
|
||||
return CAR_DOOR_FLAG_RR;
|
||||
default:
|
||||
return CAR_DOOR_FLAG_UNKNOWN;
|
||||
}
|
||||
}
|
||||
45
src/vehicles/Bike.h
Normal file
45
src/vehicles/Bike.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
|
||||
// some miami bike leftovers
|
||||
|
||||
enum eBikeNodes {
|
||||
BIKE_NODE_NONE,
|
||||
BIKE_CHASSIS,
|
||||
BIKE_FORKS_FRONT,
|
||||
BIKE_FORKS_REAR,
|
||||
BIKE_WHEEL_FRONT,
|
||||
BIKE_WHEEL_REAR,
|
||||
BIKE_MUDGUARD,
|
||||
BIKE_HANDLEBARS,
|
||||
BIKE_NUM_NODES
|
||||
};
|
||||
|
||||
class CBike : public CVehicle
|
||||
{
|
||||
public:
|
||||
RwFrame *m_aBikeNodes[BIKE_NUM_NODES]; // assuming
|
||||
uint8 unk1[96];
|
||||
AnimationId m_bikeSitAnimation;
|
||||
uint8 unk2[180];
|
||||
float m_aSuspensionSpringRatio[4];
|
||||
|
||||
/* copied from VC, one of the floats here is gone, assuming m_bike_unused1 */
|
||||
float m_aSuspensionSpringRatioPrev[4];
|
||||
float m_aWheelTimer[4];
|
||||
//float m_bike_unused1;
|
||||
int m_aWheelSkidmarkType[2];
|
||||
bool m_aWheelSkidmarkBloody[2];
|
||||
bool m_aWheelSkidmarkUnk[2];
|
||||
float m_aWheelRotation[2];
|
||||
float m_aWheelSpeed[2];
|
||||
float m_aWheelPosition[2];
|
||||
float m_aWheelBasePosition[2];
|
||||
float m_aSuspensionSpringLength[4];
|
||||
float m_aSuspensionLineLength[4];
|
||||
float m_fHeightAboveRoad;
|
||||
/**/
|
||||
|
||||
float m_fTraction;
|
||||
};
|
||||
952
src/vehicles/Boat.cpp
Normal file
952
src/vehicles/Boat.cpp
Normal file
|
|
@ -0,0 +1,952 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "General.h"
|
||||
#include "Timecycle.h"
|
||||
#include "HandlingMgr.h"
|
||||
#include "CarCtrl.h"
|
||||
#include "RwHelper.h"
|
||||
#include "ModelIndices.h"
|
||||
#include "VisibilityPlugins.h"
|
||||
#include "DMAudio.h"
|
||||
#include "Camera.h"
|
||||
#include "Darkel.h"
|
||||
#include "Explosion.h"
|
||||
#include "Particle.h"
|
||||
#include "WaterLevel.h"
|
||||
#include "Floater.h"
|
||||
#include "World.h"
|
||||
#include "Pools.h"
|
||||
#include "Pad.h"
|
||||
#include "Boat.h"
|
||||
#include "SaveBuf.h"
|
||||
|
||||
#define INVALID_ORIENTATION (-9999.99f)
|
||||
|
||||
float MAX_WAKE_LENGTH = 50.0f;
|
||||
float MIN_WAKE_INTERVAL = 1.0f;
|
||||
float WAKE_LIFETIME = 400.0f;
|
||||
|
||||
float fShapeLength = 0.4f;
|
||||
float fShapeTime = 0.05f;
|
||||
float fRangeMult = 0.75f;
|
||||
float fTimeMult = 1.0f/WAKE_LIFETIME;
|
||||
|
||||
CBoat *CBoat::apFrameWakeGeneratingBoats[4];
|
||||
|
||||
const uint32 CBoat::nSaveStructSize =
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
1156;
|
||||
#else
|
||||
sizeof(CBoat);
|
||||
#endif
|
||||
|
||||
CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner)
|
||||
{
|
||||
CVehicleModelInfo *minfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi);
|
||||
m_vehType = VEHICLE_TYPE_BOAT;
|
||||
m_fAccelerate = 0.0f;
|
||||
m_fBrake = 0.0f;
|
||||
m_fSteeringLeftRight = 0.0f;
|
||||
m_nPadID = 0;
|
||||
m_fMovingRotation = 0.0f;
|
||||
SetModelIndex(mi);
|
||||
|
||||
pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)minfo->m_handlingId);
|
||||
minfo->ChooseVehicleColour(m_currentColour1, m_currentColour2);
|
||||
|
||||
m_fMass = pHandling->fMass;
|
||||
m_fTurnMass = pHandling->fTurnMass / 2.0f;
|
||||
m_vecCentreOfMass = pHandling->CentreOfMass;
|
||||
m_fAirResistance = pHandling->Dimension.x * pHandling->Dimension.z / m_fMass;
|
||||
m_fElasticity = 0.1f;
|
||||
m_fBuoyancy = pHandling->fBuoyancy;
|
||||
m_fSteerAngle = 0.0f;
|
||||
m_fGasPedal = 0.0f;
|
||||
m_fBrakePedal = 0.0f;
|
||||
|
||||
m_fThrustZ = 0.25f;
|
||||
m_fThrustY = 0.35f;
|
||||
m_vecMoveRes = CVector(0.7f, 0.998f, 0.999f);
|
||||
m_vecTurnRes = CVector(0.85f, 0.96f, 0.96f);
|
||||
m_boat_unused3 = false;
|
||||
|
||||
m_fVolumeUnderWater = 7.0f;
|
||||
m_fPrevVolumeUnderWater = 7.0f;
|
||||
m_vecBuoyancePoint = CVector(0.0f, 0.0f, 0.0f);
|
||||
|
||||
m_nDeltaVolumeUnderWater = 0;
|
||||
bBoatInWater = true;
|
||||
bPropellerInWater = true;
|
||||
|
||||
bIsInWater = true;
|
||||
|
||||
m_boat_unused2 = 0;
|
||||
m_bIsAnchored = true;
|
||||
m_fOrientation = INVALID_ORIENTATION;
|
||||
bTouchingWater = true;
|
||||
m_fDamage = 0.0f;
|
||||
m_pSetOnFireEntity = nil;
|
||||
m_nNumWakePoints = 0;
|
||||
|
||||
for (int16 i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++)
|
||||
m_afWakePointLifeTime[i] = 0.0f;
|
||||
|
||||
m_nAmmoInClip = 20;
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::SetModelIndex(uint32 id)
|
||||
{
|
||||
CEntity::SetModelIndex(id);
|
||||
SetupModelNodes();
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::GetComponentWorldPosition(int32 component, CVector &pos)
|
||||
{
|
||||
pos = *RwMatrixGetPos(RwFrameGetLTM(m_aBoatNodes[component]));
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::ProcessControl(void)
|
||||
{
|
||||
if(m_nZoneLevel > LEVEL_GENERIC && m_nZoneLevel != CCollision::ms_collisionInMemory)
|
||||
return;
|
||||
|
||||
bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f;
|
||||
|
||||
PruneWakeTrail();
|
||||
|
||||
int r, g, b;
|
||||
RwRGBA splashColor, jetColor;
|
||||
r = 114.75f*(CTimeCycle::GetAmbientRed() + 0.5f*CTimeCycle::GetDirectionalRed());
|
||||
g = 114.75f*(CTimeCycle::GetAmbientGreen() + 0.5f*CTimeCycle::GetDirectionalGreen());
|
||||
b = 114.75f*(CTimeCycle::GetAmbientBlue() + 0.5f*CTimeCycle::GetDirectionalBlue());
|
||||
r = Clamp(r, 0, 255);
|
||||
g = Clamp(g, 0, 255);
|
||||
b = Clamp(b, 0, 255);
|
||||
splashColor.red = r;
|
||||
splashColor.green = g;
|
||||
splashColor.blue = b;
|
||||
splashColor.alpha = CGeneral::GetRandomNumberInRange(128, 150);
|
||||
|
||||
r = 242.25f*(CTimeCycle::GetAmbientRed() + 0.5f*CTimeCycle::GetDirectionalRed());
|
||||
g = 242.25f*(CTimeCycle::GetAmbientGreen() + 0.5f*CTimeCycle::GetDirectionalGreen());
|
||||
b = 242.25f*(CTimeCycle::GetAmbientBlue() + 0.5f*CTimeCycle::GetDirectionalBlue());
|
||||
r = Clamp(r, 0, 255);
|
||||
g = Clamp(g, 0, 255);
|
||||
b = Clamp(b, 0, 255);
|
||||
jetColor.red = r;
|
||||
jetColor.green = g;
|
||||
jetColor.blue = b;
|
||||
jetColor.alpha = CGeneral::GetRandomNumberInRange(96, 128);
|
||||
|
||||
CGeneral::GetRandomNumber(); // unused
|
||||
|
||||
ProcessCarAlarm();
|
||||
|
||||
switch(GetStatus()){
|
||||
case STATUS_PLAYER:
|
||||
m_bIsAnchored = false;
|
||||
m_fOrientation = INVALID_ORIENTATION;
|
||||
ProcessControlInputs(0);
|
||||
if(GetModelIndex() == MI_PREDATOR)
|
||||
DoFixedMachineGuns();
|
||||
break;
|
||||
case STATUS_SIMPLE:
|
||||
m_bIsAnchored = false;
|
||||
m_fOrientation = INVALID_ORIENTATION;
|
||||
CPhysical::ProcessControl();
|
||||
bBoatInWater = true;
|
||||
bPropellerInWater = true;
|
||||
bIsInWater = true;
|
||||
return;
|
||||
case STATUS_PHYSICS:
|
||||
m_bIsAnchored = false;
|
||||
m_fOrientation = INVALID_ORIENTATION;
|
||||
CCarCtrl::SteerAIBoatWithPhysics(this);
|
||||
break;
|
||||
case STATUS_ABANDONED:
|
||||
case STATUS_WRECKED:
|
||||
bBoatInWater = true;
|
||||
bPropellerInWater = true;
|
||||
bIsInWater = true;
|
||||
m_fSteerAngle = 0.0;
|
||||
bIsHandbrakeOn = false;
|
||||
m_fBrakePedal = 0.5f;
|
||||
m_fGasPedal = 0.0f;
|
||||
if((GetPosition() - CWorld::Players[CWorld::PlayerInFocus].GetPos()).Magnitude() > 150.0f){
|
||||
m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
|
||||
m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
float collisionDamage = pHandling->fCollisionDamageMultiplier * m_fDamageImpulse;
|
||||
#ifdef FIX_BUGS
|
||||
if (collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && m_fHealth >= 150.0f && !bCollisionProof) {
|
||||
#else
|
||||
if(collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && m_fHealth >= 150.0f){
|
||||
#endif
|
||||
float prevHealth = m_fHealth;
|
||||
if(this == FindPlayerVehicle()){
|
||||
if(bTakeLessDamage)
|
||||
m_fHealth -= (collisionDamage-25.0f)/6.0f;
|
||||
else
|
||||
m_fHealth -= (collisionDamage-25.0f)/2.0f;
|
||||
}else{
|
||||
if(collisionDamage > 60.0f && pDriver)
|
||||
pDriver->Say(SOUND_PED_ANNOYED_DRIVER);
|
||||
if(bTakeLessDamage)
|
||||
m_fHealth -= (collisionDamage-25.0f)/12.0f;
|
||||
else
|
||||
m_fHealth -= (collisionDamage-25.0f)/4.0f;
|
||||
}
|
||||
|
||||
if(m_fHealth <= 0.0f && prevHealth > 0.0f){
|
||||
m_fHealth = 1.0f;
|
||||
m_pSetOnFireEntity = m_pDamageEntity;
|
||||
}
|
||||
}
|
||||
|
||||
// Damage particles
|
||||
if(m_fHealth <= 600.0f && GetStatus() != STATUS_WRECKED &&
|
||||
Abs(GetPosition().x - TheCamera.GetPosition().x) < 200.0f &&
|
||||
Abs(GetPosition().y - TheCamera.GetPosition().y) < 200.0f){
|
||||
float speedSq = m_vecMoveSpeed.MagnitudeSqr();
|
||||
CVector smokeDir = 0.8f*m_vecMoveSpeed;
|
||||
CVector smokePos;
|
||||
switch(GetModelIndex()){
|
||||
case MI_SPEEDER:
|
||||
smokePos = CVector(0.4f, -2.4f, 0.8f);
|
||||
smokeDir += 0.05f*GetRight();
|
||||
smokeDir.z += 0.2f*m_vecMoveSpeed.z;
|
||||
break;
|
||||
case MI_REEFER:
|
||||
smokePos = CVector(2.0f, -1.0f, 0.5f);
|
||||
smokeDir += 0.07f*GetRight();
|
||||
break;
|
||||
case MI_PREDATOR:
|
||||
default:
|
||||
smokePos = CVector(-1.5f, -0.5f, 1.2f);
|
||||
smokeDir += -0.08f*GetRight();
|
||||
break;
|
||||
}
|
||||
|
||||
smokePos = GetMatrix() * smokePos;
|
||||
|
||||
// On fire
|
||||
if(m_fHealth < 150.0f){
|
||||
CParticle::AddParticle(PARTICLE_CARFLAME, smokePos,
|
||||
CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(2.25f/200.0f, 0.09f)),
|
||||
nil, 0.9f);
|
||||
CVector smokePos2 = smokePos;
|
||||
smokePos2.x += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f);
|
||||
smokePos2.y += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f);
|
||||
smokePos2.z += CGeneral::GetRandomNumberInRange(2.25f/4.0f, 2.25f);
|
||||
CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, smokePos2, CVector(0.0f, 0.0f, 0.0f));
|
||||
|
||||
m_fDamage += CTimer::GetTimeStepInMilliseconds();
|
||||
if(m_fDamage > 5000.0f)
|
||||
BlowUpCar(m_pSetOnFireEntity);
|
||||
}
|
||||
|
||||
if(speedSq < 0.25f && (CTimer::GetFrameCounter() + m_randomSeed) & 1)
|
||||
CParticle::AddParticle(PARTICLE_ENGINE_STEAM, smokePos, smokeDir);
|
||||
if(speedSq < 0.25f && m_fHealth <= 350.0f)
|
||||
CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, smokePos, 1.25f*smokeDir);
|
||||
}
|
||||
|
||||
CPhysical::ProcessControl();
|
||||
|
||||
CVector buoyanceImpulse(0.0f, 0.0f, 0.0f);
|
||||
CVector buoyancePoint(0.0f, 0.0f, 0.0f);
|
||||
if(mod_Buoyancy.ProcessBuoyancy(this, pHandling->fBuoyancy, &buoyancePoint, &buoyanceImpulse)){
|
||||
// Process boat in water
|
||||
if(0.1f * m_fMass * GRAVITY*CTimer::GetTimeStep() < buoyanceImpulse.z){
|
||||
bBoatInWater = true;
|
||||
bIsInWater = true;
|
||||
}else{
|
||||
bBoatInWater = false;
|
||||
bIsInWater = false;
|
||||
}
|
||||
|
||||
m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater;
|
||||
m_vecBuoyancePoint = buoyancePoint;
|
||||
ApplyMoveForce(buoyanceImpulse);
|
||||
if(!onLand)
|
||||
ApplyTurnForce(buoyanceImpulse, buoyancePoint);
|
||||
|
||||
if(!onLand && bBoatInWater && GetUp().z > 0.0f){
|
||||
float impulse;
|
||||
if(m_fGasPedal > 0.05f)
|
||||
impulse = m_vecMoveSpeed.MagnitudeSqr()*pHandling->fSuspensionForceLevel*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f*m_fGasPedal;
|
||||
else
|
||||
impulse = 0.0f;
|
||||
impulse = Min(impulse, GRAVITY*pHandling->fSuspensionDampingLevel*m_fMass*CTimer::GetTimeStep());
|
||||
ApplyMoveForce(impulse*GetUp());
|
||||
ApplyTurnForce(impulse*GetUp(), buoyancePoint - pHandling->fSuspensionBias*GetForward());
|
||||
}
|
||||
|
||||
// Handle boat moving forward
|
||||
if(Abs(m_fGasPedal) > 0.05f || m_vecMoveSpeed.Magnitude2D() > 0.01f){
|
||||
if(bBoatInWater)
|
||||
AddWakePoint(GetPosition());
|
||||
|
||||
float steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward());
|
||||
if (GetModelIndex() == MI_GHOST)
|
||||
steerFactor = 1.0f - DotProduct(m_vecMoveSpeed, GetForward())*0.3f;
|
||||
if(steerFactor < 0.0f) steerFactor = 0.0f;
|
||||
|
||||
CVector propeller(0.0f, -pHandling->Dimension.y*m_fThrustY, -pHandling->Dimension.z*m_fThrustZ);
|
||||
propeller = Multiply3x3(GetMatrix(), propeller);
|
||||
CVector propellerWorld = GetPosition() + propeller;
|
||||
|
||||
float steerSin = Sin(-m_fSteerAngle * steerFactor);
|
||||
float steerCos = Cos(-m_fSteerAngle * steerFactor);
|
||||
float waterLevel;
|
||||
CWaterLevel::GetWaterLevel(propellerWorld, &waterLevel, true);
|
||||
if(propellerWorld.z-0.5f < waterLevel){
|
||||
float propellerDepth = waterLevel - (propellerWorld.z - 0.5f);
|
||||
if(propellerDepth > 1.0f)
|
||||
propellerDepth = 1.0f;
|
||||
else
|
||||
propellerDepth = SQR(propellerDepth);
|
||||
bPropellerInWater = true;
|
||||
|
||||
if(Abs(m_fGasPedal) > 0.05f){
|
||||
CVector forceDir = Multiply3x3(GetMatrix(), CVector(-steerSin, steerCos, -Abs(m_fSteerAngle)));
|
||||
CVector force = propellerDepth * m_fGasPedal * 40.0f * pHandling->Transmission.fEngineAcceleration * pHandling->fMass * forceDir;
|
||||
if(force.z > 0.2f)
|
||||
force.z = SQR(1.2f - force.z) + 0.2f;
|
||||
if(onLand){
|
||||
if(m_fGasPedal < 0.0f){
|
||||
force.x *= 5.0f;
|
||||
force.y *= 5.0f;
|
||||
}
|
||||
if(force.z < 0.0f)
|
||||
force.z = 0.0f;
|
||||
ApplyMoveForce(force * CTimer::GetTimeStep());
|
||||
}else{
|
||||
ApplyMoveForce(force * CTimer::GetTimeStep());
|
||||
ApplyTurnForce(force * CTimer::GetTimeStep(), propeller - pHandling->fTractionBias*GetUp());
|
||||
float rightForce = DotProduct(GetRight(), force);
|
||||
ApplyTurnForce(-rightForce*GetRight() * CTimer::GetTimeStep(), GetUp());
|
||||
}
|
||||
|
||||
// Spray some particles
|
||||
CVector jetDir = -0.04f * force;
|
||||
if(m_fGasPedal > 0.0f){
|
||||
if(GetStatus() == STATUS_PLAYER){
|
||||
bool cameraHack = TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN ||
|
||||
TheCamera.WhoIsInControlOfTheCamera == CAMCONTROL_OBBE;
|
||||
CVector sternPos = GetColModel()->boundingBox.min;
|
||||
sternPos.x = 0.0f;
|
||||
sternPos.z = 0.0f;
|
||||
sternPos = Multiply3x3(GetMatrix(), sternPos);
|
||||
|
||||
CVector jetPos = GetPosition() + sternPos;
|
||||
if(cameraHack)
|
||||
jetPos.z = 1.0f;
|
||||
else
|
||||
jetPos.z = 0.0f;
|
||||
|
||||
#ifdef PC_PARTICLE
|
||||
CVector wakePos = GetPosition() + sternPos;
|
||||
wakePos.z -= 0.65f;
|
||||
#else
|
||||
CVector wakePos = GetPosition() + sternPos;
|
||||
wakePos.z = -0.3f;
|
||||
#endif
|
||||
|
||||
CVector wakeDir = 0.75f * jetDir;
|
||||
|
||||
CParticle::AddParticle(PARTICLE_BOAT_THRUSTJET, jetPos, jetDir, nil, 0.0f, jetColor);
|
||||
#ifdef PC_PARTICLE
|
||||
CParticle::AddParticle(PARTICLE_CAR_SPLASH, jetPos, 0.25f * jetDir, nil, 1.0f, splashColor,
|
||||
CGeneral::GetRandomNumberInRange(0, 30),
|
||||
CGeneral::GetRandomNumberInRange(0, 90), 3);
|
||||
#endif
|
||||
if(!cameraHack)
|
||||
CParticle::AddParticle(PARTICLE_BOAT_WAKE, wakePos, wakeDir, nil, 0.0f, jetColor);
|
||||
}else if((CTimer::GetFrameCounter() + m_randomSeed) & 1){
|
||||
#ifdef PC_PARTICLE
|
||||
jetDir.z = 0.018f;
|
||||
jetDir.x *= 0.01f;
|
||||
jetDir.y *= 0.01f;
|
||||
propellerWorld.z += 1.5f;
|
||||
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 1.5f, jetColor);
|
||||
#else
|
||||
jetDir.z = 0.018f;
|
||||
jetDir.x *= 0.03f;
|
||||
jetDir.y *= 0.03f;
|
||||
propellerWorld.z += 1.0f;
|
||||
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 0.0f, jetColor);
|
||||
#endif
|
||||
|
||||
#ifdef PC_PARTICLE
|
||||
CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.1f * jetDir, nil, 0.5f, splashColor,
|
||||
CGeneral::GetRandomNumberInRange(0, 30),
|
||||
CGeneral::GetRandomNumberInRange(0, 90), 3);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}else if(!onLand){
|
||||
float force = 50.0f*DotProduct(m_vecMoveSpeed, GetForward());
|
||||
force = Min(force, 10.0f);
|
||||
CVector propellerForce = propellerDepth * Multiply3x3(GetMatrix(), force*CVector(-steerSin, 0.0f, 0.0f));
|
||||
ApplyMoveForce(propellerForce * CTimer::GetTimeStep()*0.5f);
|
||||
ApplyTurnForce(propellerForce * CTimer::GetTimeStep()*0.5f, propeller);
|
||||
}
|
||||
}else
|
||||
bPropellerInWater = false;
|
||||
}
|
||||
|
||||
// Slow down or push down boat as it approaches the world limits
|
||||
m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - 1900.0f)*0.01f); // east
|
||||
m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - -1515.0f)*0.01f); // west
|
||||
m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - 600.0f)*0.01f); // north
|
||||
m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - -1900.0f)*0.01f); // south
|
||||
|
||||
if(!onLand && bBoatInWater)
|
||||
ApplyWaterResistance();
|
||||
|
||||
// No idea what exactly is going on here besides drag in YZ
|
||||
float fx = Pow(m_vecTurnRes.x, CTimer::GetTimeStep());
|
||||
float fy = Pow(m_vecTurnRes.y, CTimer::GetTimeStep());
|
||||
float fz = Pow(m_vecTurnRes.z, CTimer::GetTimeStep());
|
||||
m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space
|
||||
// TODO: figure this out
|
||||
float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx;
|
||||
m_vecTurnSpeed.y *= fy;
|
||||
m_vecTurnSpeed.z *= fz;
|
||||
float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass;
|
||||
m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world
|
||||
CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass);
|
||||
ApplyTurnForce(CVector(0.0f, 0.0f, forceUp), com + GetForward());
|
||||
|
||||
m_nDeltaVolumeUnderWater = (m_fVolumeUnderWater-m_fPrevVolumeUnderWater)*10000;
|
||||
|
||||
// Falling into water
|
||||
if(!onLand && bBoatInWater && GetUp().z > 0.0f && m_nDeltaVolumeUnderWater > 200){
|
||||
DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, m_nDeltaVolumeUnderWater);
|
||||
|
||||
float speedUp = m_vecMoveSpeed.MagnitudeSqr() * m_nDeltaVolumeUnderWater * 0.0004f;
|
||||
if(speedUp + m_vecMoveSpeed.z > pHandling->fBrakeDeceleration)
|
||||
speedUp = pHandling->fBrakeDeceleration - m_vecMoveSpeed.z;
|
||||
if(speedUp < 0.0f) speedUp = 0.0f;
|
||||
float speedFwd = DotProduct(m_vecMoveSpeed, GetForward());
|
||||
speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fTractionLoss;
|
||||
CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp);
|
||||
CVector splashImpulse = speed * m_fMass;
|
||||
ApplyMoveForce(splashImpulse);
|
||||
ApplyTurnForce(splashImpulse, buoyancePoint);
|
||||
}
|
||||
|
||||
// Spray particles on sides of boat
|
||||
#ifdef PC_PARTICLE
|
||||
if(m_nDeltaVolumeUnderWater > 75)
|
||||
#else
|
||||
if(m_nDeltaVolumeUnderWater > 120)
|
||||
#endif
|
||||
{
|
||||
float speed = m_vecMoveSpeed.Magnitude();
|
||||
float splash1Size = speed;
|
||||
float splash2Size = float(m_nDeltaVolumeUnderWater) * 0.005f * 0.2f;
|
||||
float front = 0.9f * GetColModel()->boundingBox.max.y;
|
||||
if(splash1Size > 0.75f) splash1Size = 0.75f;
|
||||
|
||||
CVector dir, pos;
|
||||
|
||||
// right
|
||||
#ifdef PC_PARTICLE
|
||||
dir = -0.5f*m_vecMoveSpeed;
|
||||
dir.z += 0.1f*speed;
|
||||
dir += 0.5f*GetRight()*speed;
|
||||
pos = front*GetForward() + 0.5f*GetRight() + GetPosition() + m_vecBuoyancePoint;
|
||||
CWaterLevel::GetWaterLevel(pos, &pos.z, true);
|
||||
#else
|
||||
dir = 0.3f*m_vecMoveSpeed;
|
||||
dir.z += 0.05f*speed;
|
||||
dir += 0.5f*GetRight()*speed;
|
||||
pos = (GetPosition() + m_vecBuoyancePoint) + (1.5f*GetRight());
|
||||
#endif
|
||||
|
||||
#ifdef PC_PARTICLE
|
||||
CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, 0.75f * dir, nil, splash1Size, splashColor,
|
||||
CGeneral::GetRandomNumberInRange(0, 30),
|
||||
CGeneral::GetRandomNumberInRange(0, 90), 1);
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size, jetColor);
|
||||
#else
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size);
|
||||
#endif
|
||||
|
||||
|
||||
// left
|
||||
#ifdef PC_PARTICLE
|
||||
dir = -0.5f*m_vecMoveSpeed;
|
||||
dir.z += 0.1f*speed;
|
||||
dir -= 0.5f*GetRight()*speed;
|
||||
pos = front*GetForward() - 0.5f*GetRight() + GetPosition() + m_vecBuoyancePoint;
|
||||
CWaterLevel::GetWaterLevel(pos, &pos.z, true);
|
||||
#else
|
||||
dir = 0.3f*m_vecMoveSpeed;
|
||||
dir.z += 0.05f*speed;
|
||||
dir -= 0.5f*GetRight()*speed;
|
||||
pos = (GetPosition() + m_vecBuoyancePoint) - (1.5f*GetRight());
|
||||
#endif
|
||||
|
||||
#ifdef PC_PARTICLE
|
||||
CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, 0.75f * dir, nil, splash1Size, splashColor,
|
||||
CGeneral::GetRandomNumberInRange(0, 30),
|
||||
CGeneral::GetRandomNumberInRange(0, 90), 1);
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size, jetColor);
|
||||
#else
|
||||
CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, splash2Size);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_fPrevVolumeUnderWater = m_fVolumeUnderWater;
|
||||
}else{
|
||||
bBoatInWater = false;
|
||||
bIsInWater = false;
|
||||
}
|
||||
|
||||
if(m_bIsAnchored){
|
||||
m_vecMoveSpeed.x = 0.0f;
|
||||
m_vecMoveSpeed.y = 0.0f;
|
||||
|
||||
if(m_fOrientation == INVALID_ORIENTATION){
|
||||
m_fOrientation = GetForward().Heading();
|
||||
}else{
|
||||
// is this some inlined CPlaceable method?
|
||||
CVector pos = GetPosition();
|
||||
GetMatrix().RotateZ(m_fOrientation - GetForward().Heading());
|
||||
GetMatrix().SetTranslateOnly(pos);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessDelayedExplosion();
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::ProcessControlInputs(uint8 pad)
|
||||
{
|
||||
m_nPadID = pad;
|
||||
if(m_nPadID > 3)
|
||||
m_nPadID = 3;
|
||||
|
||||
m_fBrake += (CPad::GetPad(pad)->GetBrake()/255.0f - m_fBrake)*0.1f;
|
||||
m_fBrake = Clamp(m_fBrake, 0.0f, 1.0f);
|
||||
|
||||
if(m_fBrake < 0.05f){
|
||||
m_fBrake = 0.0f;
|
||||
m_fAccelerate += (CPad::GetPad(pad)->GetAccelerate()/255.0f - m_fAccelerate)*0.1f;
|
||||
m_fAccelerate = Clamp(m_fAccelerate, 0.0f, 1.0f);
|
||||
}else
|
||||
m_fAccelerate = -m_fBrake*0.2f;
|
||||
|
||||
m_fSteeringLeftRight += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteeringLeftRight)*0.2f;
|
||||
m_fSteeringLeftRight = Clamp(m_fSteeringLeftRight, -1.0f, 1.0f);
|
||||
|
||||
float steeringSq = m_fSteeringLeftRight < 0.0f ? -SQR(m_fSteeringLeftRight) : SQR(m_fSteeringLeftRight);
|
||||
m_fSteerAngle = pHandling->fSteeringLock * DEGTORAD(steeringSq);
|
||||
m_fGasPedal = m_fAccelerate;
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::ApplyWaterResistance(void)
|
||||
{
|
||||
float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward());
|
||||
// TODO: figure out how this works
|
||||
float resistance = 0.001f * SQR(m_fVolumeUnderWater) * m_fMass;
|
||||
float magic = (SQR(fwdSpeed) + 0.05f) * resistance + 1.0f;
|
||||
magic = Abs(magic);
|
||||
float fx = Pow(m_vecMoveRes.x/magic, 0.5f*CTimer::GetTimeStep());
|
||||
float fy = Pow(m_vecMoveRes.y/magic, 0.5f*CTimer::GetTimeStep());
|
||||
float fz = Pow(m_vecMoveRes.z/magic, 0.5f*CTimer::GetTimeStep());
|
||||
|
||||
m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // invert - to local space
|
||||
m_vecMoveSpeed.x *= fx;
|
||||
m_vecMoveSpeed.y *= fy;
|
||||
m_vecMoveSpeed.z *= fz;
|
||||
float force = (fy - 1.0f) * m_vecMoveSpeed.y * m_fMass;
|
||||
m_vecMoveSpeed = Multiply3x3(GetMatrix(), m_vecMoveSpeed); // back to world
|
||||
|
||||
ApplyTurnForce(force*GetForward(), -GetUp());
|
||||
|
||||
if(m_vecMoveSpeed.z > 0.0f)
|
||||
m_vecMoveSpeed.z *= fz;
|
||||
else
|
||||
m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz;
|
||||
}
|
||||
|
||||
RwObject*
|
||||
GetBoatAtomicObjectCB(RwObject *object, void *data)
|
||||
{
|
||||
RpAtomic *atomic = (RpAtomic*)object;
|
||||
assert(RwObjectGetType(object) == rpATOMIC);
|
||||
if(RpAtomicGetFlags(atomic) & rpATOMICRENDER)
|
||||
*(RpAtomic**)data = atomic;
|
||||
return object;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::BlowUpCar(CEntity *culprit)
|
||||
{
|
||||
RpAtomic *atomic;
|
||||
RwFrame *frame;
|
||||
RwMatrix *matrix;
|
||||
CObject *obj;
|
||||
|
||||
if(!bCanBeDamaged)
|
||||
return;
|
||||
|
||||
// explosion pushes vehicle up
|
||||
m_vecMoveSpeed.z += 0.13f;
|
||||
SetStatus(STATUS_WRECKED);
|
||||
bRenderScorched = true;
|
||||
|
||||
m_fHealth = 0.0;
|
||||
m_nBombTimer = 0;
|
||||
TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z);
|
||||
|
||||
if(this == FindPlayerVehicle())
|
||||
FindPlayerPed()->m_fHealth = 0.0f; // kill player
|
||||
if(pDriver){
|
||||
CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION);
|
||||
pDriver->SetDead();
|
||||
pDriver->FlagToDestroyWhenNextProcessed();
|
||||
}
|
||||
|
||||
bEngineOn = false;
|
||||
bLightsOn = false;
|
||||
ChangeLawEnforcerState(false);
|
||||
|
||||
CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0);
|
||||
if(m_aBoatNodes[BOAT_MOVING] == nil)
|
||||
return;
|
||||
|
||||
// much like CAutomobile::SpawnFlyingComponent from here on
|
||||
|
||||
atomic = nil;
|
||||
RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic);
|
||||
if(atomic == nil)
|
||||
return;
|
||||
|
||||
obj = new CObject();
|
||||
if(obj == nil)
|
||||
return;
|
||||
|
||||
obj->SetModelIndexNoCreate(MI_CAR_WHEEL);
|
||||
|
||||
// object needs base model
|
||||
obj->RefModelInfo(GetModelIndex());
|
||||
|
||||
// create new atomic
|
||||
matrix = RwFrameGetLTM(m_aBoatNodes[BOAT_MOVING]);
|
||||
frame = RwFrameCreate();
|
||||
atomic = RpAtomicClone(atomic);
|
||||
*RwFrameGetMatrix(frame) = *matrix;
|
||||
RpAtomicSetFrame(atomic, frame);
|
||||
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
|
||||
obj->AttachToRwObject((RwObject*)atomic);
|
||||
|
||||
// init object
|
||||
obj->m_fMass = 10.0f;
|
||||
obj->m_fTurnMass = 25.0f;
|
||||
obj->m_fAirResistance = 0.99f;
|
||||
obj->m_fElasticity = 0.1f;
|
||||
obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f;
|
||||
obj->ObjectCreatedBy = TEMP_OBJECT;
|
||||
obj->SetIsStatic(false);
|
||||
obj->bIsPickup = false;
|
||||
|
||||
// life time
|
||||
CObject::nNoTempObjects++;
|
||||
obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000;
|
||||
|
||||
obj->m_vecMoveSpeed = m_vecMoveSpeed;
|
||||
if(GetUp().z > 0.0f)
|
||||
obj->m_vecMoveSpeed.z = 0.3f;
|
||||
else
|
||||
obj->m_vecMoveSpeed.z = 0.0f;
|
||||
|
||||
obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f;
|
||||
obj->m_vecTurnSpeed.x = 0.5f;
|
||||
|
||||
// push component away from boat
|
||||
CVector dist = obj->GetPosition() - GetPosition();
|
||||
dist.Normalise();
|
||||
if(GetUp().z > 0.0f)
|
||||
dist += GetUp();
|
||||
obj->GetMatrix().GetPosition() += dist;
|
||||
|
||||
CWorld::Add(obj);
|
||||
|
||||
atomic = nil;
|
||||
RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic);
|
||||
if(atomic)
|
||||
RpAtomicSetFlags(atomic, 0);
|
||||
}
|
||||
|
||||
RwIm3DVertex KeepWaterOutVertices[4];
|
||||
RwImVertexIndex KeepWaterOutIndices[6];
|
||||
|
||||
void
|
||||
CBoat::Render()
|
||||
{
|
||||
CMatrix matrix;
|
||||
|
||||
if (m_aBoatNodes[BOAT_MOVING] != nil) {
|
||||
matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING]));
|
||||
|
||||
CVector pos = matrix.GetPosition();
|
||||
matrix.SetRotateZ(m_fMovingRotation);
|
||||
matrix.Translate(pos);
|
||||
|
||||
matrix.UpdateRW();
|
||||
if (CVehicle::bWheelsOnlyCheat) {
|
||||
RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING]));
|
||||
}
|
||||
}
|
||||
m_fMovingRotation += 0.05f;
|
||||
((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->SetVehicleColour(m_currentColour1, m_currentColour2);
|
||||
if (!CVehicle::bWheelsOnlyCheat)
|
||||
CEntity::Render();
|
||||
#ifdef NEW_RENDERER
|
||||
if(!gbNewRenderer)
|
||||
#endif
|
||||
RenderWaterOutPolys(); // not separate function in III
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::RenderWaterOutPolys(void)
|
||||
{
|
||||
KeepWaterOutIndices[0] = 0;
|
||||
KeepWaterOutIndices[1] = 2;
|
||||
KeepWaterOutIndices[2] = 1;
|
||||
KeepWaterOutIndices[3] = 1;
|
||||
KeepWaterOutIndices[4] = 2;
|
||||
KeepWaterOutIndices[5] = 3;
|
||||
RwIm3DVertexSetRGBA(&KeepWaterOutVertices[0], 255, 255, 255, 255);
|
||||
RwIm3DVertexSetRGBA(&KeepWaterOutVertices[1], 255, 255, 255, 255);
|
||||
RwIm3DVertexSetRGBA(&KeepWaterOutVertices[2], 255, 255, 255, 255);
|
||||
RwIm3DVertexSetRGBA(&KeepWaterOutVertices[3], 255, 255, 255, 255);
|
||||
switch (GetModelIndex()) {
|
||||
case MI_SPEEDER:
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.15f, 3.61f, 1.03f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.15f, 3.61f, 1.03f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.15f, 0.06f, 1.03f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.15f, 0.06f, 1.03f);
|
||||
break;
|
||||
case MI_REEFER:
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.9f, 2.83f, 1.0f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.9f, 2.83f, 1.0f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.66f, -4.48f, 0.83f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.66f, -4.48f, 0.83f);
|
||||
break;
|
||||
case MI_PREDATOR:
|
||||
default:
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.45f, 1.9f, 0.96f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.45f, 1.9f, 0.96f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.45f, -3.75f, 0.96f);
|
||||
RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.45f, -3.75f, 0.96f);
|
||||
break;
|
||||
}
|
||||
KeepWaterOutVertices[0].u = 0.0f;
|
||||
KeepWaterOutVertices[0].v = 0.0f;
|
||||
KeepWaterOutVertices[1].u = 1.0f;
|
||||
KeepWaterOutVertices[1].v = 0.0f;
|
||||
KeepWaterOutVertices[2].u = 0.0f;
|
||||
KeepWaterOutVertices[2].v = 1.0f;
|
||||
KeepWaterOutVertices[3].u = 1.0f;
|
||||
KeepWaterOutVertices[3].v = 1.0f;
|
||||
#ifdef NEW_RENDERER
|
||||
if(!gbNewRenderer)
|
||||
#endif
|
||||
{
|
||||
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpWaterRaster);
|
||||
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
|
||||
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
|
||||
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO);
|
||||
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
|
||||
}
|
||||
if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) {
|
||||
RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6);
|
||||
RwIm3DEnd();
|
||||
}
|
||||
#ifdef NEW_RENDERER
|
||||
if(!gbNewRenderer)
|
||||
#endif
|
||||
{
|
||||
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE);
|
||||
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
|
||||
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::Teleport(CVector v)
|
||||
{
|
||||
CWorld::Remove(this);
|
||||
SetPosition(v);
|
||||
SetOrientation(0.0f, 0.0f, 0.0f);
|
||||
SetMoveSpeed(0.0f, 0.0f, 0.0f);
|
||||
SetTurnSpeed(0.0f, 0.0f, 0.0f);
|
||||
CWorld::Add(this);
|
||||
}
|
||||
|
||||
bool
|
||||
CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats)
|
||||
{
|
||||
uint8 numVerts = 0;
|
||||
|
||||
if ( apFrameWakeGeneratingBoats[0] == NULL )
|
||||
return false;
|
||||
|
||||
for ( int32 i = 0; i < 4; i++ )
|
||||
{
|
||||
CBoat *pBoat = apFrameWakeGeneratingBoats[i];
|
||||
if ( !pBoat )
|
||||
break;
|
||||
|
||||
for ( int j = 0; j < pBoat->m_nNumWakePoints; j++ )
|
||||
{
|
||||
float fDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[j]) * fShapeTime + float(j) * fShapeLength + fSize;
|
||||
|
||||
if ( Abs(pBoat->m_avec2dWakePoints[j].x - sector.x) < fDist
|
||||
&& Abs(pBoat->m_avec2dWakePoints[i].y - sector.y) < fDist )
|
||||
{
|
||||
apBoats[numVerts] = pBoat;
|
||||
numVerts = 1; // += ?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numVerts != 0;
|
||||
}
|
||||
|
||||
float
|
||||
CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat)
|
||||
{
|
||||
for ( int i = 0; i < pBoat->m_nNumWakePoints; i++ )
|
||||
{
|
||||
float fMaxDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fShapeTime + float(i) * fShapeLength;
|
||||
|
||||
CVector2D vecDist = pBoat->m_avec2dWakePoints[i] - CVector2D(vecVertex);
|
||||
|
||||
float fDist = vecDist.MagnitudeSqr();
|
||||
|
||||
if ( fDist < SQR(fMaxDist) )
|
||||
return 1.0f - Min(fRangeMult * Sqrt(fDist / SQR(fMaxDist)) + (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fTimeMult, 1.0f);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::SetupModelNodes()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < ARRAY_SIZE(m_aBoatNodes); i++)
|
||||
m_aBoatNodes[i] = nil;
|
||||
CClumpModelInfo::FillFrameArray(GetClump(), m_aBoatNodes);
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::FillBoatList()
|
||||
{
|
||||
int16 frameId = 0;
|
||||
|
||||
apFrameWakeGeneratingBoats[0] = nil;
|
||||
apFrameWakeGeneratingBoats[1] = nil;
|
||||
apFrameWakeGeneratingBoats[2] = nil;
|
||||
apFrameWakeGeneratingBoats[3] = nil;
|
||||
|
||||
for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) {
|
||||
CBoat *boat = (CBoat *)(CPools::GetVehiclePool()->GetSlot(i));
|
||||
if (boat && boat->m_vehType == VEHICLE_TYPE_BOAT) {
|
||||
int16 nNumWakePoints = boat->m_nNumWakePoints;
|
||||
if (nNumWakePoints != 0) {
|
||||
if (frameId >= ARRAY_SIZE(apFrameWakeGeneratingBoats)) {
|
||||
int16 frameId2 = -1;
|
||||
for (int16 j = 0; j < ARRAY_SIZE(apFrameWakeGeneratingBoats); j++) {
|
||||
if (apFrameWakeGeneratingBoats[j]->m_nNumWakePoints < nNumWakePoints) {
|
||||
frameId2 = j;
|
||||
nNumWakePoints = apFrameWakeGeneratingBoats[j]->m_nNumWakePoints;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameId2 != -1)
|
||||
apFrameWakeGeneratingBoats[frameId2] = boat;
|
||||
} else {
|
||||
apFrameWakeGeneratingBoats[frameId++] = boat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::PruneWakeTrail(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++){
|
||||
if(m_afWakePointLifeTime[i] <= 0.0f)
|
||||
break;
|
||||
if(m_afWakePointLifeTime[i] <= CTimer::GetTimeStep()){
|
||||
m_afWakePointLifeTime[i] = 0.0f;
|
||||
break;
|
||||
}
|
||||
m_afWakePointLifeTime[i] -= CTimer::GetTimeStep();
|
||||
}
|
||||
m_nNumWakePoints = i;
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::AddWakePoint(CVector point)
|
||||
{
|
||||
int i;
|
||||
if(m_afWakePointLifeTime[0] > 0.0f){
|
||||
if((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(1.0f)){
|
||||
for(i = Min(m_nNumWakePoints, ARRAY_SIZE(m_afWakePointLifeTime)-1); i != 0; i--){
|
||||
m_avec2dWakePoints[i] = m_avec2dWakePoints[i-1];
|
||||
m_afWakePointLifeTime[i] = m_afWakePointLifeTime[i-1];
|
||||
}
|
||||
m_avec2dWakePoints[0] = point;
|
||||
m_afWakePointLifeTime[0] = 400.0f;
|
||||
if(m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime))
|
||||
m_nNumWakePoints++;
|
||||
}
|
||||
}else{
|
||||
m_avec2dWakePoints[0] = point;
|
||||
m_afWakePointLifeTime[0] = 400.0f;
|
||||
m_nNumWakePoints = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
void
|
||||
CBoat::Save(uint8*& buf)
|
||||
{
|
||||
CVehicle::Save(buf);
|
||||
ZeroSaveBuf(buf, 1156 - 648);
|
||||
}
|
||||
|
||||
void
|
||||
CBoat::Load(uint8*& buf)
|
||||
{
|
||||
CVehicle::Load(buf);
|
||||
SkipSaveBuf(buf, 1156 - 648);
|
||||
}
|
||||
#endif
|
||||
81
src/vehicles/Boat.h
Normal file
81
src/vehicles/Boat.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
|
||||
enum eBoatNodes
|
||||
{
|
||||
BOAT_MOVING = 1,
|
||||
BOAT_RUDDER,
|
||||
BOAT_WINDSCREEN,
|
||||
NUM_BOAT_NODES
|
||||
};
|
||||
|
||||
class CBoat : public CVehicle
|
||||
{
|
||||
public:
|
||||
// 0x288
|
||||
float m_fThrustZ;
|
||||
float m_fThrustY;
|
||||
CVector m_vecMoveRes;
|
||||
CVector m_vecTurnRes;
|
||||
float m_fMovingRotation;
|
||||
int32 m_boat_unused1;
|
||||
RwFrame *m_aBoatNodes[NUM_BOAT_NODES];
|
||||
uint8 bBoatInWater : 1;
|
||||
uint8 bPropellerInWater : 1;
|
||||
bool m_bIsAnchored;
|
||||
float m_fOrientation;
|
||||
int32 m_boat_unused2;
|
||||
float m_fDamage;
|
||||
CEntity *m_pSetOnFireEntity;
|
||||
bool m_boat_unused3;
|
||||
float m_fAccelerate;
|
||||
float m_fBrake;
|
||||
float m_fSteeringLeftRight;
|
||||
uint8 m_nPadID;
|
||||
int32 m_boat_unused4;
|
||||
float m_fVolumeUnderWater;
|
||||
CVector m_vecBuoyancePoint;
|
||||
float m_fPrevVolumeUnderWater;
|
||||
int16 m_nDeltaVolumeUnderWater;
|
||||
uint16 m_nNumWakePoints;
|
||||
CVector2D m_avec2dWakePoints[32];
|
||||
float m_afWakePointLifeTime[32];
|
||||
|
||||
CBoat(int, uint8);
|
||||
|
||||
virtual void SetModelIndex(uint32 id);
|
||||
virtual void ProcessControl();
|
||||
virtual void Teleport(CVector v);
|
||||
virtual void PreRender(void) {};
|
||||
virtual void Render(void);
|
||||
virtual void ProcessControlInputs(uint8);
|
||||
virtual void GetComponentWorldPosition(int32 component, CVector &pos);
|
||||
virtual bool IsComponentPresent(int32 component) { return true; }
|
||||
virtual void BlowUpCar(CEntity *ent);
|
||||
|
||||
void RenderWaterOutPolys(void);
|
||||
void ApplyWaterResistance(void);
|
||||
void SetupModelNodes();
|
||||
void PruneWakeTrail(void);
|
||||
void AddWakePoint(CVector point);
|
||||
|
||||
static CBoat *apFrameWakeGeneratingBoats[4];
|
||||
|
||||
static bool IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats);
|
||||
static float IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat);
|
||||
static void FillBoatList(void);
|
||||
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
virtual void Save(uint8*& buf);
|
||||
virtual void Load(uint8*& buf);
|
||||
#endif
|
||||
static const uint32 nSaveStructSize;
|
||||
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CBoat, 0x484);
|
||||
|
||||
extern float MAX_WAKE_LENGTH;
|
||||
extern float MIN_WAKE_INTERVAL;
|
||||
extern float WAKE_LIFETIME;
|
||||
271
src/vehicles/CarGen.cpp
Normal file
271
src/vehicles/CarGen.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "CarGen.h"
|
||||
|
||||
#include "Automobile.h"
|
||||
#include "Boat.h"
|
||||
#include "Camera.h"
|
||||
#include "CarCtrl.h"
|
||||
#include "CutsceneMgr.h"
|
||||
#include "General.h"
|
||||
#include "Pools.h"
|
||||
#include "Streaming.h"
|
||||
#include "Timer.h"
|
||||
#include "Vehicle.h"
|
||||
#include "World.h"
|
||||
#include "SaveBuf.h"
|
||||
|
||||
uint8 CTheCarGenerators::ProcessCounter;
|
||||
uint32 CTheCarGenerators::NumOfCarGenerators;
|
||||
CCarGenerator CTheCarGenerators::CarGeneratorArray[NUM_CARGENS];
|
||||
uint8 CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter;
|
||||
uint32 CTheCarGenerators::CurrentActiveCount;
|
||||
|
||||
void CCarGenerator::SwitchOff()
|
||||
{
|
||||
#ifdef FIX_BUGS
|
||||
if (m_nUsesRemaining != 0)
|
||||
#endif
|
||||
{
|
||||
m_nUsesRemaining = 0;
|
||||
--CTheCarGenerators::CurrentActiveCount;
|
||||
}
|
||||
}
|
||||
|
||||
void CCarGenerator::SwitchOn()
|
||||
{
|
||||
m_nUsesRemaining = UINT16_MAX;
|
||||
m_nTimer = CalcNextGen();
|
||||
++CTheCarGenerators::CurrentActiveCount;
|
||||
}
|
||||
|
||||
uint32 CCarGenerator::CalcNextGen()
|
||||
{
|
||||
return CTimer::GetTimeInMilliseconds() + 4;
|
||||
}
|
||||
|
||||
void CCarGenerator::DoInternalProcessing()
|
||||
{
|
||||
if (CheckForBlockage()) {
|
||||
m_nTimer += 4;
|
||||
if (m_nUsesRemaining == 0)
|
||||
--CTheCarGenerators::CurrentActiveCount;
|
||||
return;
|
||||
}
|
||||
if (CCarCtrl::NumParkedCars >= 10)
|
||||
return;
|
||||
CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY);
|
||||
if (!CStreaming::HasModelLoaded(m_nModelIndex))
|
||||
return;
|
||||
if (CModelInfo::IsBoatModel(m_nModelIndex)){
|
||||
CBoat* pBoat = new CBoat(m_nModelIndex, PARKED_VEHICLE);
|
||||
pBoat->SetIsStatic(false);
|
||||
pBoat->bEngineOn = false;
|
||||
CVector pos = m_vecPos;
|
||||
if (pos.z <= -100.0f)
|
||||
pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y);
|
||||
pos.z += pBoat->GetDistanceFromCentreOfMassToBaseOfModel();
|
||||
pBoat->SetPosition(pos);
|
||||
pBoat->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle));
|
||||
pBoat->SetStatus(STATUS_ABANDONED);
|
||||
pBoat->m_nDoorLock = CARLOCK_UNLOCKED;
|
||||
CWorld::Add(pBoat);
|
||||
if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm)
|
||||
pBoat->m_nAlarmState = -1;
|
||||
if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock)
|
||||
pBoat->m_nDoorLock = CARLOCK_LOCKED;
|
||||
if (m_nColor1 != -1 && m_nColor2){
|
||||
pBoat->m_currentColour1 = m_nColor1;
|
||||
pBoat->m_currentColour2 = m_nColor2;
|
||||
}
|
||||
m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pBoat);
|
||||
}else{
|
||||
bool groundFound;
|
||||
CVector pos = m_vecPos;
|
||||
if (pos.z > -100.0f){
|
||||
pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &groundFound);
|
||||
}else{
|
||||
groundFound = false;
|
||||
CColPoint cp;
|
||||
CEntity* pEntity;
|
||||
groundFound = CWorld::ProcessVerticalLine(CVector(pos.x, pos.y, 1000.0f), -1000.0f,
|
||||
cp, pEntity, true, false, false, false, false, false, nil);
|
||||
if (groundFound)
|
||||
pos.z = cp.point.z;
|
||||
}
|
||||
if (!groundFound) {
|
||||
debug("CCarGenerator::DoInternalProcessing - can't find ground z for new car x = %f y = %f \n", m_vecPos.x, m_vecPos.y);
|
||||
}else{
|
||||
CAutomobile* pCar;
|
||||
|
||||
// So game crashes if it's bike :D
|
||||
if (((CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nModelIndex))->m_vehicleType != VEHICLE_TYPE_BIKE)
|
||||
pCar = new CAutomobile(m_nModelIndex, PARKED_VEHICLE);
|
||||
|
||||
pCar->SetIsStatic(false);
|
||||
pCar->bEngineOn = false;
|
||||
pos.z += pCar->GetDistanceFromCentreOfMassToBaseOfModel();
|
||||
pCar->SetPosition(pos);
|
||||
pCar->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle));
|
||||
pCar->SetStatus(STATUS_ABANDONED);
|
||||
pCar->bLightsOn = false;
|
||||
pCar->m_nDoorLock = CARLOCK_UNLOCKED;
|
||||
CWorld::Add(pCar);
|
||||
if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm)
|
||||
pCar->m_nAlarmState = -1;
|
||||
if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock)
|
||||
pCar->m_nDoorLock = CARLOCK_LOCKED;
|
||||
if (m_nColor1 != -1 && m_nColor2) {
|
||||
pCar->m_currentColour1 = m_nColor1;
|
||||
pCar->m_currentColour2 = m_nColor2;
|
||||
}
|
||||
m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pCar);
|
||||
}
|
||||
}
|
||||
#ifdef FIX_BUGS
|
||||
if (m_nUsesRemaining < UINT16_MAX)
|
||||
--m_nUsesRemaining;
|
||||
#else
|
||||
if (m_nUsesRemaining < ~0)
|
||||
--m_nUsesRemaining;
|
||||
#endif
|
||||
m_nTimer = CalcNextGen();
|
||||
if (m_nUsesRemaining == 0)
|
||||
--CTheCarGenerators::CurrentActiveCount;
|
||||
}
|
||||
|
||||
void CCarGenerator::Process()
|
||||
{
|
||||
if (m_nVehicleHandle == -1 &&
|
||||
(CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) &&
|
||||
m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayer())
|
||||
DoInternalProcessing();
|
||||
if (m_nVehicleHandle == -1)
|
||||
return;
|
||||
CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_nVehicleHandle);
|
||||
if (!pVehicle){
|
||||
m_nVehicleHandle = -1;
|
||||
return;
|
||||
}
|
||||
if (pVehicle->GetStatus() != STATUS_PLAYER)
|
||||
return;
|
||||
m_nTimer += 60000;
|
||||
m_nVehicleHandle = -1;
|
||||
m_bIsBlocking = true;
|
||||
pVehicle->bExtendedRange = false;
|
||||
}
|
||||
|
||||
void CCarGenerator::Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay)
|
||||
{
|
||||
CMatrix m1, m2, m3; /* Unused but present on stack, so I'll leave them. */
|
||||
m_vecPos = CVector(x, y, z);
|
||||
m_fAngle = angle;
|
||||
m_nModelIndex = mi;
|
||||
m_nColor1 = color1;
|
||||
m_nColor2 = color2;
|
||||
m_bForceSpawn = force;
|
||||
m_nAlarm = alarm;
|
||||
m_nDoorlock = lock;
|
||||
m_nMinDelay = min_delay;
|
||||
m_nMaxDelay = max_delay;
|
||||
m_nVehicleHandle = -1;
|
||||
m_nTimer = CTimer::GetTimeInMilliseconds() + 1;
|
||||
m_nUsesRemaining = 0;
|
||||
m_bIsBlocking = false;
|
||||
m_vecInf = CModelInfo::GetColModel(m_nModelIndex)->boundingBox.min;
|
||||
m_vecSup = CModelInfo::GetColModel(m_nModelIndex)->boundingBox.max;
|
||||
m_fSize = Max(m_vecInf.Magnitude(), m_vecSup.Magnitude());
|
||||
}
|
||||
|
||||
bool CCarGenerator::CheckForBlockage()
|
||||
{
|
||||
int16 entities;
|
||||
CWorld::FindObjectsKindaColliding(CVector(m_vecPos), m_fSize, 1, &entities, 2, nil, false, true, true, false, false);
|
||||
return entities > 0;
|
||||
}
|
||||
|
||||
bool CCarGenerator::CheckIfWithinRangeOfAnyPlayer()
|
||||
{
|
||||
CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos;
|
||||
float distance = direction.Magnitude();
|
||||
float farclip = 120.0f * TheCamera.GenerationDistMultiplier;
|
||||
float nearclip = farclip - 20.0f;
|
||||
if (distance >= farclip){
|
||||
if (m_bIsBlocking)
|
||||
m_bIsBlocking = false;
|
||||
return false;
|
||||
}
|
||||
if (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter)
|
||||
return true;
|
||||
if (m_bIsBlocking)
|
||||
return false;
|
||||
if (distance < nearclip)
|
||||
return false;
|
||||
return DotProduct2D(direction, FindPlayerSpeed()) <= 0;
|
||||
}
|
||||
|
||||
void CTheCarGenerators::Process()
|
||||
{
|
||||
if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing())
|
||||
return;
|
||||
if (++CTheCarGenerators::ProcessCounter == 4)
|
||||
CTheCarGenerators::ProcessCounter = 0;
|
||||
for (uint32 i = ProcessCounter; i < NumOfCarGenerators; i += 4)
|
||||
CTheCarGenerators::CarGeneratorArray[i].Process();
|
||||
if (GenerateEvenIfPlayerIsCloseCounter)
|
||||
GenerateEvenIfPlayerIsCloseCounter--;
|
||||
}
|
||||
|
||||
int32 CTheCarGenerators::CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay)
|
||||
{
|
||||
CarGeneratorArray[NumOfCarGenerators].Setup(x, y, z, angle, mi, color1, color2, force, alarm, lock, min_delay, max_delay);
|
||||
return NumOfCarGenerators++;
|
||||
}
|
||||
|
||||
void CTheCarGenerators::Init()
|
||||
{
|
||||
GenerateEvenIfPlayerIsCloseCounter = 0;
|
||||
NumOfCarGenerators = 0;
|
||||
ProcessCounter = 0;
|
||||
CurrentActiveCount = 0;
|
||||
}
|
||||
|
||||
void CTheCarGenerators::SaveAllCarGenerators(uint8 *buffer, uint32 *size)
|
||||
{
|
||||
const uint32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16);
|
||||
*size = sizeof(int) + nGeneralDataSize + sizeof(uint32) + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE;
|
||||
INITSAVEBUF
|
||||
WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE);
|
||||
|
||||
WriteSaveBuf(buffer, nGeneralDataSize);
|
||||
WriteSaveBuf(buffer, NumOfCarGenerators);
|
||||
WriteSaveBuf(buffer, CurrentActiveCount);
|
||||
WriteSaveBuf(buffer, ProcessCounter);
|
||||
WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter);
|
||||
WriteSaveBuf(buffer, (int16)0); // alignment
|
||||
WriteSaveBuf(buffer, (uint32)sizeof(CarGeneratorArray));
|
||||
for (int i = 0; i < NUM_CARGENS; i++)
|
||||
WriteSaveBuf(buffer, CarGeneratorArray[i]);
|
||||
VALIDATESAVEBUF(*size)
|
||||
}
|
||||
|
||||
void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size)
|
||||
{
|
||||
const int32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16);
|
||||
Init();
|
||||
INITSAVEBUF
|
||||
CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE);
|
||||
uint32 tmp;
|
||||
ReadSaveBuf(&tmp, buffer);
|
||||
assert(tmp == nGeneralDataSize);
|
||||
ReadSaveBuf(&NumOfCarGenerators, buffer);
|
||||
ReadSaveBuf(&CurrentActiveCount, buffer);
|
||||
ReadSaveBuf(&ProcessCounter, buffer);
|
||||
ReadSaveBuf(&GenerateEvenIfPlayerIsCloseCounter, buffer);
|
||||
SkipSaveBuf(buffer, 2);
|
||||
ReadSaveBuf(&tmp, buffer);
|
||||
assert(tmp == sizeof(CarGeneratorArray));
|
||||
for (int i = 0; i < NUM_CARGENS; i++)
|
||||
ReadSaveBuf(&CarGeneratorArray[i], buffer);
|
||||
VALIDATESAVEBUF(size)
|
||||
}
|
||||
54
src/vehicles/CarGen.h
Normal file
54
src/vehicles/CarGen.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
|
||||
enum {
|
||||
CARGEN_MAXACTUALLIMIT = 100
|
||||
};
|
||||
|
||||
class CCarGenerator
|
||||
{
|
||||
int32 m_nModelIndex;
|
||||
CVector m_vecPos;
|
||||
float m_fAngle;
|
||||
int16 m_nColor1;
|
||||
int16 m_nColor2;
|
||||
uint8 m_bForceSpawn;
|
||||
uint8 m_nAlarm;
|
||||
uint8 m_nDoorlock;
|
||||
int16 m_nMinDelay;
|
||||
int16 m_nMaxDelay;
|
||||
uint32 m_nTimer;
|
||||
int32 m_nVehicleHandle;
|
||||
uint16 m_nUsesRemaining;
|
||||
bool m_bIsBlocking;
|
||||
CVector m_vecInf;
|
||||
CVector m_vecSup;
|
||||
float m_fSize;
|
||||
public:
|
||||
void SwitchOff();
|
||||
void SwitchOn();
|
||||
uint32 CalcNextGen();
|
||||
void DoInternalProcessing();
|
||||
void Process();
|
||||
void Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay);
|
||||
bool CheckForBlockage();
|
||||
bool CheckIfWithinRangeOfAnyPlayer();
|
||||
void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; }
|
||||
};
|
||||
|
||||
class CTheCarGenerators
|
||||
{
|
||||
public:
|
||||
static uint8 ProcessCounter;
|
||||
static uint32 NumOfCarGenerators;
|
||||
static CCarGenerator CarGeneratorArray[NUM_CARGENS];
|
||||
static uint8 GenerateEvenIfPlayerIsCloseCounter;
|
||||
static uint32 CurrentActiveCount;
|
||||
|
||||
static void Process();
|
||||
static int32 CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay);
|
||||
static void Init();
|
||||
static void SaveAllCarGenerators(uint8 *, uint32 *);
|
||||
static void LoadAllCarGenerators(uint8 *, uint32);
|
||||
};
|
||||
759
src/vehicles/Cranes.cpp
Normal file
759
src/vehicles/Cranes.cpp
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "Cranes.h"
|
||||
|
||||
#include "Camera.h"
|
||||
#include "DMAudio.h"
|
||||
#include "Garages.h"
|
||||
#include "General.h"
|
||||
#include "Entity.h"
|
||||
#include "ModelIndices.h"
|
||||
#include "Replay.h"
|
||||
#include "Object.h"
|
||||
#include "World.h"
|
||||
#include "SaveBuf.h"
|
||||
|
||||
#define MAX_DISTANCE_TO_FIND_CRANE (10.0f)
|
||||
#define CRANE_UPDATE_RADIUS (300.0f)
|
||||
#define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f)
|
||||
#define CRUSHER_Z (-0.951f)
|
||||
#define MILITARY_Z (10.7862f)
|
||||
#define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f)
|
||||
#define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f)
|
||||
#define CAR_REWARD_MILITARY_CRANE (1500)
|
||||
#define CAR_MOVING_SPEED_THRESHOLD (0.01f)
|
||||
#define CRANE_SLOWDOWN_MULTIPLIER (0.3f)
|
||||
|
||||
#define OSCILLATION_SPEED (0.002f)
|
||||
#define CAR_ROTATION_SPEED (0.0035f)
|
||||
#define CRANE_MOVEMENT_SPEED (0.001f)
|
||||
#define HOOK_ANGLE_MOVEMENT_SPEED (0.004f)
|
||||
#define HOOK_OFFSET_MOVEMENT_SPEED (0.1f)
|
||||
#define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f)
|
||||
|
||||
#define MESSAGE_SHOW_DURATION (4000)
|
||||
|
||||
#define MAX_DISTANCE (99999.9f)
|
||||
#define MIN_VALID_POSITION (-10000.0f)
|
||||
#define DEFAULT_OFFSET (20.0f)
|
||||
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
#define CRANES_SAVE_SIZE 0x400
|
||||
#else
|
||||
#define CRANES_SAVE_SIZE sizeof(aCranes)
|
||||
#endif
|
||||
|
||||
uint32 TimerForCamInterpolation;
|
||||
|
||||
uint32 CCranes::CarsCollectedMilitaryCrane;
|
||||
int32 CCranes::NumCranes;
|
||||
CCrane CCranes::aCranes[NUM_CRANES];
|
||||
|
||||
void CCranes::InitCranes(void)
|
||||
{
|
||||
CarsCollectedMilitaryCrane = 0;
|
||||
NumCranes = 0;
|
||||
for (int i = 0; i < NUMSECTORS_X; i++) {
|
||||
for (int j = 0; j < NUMSECTORS_Y; j++) {
|
||||
for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) {
|
||||
CEntity* pEntity = (CEntity*)pNode->item;
|
||||
if (MODELID_CRANE_1 == pEntity->GetModelIndex() ||
|
||||
MODELID_CRANE_2 == pEntity->GetModelIndex() ||
|
||||
MODELID_CRANE_3 == pEntity->GetModelIndex())
|
||||
AddThisOneCrane(pEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_INDUSTRIAL).first; pNode; pNode = pNode->next) {
|
||||
CEntity* pEntity = (CEntity*)pNode->item;
|
||||
if (MODELID_CRANE_1 == pEntity->GetModelIndex() ||
|
||||
MODELID_CRANE_2 == pEntity->GetModelIndex() ||
|
||||
MODELID_CRANE_3 == pEntity->GetModelIndex())
|
||||
AddThisOneCrane(pEntity);
|
||||
}
|
||||
}
|
||||
|
||||
void CCranes::AddThisOneCrane(CEntity* pEntity)
|
||||
{
|
||||
pEntity->GetMatrix().ResetOrientation();
|
||||
if (NumCranes >= NUM_CRANES)
|
||||
return;
|
||||
CCrane* pCrane = &aCranes[NumCranes];
|
||||
pCrane->Init();
|
||||
pCrane->m_pCraneEntity = (CBuilding*)pEntity;
|
||||
pCrane->m_nCraneStatus = CCrane::NONE;
|
||||
pCrane->m_fHookAngle = NumCranes; // lol wtf
|
||||
while (pCrane->m_fHookAngle > TWOPI)
|
||||
pCrane->m_fHookAngle -= TWOPI;
|
||||
pCrane->m_fHookOffset = DEFAULT_OFFSET;
|
||||
pCrane->m_fHookHeight = DEFAULT_OFFSET;
|
||||
pCrane->m_nTimeForNextCheck = 0;
|
||||
pCrane->m_nCraneState = CCrane::IDLE;
|
||||
pCrane->m_bWasMilitaryCrane = false;
|
||||
pCrane->m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[NumCranes]);
|
||||
if (pCrane->m_nAudioEntity >= 0)
|
||||
DMAudio.SetEntityStatus(pCrane->m_nAudioEntity, TRUE);
|
||||
pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex());
|
||||
// Is this used to avoid military crane?
|
||||
if (pCrane->m_bIsTop || pEntity->GetPosition().y > 0.0f) {
|
||||
CObject* pHook = new CObject(MI_MAGNET, false);
|
||||
pHook->ObjectCreatedBy = MISSION_OBJECT;
|
||||
pHook->bUsesCollision = false;
|
||||
pHook->bExplosionProof = true;
|
||||
pHook->bAffectedByGravity = false;
|
||||
pCrane->m_pHook = pHook;
|
||||
pCrane->CalcHookCoordinates(&pCrane->m_vecHookCurPos.x, &pCrane->m_vecHookCurPos.y, &pCrane->m_vecHookCurPos.z);
|
||||
pCrane->SetHookMatrix();
|
||||
}
|
||||
else
|
||||
pCrane->m_pHook = nil;
|
||||
NumCranes++;
|
||||
}
|
||||
|
||||
void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY)
|
||||
{
|
||||
float fMinDistance = MAX_DISTANCE;
|
||||
float X = fPosX, Y = fPosY;
|
||||
if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) {
|
||||
X = fDropOffX;
|
||||
Y = fDropOffY;
|
||||
}
|
||||
int index = 0;
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude();
|
||||
if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) {
|
||||
fMinDistance = distance;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
#ifdef FIX_BUGS // classic
|
||||
if (fMinDistance == MAX_DISTANCE)
|
||||
return;
|
||||
#endif
|
||||
CCrane* pCrane = &aCranes[index];
|
||||
pCrane->m_fPickupX1 = fInfX;
|
||||
pCrane->m_fPickupX2 = fSupX;
|
||||
pCrane->m_fPickupY1 = fInfY;
|
||||
pCrane->m_fPickupY2 = fSupY;
|
||||
pCrane->m_vecDropoffTarget.x = fDropOffX;
|
||||
pCrane->m_vecDropoffTarget.y = fDropOffY;
|
||||
pCrane->m_vecDropoffTarget.z = fDropOffZ;
|
||||
pCrane->m_nCraneStatus = CCrane::ACTIVATED;
|
||||
pCrane->m_pVehiclePickedUp = nil;
|
||||
pCrane->m_nVehiclesCollected = 0;
|
||||
pCrane->m_fDropoffHeading = fHeading;
|
||||
pCrane->m_bIsCrusher = bIsCrusher;
|
||||
pCrane->m_bIsMilitaryCrane = bIsMilitary;
|
||||
bool military = true;
|
||||
if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane)
|
||||
military = false;
|
||||
pCrane->m_bWasMilitaryCrane = military;
|
||||
pCrane->m_nTimeForNextCheck = 0;
|
||||
pCrane->m_nCraneState = CCrane::IDLE;
|
||||
float Z;
|
||||
if (bIsCrusher)
|
||||
Z = CRUSHER_Z;
|
||||
else if (bIsMilitary)
|
||||
Z = MILITARY_Z;
|
||||
else
|
||||
Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2);
|
||||
pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight);
|
||||
pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight);
|
||||
}
|
||||
|
||||
void CCranes::DeActivateCrane(float X, float Y)
|
||||
{
|
||||
float fMinDistance = MAX_DISTANCE;
|
||||
int index = 0;
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude();
|
||||
if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) {
|
||||
fMinDistance = distance;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
#ifdef FIX_BUGS // classic
|
||||
if (fMinDistance == MAX_DISTANCE)
|
||||
return;
|
||||
#endif
|
||||
aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED;
|
||||
aCranes[index].m_nCraneState = CCrane::IDLE;
|
||||
}
|
||||
|
||||
bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle)
|
||||
{
|
||||
int index = 0;
|
||||
bool result = false;
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude();
|
||||
if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) {
|
||||
if (aCranes[i].m_nCraneState == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneState == CCrane::ROTATING_TARGET)
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CCranes::UpdateCranes(void)
|
||||
{
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher ||
|
||||
(TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x &&
|
||||
TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x &&
|
||||
TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y &&
|
||||
TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y))
|
||||
aCranes[i].Update();
|
||||
}
|
||||
}
|
||||
|
||||
void CCrane::Update(void)
|
||||
{
|
||||
if (CReplay::IsPlayingBack())
|
||||
return;
|
||||
if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) &&
|
||||
Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS &&
|
||||
Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) ||
|
||||
m_nCraneState != IDLE) {
|
||||
switch (m_nCraneState) {
|
||||
case IDLE:
|
||||
if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) &&
|
||||
CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) {
|
||||
CWorld::AdvanceCurrentScanCode();
|
||||
#ifdef FIX_BUGS
|
||||
int xstart = Max(0, CWorld::GetSectorIndexX(m_fPickupX1));
|
||||
int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2));
|
||||
int ystart = Max(0, CWorld::GetSectorIndexY(m_fPickupY1));
|
||||
int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2));
|
||||
#else
|
||||
int xstart = CWorld::GetSectorIndexX(m_fPickupX1);
|
||||
int xend = CWorld::GetSectorIndexX(m_fPickupX2);
|
||||
int ystart = CWorld::GetSectorIndexY(m_fPickupY1);
|
||||
int yend = CWorld::GetSectorIndexY(m_fPickupY1);
|
||||
#endif
|
||||
assert(xstart <= xend);
|
||||
assert(ystart <= yend);
|
||||
for (int i = xstart; i <= xend; i++) {
|
||||
for (int j = ystart; j <= yend; j++) {
|
||||
FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]);
|
||||
FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GOING_TOWARDS_TARGET:
|
||||
if (m_pVehiclePickedUp){
|
||||
if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 ||
|
||||
m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 ||
|
||||
m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 ||
|
||||
m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 ||
|
||||
m_pVehiclePickedUp->pDriver ||
|
||||
Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD ||
|
||||
Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD ||
|
||||
Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD ||
|
||||
(FindPlayerPed()->GetPedState() == PED_ENTER_CAR
|
||||
#ifdef FIX_BUGS
|
||||
|| FindPlayerPed()->GetPedState() == PED_CARJACK
|
||||
#endif
|
||||
) && FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) {
|
||||
m_pVehiclePickedUp = nil;
|
||||
m_nCraneState = IDLE;
|
||||
}
|
||||
else {
|
||||
float fAngle, fOffset, fHeight;
|
||||
FindParametersForTarget(
|
||||
m_pVehiclePickedUp->GetPosition().x,
|
||||
m_pVehiclePickedUp->GetPosition().y,
|
||||
m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z,
|
||||
&fAngle, &fOffset, &fHeight);
|
||||
if (GoTowardsTarget(fAngle, fOffset, fHeight)) {
|
||||
CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos;
|
||||
distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z;
|
||||
if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) {
|
||||
m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT;
|
||||
m_vecHookVelocity *= 0.4f;
|
||||
m_pVehiclePickedUp->bLightsOn = false;
|
||||
m_pVehiclePickedUp->bUsesCollision = false;
|
||||
if (m_bIsCrusher)
|
||||
m_pVehiclePickedUp->bCollisionProof = true;
|
||||
DMAudio.PlayOneShot(m_nAudioEntity, SOUND_CRANE_PICKUP, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
m_nCraneState = IDLE;
|
||||
break;
|
||||
case LIFTING_TARGET:
|
||||
RotateCarriedCarProperly();
|
||||
if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER))
|
||||
m_nCraneState = ROTATING_TARGET;
|
||||
if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) {
|
||||
m_pVehiclePickedUp = nil;
|
||||
m_nCraneState = IDLE;
|
||||
}
|
||||
break;
|
||||
case GOING_TOWARDS_TARGET_ONLY_HEIGHT:
|
||||
RotateCarriedCarProperly();
|
||||
if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER))
|
||||
m_nCraneState = LIFTING_TARGET;
|
||||
TimerForCamInterpolation = CTimer::GetTimeInMilliseconds();
|
||||
if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) {
|
||||
m_pVehiclePickedUp = nil;
|
||||
m_nCraneState = IDLE;
|
||||
}
|
||||
break;
|
||||
case ROTATING_TARGET:
|
||||
{
|
||||
bool bRotateFinished = RotateCarriedCarProperly();
|
||||
bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f);
|
||||
if (bMovementFinished && bRotateFinished) {
|
||||
float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f;
|
||||
if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) {
|
||||
m_nCraneState = DROPPING_TARGET;
|
||||
if (m_pVehiclePickedUp) {
|
||||
m_pVehiclePickedUp->bUsesCollision = true;
|
||||
m_pVehiclePickedUp->m_nStaticFrames = 0;
|
||||
++m_nVehiclesCollected;
|
||||
if (m_bIsMilitaryCrane) {
|
||||
CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex());
|
||||
if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) {
|
||||
CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE;
|
||||
CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1);
|
||||
}
|
||||
CWorld::Remove(m_pVehiclePickedUp);
|
||||
delete m_pVehiclePickedUp;
|
||||
}
|
||||
}
|
||||
m_pVehiclePickedUp = nil;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DROPPING_TARGET:
|
||||
if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) {
|
||||
m_nCraneState = IDLE;
|
||||
m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
CVector vecHook;
|
||||
CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z);
|
||||
m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED;
|
||||
m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep());
|
||||
m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep();
|
||||
m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep();
|
||||
m_vecHookCurPos.z = vecHook.z;
|
||||
switch (m_nCraneState) {
|
||||
case LIFTING_TARGET:
|
||||
case GOING_TOWARDS_TARGET_ONLY_HEIGHT:
|
||||
case ROTATING_TARGET:
|
||||
if (m_pVehiclePickedUp) {
|
||||
m_pVehiclePickedUp->SetPosition(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z);
|
||||
m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f);
|
||||
CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f);
|
||||
up.Normalise();
|
||||
m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up);
|
||||
m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight());
|
||||
m_pVehiclePickedUp->GetUp() = up;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF;
|
||||
// 16 options, lasting 2048 ms each
|
||||
// a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug?
|
||||
if (rnd < 4) {
|
||||
m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep();
|
||||
if (m_fHookAngle < 0.0f)
|
||||
m_fHookAngle += TWOPI;
|
||||
}
|
||||
else if (rnd > 5 && rnd < 12) {
|
||||
m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep();
|
||||
if (m_fHookAngle > TWOPI)
|
||||
m_fHookAngle -= TWOPI;
|
||||
}
|
||||
CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z);
|
||||
m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f;
|
||||
}
|
||||
float fCos = Cos(m_fHookAngle);
|
||||
float fSin = Sin(m_fHookAngle);
|
||||
m_pCraneEntity->GetRight().x = fCos;
|
||||
m_pCraneEntity->GetForward().y = fCos;
|
||||
m_pCraneEntity->GetRight().y = fSin;
|
||||
m_pCraneEntity->GetForward().x = -fSin;
|
||||
m_pCraneEntity->GetMatrix().UpdateRW();
|
||||
m_pCraneEntity->UpdateRwFrame();
|
||||
SetHookMatrix();
|
||||
}
|
||||
|
||||
bool CCrane::RotateCarriedCarProperly()
|
||||
{
|
||||
if (m_fDropoffHeading <= 0.0f)
|
||||
return true;
|
||||
if (!m_pVehiclePickedUp)
|
||||
return true;
|
||||
float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y);
|
||||
while (fAngleDelta < -HALFPI)
|
||||
fAngleDelta += PI;
|
||||
while (fAngleDelta > HALFPI)
|
||||
fAngleDelta -= PI;
|
||||
float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep();
|
||||
if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied?
|
||||
return true;
|
||||
m_pVehiclePickedUp->GetMatrix().RotateZ(fAngleDelta < 0 ? -fDeltaThisFrame : fDeltaThisFrame);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCrane::FindCarInSectorList(CPtrList* pList)
|
||||
{
|
||||
CPtrNode* node;
|
||||
for (node = pList->first; node; node = node->next) {
|
||||
CVehicle* pVehicle = (CVehicle*)node->item;
|
||||
if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode())
|
||||
continue;
|
||||
pVehicle->m_scanCode = CWorld::GetCurrentScanCode();
|
||||
if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 ||
|
||||
pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2)
|
||||
continue;
|
||||
if (pVehicle->pDriver)
|
||||
continue;
|
||||
if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD ||
|
||||
Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD ||
|
||||
Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD)
|
||||
continue;
|
||||
if (!pVehicle->IsCar() || pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f)
|
||||
continue;
|
||||
if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) ||
|
||||
m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) {
|
||||
if (!pVehicle->bCraneMessageDone) {
|
||||
pVehicle->bCraneMessageDone = true;
|
||||
if (!m_bIsMilitaryCrane)
|
||||
CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle.
|
||||
else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex()))
|
||||
CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal.
|
||||
else
|
||||
CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model.
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_pVehiclePickedUp = pVehicle;
|
||||
pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp);
|
||||
m_nCraneState = GOING_TOWARDS_TARGET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CCrane::DoesCranePickUpThisCarType(uint32 mi)
|
||||
{
|
||||
if (m_bIsCrusher) {
|
||||
return mi != MI_FIRETRUCK &&
|
||||
mi != MI_TRASH &&
|
||||
#ifdef FIX_BUGS
|
||||
mi != MI_COACH &&
|
||||
#else
|
||||
mi != MI_BLISTA &&
|
||||
#endif
|
||||
mi != MI_SECURICA &&
|
||||
mi != MI_BUS &&
|
||||
mi != MI_DODO &&
|
||||
mi != MI_RHINO;
|
||||
}
|
||||
if (m_bIsMilitaryCrane) {
|
||||
return mi == MI_FIRETRUCK ||
|
||||
mi == MI_AMBULAN ||
|
||||
mi == MI_ENFORCER ||
|
||||
mi == MI_FBICAR ||
|
||||
mi == MI_RHINO ||
|
||||
mi == MI_BARRACKS ||
|
||||
mi == MI_POLICE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi)
|
||||
{
|
||||
switch (mi) {
|
||||
case MI_FIRETRUCK: return (CarsCollectedMilitaryCrane & 1);
|
||||
case MI_AMBULAN: return (CarsCollectedMilitaryCrane & 2);
|
||||
case MI_ENFORCER: return (CarsCollectedMilitaryCrane & 4);
|
||||
case MI_FBICAR: return (CarsCollectedMilitaryCrane & 8);
|
||||
case MI_RHINO: return (CarsCollectedMilitaryCrane & 0x10);
|
||||
case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20);
|
||||
case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40);
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCranes::RegisterCarForMilitaryCrane(uint32 mi)
|
||||
{
|
||||
switch (mi) {
|
||||
case MI_FIRETRUCK: CarsCollectedMilitaryCrane |= 1; break;
|
||||
case MI_AMBULAN: CarsCollectedMilitaryCrane |= 2; break;
|
||||
case MI_ENFORCER: CarsCollectedMilitaryCrane |= 4; break;
|
||||
case MI_FBICAR: CarsCollectedMilitaryCrane |= 8; break;
|
||||
case MI_RHINO: CarsCollectedMilitaryCrane |= 0x10; break;
|
||||
case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break;
|
||||
case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()
|
||||
{
|
||||
return (CarsCollectedMilitaryCrane & 0x7F) == 0x7F;
|
||||
}
|
||||
|
||||
bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier)
|
||||
{
|
||||
bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished;
|
||||
float fHookAngleDelta = fAngleToTarget - m_fHookAngle;
|
||||
while (fHookAngleDelta > PI)
|
||||
fHookAngleDelta -= TWOPI;
|
||||
while (fHookAngleDelta < -PI)
|
||||
fHookAngleDelta += TWOPI;
|
||||
float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED;
|
||||
if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) {
|
||||
m_fHookAngle = fAngleToTarget;
|
||||
bAngleMovementFinished = true;
|
||||
} else {
|
||||
if (fHookAngleDelta < 0.0f) {
|
||||
m_fHookAngle -= fHookAngleChangeThisFrame;
|
||||
if (m_fHookAngle < 0.0f)
|
||||
m_fHookAngle += TWOPI;
|
||||
}
|
||||
else {
|
||||
m_fHookAngle += fHookAngleChangeThisFrame;
|
||||
if (m_fHookAngle > TWOPI)
|
||||
m_fHookAngle -= TWOPI;
|
||||
}
|
||||
bAngleMovementFinished = false;
|
||||
}
|
||||
float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset;
|
||||
float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED;
|
||||
if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) {
|
||||
m_fHookOffset = fDistanceToTarget;
|
||||
bOffsetMovementFinished = true;
|
||||
} else {
|
||||
if (fHookOffsetDelta < 0.0f)
|
||||
m_fHookOffset -= fHookOffsetChangeThisFrame;
|
||||
else
|
||||
m_fHookOffset += fHookOffsetChangeThisFrame;
|
||||
bOffsetMovementFinished = false;
|
||||
}
|
||||
float fHookHeightDelta = fTargetHeight - m_fHookHeight;
|
||||
float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED;
|
||||
if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) {
|
||||
m_fHookHeight = fTargetHeight;
|
||||
bHeightMovementFinished = true;
|
||||
} else {
|
||||
if (fHookHeightDelta < 0.0f)
|
||||
m_fHookHeight -= fHookHeightChangeThisFrame;
|
||||
else
|
||||
m_fHookHeight += fHookHeightChangeThisFrame;
|
||||
bHeightMovementFinished = false;
|
||||
}
|
||||
return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished;
|
||||
}
|
||||
|
||||
bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier)
|
||||
{
|
||||
bool bHeightMovementFinished;
|
||||
float fHookHeightDelta = fTargetHeight - m_fHookHeight;
|
||||
float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED;
|
||||
if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) {
|
||||
m_fHookHeight = fTargetHeight;
|
||||
bHeightMovementFinished = true;
|
||||
} else {
|
||||
if (fHookHeightDelta < 0.0f)
|
||||
m_fHookHeight -= fHookHeightChangeThisFrame;
|
||||
else
|
||||
m_fHookHeight += fHookHeightChangeThisFrame;
|
||||
bHeightMovementFinished = false;
|
||||
}
|
||||
return bHeightMovementFinished;
|
||||
}
|
||||
|
||||
void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight)
|
||||
{
|
||||
*pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y);
|
||||
*pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude();
|
||||
*pHeight = Z;
|
||||
}
|
||||
|
||||
void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ)
|
||||
{
|
||||
*pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x;
|
||||
*pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y;
|
||||
*pZ = m_fHookHeight;
|
||||
}
|
||||
|
||||
void CCrane::SetHookMatrix()
|
||||
{
|
||||
if (m_pHook == nil)
|
||||
return;
|
||||
m_pHook->SetPosition(m_vecHookCurPos);
|
||||
CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f);
|
||||
up.Normalise();
|
||||
m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up);
|
||||
m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight());
|
||||
m_pHook->GetUp() = up;
|
||||
m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI);
|
||||
m_pHook->GetMatrix().UpdateRW();
|
||||
m_pHook->UpdateRwFrame();
|
||||
CWorld::Remove(m_pHook);
|
||||
CWorld::Add(m_pHook);
|
||||
}
|
||||
|
||||
bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle)
|
||||
{
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
if (pVehicle == aCranes[i].m_pVehiclePickedUp) {
|
||||
switch (aCranes[i].m_nCraneState) {
|
||||
case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT:
|
||||
case CCrane::LIFTING_TARGET:
|
||||
case CCrane::ROTATING_TARGET:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle)
|
||||
{
|
||||
for (int i = 0; i < NumCranes; i++) {
|
||||
if (pVehicle == aCranes[i].m_pVehiclePickedUp)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCranes::Save(uint8* buf, uint32* size)
|
||||
{
|
||||
INITSAVEBUF
|
||||
|
||||
*size = 2 * sizeof(uint32) + CRANES_SAVE_SIZE;
|
||||
WriteSaveBuf(buf, NumCranes);
|
||||
WriteSaveBuf(buf, CarsCollectedMilitaryCrane);
|
||||
for (int i = 0; i < NUM_CRANES; i++) {
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
int32 tmp = aCranes[i].m_pCraneEntity != nil ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pCraneEntity) + 1 : 0;
|
||||
WriteSaveBuf(buf, tmp);
|
||||
tmp = aCranes[i].m_pHook != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pHook) + 1 : 0;
|
||||
WriteSaveBuf(buf, tmp);
|
||||
WriteSaveBuf(buf, aCranes[i].m_nAudioEntity);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupX1);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupX2);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupY1);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupY2);
|
||||
WriteSaveBuf(buf, aCranes[i].m_vecDropoffTarget);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fDropoffHeading);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupAngle);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fDropoffAngle);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupDistance);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fDropoffDistance);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fPickupHeight);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fDropoffHeight);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fHookAngle);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fHookOffset);
|
||||
WriteSaveBuf(buf, aCranes[i].m_fHookHeight);
|
||||
WriteSaveBuf(buf, aCranes[i].m_vecHookInitPos);
|
||||
WriteSaveBuf(buf, aCranes[i].m_vecHookCurPos);
|
||||
WriteSaveBuf(buf, aCranes[i].m_vecHookVelocity);
|
||||
tmp = aCranes[i].m_pVehiclePickedUp != nil ? CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pVehiclePickedUp) + 1 : 0;
|
||||
WriteSaveBuf(buf, tmp);
|
||||
WriteSaveBuf(buf, aCranes[i].m_nTimeForNextCheck);
|
||||
WriteSaveBuf(buf, aCranes[i].m_nCraneStatus);
|
||||
WriteSaveBuf(buf, aCranes[i].m_nCraneState);
|
||||
WriteSaveBuf(buf, aCranes[i].m_nVehiclesCollected);
|
||||
WriteSaveBuf(buf, aCranes[i].m_bIsCrusher);
|
||||
WriteSaveBuf(buf, aCranes[i].m_bIsMilitaryCrane);
|
||||
WriteSaveBuf(buf, aCranes[i].m_bWasMilitaryCrane);
|
||||
WriteSaveBuf(buf, aCranes[i].m_bIsTop);
|
||||
ZeroSaveBuf(buf, 1);
|
||||
#else
|
||||
CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]);
|
||||
if (pCrane->m_pCraneEntity != nil)
|
||||
pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1);
|
||||
if (pCrane->m_pHook != nil)
|
||||
pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1);
|
||||
if (pCrane->m_pVehiclePickedUp != nil)
|
||||
pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALIDATESAVEBUF(*size);
|
||||
}
|
||||
|
||||
void CCranes::Load(uint8* buf, uint32 size)
|
||||
{
|
||||
INITSAVEBUF
|
||||
|
||||
ReadSaveBuf(&NumCranes, buf);
|
||||
ReadSaveBuf(&CarsCollectedMilitaryCrane, buf);
|
||||
for (int i = 0; i < NUM_CRANES; i++) {
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
int32 tmp;
|
||||
ReadSaveBuf(&tmp, buf);
|
||||
aCranes[i].m_pCraneEntity = tmp != 0 ? CPools::GetBuildingPool()->GetSlot(tmp - 1) : nil;
|
||||
ReadSaveBuf(&tmp, buf);
|
||||
aCranes[i].m_pHook = tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil;
|
||||
ReadSaveBuf(&aCranes[i].m_nAudioEntity, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupX1, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupX2, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupY1, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupY2, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_vecDropoffTarget, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fDropoffHeading, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupAngle, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fDropoffAngle, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupDistance, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fDropoffDistance, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fPickupHeight, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fDropoffHeight, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fHookAngle, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fHookOffset, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_fHookHeight, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_vecHookInitPos, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_vecHookCurPos, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_vecHookVelocity, buf);
|
||||
ReadSaveBuf(&tmp, buf);
|
||||
aCranes[i].m_pVehiclePickedUp = tmp != 0 ? CPools::GetVehiclePool()->GetSlot(tmp - 1) : nil;
|
||||
ReadSaveBuf(&aCranes[i].m_nTimeForNextCheck, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_nCraneStatus, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_nCraneState, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_nVehiclesCollected, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_bIsCrusher, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_bIsMilitaryCrane, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_bWasMilitaryCrane, buf);
|
||||
ReadSaveBuf(&aCranes[i].m_bIsTop, buf);
|
||||
SkipSaveBuf(buf, 1);
|
||||
#else
|
||||
ReadSaveBuf(&aCranes[i], buf);
|
||||
}
|
||||
for (int i = 0; i < NUM_CRANES; i++) {
|
||||
CCrane *pCrane = &aCranes[i];
|
||||
if (pCrane->m_pCraneEntity != nil)
|
||||
pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uintptr)pCrane->m_pCraneEntity - 1);
|
||||
if (pCrane->m_pHook != nil)
|
||||
pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1);
|
||||
if (pCrane->m_pVehiclePickedUp != nil)
|
||||
pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1);
|
||||
#endif
|
||||
}
|
||||
for (int i = 0; i < NUM_CRANES; i++) {
|
||||
aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]);
|
||||
if (aCranes[i].m_nAudioEntity != 0)
|
||||
DMAudio.SetEntityStatus(aCranes[i].m_nAudioEntity, TRUE);
|
||||
}
|
||||
|
||||
VALIDATESAVEBUF(size);
|
||||
}
|
||||
97
src/vehicles/Cranes.h
Normal file
97
src/vehicles/Cranes.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#include "World.h"
|
||||
|
||||
class CVehicle;
|
||||
class CEntity;
|
||||
class CObject;
|
||||
class CBuilding;
|
||||
|
||||
class CCrane
|
||||
{
|
||||
public:
|
||||
enum CraneState {
|
||||
IDLE = 0,
|
||||
GOING_TOWARDS_TARGET = 1,
|
||||
LIFTING_TARGET = 2,
|
||||
GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3,
|
||||
ROTATING_TARGET = 4,
|
||||
DROPPING_TARGET = 5
|
||||
};
|
||||
enum CraneStatus {
|
||||
NONE = 0,
|
||||
ACTIVATED = 1,
|
||||
DEACTIVATED = 2
|
||||
};
|
||||
CBuilding *m_pCraneEntity;
|
||||
CObject *m_pHook;
|
||||
int32 m_nAudioEntity;
|
||||
float m_fPickupX1;
|
||||
float m_fPickupX2;
|
||||
float m_fPickupY1;
|
||||
float m_fPickupY2;
|
||||
CVector m_vecDropoffTarget;
|
||||
float m_fDropoffHeading;
|
||||
float m_fPickupAngle;
|
||||
float m_fDropoffAngle;
|
||||
float m_fPickupDistance;
|
||||
float m_fDropoffDistance;
|
||||
float m_fPickupHeight;
|
||||
float m_fDropoffHeight;
|
||||
float m_fHookAngle;
|
||||
float m_fHookOffset;
|
||||
float m_fHookHeight;
|
||||
CVector m_vecHookInitPos;
|
||||
CVector m_vecHookCurPos;
|
||||
CVector2D m_vecHookVelocity;
|
||||
CVehicle *m_pVehiclePickedUp;
|
||||
uint32 m_nTimeForNextCheck;
|
||||
uint8 m_nCraneStatus;
|
||||
uint8 m_nCraneState;
|
||||
uint8 m_nVehiclesCollected;
|
||||
bool m_bIsCrusher;
|
||||
bool m_bIsMilitaryCrane;
|
||||
bool m_bWasMilitaryCrane;
|
||||
bool m_bIsTop;
|
||||
|
||||
void Init(void) { memset(this, 0, sizeof(*this)); }
|
||||
void Update(void);
|
||||
bool RotateCarriedCarProperly(void);
|
||||
void FindCarInSectorList(CPtrList* pList);
|
||||
bool DoesCranePickUpThisCarType(uint32 mi);
|
||||
bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f);
|
||||
bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f);
|
||||
void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight);
|
||||
void CalcHookCoordinates(float* pX, float* pY, float* pZ);
|
||||
void SetHookMatrix(void);
|
||||
|
||||
float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); };
|
||||
float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); }
|
||||
float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); }
|
||||
float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); }
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CCrane, 128);
|
||||
|
||||
class CCranes
|
||||
{
|
||||
public:
|
||||
static void InitCranes(void);
|
||||
static void AddThisOneCrane(CEntity* pCraneEntity);
|
||||
static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY);
|
||||
static void DeActivateCrane(float fX, float fY);
|
||||
static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle);
|
||||
static void UpdateCranes(void);
|
||||
static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi);
|
||||
static void RegisterCarForMilitaryCrane(uint32 mi);
|
||||
static bool HaveAllCarsBeenCollectedByMilitaryCrane(void);
|
||||
static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle);
|
||||
static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle);
|
||||
static void Save(uint8* buf, uint32* size);
|
||||
static void Load(uint8* buf, uint32 size); // out of class in III PC and later because of SecuROM
|
||||
|
||||
static uint32 CarsCollectedMilitaryCrane;
|
||||
static int32 NumCranes;
|
||||
static CCrane aCranes[NUM_CRANES];
|
||||
};
|
||||
229
src/vehicles/DamageManager.cpp
Normal file
229
src/vehicles/DamageManager.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "General.h"
|
||||
#include "Vehicle.h"
|
||||
#include "DamageManager.h"
|
||||
|
||||
|
||||
float G_aComponentDamage[] = { 2.5f, 1.25f, 3.2f, 1.4f, 2.5f, 2.8f, 0.5f };
|
||||
|
||||
CDamageManager::CDamageManager(void)
|
||||
{
|
||||
ResetDamageStatus();
|
||||
m_fWheelDamageEffect = 0.75f;
|
||||
field_18 = 1;
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::ResetDamageStatus(void)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::FuckCarCompletely(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
m_wheelStatus[0] = WHEEL_STATUS_MISSING;
|
||||
// wheels 1-3 not reset?
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++)
|
||||
m_doorStatus[i] = DOOR_STATUS_MISSING;
|
||||
|
||||
for(i = 0; i < 3; i++){
|
||||
#ifdef FIX_BUGS
|
||||
ProgressPanelDamage(VEHBUMPER_FRONT);
|
||||
ProgressPanelDamage(VEHBUMPER_REAR);
|
||||
#else
|
||||
// this can't be right
|
||||
ProgressPanelDamage(COMPONENT_BUMPER_FRONT);
|
||||
ProgressPanelDamage(COMPONENT_BUMPER_REAR);
|
||||
#endif
|
||||
}
|
||||
// Why set to no damage?
|
||||
#ifndef FIX_BUGS
|
||||
m_lightStatus = 0;
|
||||
m_panelStatus = 0;
|
||||
#endif
|
||||
SetEngineStatus(250);
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::ApplyDamage(tComponent component, float damage, float unused)
|
||||
{
|
||||
tComponentGroup group;
|
||||
uint8 subComp;
|
||||
|
||||
GetComponentGroup(component, &group, &subComp);
|
||||
damage *= G_aComponentDamage[group];
|
||||
if(damage > 150.0f){
|
||||
switch(group){
|
||||
case COMPGROUP_WHEEL:
|
||||
ProgressWheelDamage(subComp);
|
||||
break;
|
||||
case COMPGROUP_DOOR:
|
||||
case COMPGROUP_BOOT:
|
||||
ProgressDoorDamage(subComp);
|
||||
break;
|
||||
case COMPGROUP_BONNET:
|
||||
if(damage > 220.0f)
|
||||
ProgressEngineDamage();
|
||||
ProgressDoorDamage(subComp);
|
||||
break;
|
||||
case COMPGROUP_PANEL:
|
||||
// so windscreen is a light?
|
||||
SetLightStatus((eLights)subComp, 1);
|
||||
// fall through
|
||||
case COMPGROUP_BUMPER:
|
||||
if(damage > 220.0f &&
|
||||
(component == COMPONENT_PANEL_FRONT_LEFT ||
|
||||
component == COMPONENT_PANEL_FRONT_RIGHT ||
|
||||
component == COMPONENT_PANEL_WINDSCREEN))
|
||||
ProgressEngineDamage();
|
||||
ProgressPanelDamage(subComp);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *subComp)
|
||||
{
|
||||
*subComp = -2; // ??
|
||||
|
||||
// This is done very strangely in the game, maybe an optimized switch?
|
||||
if(component >= COMPONENT_PANEL_FRONT_LEFT){
|
||||
if(component >= COMPONENT_BUMPER_FRONT)
|
||||
*componentGroup = COMPGROUP_BUMPER;
|
||||
else
|
||||
*componentGroup = COMPGROUP_PANEL;
|
||||
*subComp = component - COMPONENT_PANEL_FRONT_LEFT;
|
||||
return true;
|
||||
}else if(component >= COMPONENT_DOOR_BONNET){
|
||||
if(component == COMPONENT_DOOR_BONNET)
|
||||
*componentGroup = COMPGROUP_BONNET;
|
||||
else if(component == COMPONENT_DOOR_BOOT)
|
||||
*componentGroup = COMPGROUP_BOOT;
|
||||
else
|
||||
*componentGroup = COMPGROUP_DOOR;
|
||||
*subComp = component - COMPONENT_DOOR_BONNET;
|
||||
return true;
|
||||
}else if(component >= COMPONENT_WHEEL_FRONT_LEFT){
|
||||
*componentGroup = COMPGROUP_WHEEL;
|
||||
*subComp = component - COMPONENT_WHEEL_FRONT_LEFT;
|
||||
return true;
|
||||
}else if(component >= COMPONENT_DEFAULT){
|
||||
*componentGroup = COMPGROUP_DEFAULT;
|
||||
*subComp = COMPONENT_DEFAULT;
|
||||
return true;
|
||||
}else
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::SetDoorStatus(int32 door, uint32 status)
|
||||
{
|
||||
m_doorStatus[door] = status;
|
||||
}
|
||||
|
||||
int32
|
||||
CDamageManager::GetDoorStatus(int32 door)
|
||||
{
|
||||
return m_doorStatus[door];
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::ProgressDoorDamage(uint8 door)
|
||||
{
|
||||
int status = GetDoorStatus(door);
|
||||
if(status == PANEL_STATUS_MISSING)
|
||||
return false;
|
||||
SetDoorStatus(door, status+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::SetPanelStatus(int32 panel, uint32 status)
|
||||
{
|
||||
m_panelStatus = dpb(status, panel*4, 4, m_panelStatus);
|
||||
}
|
||||
|
||||
int32
|
||||
CDamageManager::GetPanelStatus(int32 panel)
|
||||
{
|
||||
return ldb(panel*4, 4, m_panelStatus);
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::ProgressPanelDamage(uint8 panel)
|
||||
{
|
||||
int status = GetPanelStatus(panel);
|
||||
if(status == DOOR_STATUS_MISSING)
|
||||
return false;
|
||||
SetPanelStatus(panel, status+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::SetLightStatus(eLights light, uint32 status)
|
||||
{
|
||||
m_lightStatus = dpb(status, light*2, 2, m_lightStatus);
|
||||
}
|
||||
|
||||
int32
|
||||
CDamageManager::GetLightStatus(eLights light)
|
||||
{
|
||||
return ldb(light*2, 2, m_lightStatus);
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::SetWheelStatus(int32 wheel, uint32 status)
|
||||
{
|
||||
m_wheelStatus[wheel] = status;
|
||||
}
|
||||
|
||||
int32
|
||||
CDamageManager::GetWheelStatus(int32 wheel)
|
||||
{
|
||||
return m_wheelStatus[wheel];
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::ProgressWheelDamage(uint8 wheel)
|
||||
{
|
||||
int status = GetWheelStatus(wheel);
|
||||
if(status == WHEEL_STATUS_MISSING)
|
||||
return false;
|
||||
SetWheelStatus(wheel, status+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CDamageManager::SetEngineStatus(uint32 status)
|
||||
{
|
||||
if(status > 250)
|
||||
m_engineStatus = 250;
|
||||
else
|
||||
m_engineStatus = status;
|
||||
}
|
||||
|
||||
int32
|
||||
CDamageManager::GetEngineStatus(void)
|
||||
{
|
||||
return m_engineStatus;
|
||||
}
|
||||
|
||||
bool
|
||||
CDamageManager::ProgressEngineDamage(void)
|
||||
{
|
||||
int status = GetEngineStatus();
|
||||
int newstatus = status + 32 + (CGeneral::GetRandomNumber() & 0x1F);
|
||||
if(status < ENGINE_STATUS_ON_FIRE && newstatus > ENGINE_STATUS_ON_FIRE-1)
|
||||
newstatus = ENGINE_STATUS_ON_FIRE-1;
|
||||
SetEngineStatus(newstatus);
|
||||
return true;
|
||||
}
|
||||
115
src/vehicles/DamageManager.h
Normal file
115
src/vehicles/DamageManager.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// TODO: move some of this into Vehicle.h
|
||||
|
||||
enum eEngineStatus
|
||||
{
|
||||
ENGINE_STATUS_STEAM1 = 100,
|
||||
ENGINE_STATUS_STEAM2 = 150,
|
||||
ENGINE_STATUS_SMOKE = 200,
|
||||
ENGINE_STATUS_ON_FIRE = 225
|
||||
};
|
||||
|
||||
enum eDoorStatus
|
||||
{
|
||||
DOOR_STATUS_OK,
|
||||
DOOR_STATUS_SMASHED,
|
||||
DOOR_STATUS_SWINGING,
|
||||
DOOR_STATUS_MISSING
|
||||
};
|
||||
|
||||
enum ePanelStatus
|
||||
{
|
||||
PANEL_STATUS_OK,
|
||||
PANEL_STATUS_SMASHED1,
|
||||
PANEL_STATUS_SMASHED2,
|
||||
PANEL_STATUS_MISSING,
|
||||
};
|
||||
|
||||
enum eWheelStatus
|
||||
{
|
||||
WHEEL_STATUS_OK,
|
||||
WHEEL_STATUS_BURST,
|
||||
WHEEL_STATUS_MISSING
|
||||
};
|
||||
|
||||
enum eLightStatus
|
||||
{
|
||||
LIGHT_STATUS_OK,
|
||||
LIGHT_STATUS_BROKEN
|
||||
};
|
||||
|
||||
enum tComponent
|
||||
{
|
||||
COMPONENT_DEFAULT,
|
||||
COMPONENT_WHEEL_FRONT_LEFT,
|
||||
COMPONENT_WHEEL_FRONT_RIGHT,
|
||||
COMPONENT_WHEEL_REAR_LEFT,
|
||||
COMPONENT_WHEEL_REAR_RIGHT,
|
||||
COMPONENT_DOOR_BONNET,
|
||||
COMPONENT_DOOR_BOOT,
|
||||
COMPONENT_DOOR_FRONT_LEFT,
|
||||
COMPONENT_DOOR_FRONT_RIGHT,
|
||||
COMPONENT_DOOR_REAR_LEFT,
|
||||
COMPONENT_DOOR_REAR_RIGHT,
|
||||
COMPONENT_PANEL_FRONT_LEFT,
|
||||
COMPONENT_PANEL_FRONT_RIGHT,
|
||||
COMPONENT_PANEL_REAR_LEFT,
|
||||
COMPONENT_PANEL_REAR_RIGHT,
|
||||
COMPONENT_PANEL_WINDSCREEN,
|
||||
COMPONENT_BUMPER_FRONT,
|
||||
COMPONENT_BUMPER_REAR,
|
||||
};
|
||||
|
||||
enum tComponentGroup
|
||||
{
|
||||
COMPGROUP_BUMPER,
|
||||
COMPGROUP_WHEEL,
|
||||
COMPGROUP_DOOR,
|
||||
COMPGROUP_BONNET,
|
||||
COMPGROUP_BOOT,
|
||||
COMPGROUP_PANEL,
|
||||
COMPGROUP_DEFAULT,
|
||||
};
|
||||
|
||||
enum eLights;
|
||||
|
||||
class CDamageManager
|
||||
{
|
||||
public:
|
||||
|
||||
float m_fWheelDamageEffect;
|
||||
uint8 m_engineStatus;
|
||||
uint8 m_wheelStatus[4];
|
||||
uint8 m_doorStatus[6];
|
||||
uint32 m_lightStatus;
|
||||
uint32 m_panelStatus;
|
||||
uint8 field_18;
|
||||
|
||||
CDamageManager(void);
|
||||
|
||||
void ResetDamageStatus(void);
|
||||
void FuckCarCompletely(void);
|
||||
bool ApplyDamage(tComponent component, float damage, float unused);
|
||||
bool GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *foo);
|
||||
|
||||
void SetDoorStatus(int32 door, uint32 status);
|
||||
int32 GetDoorStatus(int32 door);
|
||||
bool ProgressDoorDamage(uint8 door);
|
||||
void SetPanelStatus(int32 panel, uint32 status);
|
||||
int32 GetPanelStatus(int32 panel);
|
||||
bool ProgressPanelDamage(uint8 panel);
|
||||
// needed for CReplay
|
||||
static int32 GetPanelStatus(uint32 panelstatus, int32 panel) { return ldb(panel*4, 4, panelstatus); }
|
||||
void SetLightStatus(eLights light, uint32 status);
|
||||
int32 GetLightStatus(eLights light);
|
||||
void SetWheelStatus(int32 wheel, uint32 status);
|
||||
int32 GetWheelStatus(int32 wheel);
|
||||
bool ProgressWheelDamage(uint8 wheel);
|
||||
void SetEngineStatus(uint32 status);
|
||||
int32 GetEngineStatus(void);
|
||||
bool ProgressEngineDamage(void);
|
||||
};
|
||||
VALIDATE_SIZE(CDamageManager, 0x1C);
|
||||
170
src/vehicles/Door.cpp
Normal file
170
src/vehicles/Door.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "Vehicle.h"
|
||||
#include "Door.h"
|
||||
|
||||
CDoor::CDoor(void)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
void
|
||||
CDoor::Open(float ratio)
|
||||
{
|
||||
float open;
|
||||
|
||||
m_fPrevAngle = m_fAngle;
|
||||
open = RetAngleWhenOpen();
|
||||
if(ratio < 1.0f){
|
||||
m_fAngle = open*ratio;
|
||||
if(m_fAngle == 0.0f)
|
||||
m_fAngVel = 0.0f;
|
||||
}else{
|
||||
m_nDoorState = DOORST_OPEN;
|
||||
m_fAngle = open;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDoor::Process(CVehicle *vehicle)
|
||||
{
|
||||
static CVector vecOffset(1.0f, 0.0f, 0.0f);
|
||||
CVector speed = vehicle->GetSpeed(vecOffset);
|
||||
CVector vecSpeedDiff = speed - m_vecSpeed;
|
||||
vecSpeedDiff = Multiply3x3(vecSpeedDiff, vehicle->GetMatrix());
|
||||
|
||||
// air resistance
|
||||
float fSpeedDiff = 0.0f; // uninitialized in game
|
||||
switch(m_nAxis){
|
||||
case 0: // x-axis
|
||||
if(m_nDirn)
|
||||
fSpeedDiff = vecSpeedDiff.y + vecSpeedDiff.z;
|
||||
else
|
||||
fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.z);
|
||||
break;
|
||||
|
||||
// we don't support y axis apparently?
|
||||
|
||||
case 2: // z-axis
|
||||
if(m_nDirn)
|
||||
fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.x);
|
||||
else
|
||||
fSpeedDiff = vecSpeedDiff.y - vecSpeedDiff.x;
|
||||
break;
|
||||
}
|
||||
fSpeedDiff = Clamp(fSpeedDiff, -0.2f, 0.2f);
|
||||
if(Abs(fSpeedDiff) > 0.002f)
|
||||
m_fAngVel += fSpeedDiff;
|
||||
m_fAngVel *= 0.945f;
|
||||
m_fAngVel = Clamp(m_fAngVel, -0.3f, 0.3f);
|
||||
|
||||
m_fAngle += m_fAngVel;
|
||||
m_nDoorState = DOORST_SWINGING;
|
||||
if(m_fAngle > m_fMaxAngle){
|
||||
m_fAngle = m_fMaxAngle;
|
||||
m_fAngVel *= -0.8f;
|
||||
m_nDoorState = DOORST_OPEN;
|
||||
}
|
||||
if(m_fAngle < m_fMinAngle){
|
||||
m_fAngle = m_fMinAngle;
|
||||
m_fAngVel *= -0.8f;
|
||||
m_nDoorState = DOORST_CLOSED;
|
||||
}
|
||||
m_vecSpeed = speed;
|
||||
}
|
||||
|
||||
float
|
||||
CDoor::RetAngleWhenClosed(void)
|
||||
{
|
||||
if(Abs(m_fMaxAngle) < Abs(m_fMinAngle))
|
||||
return m_fMaxAngle;
|
||||
else
|
||||
return m_fMinAngle;
|
||||
}
|
||||
|
||||
float
|
||||
CDoor::RetAngleWhenOpen(void)
|
||||
{
|
||||
if(Abs(m_fMaxAngle) < Abs(m_fMinAngle))
|
||||
return m_fMinAngle;
|
||||
else
|
||||
return m_fMaxAngle;
|
||||
}
|
||||
|
||||
float
|
||||
CDoor::GetAngleOpenRatio(void)
|
||||
{
|
||||
float open = RetAngleWhenOpen();
|
||||
if(open == 0.0f)
|
||||
return 0.0f;
|
||||
return m_fAngle/open;
|
||||
}
|
||||
|
||||
bool
|
||||
CDoor::IsFullyOpen(void)
|
||||
{
|
||||
// why -0.5? that's around 28 deg less than fully open
|
||||
if(Abs(m_fAngle) < Abs(RetAngleWhenOpen()) - 0.5f)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
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();
|
||||
}
|
||||
69
src/vehicles/Door.h
Normal file
69
src/vehicles/Door.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
class CVehicle;
|
||||
|
||||
enum eDoorState
|
||||
{
|
||||
DOORST_SWINGING,
|
||||
// actually wrong though,
|
||||
// OPEN is really MAX_ANGLE and CLOSED is MIN_ANGLE
|
||||
DOORST_OPEN,
|
||||
DOORST_CLOSED
|
||||
};
|
||||
|
||||
class CDoor
|
||||
{
|
||||
public:
|
||||
float m_fMaxAngle;
|
||||
float m_fMinAngle;
|
||||
// direction of rotation for air resistance
|
||||
int8 m_nDirn;
|
||||
// axis in which this door rotates
|
||||
int8 m_nAxis;
|
||||
int8 m_nDoorState;
|
||||
float m_fAngle;
|
||||
float m_fPrevAngle;
|
||||
float m_fAngVel;
|
||||
CVector m_vecSpeed;
|
||||
|
||||
CDoor(void);
|
||||
void Init(float minAngle, float maxAngle, int8 dir, int8 axis) {
|
||||
m_fMinAngle = minAngle;
|
||||
m_fMaxAngle = maxAngle;
|
||||
m_nDirn = dir;
|
||||
m_nAxis = axis;
|
||||
}
|
||||
void Open(float ratio);
|
||||
void Process(CVehicle *veh);
|
||||
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);
|
||||
};
|
||||
185
src/vehicles/Floater.cpp
Normal file
185
src/vehicles/Floater.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "Timer.h"
|
||||
#include "WaterLevel.h"
|
||||
#include "ModelIndices.h"
|
||||
#include "Physical.h"
|
||||
#include "Vehicle.h"
|
||||
#include "Floater.h"
|
||||
|
||||
cBuoyancy mod_Buoyancy;
|
||||
|
||||
float fVolMultiplier = 1.0f;
|
||||
// amount of boat volume in bounding box
|
||||
// 1.0-volume is the empty space in the bbox
|
||||
float fBoatVolumeDistribution[9] = {
|
||||
// rear
|
||||
0.75f, 0.9f, 0.75f,
|
||||
0.95f, 1.0f, 0.95f,
|
||||
0.3f, 0.7f, 0.3f
|
||||
// bow
|
||||
};
|
||||
|
||||
bool
|
||||
cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse)
|
||||
{
|
||||
m_numSteps = 2.0f;
|
||||
|
||||
if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->bTouchingWater))
|
||||
return false;
|
||||
m_matrix = phys->GetMatrix();
|
||||
|
||||
PreCalcSetup(phys, buoyancy);
|
||||
SimpleCalcBuoyancy();
|
||||
float f = CalcBuoyancyForce(phys, point, impulse);
|
||||
if(m_isBoat)
|
||||
return true;
|
||||
return f != 0.0f;
|
||||
}
|
||||
|
||||
void
|
||||
cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy)
|
||||
{
|
||||
CColModel *colModel;
|
||||
|
||||
m_isBoat = phys->IsVehicle() && ((CVehicle*)phys)->IsBoat();
|
||||
colModel = phys->GetColModel();
|
||||
m_dimMin = colModel->boundingBox.min;
|
||||
m_dimMax = colModel->boundingBox.max;
|
||||
|
||||
if(m_isBoat){
|
||||
if(phys->GetModelIndex() == MI_PREDATOR){
|
||||
m_dimMax.y *= 0.9f;
|
||||
m_dimMin.y *= 0.9f;
|
||||
}else if(phys->GetModelIndex() == MI_SPEEDER){
|
||||
m_dimMax.y *= 1.1f;
|
||||
m_dimMin.y *= 0.9f;
|
||||
}else if(phys->GetModelIndex() == MI_REEFER){
|
||||
m_dimMin.y *= 0.9f;
|
||||
}else{
|
||||
m_dimMax.y *= 0.9f;
|
||||
m_dimMin.y *= 0.9f;
|
||||
}
|
||||
}
|
||||
|
||||
m_step = (m_dimMax - m_dimMin)/m_numSteps;
|
||||
|
||||
if(m_step.z > m_step.x && m_step.z > m_step.y){
|
||||
m_stepRatio.x = m_step.x/m_step.z;
|
||||
m_stepRatio.y = m_step.y/m_step.z;
|
||||
m_stepRatio.z = 1.0f;
|
||||
}else if(m_step.y > m_step.x && m_step.y > m_step.z){
|
||||
m_stepRatio.x = m_step.x/m_step.y;
|
||||
m_stepRatio.y = 1.0f;
|
||||
m_stepRatio.z = m_step.z/m_step.y;
|
||||
}else{
|
||||
m_stepRatio.x = 1.0f;
|
||||
m_stepRatio.y = m_step.y/m_step.x;
|
||||
m_stepRatio.z = m_step.z/m_step.x;
|
||||
}
|
||||
|
||||
m_haveVolume = false;
|
||||
m_numPartialVolumes = 1.0f;
|
||||
m_volumeUnderWater = 0.0f;
|
||||
m_impulsePoint = CVector(0.0f, 0.0f, 0.0f);
|
||||
m_position = phys->GetPosition();
|
||||
m_positionZ = CVector(0.0f, 0.0f, m_position.z);
|
||||
m_buoyancy = buoyancy;
|
||||
m_waterlevel += m_waterLevelInc;
|
||||
}
|
||||
|
||||
void
|
||||
cBuoyancy::SimpleCalcBuoyancy(void)
|
||||
{
|
||||
float x, y;
|
||||
int ix, i;
|
||||
tWaterLevel waterPosition;
|
||||
|
||||
// Floater is divided into 3x3 parts. Process and sum each of them
|
||||
ix = 0;
|
||||
for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){
|
||||
i = ix;
|
||||
for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){
|
||||
CVector waterLevel(x, y, 0.0f);
|
||||
FindWaterLevel(m_positionZ, &waterLevel, &waterPosition);
|
||||
fVolMultiplier = m_isBoat ? fBoatVolumeDistribution[i] : 1.0f;
|
||||
if(waterPosition != FLOATER_ABOVE_WATER)
|
||||
SimpleSumBuoyancyData(waterLevel, waterPosition);
|
||||
i += 3;
|
||||
}
|
||||
ix++;
|
||||
}
|
||||
|
||||
m_volumeUnderWater /= (m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f);
|
||||
}
|
||||
|
||||
float
|
||||
cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition)
|
||||
{
|
||||
static float fThisVolume;
|
||||
static CVector AverageOfWaterLevel;
|
||||
static float fFraction;
|
||||
static float fRemainingSlice;
|
||||
|
||||
float submerged = Abs(waterLevel.z - m_dimMin.z);
|
||||
// subtract empty space from submerged volume
|
||||
fThisVolume = submerged - (1.0f - fVolMultiplier);
|
||||
if(fThisVolume < 0.0f)
|
||||
return 0.0f;
|
||||
|
||||
if(m_isBoat){
|
||||
fThisVolume *= fVolMultiplier;
|
||||
if(fThisVolume < 0.5f)
|
||||
fThisVolume = 2.0f*sq(fThisVolume);
|
||||
if(fThisVolume < 1.0f)
|
||||
fThisVolume = sq(fThisVolume);
|
||||
fThisVolume = sq(fThisVolume);
|
||||
}
|
||||
|
||||
m_volumeUnderWater += fThisVolume;
|
||||
|
||||
AverageOfWaterLevel.x = waterLevel.x * m_stepRatio.x;
|
||||
AverageOfWaterLevel.y = waterLevel.y * m_stepRatio.y;
|
||||
AverageOfWaterLevel.z = (waterLevel.z+m_dimMin.z)/2.0f * m_stepRatio.z;
|
||||
|
||||
if(m_flipAverage)
|
||||
AverageOfWaterLevel = -AverageOfWaterLevel;
|
||||
|
||||
fFraction = 1.0f/m_numPartialVolumes;
|
||||
fRemainingSlice = 1.0f - fFraction;
|
||||
m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction;
|
||||
m_numPartialVolumes += 1.0f;
|
||||
m_haveVolume = true;
|
||||
return fThisVolume;
|
||||
}
|
||||
|
||||
void
|
||||
cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition)
|
||||
{
|
||||
*waterPosition = FLOATER_IN_WATER;
|
||||
// waterLevel is a local x,y point
|
||||
// m_position is the global position of our floater
|
||||
// zpos is the global z coordinate of our floater
|
||||
CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel);
|
||||
CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z,
|
||||
&waterLevel->z, true);
|
||||
waterLevel->z -= xWaterLevel.z + zpos.z; // make local
|
||||
if(waterLevel->z > m_dimMax.z){
|
||||
waterLevel->z = m_dimMax.z;
|
||||
*waterPosition = FLOATER_UNDER_WATER;
|
||||
}else if(waterLevel->z < m_dimMin.z){
|
||||
waterLevel->z = m_dimMin.z;
|
||||
*waterPosition = FLOATER_ABOVE_WATER;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse)
|
||||
{
|
||||
if(!m_haveVolume)
|
||||
return false;
|
||||
|
||||
*point = Multiply3x3(m_matrix, m_impulsePoint);
|
||||
*impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep());
|
||||
return true;
|
||||
}
|
||||
45
src/vehicles/Floater.h
Normal file
45
src/vehicles/Floater.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
class CPhysical;
|
||||
|
||||
enum tWaterLevel
|
||||
{
|
||||
FLOATER_ABOVE_WATER,
|
||||
FLOATER_IN_WATER,
|
||||
FLOATER_UNDER_WATER,
|
||||
};
|
||||
|
||||
class cBuoyancy
|
||||
{
|
||||
public:
|
||||
CVector m_position;
|
||||
CMatrix m_matrix;
|
||||
int m_field_54;
|
||||
CVector m_positionZ;
|
||||
float m_waterlevel;
|
||||
float m_waterLevelInc;
|
||||
float m_buoyancy;
|
||||
CVector m_dimMax;
|
||||
CVector m_dimMin;
|
||||
float m_numPartialVolumes;
|
||||
int m_field_8C;
|
||||
int m_field_90;
|
||||
int m_field_94;
|
||||
bool m_haveVolume;
|
||||
CVector m_step;
|
||||
CVector m_stepRatio;
|
||||
float m_numSteps;
|
||||
bool m_flipAverage;
|
||||
char m_field_B9;
|
||||
bool m_isBoat;
|
||||
float m_volumeUnderWater;
|
||||
CVector m_impulsePoint;
|
||||
|
||||
bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse);
|
||||
void PreCalcSetup(CPhysical *phys, float buoyancy);
|
||||
void SimpleCalcBuoyancy(void);
|
||||
float SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition);
|
||||
void FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition);
|
||||
bool CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point);
|
||||
};
|
||||
extern cBuoyancy mod_Buoyancy;
|
||||
270
src/vehicles/HandlingMgr.cpp
Normal file
270
src/vehicles/HandlingMgr.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "FileMgr.h"
|
||||
#include "Physical.h"
|
||||
#include "HandlingMgr.h"
|
||||
|
||||
cHandlingDataMgr mod_HandlingManager;
|
||||
|
||||
const char *HandlingFilename = "HANDLING.CFG";
|
||||
|
||||
const char VehicleNames[NUMHANDLINGS][14] = {
|
||||
"LANDSTAL",
|
||||
"IDAHO",
|
||||
"STINGER",
|
||||
"LINERUN",
|
||||
"PEREN",
|
||||
"SENTINEL",
|
||||
"PATRIOT",
|
||||
"FIRETRUK",
|
||||
"TRASH",
|
||||
"STRETCH",
|
||||
"MANANA",
|
||||
"INFERNUS",
|
||||
"BLISTA",
|
||||
"PONY",
|
||||
"MULE",
|
||||
"CHEETAH",
|
||||
"AMBULAN",
|
||||
"FBICAR",
|
||||
"MOONBEAM",
|
||||
"ESPERANT",
|
||||
"TAXI",
|
||||
"KURUMA",
|
||||
"BOBCAT",
|
||||
"MRWHOOP",
|
||||
"BFINJECT",
|
||||
"POLICE",
|
||||
"ENFORCER",
|
||||
"SECURICA",
|
||||
"BANSHEE",
|
||||
"PREDATOR",
|
||||
"BUS",
|
||||
"RHINO",
|
||||
"BARRACKS",
|
||||
"TRAIN",
|
||||
"HELI",
|
||||
"DODO",
|
||||
"COACH",
|
||||
"CABBIE",
|
||||
"STALLION",
|
||||
"RUMPO",
|
||||
"RCBANDIT",
|
||||
"BELLYUP",
|
||||
"MRWONGS",
|
||||
"MAFIA",
|
||||
"YARDIE",
|
||||
"YAKUZA",
|
||||
"DIABLOS",
|
||||
"COLUMB",
|
||||
"HOODS",
|
||||
"AIRTRAIN",
|
||||
"DEADDODO",
|
||||
"SPEEDER",
|
||||
"REEFER",
|
||||
"PANLANT",
|
||||
"FLATBED",
|
||||
"YANKEE",
|
||||
"BORGNINE"
|
||||
};
|
||||
|
||||
cHandlingDataMgr::cHandlingDataMgr(void)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::Initialise(void)
|
||||
{
|
||||
LoadHandlingData();
|
||||
field_0 = 0.1f;
|
||||
fWheelFriction = 0.9f;
|
||||
field_8 = 1.0f;
|
||||
field_C = 0.8f;
|
||||
field_10 = 0.98f;
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::LoadHandlingData(void)
|
||||
{
|
||||
char *start, *end;
|
||||
char line[201]; // weird value
|
||||
char delim[4]; // not sure
|
||||
char *word;
|
||||
int field, handlingId;
|
||||
int keepGoing;
|
||||
tHandlingData *handling;
|
||||
|
||||
CFileMgr::SetDir("DATA");
|
||||
CFileMgr::LoadFile(HandlingFilename, work_buff, sizeof(work_buff), "r");
|
||||
CFileMgr::SetDir("");
|
||||
|
||||
start = (char*)work_buff;
|
||||
end = start+1;
|
||||
handling = nil;
|
||||
keepGoing = 1;
|
||||
|
||||
while(keepGoing){
|
||||
// find end of line
|
||||
while(*end != '\n') end++;
|
||||
|
||||
// get line
|
||||
strncpy(line, start, end - start);
|
||||
line[end - start] = '\0';
|
||||
start = end+1;
|
||||
end = start+1;
|
||||
|
||||
// yeah, this is kinda crappy
|
||||
if(strcmp(line, ";the end") == 0)
|
||||
keepGoing = 0;
|
||||
else if(line[0] != ';'){
|
||||
field = 0;
|
||||
strcpy(delim, " \t");
|
||||
// FIX: game seems to use a do-while loop here
|
||||
for(word = strtok(line, delim); word; word = strtok(nil, delim)){
|
||||
switch(field){
|
||||
case 0:
|
||||
handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS);
|
||||
assert(handlingId >= 0 && handlingId < NUMHANDLINGS);
|
||||
handling = &HandlingData[handlingId];
|
||||
handling->nIdentifier = (tVehicleType)handlingId;
|
||||
break;
|
||||
case 1: handling->fMass = strtod(word, nil); break;
|
||||
case 2: handling->Dimension.x = strtod(word, nil); break;
|
||||
case 3: handling->Dimension.y = strtod(word, nil); break;
|
||||
case 4: handling->Dimension.z = strtod(word, nil); break;
|
||||
case 5: handling->CentreOfMass.x = strtod(word, nil); break;
|
||||
case 6: handling->CentreOfMass.y = strtod(word, nil); break;
|
||||
case 7: handling->CentreOfMass.z = strtod(word, nil); break;
|
||||
case 8: handling->nPercentSubmerged = atoi(word); break;
|
||||
case 9: handling->fTractionMultiplier = strtod(word, nil); break;
|
||||
case 10: handling->fTractionLoss = strtod(word, nil); break;
|
||||
case 11: handling->fTractionBias = strtod(word, nil); break;
|
||||
case 12: handling->Transmission.nNumberOfGears = atoi(word); break;
|
||||
case 13: handling->Transmission.fMaxVelocity = strtod(word, nil); break;
|
||||
case 14: handling->Transmission.fEngineAcceleration = strtod(word, nil) * 0.4; break;
|
||||
case 15: handling->Transmission.nDriveType = word[0]; break;
|
||||
case 16: handling->Transmission.nEngineType = word[0]; break;
|
||||
case 17: handling->fBrakeDeceleration = strtod(word, nil); break;
|
||||
case 18: handling->fBrakeBias = strtod(word, nil); break;
|
||||
case 19: handling->bABS = !!atoi(word); break;
|
||||
case 20: handling->fSteeringLock = strtod(word, nil); break;
|
||||
case 21: handling->fSuspensionForceLevel = strtod(word, nil); break;
|
||||
case 22: handling->fSuspensionDampingLevel = strtod(word, nil); break;
|
||||
case 23: handling->fSeatOffsetDistance = strtod(word, nil); break;
|
||||
case 24: handling->fCollisionDamageMultiplier = strtod(word, nil); break;
|
||||
case 25: handling->nMonetaryValue = atoi(word); break;
|
||||
case 26: handling->fSuspensionUpperLimit = strtod(word, nil); break;
|
||||
case 27: handling->fSuspensionLowerLimit = strtod(word, nil); break;
|
||||
case 28: handling->fSuspensionBias = strtod(word, nil); break;
|
||||
case 29:
|
||||
sscanf(word, "%x", &handling->Flags);
|
||||
handling->Transmission.Flags = handling->Flags;
|
||||
break;
|
||||
case 30: handling->FrontLights = atoi(word); break;
|
||||
case 31: handling->RearLights = atoi(word); break;
|
||||
}
|
||||
field++;
|
||||
}
|
||||
ConvertDataToGameUnits(handling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
cHandlingDataMgr::FindExactWord(const char *word, const char *words, int wordLen, int numWords)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < numWords; i++){
|
||||
// BUG: the game does something really stupid here, it's fixed here
|
||||
if(strncmp(word, words, wordLen) == 0)
|
||||
return i;
|
||||
words += wordLen;
|
||||
}
|
||||
return numWords;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling)
|
||||
{
|
||||
// acceleration is in ms^-2, but we need mf^-2 where f is one frame time (50fps)
|
||||
float velocity, a, b;
|
||||
|
||||
handling->Transmission.fEngineAcceleration *= 1.0f/(50.0f*50.0f);
|
||||
handling->Transmission.fMaxVelocity *= 1000.0f/(60.0f*60.0f * 50.0f);
|
||||
handling->fBrakeDeceleration *= 1.0f/(50.0f*50.0f);
|
||||
handling->fTurnMass = (sq(handling->Dimension.x) + sq(handling->Dimension.y)) * handling->fMass / 12.0f;
|
||||
if(handling->fTurnMass < 10.0f)
|
||||
handling->fTurnMass *= 5.0f;
|
||||
handling->fInvMass = 1.0f/handling->fMass;
|
||||
handling->fBuoyancy = 100.0f/handling->nPercentSubmerged * GRAVITY*handling->fMass;
|
||||
|
||||
// Don't quite understand this. What seems to be going on is that
|
||||
// we calculate a drag (air resistance) deceleration for a given velocity and
|
||||
// find the intersection between that and the max engine acceleration.
|
||||
// at that point the car cannot accelerate any further and we've found the max velocity.
|
||||
a = 0.0f;
|
||||
b = 100.0f;
|
||||
velocity = handling->Transmission.fMaxVelocity;
|
||||
while(a < b && velocity > 0.0f){
|
||||
velocity -= 0.01f;
|
||||
// what's the 1/6?
|
||||
a = handling->Transmission.fEngineAcceleration/6.0f;
|
||||
// no density or drag coefficient here...
|
||||
float a_drag = 0.5f*SQR(velocity) * handling->Dimension.x*handling->Dimension.z / handling->fMass;
|
||||
// can't make sense of this... maybe v - v/(drag + 1) ? but that doesn't make so much sense either
|
||||
b = -velocity * (1.0f/(a_drag + 1.0f) - 1.0f);
|
||||
}
|
||||
|
||||
if(handling->nIdentifier == HANDLING_RCBANDIT){
|
||||
handling->Transmission.fMaxCruiseVelocity = handling->Transmission.fMaxVelocity;
|
||||
}else{
|
||||
handling->Transmission.fMaxCruiseVelocity = velocity;
|
||||
handling->Transmission.fMaxVelocity = velocity * 1.2f;
|
||||
}
|
||||
handling->Transmission.fMaxReverseVelocity = -0.2f;
|
||||
|
||||
if(handling->Transmission.nDriveType == '4')
|
||||
handling->Transmission.fEngineAcceleration /= 4.0f;
|
||||
else
|
||||
handling->Transmission.fEngineAcceleration /= 2.0f;
|
||||
|
||||
handling->Transmission.InitGearRatios();
|
||||
}
|
||||
|
||||
int32
|
||||
cHandlingDataMgr::GetHandlingId(const char *name)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < NUMHANDLINGS; i++)
|
||||
if(strncmp(VehicleNames[i], name, 14) == 0)
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::ConvertDataToWorldUnits(tHandlingData *handling)
|
||||
{
|
||||
// TODO: mobile code
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::RangeCheck(tHandlingData *handling)
|
||||
{
|
||||
// TODO: mobile code
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::ModifyHandlingValue(CVehicle *, const tVehicleType &, const tField &, const bool &)
|
||||
{
|
||||
// TODO: mobile code
|
||||
}
|
||||
|
||||
void
|
||||
cHandlingDataMgr::DisplayHandlingData(CVehicle *, tHandlingData *, uint8, bool)
|
||||
{
|
||||
// TODO: mobile code
|
||||
}
|
||||
156
src/vehicles/HandlingMgr.h
Normal file
156
src/vehicles/HandlingMgr.h
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#pragma once
|
||||
|
||||
#include "Transmission.h"
|
||||
|
||||
enum tVehicleType
|
||||
{
|
||||
HANDLING_LANDSTAL,
|
||||
HANDLING_IDAHO,
|
||||
HANDLING_STINGER,
|
||||
HANDLING_LINERUN,
|
||||
HANDLING_PEREN,
|
||||
HANDLING_SENTINEL,
|
||||
HANDLING_PATRIOT,
|
||||
HANDLING_FIRETRUK,
|
||||
HANDLING_TRASH,
|
||||
HANDLING_STRETCH,
|
||||
HANDLING_MANANA,
|
||||
HANDLING_INFERNUS,
|
||||
HANDLING_BLISTA,
|
||||
HANDLING_PONY,
|
||||
HANDLING_MULE,
|
||||
HANDLING_CHEETAH,
|
||||
HANDLING_AMBULAN,
|
||||
HANDLING_FBICAR,
|
||||
HANDLING_MOONBEAM,
|
||||
HANDLING_ESPERANT,
|
||||
HANDLING_TAXI,
|
||||
HANDLING_KURUMA,
|
||||
HANDLING_BOBCAT,
|
||||
HANDLING_MRWHOOP,
|
||||
HANDLING_BFINJECT,
|
||||
HANDLING_POLICE,
|
||||
HANDLING_ENFORCER,
|
||||
HANDLING_SECURICA,
|
||||
HANDLING_BANSHEE,
|
||||
HANDLING_PREDATOR,
|
||||
HANDLING_BUS,
|
||||
HANDLING_RHINO,
|
||||
HANDLING_BARRACKS,
|
||||
HANDLING_TRAIN,
|
||||
HANDLING_HELI,
|
||||
HANDLING_DODO,
|
||||
HANDLING_COACH,
|
||||
HANDLING_CABBIE,
|
||||
HANDLING_STALLION,
|
||||
HANDLING_RUMPO,
|
||||
HANDLING_RCBANDIT,
|
||||
HANDLING_BELLYUP,
|
||||
HANDLING_MRWONGS,
|
||||
HANDLING_MAFIA,
|
||||
HANDLING_YARDIE,
|
||||
HANDLING_YAKUZA,
|
||||
HANDLING_DIABLOS,
|
||||
HANDLING_COLUMB,
|
||||
HANDLING_HOODS,
|
||||
HANDLING_AIRTRAIN,
|
||||
HANDLING_DEADDODO,
|
||||
HANDLING_SPEEDER,
|
||||
HANDLING_REEFER,
|
||||
HANDLING_PANLANT,
|
||||
HANDLING_FLATBED,
|
||||
HANDLING_YANKEE,
|
||||
HANDLING_BORGNINE,
|
||||
|
||||
NUMHANDLINGS
|
||||
};
|
||||
|
||||
enum tField // most likely a handling field enum, never used so :shrug:
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
HANDLING_1G_BOOST = 1,
|
||||
HANDLING_2G_BOOST = 2,
|
||||
HANDLING_REV_BONNET = 4,
|
||||
HANDLING_HANGING_BOOT = 8,
|
||||
HANDLING_NO_DOORS = 0x10,
|
||||
HANDLING_IS_VAN = 0x20,
|
||||
HANDLING_IS_BUS = 0x40,
|
||||
HANDLING_IS_LOW = 0x80,
|
||||
HANDLING_DBL_EXHAUST = 0x100,
|
||||
HANDLING_TAILGATE_BOOT = 0x200,
|
||||
HANDLING_NOSWING_BOOT = 0x400,
|
||||
HANDLING_NONPLAYER_STABILISER = 0x800,
|
||||
HANDLING_NEUTRALHANDLING = 0x1000,
|
||||
HANDLING_HAS_NO_ROOF = 0x2000,
|
||||
HANDLING_IS_BIG = 0x4000,
|
||||
HANDLING_HALOGEN_LIGHTS = 0x8000,
|
||||
};
|
||||
|
||||
struct tHandlingData
|
||||
{
|
||||
tVehicleType nIdentifier;
|
||||
float fMass;
|
||||
float fInvMass;
|
||||
float fTurnMass;
|
||||
CVector Dimension;
|
||||
CVector CentreOfMass;
|
||||
int8 nPercentSubmerged;
|
||||
float fBuoyancy;
|
||||
float fTractionMultiplier;
|
||||
cTransmission Transmission;
|
||||
float fBrakeDeceleration;
|
||||
float fBrakeBias;
|
||||
int8 bABS;
|
||||
float fSteeringLock;
|
||||
float fTractionLoss;
|
||||
float fTractionBias;
|
||||
float fUnused;
|
||||
float fSuspensionForceLevel;
|
||||
float fSuspensionDampingLevel;
|
||||
float fSuspensionUpperLimit;
|
||||
float fSuspensionLowerLimit;
|
||||
float fSuspensionBias;
|
||||
float fCollisionDamageMultiplier;
|
||||
uint32 Flags;
|
||||
float fSeatOffsetDistance;
|
||||
int32 nMonetaryValue;
|
||||
int8 FrontLights;
|
||||
int8 RearLights;
|
||||
};
|
||||
VALIDATE_SIZE(tHandlingData, 0xD8);
|
||||
|
||||
class CVehicle;
|
||||
|
||||
class cHandlingDataMgr
|
||||
{
|
||||
float field_0; // unused it seems
|
||||
public:
|
||||
float fWheelFriction; // wheel related
|
||||
private:
|
||||
float field_8; //
|
||||
float field_C; // unused it seems
|
||||
float field_10; //
|
||||
tHandlingData HandlingData[NUMHANDLINGS];
|
||||
uint32 field_302C; // unused it seems
|
||||
|
||||
public:
|
||||
cHandlingDataMgr(void);
|
||||
void Initialise(void);
|
||||
void LoadHandlingData(void);
|
||||
int FindExactWord(const char *word, const char *words, int wordLen, int numWords);
|
||||
void ConvertDataToWorldUnits(tHandlingData *handling);
|
||||
void ConvertDataToGameUnits(tHandlingData *handling);
|
||||
void RangeCheck(tHandlingData *handling);
|
||||
void ModifyHandlingValue(CVehicle *, const tVehicleType &, const tField &, const bool &);
|
||||
void DisplayHandlingData(CVehicle *, tHandlingData *, uint8, bool);
|
||||
int32 GetHandlingId(const char *name);
|
||||
tHandlingData *GetHandlingData(tVehicleType id) { return &HandlingData[id]; }
|
||||
bool HasRearWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'F'; }
|
||||
bool HasFrontWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'R'; }
|
||||
};
|
||||
VALIDATE_SIZE(cHandlingDataMgr, 0x3030);
|
||||
extern cHandlingDataMgr mod_HandlingManager;
|
||||
1070
src/vehicles/Heli.cpp
Normal file
1070
src/vehicles/Heli.cpp
Normal file
File diff suppressed because it is too large
Load diff
101
src/vehicles/Heli.h
Normal file
101
src/vehicles/Heli.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
|
||||
class CObject;
|
||||
|
||||
enum eHeliNodes
|
||||
{
|
||||
HELI_CHASSIS = 1,
|
||||
HELI_TOPROTOR,
|
||||
HELI_BACKROTOR,
|
||||
HELI_TAIL,
|
||||
HELI_TOPKNOT,
|
||||
HELI_SKID_LEFT,
|
||||
HELI_SKID_RIGHT,
|
||||
NUM_HELI_NODES
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
HELI_RANDOM0,
|
||||
HELI_RANDOM1,
|
||||
HELI_SCRIPT,
|
||||
HELI_CATALINA,
|
||||
NUM_HELIS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
HELI_TYPE_RANDOM,
|
||||
HELI_TYPE_SCRIPT,
|
||||
HELI_TYPE_CATALINA,
|
||||
};
|
||||
|
||||
|
||||
class CHeli : public CVehicle
|
||||
{
|
||||
public:
|
||||
// 0x288
|
||||
RwFrame *m_aHeliNodes[NUM_HELI_NODES];
|
||||
int8 m_heliStatus;
|
||||
float m_fSearchLightX;
|
||||
float m_fSearchLightY;
|
||||
uint32 m_nExplosionTimer;
|
||||
float m_fRotation;
|
||||
float m_fAngularSpeed;
|
||||
float m_fTargetZ;
|
||||
float m_fSearchLightIntensity;
|
||||
int8 m_nHeliId;
|
||||
int8 m_heliType;
|
||||
int8 m_pathState;
|
||||
float m_aSearchLightHistoryX[6];
|
||||
float m_aSearchLightHistoryY[6];
|
||||
uint32 m_nSearchLightTimer;
|
||||
uint32 m_nShootTimer;
|
||||
uint32 m_nLastShotTime;
|
||||
uint32 m_nBulletDamage;
|
||||
float m_fRotorRotation;
|
||||
float m_fHeliDustZ[8];
|
||||
uint32 m_nPoliceShoutTimer;
|
||||
float m_fTargetOffset;
|
||||
bool m_bTestRight;
|
||||
|
||||
static CHeli *pHelis[NUM_HELIS];
|
||||
static int16 NumRandomHelis;
|
||||
static uint32 TestForNewRandomHelisTimer;
|
||||
static int16 NumScriptHelis; // unused
|
||||
static bool CatalinaHeliOn;
|
||||
static bool CatalinaHasBeenShotDown;
|
||||
static bool ScriptHeliOn;
|
||||
|
||||
CHeli(int32 id, uint8 CreatedBy);
|
||||
|
||||
// from CEntity
|
||||
void SetModelIndex(uint32 id);
|
||||
void ProcessControl(void);
|
||||
void PreRender(void);
|
||||
void Render(void);
|
||||
|
||||
void PreRenderAlways(void);
|
||||
CObject *SpawnFlyingComponent(int32 component);
|
||||
|
||||
static void InitHelis(void);
|
||||
static CHeli *GenerateHeli(bool catalina); // out of class in III PC and later because of SecuROM
|
||||
static void UpdateHelis(void);
|
||||
static void SpecialHeliPreRender(void);
|
||||
static bool TestRocketCollision(CVector *coors);
|
||||
static bool TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage);
|
||||
|
||||
static void StartCatalinaFlyBy(void); // out of class in III PC and later because of SecuROM
|
||||
static void RemoveCatalinaHeli(void);
|
||||
static CHeli *FindPointerToCatalinasHeli(void);
|
||||
static void CatalinaTakeOff(void);
|
||||
static void MakeCatalinaHeliFlyAway(void);
|
||||
static bool HasCatalinaBeenShotDown(void);
|
||||
|
||||
static void ActivateHeli(bool activate);
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CHeli, 0x33C);
|
||||
|
||||
975
src/vehicles/Plane.cpp
Normal file
975
src/vehicles/Plane.cpp
Normal file
|
|
@ -0,0 +1,975 @@
|
|||
#include "common.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "General.h"
|
||||
#include "ModelIndices.h"
|
||||
#include "FileMgr.h"
|
||||
#include "Streaming.h"
|
||||
#include "Replay.h"
|
||||
#include "Camera.h"
|
||||
#include "DMAudio.h"
|
||||
#include "Wanted.h"
|
||||
#include "Coronas.h"
|
||||
#include "Particle.h"
|
||||
#include "Explosion.h"
|
||||
#include "World.h"
|
||||
#include "HandlingMgr.h"
|
||||
#include "Plane.h"
|
||||
#include "MemoryHeap.h"
|
||||
|
||||
CPlaneNode *pPathNodes;
|
||||
CPlaneNode *pPath2Nodes;
|
||||
CPlaneNode *pPath3Nodes;
|
||||
CPlaneNode *pPath4Nodes;
|
||||
int32 NumPathNodes;
|
||||
int32 NumPath2Nodes;
|
||||
int32 NumPath3Nodes;
|
||||
int32 NumPath4Nodes;
|
||||
float TotalLengthOfFlightPath;
|
||||
float TotalLengthOfFlightPath2;
|
||||
float TotalLengthOfFlightPath3;
|
||||
float TotalLengthOfFlightPath4;
|
||||
float TotalDurationOfFlightPath;
|
||||
float TotalDurationOfFlightPath2;
|
||||
float TotalDurationOfFlightPath3;
|
||||
float TotalDurationOfFlightPath4;
|
||||
float LandingPoint;
|
||||
float TakeOffPoint;
|
||||
CPlaneInterpolationLine aPlaneLineBits[6];
|
||||
|
||||
float PlanePathPosition[3];
|
||||
float OldPlanePathPosition[3];
|
||||
float PlanePathSpeed[3];
|
||||
float PlanePath2Position[3];
|
||||
float PlanePath3Position;
|
||||
float PlanePath4Position;
|
||||
float PlanePath2Speed[3];
|
||||
float PlanePath3Speed;
|
||||
float PlanePath4Speed;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
CESNA_STATUS_NONE, // doesn't even exist
|
||||
CESNA_STATUS_FLYING,
|
||||
CESNA_STATUS_DESTROYED,
|
||||
CESNA_STATUS_LANDED,
|
||||
};
|
||||
|
||||
int32 CesnaMissionStatus;
|
||||
int32 CesnaMissionStartTime;
|
||||
CPlane *pDrugRunCesna;
|
||||
int32 DropOffCesnaMissionStatus;
|
||||
int32 DropOffCesnaMissionStartTime;
|
||||
CPlane *pDropOffCesna;
|
||||
|
||||
|
||||
CPlane::CPlane(int32 id, uint8 CreatedBy)
|
||||
: CVehicle(CreatedBy)
|
||||
{
|
||||
CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
|
||||
m_vehType = VEHICLE_TYPE_PLANE;
|
||||
pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)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;
|
||||
|
||||
SetStatus(STATUS_PLANE);
|
||||
bIsBIGBuilding = true;
|
||||
m_level = LEVEL_GENERIC;
|
||||
|
||||
#ifdef FIX_BUGS
|
||||
m_isFarAway = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
CPlane::~CPlane()
|
||||
{
|
||||
DeleteRwObject();
|
||||
}
|
||||
|
||||
void
|
||||
CPlane::SetModelIndex(uint32 id)
|
||||
{
|
||||
CVehicle::SetModelIndex(id);
|
||||
}
|
||||
|
||||
void
|
||||
CPlane::DeleteRwObject(void)
|
||||
{
|
||||
if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){
|
||||
GetMatrix().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);
|
||||
|
||||
CVector dir;
|
||||
for(i = 0; i < 40; i++){
|
||||
dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
|
||||
dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
|
||||
dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f);
|
||||
int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30);
|
||||
if(CGeneral::GetRandomNumber() & 1)
|
||||
rotSpeed = -rotSpeed;
|
||||
int f = ++nFrameGen & 3;
|
||||
CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir,
|
||||
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
|
||||
colors[nFrameGen&7], 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);
|
||||
|
||||
CVector dir;
|
||||
for(i = 0; i < 40; i++){
|
||||
dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
|
||||
dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f);
|
||||
dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f);
|
||||
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), dir,
|
||||
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
|
||||
colors[nFrameGen&7], 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 == 62){
|
||||
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
|
||||
GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f);
|
||||
GetMatrix().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);
|
||||
GetMatrix().GetRight() = right;
|
||||
GetMatrix().GetUp() = up;
|
||||
GetMatrix().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()).MagnitudeSqr2D() < 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 = nil;
|
||||
FlagToDestroyWhenNextProcessed();
|
||||
}
|
||||
}else if(m_bIsDropOffCesna){
|
||||
planePathPosition = PlanePath4Position;
|
||||
totalLengthOfFlightPath = TotalLengthOfFlightPath4;
|
||||
pathNodes = pPath4Nodes;
|
||||
planePathSpeed = PlanePath4Speed;
|
||||
numPathNodes = NumPath4Nodes;
|
||||
if(DropOffCesnaMissionStatus == CESNA_STATUS_LANDED){
|
||||
pDropOffCesna = nil;
|
||||
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 30 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
|
||||
GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f);
|
||||
GetMatrix().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);
|
||||
GetMatrix().GetRight() = right;
|
||||
GetMatrix().GetUp() = up;
|
||||
GetMatrix().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()).MagnitudeSqr2D() < 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){
|
||||
PUSH_MEMID(MEMID_WORLD);
|
||||
m_rwObject = CModelInfo::GetModelInfo(mi->m_planeLodId)->CreateInstance();
|
||||
POP_MEMID();
|
||||
if(m_rwObject)
|
||||
GetMatrix().AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)));
|
||||
}
|
||||
}
|
||||
}else if(CStreaming::HasModelLoaded(GetModelIndex())){
|
||||
if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){
|
||||
// Get rid of LOD model
|
||||
GetMatrix().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 = 618.75f/600.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 = -618.75f/600.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->SetStatus(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->SetStatus(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_DEADDODO) &&
|
||||
#else
|
||||
plane->GetModelIndex() != MI_AIRTRAIN && plane->GetModelIndex() == MI_DEADDODO && // 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->SetStatus(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->SetStatus(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");
|
||||
}
|
||||
|
||||
const CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); }
|
||||
const CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDropOffCesna->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; }
|
||||
71
src/vehicles/Plane.h
Normal file
71
src/vehicles/Plane.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
|
||||
enum ePlaneNodes
|
||||
{
|
||||
PLANE_WHEEL_FRONT = 2,
|
||||
PLANE_WHEEL_READ,
|
||||
NUM_PLANE_NODES
|
||||
};
|
||||
|
||||
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_nPlaneId;
|
||||
int16 m_isFarAway;
|
||||
int16 m_nCurPathNode;
|
||||
float m_fSpeed;
|
||||
uint32 m_nFrameWhenHit;
|
||||
bool m_bHasBeenHit;
|
||||
bool m_bIsDrugRunCesna;
|
||||
bool m_bIsDropOffCesna;
|
||||
|
||||
CPlane(int32 id, uint8 CreatedBy);
|
||||
~CPlane(void);
|
||||
|
||||
// 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 const CVector FindDrugPlaneCoordinates(void);
|
||||
static const CVector FindDropOffCesnaCoordinates(void);
|
||||
static bool HasCesnaLanded(void);
|
||||
static bool HasCesnaBeenDestroyed(void);
|
||||
static bool HasDropOffCesnaBeenShotDown(void);
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CPlane, 0x29C);
|
||||
|
||||
extern float LandingPoint;
|
||||
extern float TakeOffPoint;
|
||||
extern float PlanePathPosition[3];
|
||||
738
src/vehicles/Train.cpp
Normal file
738
src/vehicles/Train.cpp
Normal file
|
|
@ -0,0 +1,738 @@
|
|||
#include "common.h"
|
||||
#include "main.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 "DMAudio.h"
|
||||
#include "HandlingMgr.h"
|
||||
#include "Train.h"
|
||||
#include "AudioScriptObject.h"
|
||||
|
||||
static CTrainNode* pTrackNodes;
|
||||
static int16 NumTrackNodes;
|
||||
static float StationDist[3] = { 873.0f, 1522.0f, 2481.0f };
|
||||
static float TotalLengthOfTrack;
|
||||
static float TotalDurationOfTrack;
|
||||
static CTrainInterpolationLine aLineBits[17];
|
||||
static float EngineTrackPosition[2];
|
||||
static float EngineTrackSpeed[2];
|
||||
|
||||
static CTrainNode* pTrackNodes_S;
|
||||
static int16 NumTrackNodes_S;
|
||||
static float StationDist_S[4] = { 55.0f, 1388.0f, 2337.0f, 3989.0f };
|
||||
static float TotalLengthOfTrack_S;
|
||||
static float TotalDurationOfTrack_S;
|
||||
static CTrainInterpolationLine aLineBits_S[18];
|
||||
static float EngineTrackPosition_S[4];
|
||||
static float EngineTrackSpeed_S[4];
|
||||
|
||||
CVector CTrain::aStationCoors[3];
|
||||
CVector CTrain::aStationCoors_S[4];
|
||||
|
||||
static bool bTrainArrivalAnnounced[3] = {false, false, false};
|
||||
|
||||
CTrain::CTrain(int32 id, uint8 CreatedBy)
|
||||
: CVehicle(CreatedBy)
|
||||
{
|
||||
CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
|
||||
m_vehType = VEHICLE_TYPE_TRAIN;
|
||||
pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)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_nTrackId = TRACK_ELTRAIN;
|
||||
m_nNumMaxPassengers = 5;
|
||||
m_nDoorTimer = CTimer::GetTimeInMilliseconds();
|
||||
m_nDoorState = TRAIN_DOOR_CLOSED;
|
||||
|
||||
bUsesCollision = true;
|
||||
SetStatus(STATUS_TRAIN_MOVING);
|
||||
|
||||
#ifdef FIX_BUGS
|
||||
m_isFarAway = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CTrain::SetModelIndex(uint32 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
CVehicle::SetModelIndex(id);
|
||||
for(i = 0; i < NUM_TRAIN_NODES; 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
|
||||
SetPosition((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){
|
||||
SetStatus(STATUS_TRAIN_MOVING);
|
||||
m_bTrainStopping = false;
|
||||
m_bProcessDoor = true;
|
||||
}else{
|
||||
SetStatus(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_TRAIN_DOOR_CLOSE, 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_TRAIN_DOOR_OPEN, 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 && GetStatus()== 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::GetSectorIndexY(front.y - 3.0f);
|
||||
if(ymin < 0) ymin = 0;
|
||||
ymax = CWorld::GetSectorIndexY(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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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_vehDoor;
|
||||
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->SetStatus(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->SetStatus(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(Const 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
|
||||
PlayAnnouncement(uint8 sound, uint8 station)
|
||||
{
|
||||
// this was gone in a PC version but inlined on PS2
|
||||
cAudioScriptObject *obj = new cAudioScriptObject;
|
||||
obj->AudioId = sound;
|
||||
obj->Posn = CTrain::aStationCoors[station];
|
||||
obj->AudioEntity = AEHANDLE_NONE;
|
||||
DMAudio.CreateOneShotScriptObject(obj);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessTrainAnnouncements(void)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(StationDist); i++) {
|
||||
for (int j = 0; j < ARRAY_SIZE(EngineTrackPosition); j++) {
|
||||
if (!bTrainArrivalAnnounced[i]) {
|
||||
float preDist = StationDist[i] - 100.0f;
|
||||
if (preDist < 0.0f)
|
||||
preDist += TotalLengthOfTrack;
|
||||
if (EngineTrackPosition[j] > preDist && EngineTrackPosition[j] < StationDist[i]) {
|
||||
bTrainArrivalAnnounced[i] = true;
|
||||
PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_1, i);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
float postDist = StationDist[i] + 10.0f;
|
||||
#ifdef FIX_BUGS
|
||||
if (postDist > TotalLengthOfTrack)
|
||||
postDist -= TotalLengthOfTrack;
|
||||
#else
|
||||
if (postDist < 0.0f) // does this even make sense here?
|
||||
postDist += TotalLengthOfTrack;
|
||||
#endif
|
||||
if (EngineTrackPosition[j] > StationDist[i] && EngineTrackPosition[j] < postDist) {
|
||||
bTrainArrivalAnnounced[i] = false;
|
||||
PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_2, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
86
src/vehicles/Train.h
Normal file
86
src/vehicles/Train.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vehicle.h"
|
||||
#include "Door.h"
|
||||
|
||||
enum
|
||||
{
|
||||
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,
|
||||
NUM_TRAIN_NODES
|
||||
};
|
||||
|
||||
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
|
||||
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[NUM_TRAIN_NODES];
|
||||
|
||||
// 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(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists,
|
||||
float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail);
|
||||
static void UpdateTrains(void);
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CTrain, 0x2E4);
|
||||
138
src/vehicles/Transmission.cpp
Normal file
138
src/vehicles/Transmission.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "common.h"
|
||||
|
||||
#include "Timer.h"
|
||||
#include "HandlingMgr.h"
|
||||
#include "Transmission.h"
|
||||
|
||||
void
|
||||
cTransmission::InitGearRatios(void)
|
||||
{
|
||||
static tGear *pGearRatio0 = nil;
|
||||
static tGear *pGearRatio1 = nil;
|
||||
int i;
|
||||
float velocityDiff;
|
||||
|
||||
memset(Gears, 0, sizeof(Gears));
|
||||
|
||||
for(i = 1; i <= nNumberOfGears; i++){
|
||||
pGearRatio0 = &Gears[i-1];
|
||||
pGearRatio1 = &Gears[i];
|
||||
|
||||
pGearRatio1->fMaxVelocity = (float)i / nNumberOfGears * fMaxVelocity;
|
||||
|
||||
velocityDiff = pGearRatio1->fMaxVelocity - pGearRatio0->fMaxVelocity;
|
||||
|
||||
if(i >= nNumberOfGears){
|
||||
pGearRatio1->fShiftUpVelocity = fMaxVelocity;
|
||||
}else{
|
||||
Gears[i+1].fShiftDownVelocity = velocityDiff*0.42f + pGearRatio0->fMaxVelocity;
|
||||
pGearRatio1->fShiftUpVelocity = velocityDiff*0.6667f + pGearRatio0->fMaxVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse gear
|
||||
Gears[0].fMaxVelocity = fMaxReverseVelocity;
|
||||
Gears[0].fShiftUpVelocity = -0.01f;
|
||||
Gears[0].fShiftDownVelocity = fMaxReverseVelocity;
|
||||
|
||||
Gears[1].fShiftDownVelocity = -0.01f;
|
||||
}
|
||||
|
||||
void
|
||||
cTransmission::CalculateGearForSimpleCar(float speed, uint8 &gear)
|
||||
{
|
||||
static tGear *pGearRatio;
|
||||
|
||||
pGearRatio = &Gears[gear];
|
||||
fCurVelocity = speed;
|
||||
if(speed > pGearRatio->fShiftUpVelocity)
|
||||
gear++;
|
||||
else if(speed < pGearRatio->fShiftDownVelocity){
|
||||
if(gear - 1 < 0)
|
||||
gear = 0;
|
||||
else
|
||||
gear--;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat)
|
||||
{
|
||||
static float fAcceleration = 0.0f;
|
||||
static float fVelocity;
|
||||
static float fCheat;
|
||||
static tGear *pGearRatio;
|
||||
|
||||
fVelocity = velocity;
|
||||
if(fVelocity < fMaxReverseVelocity){
|
||||
fVelocity = fMaxReverseVelocity;
|
||||
return 0.0f;
|
||||
}
|
||||
if(fVelocity > fMaxVelocity){
|
||||
fVelocity = fMaxVelocity;
|
||||
return 0.0f;
|
||||
}
|
||||
fCurVelocity = fVelocity;
|
||||
|
||||
assert(gear <= nNumberOfGears);
|
||||
|
||||
pGearRatio = &Gears[gear];
|
||||
if(fVelocity > pGearRatio->fShiftUpVelocity){
|
||||
if(gear != 0 || gasPedal > 0.0f){
|
||||
gear++;
|
||||
time = 0.0f;
|
||||
return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false);
|
||||
}
|
||||
}else if(fVelocity < pGearRatio->fShiftDownVelocity && gear != 0){
|
||||
if(gear != 1 || gasPedal < 0.0f){
|
||||
gear--;
|
||||
time = 0.0f;
|
||||
return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false);
|
||||
}
|
||||
}
|
||||
|
||||
if(time > 0.0f){
|
||||
// changing gears currently, can't accelerate
|
||||
fAcceleration = 0.0f;
|
||||
time -= CTimer::GetTimeStepInSeconds();
|
||||
}else{
|
||||
float speedMul, accelMul;
|
||||
|
||||
if(gear < 1){
|
||||
// going reverse
|
||||
accelMul = (Flags & HANDLING_2G_BOOST) ? 2.0f : 1.0f;
|
||||
speedMul = -1.0f;
|
||||
}else if(nNumberOfGears == 1){
|
||||
accelMul = 1.0f;
|
||||
speedMul = 1.0f;
|
||||
}else{
|
||||
// BUG or not? this is 1.0 normally but 0.0 in the highest gear
|
||||
float f = 1.0f - (gear-1)/(nNumberOfGears-1);
|
||||
speedMul = 3.0f*sq(f) + 1.0f;
|
||||
// This is pretty ugly, could be written more clearly
|
||||
if(Flags & HANDLING_2G_BOOST){
|
||||
if(gear == 1)
|
||||
accelMul = (Flags & HANDLING_1G_BOOST) ? 3.0f : 2.0f;
|
||||
else if(gear == 2)
|
||||
accelMul = 1.3f;
|
||||
else
|
||||
accelMul = 1.0f;
|
||||
}else if(Flags & HANDLING_1G_BOOST && gear == 1){
|
||||
accelMul = 3.0f;
|
||||
}else
|
||||
accelMul = 1.0f;
|
||||
}
|
||||
|
||||
if(cheat)
|
||||
fCheat = 1.2f;
|
||||
else
|
||||
fCheat = 1.0f;
|
||||
float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat;
|
||||
float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity);
|
||||
if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat))
|
||||
fAcceleration = gasPedal * accel * CTimer::GetTimeStep();
|
||||
else
|
||||
fAcceleration = 0.0f;
|
||||
}
|
||||
return fAcceleration;
|
||||
}
|
||||
28
src/vehicles/Transmission.h
Normal file
28
src/vehicles/Transmission.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
struct tGear
|
||||
{
|
||||
float fMaxVelocity;
|
||||
float fShiftUpVelocity;
|
||||
float fShiftDownVelocity;
|
||||
};
|
||||
|
||||
class cTransmission
|
||||
{
|
||||
public:
|
||||
// Gear 0 is reverse, 1-5 are forward
|
||||
tGear Gears[6];
|
||||
char nDriveType;
|
||||
char nEngineType;
|
||||
int8 nNumberOfGears;
|
||||
uint8 Flags;
|
||||
float fEngineAcceleration;
|
||||
float fMaxVelocity;
|
||||
float fMaxCruiseVelocity;
|
||||
float fMaxReverseVelocity;
|
||||
float fCurVelocity;
|
||||
|
||||
void InitGearRatios(void);
|
||||
void CalculateGearForSimpleCar(float speed, uint8 &gear);
|
||||
float CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat);
|
||||
};
|
||||
1385
src/vehicles/Vehicle.cpp
Normal file
1385
src/vehicles/Vehicle.cpp
Normal file
File diff suppressed because it is too large
Load diff
293
src/vehicles/Vehicle.h
Normal file
293
src/vehicles/Vehicle.h
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#pragma once
|
||||
|
||||
#include "Physical.h"
|
||||
#include "AutoPilot.h"
|
||||
#include "ModelIndices.h"
|
||||
#include "AnimationId.h"
|
||||
#include "WeaponType.h"
|
||||
#include "Collision.h"
|
||||
|
||||
class CPed;
|
||||
class CFire;
|
||||
struct tHandlingData;
|
||||
|
||||
enum {
|
||||
RANDOM_VEHICLE = 1,
|
||||
MISSION_VEHICLE = 2,
|
||||
PARKED_VEHICLE = 3,
|
||||
PERMANENT_VEHICLE = 4,
|
||||
};
|
||||
|
||||
enum eCarLock {
|
||||
CARLOCK_NOT_USED,
|
||||
CARLOCK_UNLOCKED,
|
||||
CARLOCK_LOCKED,
|
||||
CARLOCK_LOCKOUT_PLAYER_ONLY,
|
||||
CARLOCK_LOCKED_PLAYER_INSIDE,
|
||||
CARLOCK_LOCKED_INITIALLY,
|
||||
CARLOCK_FORCE_SHUT_DOORS
|
||||
};
|
||||
|
||||
enum eDoors
|
||||
{
|
||||
DOOR_BONNET = 0,
|
||||
DOOR_BOOT,
|
||||
DOOR_FRONT_LEFT,
|
||||
DOOR_FRONT_RIGHT,
|
||||
DOOR_REAR_LEFT,
|
||||
DOOR_REAR_RIGHT
|
||||
};
|
||||
|
||||
enum ePanels
|
||||
{
|
||||
VEHPANEL_FRONT_LEFT,
|
||||
VEHPANEL_FRONT_RIGHT,
|
||||
VEHPANEL_REAR_LEFT,
|
||||
VEHPANEL_REAR_RIGHT,
|
||||
VEHPANEL_WINDSCREEN,
|
||||
VEHBUMPER_FRONT,
|
||||
VEHBUMPER_REAR,
|
||||
};
|
||||
|
||||
enum eLights
|
||||
{
|
||||
VEHLIGHT_FRONT_LEFT,
|
||||
VEHLIGHT_FRONT_RIGHT,
|
||||
VEHLIGHT_REAR_LEFT,
|
||||
VEHLIGHT_REAR_RIGHT,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CAR_PIECE_BONNET = 1,
|
||||
CAR_PIECE_BOOT,
|
||||
CAR_PIECE_BUMP_FRONT,
|
||||
CAR_PIECE_BUMP_REAR,
|
||||
CAR_PIECE_DOOR_LF,
|
||||
CAR_PIECE_DOOR_RF,
|
||||
CAR_PIECE_DOOR_LR,
|
||||
CAR_PIECE_DOOR_RR,
|
||||
CAR_PIECE_WING_LF,
|
||||
CAR_PIECE_WING_RF,
|
||||
CAR_PIECE_WING_LR,
|
||||
CAR_PIECE_WING_RR,
|
||||
CAR_PIECE_WHEEL_LF,
|
||||
CAR_PIECE_WHEEL_RF,
|
||||
CAR_PIECE_WHEEL_LR,
|
||||
CAR_PIECE_WHEEL_RR,
|
||||
CAR_PIECE_WINDSCREEN,
|
||||
};
|
||||
|
||||
enum tWheelState
|
||||
{
|
||||
WHEEL_STATE_NORMAL, // standing still or rolling normally
|
||||
WHEEL_STATE_SPINNING, // rotating but not moving
|
||||
WHEEL_STATE_SKIDDING,
|
||||
WHEEL_STATE_FIXED, // not rotating
|
||||
};
|
||||
|
||||
enum eFlightModel
|
||||
{
|
||||
FLIGHT_MODEL_DODO,
|
||||
// not used in III
|
||||
FLIGHT_MODEL_RCPLANE,
|
||||
FLIGHT_MODEL_HELI,
|
||||
FLIGHT_MODEL_SEAPLANE
|
||||
};
|
||||
|
||||
// TODO: what is this even?
|
||||
enum eBikeWheelSpecial {
|
||||
BIKE_WHEELSPEC_0, // both wheels on ground
|
||||
BIKE_WHEELSPEC_1, // rear wheel on ground
|
||||
BIKE_WHEELSPEC_2, // only front wheel on ground
|
||||
BIKE_WHEELSPEC_3, // can't happen
|
||||
};
|
||||
|
||||
|
||||
class CVehicle : public CPhysical
|
||||
{
|
||||
public:
|
||||
// 0x128
|
||||
tHandlingData *pHandling;
|
||||
CAutoPilot AutoPilot;
|
||||
uint8 m_currentColour1;
|
||||
uint8 m_currentColour2;
|
||||
int8 m_aExtras[2];
|
||||
int16 m_nAlarmState;
|
||||
int16 m_nMissionValue;
|
||||
CPed *pDriver;
|
||||
CPed *pPassengers[8];
|
||||
uint8 m_nNumPassengers;
|
||||
int8 m_nNumGettingIn;
|
||||
int8 m_nGettingInFlags;
|
||||
int8 m_nGettingOutFlags;
|
||||
uint8 m_nNumMaxPassengers;
|
||||
float field_1D0[4];
|
||||
CEntity *m_pCurGroundEntity;
|
||||
CFire *m_pCarFire;
|
||||
float m_fSteerAngle;
|
||||
float m_fGasPedal;
|
||||
float m_fBrakePedal;
|
||||
uint8 VehicleCreatedBy;
|
||||
|
||||
// cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R*
|
||||
uint8 bIsLawEnforcer: 1; // Is this guy chasing the player at the moment
|
||||
uint8 bIsAmbulanceOnDuty: 1; // Ambulance trying to get to an accident
|
||||
uint8 bIsFireTruckOnDuty: 1; // Firetruck trying to get to a fire
|
||||
uint8 bIsLocked: 1; // Is this guy locked by the script (cannot be removed)
|
||||
uint8 bEngineOn: 1; // For sound purposes. Parked cars have their engines switched off (so do destroyed cars)
|
||||
uint8 bIsHandbrakeOn: 1; // How's the handbrake doing ?
|
||||
uint8 bLightsOn: 1; // Are the lights switched on ?
|
||||
uint8 bFreebies: 1; // Any freebies left in this vehicle ?
|
||||
|
||||
uint8 bIsVan: 1; // Is this vehicle a van (doors at back of vehicle)
|
||||
uint8 bIsBus: 1; // Is this vehicle a bus
|
||||
uint8 bIsBig: 1; // Is this vehicle a bus
|
||||
uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims
|
||||
uint8 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way)
|
||||
uint8 bWarnedPeds : 1; // Has scan and warn peds of danger been processed?
|
||||
uint8 bCraneMessageDone : 1; // A crane message has been printed for this car allready
|
||||
uint8 bExtendedRange : 1; // This vehicle needs to be a bit further away to get deleted
|
||||
|
||||
uint8 bTakeLessDamage : 1; // This vehicle is stronger (takes about 1/4 of damage)
|
||||
uint8 bIsDamaged : 1; // This vehicle has been damaged and is displaying all its components
|
||||
uint8 bHasBeenOwnedByPlayer : 1;// To work out whether stealing it is a crime
|
||||
uint8 bFadeOut : 1; // Fade vehicle out
|
||||
uint8 bIsBeingCarJacked : 1; // Fade vehicle out
|
||||
uint8 bCreateRoadBlockPeds : 1; // If this vehicle gets close enough we will create peds (coppers or gang members) round it
|
||||
uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions
|
||||
uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure
|
||||
|
||||
uint8 bOccupantsHaveBeenGenerated : 1; // Is true if the occupants have already been generated. (Shouldn't happen again)
|
||||
uint8 bGunSwitchedOff : 1; // Level designers can use this to switch off guns on boats
|
||||
uint8 bVehicleColProcessed : 1;// Has ProcessEntityCollision been processed for this car?
|
||||
uint8 bIsCarParkVehicle : 1; // Car has been created using the special CAR_PARK script command
|
||||
uint8 bHasAlreadyBeenRecorded : 1; // Used for replays
|
||||
|
||||
int8 m_numPedsUseItAsCover;
|
||||
uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default)
|
||||
int8 m_nPacManPickupsCarried;
|
||||
uint8 m_nRoadblockType;
|
||||
int16 m_nRoadblockNode;
|
||||
float m_fHealth; // 1000.0f = full health. 250.0f = fire. 0 -> explode
|
||||
uint8 m_nCurrentGear;
|
||||
float m_fChangeGearTime;
|
||||
uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats)
|
||||
uint32 m_nTimeOfDeath;
|
||||
uint16 m_nTimeBlocked;
|
||||
int16 m_nBombTimer; // goes down with each frame
|
||||
CEntity *m_pBlowUpEntity;
|
||||
float m_fMapObjectHeightAhead; // front Z?
|
||||
float m_fMapObjectHeightBehind; // rear Z?
|
||||
eCarLock m_nDoorLock;
|
||||
int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage
|
||||
uint8 m_nRadioStation;
|
||||
uint8 m_bRainAudioCounter;
|
||||
uint8 m_bRainSamplesCounter;
|
||||
uint8 m_nCarHornTimer;
|
||||
uint8 m_nCarHornPattern; // last horn?
|
||||
bool m_bSirenOrAlarm;
|
||||
int8 m_comedyControlState;
|
||||
CStoredCollPoly m_aCollPolys[2]; // poly which is under front/rear part of car
|
||||
float m_fSteerInput;
|
||||
eVehicleType m_vehType;
|
||||
|
||||
static void *operator new(size_t) throw();
|
||||
static void *operator new(size_t sz, int slot) throw();
|
||||
static void operator delete(void*, size_t) throw();
|
||||
static void operator delete(void*, int) throw();
|
||||
|
||||
CVehicle(void) {} // FAKE
|
||||
CVehicle(uint8 CreatedBy);
|
||||
~CVehicle(void);
|
||||
// from CEntity
|
||||
void SetModelIndex(uint32 id);
|
||||
bool SetupLighting(void);
|
||||
void RemoveLighting(bool);
|
||||
void FlagToDestroyWhenNextProcessed(void) {}
|
||||
|
||||
virtual void ProcessControlInputs(uint8) {}
|
||||
virtual void GetComponentWorldPosition(int32 component, CVector &pos) {}
|
||||
virtual bool IsComponentPresent(int32 component) { return false; }
|
||||
virtual void SetComponentRotation(int32 component, CVector rotation) {}
|
||||
virtual void OpenDoor(int32, eDoors door, float) {}
|
||||
virtual void ProcessOpenDoor(uint32, uint32, float) {}
|
||||
virtual bool IsDoorReady(eDoors door) { return false; }
|
||||
virtual bool IsDoorFullyOpen(eDoors door) { return false; }
|
||||
virtual bool IsDoorClosed(eDoors door) { return false; }
|
||||
virtual bool IsDoorMissing(eDoors door) { return false; }
|
||||
virtual void RemoveRefsToVehicle(CEntity *ent) {}
|
||||
virtual void BlowUpCar(CEntity *ent) {}
|
||||
virtual bool SetUpWheelColModel(CColModel *colModel) { return false; }
|
||||
virtual void BurstTyre(uint8 tyre) {}
|
||||
virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false;}
|
||||
virtual float GetHeightAboveRoad(void);
|
||||
virtual void PlayCarHorn(void) {}
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
virtual void Save(uint8*& buf);
|
||||
virtual void Load(uint8*& buf);
|
||||
#endif
|
||||
|
||||
bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; }
|
||||
bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; }
|
||||
bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; }
|
||||
bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; }
|
||||
bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; }
|
||||
bool IsBike(void) { return m_vehType == VEHICLE_TYPE_BIKE; }
|
||||
|
||||
void FlyingControl(eFlightModel flightModel);
|
||||
void ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint,
|
||||
int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus);
|
||||
void ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust,
|
||||
float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus);
|
||||
void ExtinguishCarFire(void);
|
||||
void ProcessDelayedExplosion(void);
|
||||
float ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius);
|
||||
bool IsLawEnforcementVehicle(void);
|
||||
void ChangeLawEnforcerState(uint8 enable);
|
||||
bool UsesSiren(uint32 id);
|
||||
bool IsVehicleNormal(void);
|
||||
bool CarHasRoof(void);
|
||||
bool IsUpsideDown(void);
|
||||
bool IsOnItsSide(void);
|
||||
bool CanBeDeleted(void);
|
||||
bool CanPedOpenLocks(CPed *ped);
|
||||
bool CanPedEnterCar(void);
|
||||
bool CanPedExitCar(void);
|
||||
// do these two actually return something?
|
||||
CPed *SetUpDriver(void);
|
||||
CPed *SetupPassenger(int n);
|
||||
void SetDriver(CPed *driver);
|
||||
bool AddPassenger(CPed *passenger);
|
||||
bool AddPassenger(CPed *passenger, uint8 n);
|
||||
void RemovePassenger(CPed *passenger);
|
||||
void RemoveDriver(void);
|
||||
void ProcessCarAlarm(void);
|
||||
bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius);
|
||||
bool ShufflePassengersToMakeSpace(void);
|
||||
void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage);
|
||||
void DoFixedMachineGuns(void);
|
||||
|
||||
#ifdef FIX_BUGS
|
||||
bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1 && GetStatus() != STATUS_WRECKED; }
|
||||
#else
|
||||
bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; }
|
||||
#endif
|
||||
CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); }
|
||||
bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE; }
|
||||
AnimationId GetDriverAnim(void) { return IsCar() && bLowVehicle ? ANIM_STD_CAR_SIT_LO : (IsBoat() && GetModelIndex() != MI_SPEEDER ? ANIM_STD_BOAT_DRIVE : ANIM_STD_CAR_SIT); }
|
||||
|
||||
static bool bWheelsOnlyCheat;
|
||||
static bool bAllDodosCheat;
|
||||
static bool bCheat3;
|
||||
static bool bCheat4;
|
||||
static bool bCheat5;
|
||||
#ifdef ALT_DODO_CHEAT
|
||||
static bool bAltDodoCheat;
|
||||
#endif
|
||||
static bool m_bDisableMouseSteering;
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CVehicle, 0x288);
|
||||
|
||||
void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle);
|
||||
Loading…
Add table
Add a link
Reference in a new issue