summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraap <aap@papnet.eu>2019-08-04 00:31:00 +0200
committeraap <aap@papnet.eu>2019-08-04 00:31:00 +0200
commita3e3527a3b8f260db285c76dc1044baab8a2f773 (patch)
tree17869d4d50729e13497f7769679bdec0fef0a069
parentMerge pull request #181 from Nick007J/master (diff)
downloadre3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar.gz
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar.bz2
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar.lz
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar.xz
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.tar.zst
re3-a3e3527a3b8f260db285c76dc1044baab8a2f773.zip
-rw-r--r--src/core/Stats.cpp4
-rw-r--r--src/core/Stats.h2
-rw-r--r--src/core/Wanted.h2
-rw-r--r--src/core/World.cpp2
-rw-r--r--src/core/World.h4
-rw-r--r--src/core/re3.cpp34
-rw-r--r--src/math/Vector2D.h23
-rw-r--r--src/modelinfo/VehicleModelInfo.cpp15
-rw-r--r--src/vehicles/Heli.cpp1042
-rw-r--r--src/vehicles/Heli.h87
-rw-r--r--src/vehicles/Plane.cpp11
-rw-r--r--src/vehicles/Train.cpp2
-rw-r--r--src/vehicles/Train.h5
-rw-r--r--src/vehicles/Vehicle.cpp3
-rw-r--r--src/vehicles/Vehicle.h3
15 files changed, 1187 insertions, 52 deletions
diff --git a/src/core/Stats.cpp b/src/core/Stats.cpp
index 01bbf82e..9d0e7df1 100644
--- a/src/core/Stats.cpp
+++ b/src/core/Stats.cpp
@@ -7,8 +7,10 @@ bool& CStats::CommercialPassed = *(bool*)0x8F4334;
bool& CStats::IndustrialPassed = *(bool*)0x8E2A68;
int32 &CStats::NumberKillFrenziesPassed = *(int32*)0x8E287C;
int32 &CStats::PeopleKilledByOthers = *(int32*)0x8E2C50;
+int32 &CStats::HelisDestroyed = *(int32*)0x8E2A64;
+int32 *CStats::PedsKilledOfThisType = (int32*)0x880DBC;
void CStats::AnotherKillFrenzyPassed()
{
++NumberKillFrenziesPassed;
-} \ No newline at end of file
+}
diff --git a/src/core/Stats.h b/src/core/Stats.h
index c536465f..90db25e8 100644
--- a/src/core/Stats.h
+++ b/src/core/Stats.h
@@ -9,6 +9,8 @@ public:
static bool& IndustrialPassed;
static int32 &NumberKillFrenziesPassed;
static int32 &PeopleKilledByOthers;
+ static int32 &HelisDestroyed;
+ static int32 *PedsKilledOfThisType; //[NUM_PEDTYPES]
public:
static void AnotherKillFrenzyPassed();
diff --git a/src/core/Wanted.h b/src/core/Wanted.h
index 7cd89b7e..34a4b58d 100644
--- a/src/core/Wanted.h
+++ b/src/core/Wanted.h
@@ -77,6 +77,8 @@ public:
void ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare);
void UpdateWantedLevel();
+ bool IsIgnored(void) { return m_bIgnoredByCops || m_bIgnoredByEveryone; }
+
static int32 WorkOutPolicePresence(CVector posn, float radius);
static void SetMaximumWantedLevel(int32 level);
};
diff --git a/src/core/World.cpp b/src/core/World.cpp
index 0440a951..c6eb831c 100644
--- a/src/core/World.cpp
+++ b/src/core/World.cpp
@@ -646,7 +646,7 @@ CWorld::FindObjectsInRange(CVector &centre, float distance, bool ignoreZ, short
}
CEntity*
-CWorld::TestSphereAgainstWorld(CVector centre, float distance, CEntity* entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects)
+CWorld::TestSphereAgainstWorld(CVector centre, float distance, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects)
{
CEntity* foundE = nil;
diff --git a/src/core/World.h b/src/core/World.h
index e4f46589..6c52da5a 100644
--- a/src/core/World.h
+++ b/src/core/World.h
@@ -95,8 +95,8 @@ public:
static bool GetIsLineOfSightSectorClear(CSector &sector, const CColLine &line, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false);
static bool GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, bool ignoreSomeObjects = false);
- static CEntity* TestSphereAgainstWorld(CVector, float, CEntity*, bool, bool, bool, bool, bool, bool);
- static CEntity* TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool);
+ static CEntity *TestSphereAgainstWorld(CVector centre, float distance, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects);
+ static CEntity *TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool);
static void FindObjectsInRangeSectorList(CPtrList&, CVector&, float, bool, short*, short, CEntity**);
static void FindObjectsInRange(CVector&, float, bool, short*, short, CEntity**, bool, bool, bool, bool, bool);
static float FindGroundZForCoord(float x, float y);
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 35b3cfa4..9681160f 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -14,6 +14,7 @@
#include "Streaming.h"
#include "PathFind.h"
#include "Boat.h"
+#include "Heli.h"
#include "Automobile.h"
#include "debugmenu_public.h"
@@ -318,6 +319,12 @@ DebugMenuPopulate(void)
DebugMenuAddCmd("Debug", "Toggle Comedy Controls", ToggleComedy);
DebugMenuAddCmd("Debug", "Place Car on Road", PlaceOnRoad);
+ DebugMenuAddVarBool8("Debug", "Catalina Heli On", (int8*)&CHeli::CatalinaHeliOn, nil);
+ DebugMenuAddCmd("Debug", "Catalina Fly By", CHeli::StartCatalinaFlyBy);
+ DebugMenuAddCmd("Debug", "Catalina Take Off", CHeli::CatalinaTakeOff);
+ DebugMenuAddCmd("Debug", "Catalina Fly Away", CHeli::MakeCatalinaHeliFlyAway);
+ DebugMenuAddVarBool8("Debug", "Script Heli On", (int8*)0x95CD43, nil);
+
DebugMenuAddVarBool8("Debug", "Show Ped Road Groups", (int8*)&gbShowPedRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Car Road Groups", (int8*)&gbShowCarRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Collision Lines", (int8*)&gbShowCollisionLines, nil);
@@ -347,29 +354,6 @@ delayedPatches10(int a, int b)
}
*/
-void __declspec(naked) HeadlightsFix()
-{
- static const float fMinusOne = -1.0f;
- _asm
- {
- fld [esp+708h-690h]
- fcomp fMinusOne
- fnstsw ax
- and ah, 5
- cmp ah, 1
- jnz HeadlightsFix_DontLimit
- fld fMinusOne
- fstp [esp+708h-690h]
-
-HeadlightsFix_DontLimit:
- fld [esp+708h-690h]
- fabs
- fld st
- push 0x5382F2
- retn
- }
-}
-
const int re3_buffsize = 1024;
static char re3_buff[re3_buffsize];
@@ -454,10 +438,6 @@ patch()
InjectHook(0x475E00, printf, PATCH_JUMP); // _Error
- // stolen from silentpatch (sorry)
- Patch<WORD>(0x5382BF, 0x0EEB);
- InjectHook(0x5382EC, HeadlightsFix, PATCH_JUMP);
-
// InterceptCall(&open_script_orig, open_script, 0x438869);
// InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E);
diff --git a/src/math/Vector2D.h b/src/math/Vector2D.h
index c8835ec0..76664522 100644
--- a/src/math/Vector2D.h
+++ b/src/math/Vector2D.h
@@ -20,6 +20,29 @@ public:
}else
x = 0.0f;
}
+ const CVector2D &operator+=(CVector2D const &right) {
+ x += right.x;
+ y += right.y;
+ return *this;
+ }
+
+ const CVector2D &operator-=(CVector2D const &right) {
+ x -= right.x;
+ y -= right.y;
+ return *this;
+ }
+
+ const CVector2D &operator*=(float right) {
+ x *= right;
+ y *= right;
+ return *this;
+ }
+
+ const CVector2D &operator/=(float right) {
+ x /= right;
+ y /= right;
+ return *this;
+ }
CVector2D operator-(const CVector2D &rhs) const {
return CVector2D(x-rhs.x, y-rhs.y);
}
diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/modelinfo/VehicleModelInfo.cpp
index c031b76e..19100d84 100644
--- a/src/modelinfo/VehicleModelInfo.cpp
+++ b/src/modelinfo/VehicleModelInfo.cpp
@@ -14,6 +14,7 @@
#include "Automobile.h"
#include "Train.h"
#include "Plane.h"
+#include "Heli.h"
#include "ModelIndices.h"
#include "ModelInfo.h"
@@ -98,13 +99,13 @@ RwObjectNameIdAssocation trainIds[] = {
};
RwObjectNameIdAssocation heliIds[] = {
- { "chassis_dummy", 1, VEHICLE_FLAG_COLLAPSE },
- { "toprotor", 2, 0 },
- { "backrotor", 3, 0 },
- { "tail", 4, 0 },
- { "topknot", 5, 0 },
- { "skid_left", 6, 0 },
- { "skid_right", 7, 0 },
+ { "chassis_dummy", HELI_CHASSIS, VEHICLE_FLAG_COLLAPSE },
+ { "toprotor", HELI_TOPROTOR, 0 },
+ { "backrotor", HELI_BACKROTOR, 0 },
+ { "tail", HELI_TAIL, 0 },
+ { "topknot", HELI_TOPKNOT, 0 },
+ { "skid_left", HELI_SKID_LEFT, 0 },
+ { "skid_right", HELI_SKID_RIGHT, 0 },
{ nil, 0, 0 }
};
diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp
index d43e8c19..9b1a651d 100644
--- a/src/vehicles/Heli.cpp
+++ b/src/vehicles/Heli.cpp
@@ -1,21 +1,1055 @@
#include "common.h"
+#include "main.h"
#include "patcher.h"
+#include "General.h"
+#include "Darkel.h"
+#include "Stats.h"
+#include "SurfaceTable.h"
+#include "ModelIndices.h"
+#include "Streaming.h"
+#include "Camera.h"
+#include "VisibilityPlugins.h"
+#include "ZoneCull.h"
+#include "Particle.h"
+#include "Shadows.h"
+#include "Coronas.h"
+#include "Explosion.h"
+#include "TimeCycle.h"
+#include "TempColModels.h"
+#include "World.h"
+#include "WaterLevel.h"
+#include "PlayerPed.h"
+#include "Object.h"
+#include "HandlingMgr.h"
#include "Heli.h"
-CHeli::CHeli(int mi, uint8 owner)
+enum
{
- ctor(mi, owner);
+ HELI_STATUS_HOVER,
+ HELI_STATUS_CHASE_PLAYER,
+ HELI_STATUS_FLY_AWAY,
+ HELI_STATUS_SHOT_DOWN,
+ HELI_STATUS_HOVER2,
+};
+
+CHeli **CHeli::pHelis = (CHeli**)0x72CF50;
+int16 &CHeli::NumRandomHelis = *(int16*)0x95CCAA;
+uint32 &CHeli::TestForNewRandomHelisTimer = *(uint32*)0x8F1A7C;
+int16 CHeli::NumScriptHelis; // unused
+bool &CHeli::CatalinaHeliOn = *(bool*)0x95CD85;
+bool &CHeli::CatalinaHasBeenShotDown = *(bool*)0x95CD56;
+bool &CHeli::ScriptHeliOn = *(bool*)0x95CD43;
+
+CHeli::CHeli(int32 id, uint8 CreatedBy)
+ : CVehicle(CreatedBy)
+{
+ int i;
+
+ CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
+ m_vehType = VEHICLE_TYPE_HELI;
+ pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId);
+ SetModelIndex(id);
+ m_heliStatus = HELI_STATUS_HOVER;
+ m_pathState = 0;
+
+ m_fMass = 100000000.0f;
+ m_fTurnMass = 100000000.0f;
+ m_fAirResistance = 0.9994f;
+ m_fElasticity = 0.05f;
+
+ m_nHeliId = 0;
+ m_fRotorRotation = 0.0f;
+ m_nBulletDamage = 0;
+ m_fAngularSpeed = 0.0f;
+ m_fRotation = 0.0f;
+ m_nSearchLightTimer = CTimer::GetTimeInMilliseconds();
+ for(i = 0; i < 6; i++){
+ m_aSearchLightHistoryX[i] = 0.0f;
+ m_aSearchLightHistoryY[i] = 0.0f;
+ }
+
+ for(i = 0; i < 8; i++)
+ m_fHeliDustZ[i] = -50.0f;
+
+ m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds();
+ m_status = STATUS_HELI;
+ m_bTestRight = true;
+ m_fTargetOffset = 0.0f;
+ m_fSearchLightX = m_fSearchLightY = 0.0f;
+}
+
+void
+CHeli::SetModelIndex(uint32 id)
+{
+ int i;
+
+ CVehicle::SetModelIndex(id);
+ for(i = 0; i < NUM_HELI_NODES; i++)
+ m_aHeliNodes[i] = nil;
+ CClumpModelInfo::FillFrameArray(GetClump(), m_aHeliNodes);
+}
+
+static float CatalinaTargetX[7] = { -478.0, -677.0, -907.0, -1095.0, -1152.0, -1161.0, -1161.0 };
+static float CatalinaTargetY[7] = { 227.0, 206.0, 210.0, 242.0, 278.0, 341.0, 341.0 };
+static float CatalinaTargetZ[7] = { 77.0, 66.0, 60.0, 53.0, 51.0, 46.0, 30.0 };
+static float DamPathX[6] = { -1191.0, -1176.0, -1128.0, -1072.0, -1007.0, -971.0 };
+static float DamPathY[6] = { 350.0, 388.0, 429.0, 447.0, 449.0, 416.0 };
+static float DamPathZ[6] = { 42.0, 37.0, 28.0, 28.0, 31.0, 33.0 };
+static float ShortPathX[4] = { -974.0, -1036.0, -1112.0, -1173.0 };
+static float ShortPathY[4] = { 340.0, 312.0, 317.0, 294.0 };
+static float ShortPathZ[4] = { 41.0, 38.0, 32.0, 39.0 };
+static float LongPathX[7] = { -934.0, -905.0, -906.0, -1063.0, -1204.0, -1233.0, -1207.0 };
+static float LongPathY[7] = { 371.0, 362.0, 488.0, 548.0, 451.0, 346.0, 308.0 };
+static float LongPathZ[7] = { 57.0, 90.0, 105.0, 100.0, 81.0, 79.0, 70.0 };
+
+static int PathPoint;
+
+void
+CHeli::ProcessControl(void)
+{
+ int i;
+
+ if(gbModelViewer)
+ return;
+
+ // Find target
+ CVector target(0.0f, 0.0f, 0.0f);
+ CVector2D vTargetDist;
+ if(m_heliType == HELI_TYPE_CATALINA && m_heliStatus != HELI_STATUS_SHOT_DOWN){
+ switch(m_pathState){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ target.x = CatalinaTargetX[m_pathState];
+ target.y = CatalinaTargetY[m_pathState];
+ target.z = CatalinaTargetZ[m_pathState];
+ if((target - GetPosition()).Magnitude() < 9.0f)
+ m_pathState++;
+ break;
+ case 6:
+ target.x = CatalinaTargetX[m_pathState];
+ target.y = CatalinaTargetY[m_pathState];
+ target.z = CatalinaTargetZ[m_pathState];
+ if(GetPosition().z > 31.55f)
+ break;
+ m_pathState = 7;
+ GetPosition().z = 31.55f;
+ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+ break;
+ case 7:
+ GetPosition().z = 31.55f;
+ target = GetPosition();
+ break;
+
+
+ // Take off
+ case 8:
+ target.x = GetPosition().x;
+ target.y = GetPosition().y;
+ target.z = 74.0f;
+ if(GetPosition().z < 40.0f)
+ break;
+ PathPoint = 2;
+ m_pathState = 9;
+ break;
+ // Circle around dam
+ case 9:
+ target.x = DamPathX[PathPoint];
+ target.y = DamPathY[PathPoint];
+ target.z = DamPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 6){
+ m_pathState = 10;
+ PathPoint = 0;
+ }
+ }
+ break;
+ case 10:
+ target.x = ShortPathX[PathPoint];
+ target.y = ShortPathY[PathPoint];
+ target.z = ShortPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 3){
+ m_pathState = 9;
+ PathPoint = 1;
+ }
+ }
+ break;
+ // how do we get here?
+ case 11:
+ target.x = LongPathX[PathPoint];
+ target.y = LongPathY[PathPoint];
+ target.z = LongPathZ[PathPoint];
+ if((target - GetPosition()).Magnitude() < 9.0f){
+ PathPoint++;
+ if(PathPoint >= 7){
+ m_pathState = 9;
+ PathPoint = 0;
+ }
+ }
+ break;
+
+
+ // Fly away
+ case 12:
+ target.x = GetPosition().x;
+ target.y = GetPosition().y;
+ target.z = 200.0f;
+ break;
+ }
+
+ vTargetDist = target - GetPosition();
+ m_fTargetZ = target.z;
+ if(m_pathState == 6){
+ GetPosition().x = GetPosition().x*0.99f + target.x*0.01f;
+ GetPosition().y = GetPosition().y*0.99f + target.y*0.01f;
+ }
+ }else{
+ vTargetDist = FindPlayerCoors() - GetPosition();
+ m_fTargetZ = FindPlayerCoors().z;
+
+ // Heli flies away to (0, 0)
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY && GetPosition().z > 20.0f){
+ vTargetDist.x = 0.0f - GetPosition().x;
+ vTargetDist.y = 0.0f - GetPosition().y;
+ }
+
+ float groundZ;
+ switch(m_heliStatus){
+ case HELI_STATUS_HOVER:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f;
+ break;
+ case HELI_STATUS_SHOT_DOWN:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset;
+ break;
+ case HELI_STATUS_HOVER2:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset;
+ break;
+ default:
+ groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil);
+ m_fTargetZ = max(groundZ, m_fTargetZ) + 12.0f;
+ break;
+ }
+
+ // Move up if too low
+ if(GetPosition().z - 2.0f < groundZ && m_heliStatus != HELI_STATUS_SHOT_DOWN)
+ m_vecMoveSpeed.z += CTimer::GetTimeStep()*0.01f;
+ m_vecMoveSpeed.z = clamp(m_vecMoveSpeed.z, -0.3f, 0.3f);
+ }
+
+ float fTargetDist = vTargetDist.Magnitude();
+
+ switch(m_heliStatus){
+ case HELI_STATUS_HOVER:
+ case HELI_STATUS_HOVER2:{
+ float targetHeight;
+ if(m_heliType == HELI_TYPE_CATALINA)
+ targetHeight = 8.0f;
+ else
+ targetHeight = 40.0f - m_nHeliId*10.0f;
+ if(fTargetDist > targetHeight)
+ m_heliStatus = HELI_STATUS_CHASE_PLAYER;
+ }
+ // fall through, BUG?
+ case HELI_STATUS_CHASE_PLAYER:{
+ float targetHeight;
+ if(m_heliType == HELI_TYPE_CATALINA)
+ targetHeight = 4.0f;
+ else
+ targetHeight = 30.0f - m_nHeliId*7.5f;
+ if(fTargetDist < 1.0f ||
+ fTargetDist < targetHeight && CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false))
+ m_heliStatus = HELI_STATUS_HOVER;
+ }
+ }
+
+ // Find xy speed
+ float speed;
+ if(fTargetDist > 100.0f)
+ speed = 1.0f;
+ else if(fTargetDist > 75.0f)
+ speed = 0.7f;
+ else
+ speed = 0.4f;
+ if(m_heliStatus == HELI_STATUS_HOVER || m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN)
+ speed = 0.0f;
+
+ if(fTargetDist != 0.0f)
+ vTargetDist /= fTargetDist;
+ else
+ vTargetDist.x = 1.0f;
+ CVector2D targetSpeed = vTargetDist * speed;
+
+ if(m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ bool force = !!((CTimer::GetFrameCounter() + m_randomSeed) & 8);
+ if(m_bTestRight){
+ if(force || CWorld::TestSphereAgainstWorld(GetPosition() + 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ m_fTargetOffset -= CTimer::GetTimeStep()*0.05f;
+ targetSpeed.x -= -vTargetDist.x*0.15f;
+ targetSpeed.y -= vTargetDist.y*0.15f;
+ }else{
+ targetSpeed.x -= -vTargetDist.x*0.05f;
+ targetSpeed.y -= vTargetDist.y*0.05f;
+ }
+ }else{
+ m_bTestRight = false;
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_fTargetOffset += 5.0f;
+ else
+ m_fTargetOffset -= 5.0f;
+ }
+ }else{
+ if(force || CWorld::TestSphereAgainstWorld(GetPosition() - 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN){
+ m_fTargetOffset -= CTimer::GetTimeStep()*0.05f;
+ targetSpeed.x += -vTargetDist.x*0.15f;
+ targetSpeed.y += vTargetDist.y*0.15f;
+ }else{
+ targetSpeed.x += -vTargetDist.x*0.05f;
+ targetSpeed.y += vTargetDist.y*0.05f;
+ }
+ }else{
+ m_bTestRight = true;
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_fTargetOffset += 5.0f;
+ else
+ m_fTargetOffset -= 5.0f;
+ }
+ }
+
+ if(m_fTargetOffset > 30.0f)
+ m_fTargetOffset = 30.0f;
+
+ if(m_heliStatus == HELI_STATUS_SHOT_DOWN && force){
+ if(CWorld::TestSphereAgainstWorld(GetPosition() + 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false) ||
+ CWorld::TestSphereAgainstWorld(GetPosition() - 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false))
+ m_nExplosionTimer = CTimer::GetPreviousTimeInMilliseconds();
+ }
+ }else
+ if(m_fTargetOffset >= 2.0f)
+ m_fTargetOffset -= 2.0f;
+
+ if(m_heliType == HELI_TYPE_CATALINA)
+ if(m_pathState == 9 || m_pathState == 11 || m_pathState == 10){
+ float f = Pow(0.997f, CTimer::GetTimeStep());
+ m_vecMoveSpeed.x *= f;
+ m_vecMoveSpeed.y *= f;
+ }
+
+ CVector2D speedDir = targetSpeed - m_vecMoveSpeed;
+ float speedDiff = speedDir.Magnitude();
+ if(speedDiff != 0.0f)
+ speedDir /= speedDiff;
+ else
+ speedDir.x = 1.0f;
+ float speedInc = CTimer::GetTimeStep()*0.002f;
+ if(speedDiff < speedInc){
+ m_vecMoveSpeed.x = targetSpeed.x;
+ m_vecMoveSpeed.y = targetSpeed.y;
+ }else{
+ m_vecMoveSpeed.x += speedDir.x*speedInc;
+ m_vecMoveSpeed.y += speedDir.y*speedInc;
+ }
+ GetPosition().x += m_vecMoveSpeed.x*CTimer::GetTimeStep();
+ GetPosition().y += m_vecMoveSpeed.y*CTimer::GetTimeStep();
+
+ // Find z target
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY)
+ m_fTargetZ = 1000.0f;
+ if((CTimer::GetTimeInMilliseconds() + 800*m_nHeliId) & 0x800)
+ m_fTargetZ += 2.0f;
+ m_fTargetZ += m_nHeliId*5.0f;
+
+ // Find z speed
+ float targetSpeedZ = (m_fTargetZ - GetPosition().z)*0.01f;
+ float speedDiffZ = targetSpeedZ - m_vecMoveSpeed.z;
+ float speedIncZ = CTimer::GetTimeStep()*0.001f;
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY)
+ speedIncZ *= 1.5f;
+ if(Abs(speedDiffZ) < speedIncZ)
+ m_vecMoveSpeed.z = targetSpeedZ;
+ else if(speedDiffZ < 0.0f)
+ m_vecMoveSpeed.z -= speedIncZ;
+ else
+ m_vecMoveSpeed.z += speedIncZ*1.5f;
+ GetPosition().z += m_vecMoveSpeed.z*CTimer::GetTimeStep();
+
+ // Find angular speed
+ float targetAngularSpeed;
+ m_fAngularSpeed *= Pow(0.995f, CTimer::GetTimeStep());
+ if(fTargetDist < 8.0f)
+ targetAngularSpeed = 0.0f;
+ else{
+ float rotationDiff = CGeneral::GetATanOfXY(vTargetDist.x, vTargetDist.y) - m_fRotation;
+ while(rotationDiff < -3.14f) rotationDiff += 6.28f;
+ while(rotationDiff > 3.14f) rotationDiff -= 6.28f;
+ if(Abs(rotationDiff) > 0.4f){
+ if(rotationDiff < 0.0f)
+ targetAngularSpeed = -0.2f;
+ else
+ targetAngularSpeed = 0.2f;
+ }else
+ targetAngularSpeed = 0.0f;
+ }
+ float angularSpeedDiff = targetAngularSpeed - m_fAngularSpeed;
+ float angularSpeedInc = CTimer::GetTimeStep()*0.0001f;
+ if(Abs(angularSpeedDiff) < angularSpeedInc)
+ m_fAngularSpeed = targetAngularSpeed;
+ else if(angularSpeedDiff < 0.0f)
+ m_fAngularSpeed -= angularSpeedInc;
+ else
+ m_fAngularSpeed += angularSpeedInc;
+ m_fRotation += m_fAngularSpeed * CTimer::GetTimeStep();
+
+ // Set matrix
+ CVector up(3.0f*m_vecMoveSpeed.x, 3.0f*m_vecMoveSpeed.y, 1.0f);
+ up.Normalise();
+ CVector fwd(-Cos(m_fRotation), -Sin(m_fRotation), 0.0f); // not really forward
+ CVector right = CrossProduct(up, fwd);
+ fwd = CrossProduct(up, right);
+ GetRight() = right;
+ GetForward() = fwd;
+ GetUp() = up;
+
+ // Search light and shooting
+ if(m_heliStatus == HELI_STATUS_FLY_AWAY || m_heliType == HELI_TYPE_CATALINA || CCullZones::PlayerNoRain())
+ m_fSearchLightIntensity = 0.0f;
+ else{
+ // Update search light history once every 1000ms
+ int timeDiff = CTimer::GetTimeInMilliseconds() - m_nSearchLightTimer;
+ while(timeDiff > 1000){
+ for(i = 5; i > 0; i--){
+ m_aSearchLightHistoryX[i] = m_aSearchLightHistoryX[i-1];
+ m_aSearchLightHistoryY[i] = m_aSearchLightHistoryY[i-1];
+ }
+ m_aSearchLightHistoryX[0] = FindPlayerCoors().x + FindPlayerSpeed().x*50.0f*(m_nHeliId+2);
+ m_aSearchLightHistoryY[0] = FindPlayerCoors().y + FindPlayerSpeed().y*50.0f*(m_nHeliId+2);
+
+ timeDiff -= 1000;
+ m_nSearchLightTimer += 1000;
+ }
+ assert(timeDiff <= 1000);
+ float f1 = timeDiff/1000.0f;
+ float f2 = 1.0f - f1;
+ m_fSearchLightX = m_aSearchLightHistoryX[m_nHeliId+2]*f2 + m_aSearchLightHistoryX[m_nHeliId+2-1]*f1;
+ m_fSearchLightY = m_aSearchLightHistoryY[m_nHeliId+2]*f2 + m_aSearchLightHistoryY[m_nHeliId+2-1]*f1;
+
+ float searchLightDist = (CVector2D(m_fSearchLightX, m_fSearchLightY) - GetPosition()).Magnitude();
+ if(searchLightDist > 60.0f)
+ m_fSearchLightIntensity = 0.0f;
+ else if(searchLightDist < 40.0f)
+ m_fSearchLightIntensity = 1.0f;
+ else
+ m_fSearchLightIntensity = 1.0f - (40.0f-searchLightDist)/40.0f;
+
+ if(m_fSearchLightIntensity < 0.9f || sq(FindPlayerCoors().x-m_fSearchLightX) + sq(FindPlayerCoors().y-m_fSearchLightY) > sq(7.0f))
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ else if(CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_HELI_PLAYER_FOUND, 0.0f);
+ m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF);
+ }
+
+ // Shoot
+ int shootTimeout;
+ if(m_heliType == HELI_TYPE_RANDOM){
+ switch(FindPlayerPed()->m_pWanted->m_nWantedLevel){
+ case 0:
+ case 1:
+ case 2: shootTimeout = 999999; break;
+ case 3: shootTimeout = 10000; break;
+ case 4: shootTimeout = 5000; break;
+ case 5: shootTimeout = 3500; break;
+ case 6: shootTimeout = 2000; break;
+ }
+ if(CCullZones::NoPolice())
+ shootTimeout /= 2;
+ }else
+ shootTimeout = 1500;
+
+ if(FindPlayerPed()->m_pWanted->IsIgnored())
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ else{
+ // Check if line of sight is clear
+ if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout &&
+ CTimer::GetPreviousTimeInMilliseconds() <= m_nShootTimer + shootTimeout){
+ if(CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)){
+ if(m_heliStatus == HELI_STATUS_HOVER2)
+ m_heliStatus = HELI_STATUS_HOVER;
+ }else{
+ m_nShootTimer = CTimer::GetTimeInMilliseconds();
+ if(m_heliStatus == HELI_STATUS_HOVER)
+ m_heliStatus = HELI_STATUS_HOVER2;
+ }
+ }
+
+ // Shoot!
+ if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout &&
+ CTimer::GetTimeInMilliseconds() > m_nLastShotTime + 200){
+ CVector shotTarget = FindPlayerCoors();
+ // some inaccuracy
+ shotTarget.x += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f;
+ shotTarget.y += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f;
+ CVector direction = FindPlayerCoors() - GetPosition();
+ direction.Normalise();
+ shotTarget += 3.0f*direction;
+ CVector shotSource = GetPosition();
+ shotSource += 3.0f*direction;
+ FireOneInstantHitRound(&shotSource, &shotTarget, 20);
+ DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f);
+ m_nLastShotTime = CTimer::GetTimeInMilliseconds();
+ }
+ }
+ }
+
+ // Drop Catalina's bombs
+ if(m_heliType == HELI_TYPE_CATALINA && m_pathState > 8 && (CTimer::GetTimeInMilliseconds()>>9) != (CTimer::GetPreviousTimeInMilliseconds()>>9)){
+ CVector bombPos = GetPosition() - 60.0f*m_vecMoveSpeed;
+ if(sq(FindPlayerCoors().x-bombPos.x) + sq(FindPlayerCoors().y-bombPos.y) < sq(35.0f)){
+ bool found;
+ float groundZ = CWorld::FindGroundZFor3DCoord(bombPos.x, bombPos.y, bombPos.z, &found);
+ float waterZ;
+ if(!CWaterLevel::GetWaterLevelNoWaves(bombPos.x, bombPos.y, bombPos.z, &waterZ))
+ waterZ = 0.0f;
+ if(groundZ > waterZ){
+ bombPos.z = groundZ + 2.0f;
+ CExplosion::AddExplosion(nil, this, EXPLOSION_HELI_BOMB, bombPos, 0);
+ }else{
+ bombPos.z = waterZ;
+ CVector dir;
+ for(i = 0; i < 16; i++){
+ dir.x = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f;
+ dir.y = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f;
+ dir.z = 0.5f;
+ CParticle::AddParticle(PARTICLE_BOAT_SPLASH, bombPos, dir, nil, 0.2f);
+ }
+ }
+ }
+ }
+
+ RemoveAndAdd();
+ bIsInSafePosition = true;
+ GetMatrix().UpdateRW();
+ UpdateRwFrame();
+}
+
+void
+CHeli::PreRender(void)
+{
+ float angle;
+ uint8 i;
+ CColPoint point;
+ CEntity *entity;
+ uint8 r, g, b;
+ float testLowZ = FindPlayerCoors().z - 10.0f;
+ float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f;
+ int frm = CTimer::GetFrameCounter() & 7;
+
+ i = 0;
+ for(angle = 0.0f; angle < TWOPI; angle += TWOPI/32){
+ CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f);
+ CVector dir = pos*0.01f;
+ pos += GetPosition();
+
+ if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil))
+ m_fHeliDustZ[frm] = point.point.z;
+ else
+ m_fHeliDustZ[frm] = -101.0f;
+
+ switch(point.surfaceB){
+ default:
+ case SURFACE_TARMAC:
+ r = 10;
+ g = 10;
+ b = 10;
+ break;
+ case SURFACE_GRASS:
+ r = 10;
+ g = 6;
+ b = 3;
+ break;
+ case SURFACE_DIRT:
+ r = 10;
+ g = 8;
+ b = 7;
+ break;
+ case SURFACE_DIRTTRACK:
+ r = 10;
+ g = 6;
+ b = 3;
+ break;
+ }
+ RwRGBA col = { r, g, b, 32 };
+ pos.z = m_fHeliDustZ[(i - (i&3))/4]; // advance every 4 iterations, why not just /4?
+ if(pos.z > -200.0f && GetPosition().z - pos.z < 20.0f)
+ CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, 0.0f, col);
+ i++;
+ }
+}
+
+void
+CHeli::Render(void)
+{
+ CMatrix mat;
+ CVector pos;
+
+ mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_TOPROTOR]));
+ pos = mat.GetPosition();
+ mat.SetRotateZ(m_fRotorRotation);
+ mat.Translate(pos);
+ mat.UpdateRW();
+
+ m_fRotorRotation += 3.14f/6.5f;
+ if(m_fRotorRotation > 6.28f)
+ m_fRotorRotation -= 6.28f;
+
+ mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_BACKROTOR]));
+ pos = mat.GetPosition();
+ mat.SetRotateX(m_fRotorRotation);
+ mat.Translate(pos);
+ mat.UpdateRW();
+
+ CEntity::Render();
+}
+
+void
+CHeli::PreRenderAlways(void)
+{
+ CVector shadowPos(m_fSearchLightX, m_fSearchLightY, GetPosition().z);
+ if(m_fSearchLightIntensity > 0.0f){
+ CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &shadowPos,
+ 6.0f, 0.0f, 0.0f, -6.0f,
+ 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity,
+ 50.0f, true, 1.0f);
+
+ CVector front = GetMatrix() * CVector(0.0f, 7.0f, 0.0f);
+ CVector toPlayer = FindPlayerCoors() - front;
+ toPlayer.Normalise();
+ float intensity = m_fSearchLightIntensity*sq(CTimeCycle::GetSpriteBrightness());
+ if(DotProduct(toPlayer, TheCamera.GetForward()) < -0.8f)
+ CCoronas::RegisterCorona((uintptr)this, 255*intensity, 255*intensity, 255*intensity, 255,
+ front, 10.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ else
+ CCoronas::RegisterCorona((uintptr)this, 200*intensity, 200*intensity, 200*intensity, 255,
+ front, 8.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ }
+
+ CVector back = GetMatrix() * CVector(0.0f, -9.0f, 0.0f);
+ if(CTimer::GetTimeInMilliseconds() & 0x100)
+ CCoronas::RegisterCorona((uintptr)this + 2, 255, 0, 0, 255,
+ back, 1.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+ else
+ CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 255,
+ back, 1.0f, 60.0f, CCoronas::TYPE_STAR,
+ CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
+}
+
+RwObject*
+GetHeliAtomicObjectCB(RwObject *object, void *data)
+{
+ RpAtomic *atomic = (RpAtomic*)object;
+ assert(RwObjectGetType(object) == rpATOMIC);
+ if(RpAtomicGetFlags(atomic) & rpATOMICRENDER)
+ *(RpAtomic**)data = atomic;
+ return object;
+}
+
+CObject*
+CHeli::SpawnFlyingComponent(int32 component)
+{
+ RpAtomic *atomic;
+ RwFrame *frame;
+ RwMatrix *matrix;
+ CObject *obj;
+
+ if(m_aHeliNodes[component] == nil)
+ return nil;
+
+ atomic = nil;
+ RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic);
+ if(atomic == nil)
+ return nil;
+
+ obj = new CObject;
+ if(obj == nil)
+ return nil;
+
+ obj->SetModelIndexNoCreate(MI_CAR_WHEEL);
+ // object needs base model
+ obj->RefModelInfo(GetModelIndex());
+
+ // create new atomic
+ matrix = RwFrameGetLTM(m_aHeliNodes[component]);
+ 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->bIsStatic = false;
+ obj->bIsPickup = false;
+
+ // life time
+ CObject::nNoTempObjects++;
+ if(component == HELI_TOPROTOR)
+ obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 1000;
+ else
+ obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 3000;
+
+ obj->m_vecMoveSpeed = m_vecMoveSpeed;
+ if(obj->m_vecMoveSpeed.z > 0.0f)
+ obj->m_vecMoveSpeed.z = 0.3f;
+ else
+ obj->m_vecMoveSpeed.z = 0.0f;
+
+ obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f;
+
+ if(component == HELI_BACKROTOR)
+ obj->m_vecTurnSpeed.x = 0.5f;
+ else if(component == HELI_TOPROTOR || component == HELI_TOPKNOT)
+ obj->m_vecTurnSpeed.z = 0.5f;
+ else
+ obj->m_vecTurnSpeed.y = 0.5f;
+
+ obj->bRenderScorched = true;
+
+ CWorld::Add(obj);
+
+ atomic = nil;
+ RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic);
+ if(atomic)
+ RpAtomicSetFlags(atomic, 0);
+
+ return obj;
}
-WRAPPER CHeli* CHeli::ctor(int, uint8) { EAXJMP(0x547220); }
-WRAPPER void CHeli::SpecialHeliPreRender(void) { EAXJMP(0x54AE10); }
+
+
+void
+CHeli::InitHelis(void)
+{
+ int i;
+
+ NumRandomHelis = 0;
+ TestForNewRandomHelisTimer = 0;
+ NumScriptHelis = 0;
+ CatalinaHeliOn = false;
+ ScriptHeliOn = false;
+ for(i = 0; i < NUM_HELIS; i++)
+ pHelis[i] = nil;
+
+ ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_ESCAPE))->SetColModel(&CTempColModels::ms_colModelPed1);
+ ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1);
+}
+
+CHeli*
+GenerateHeli(bool catalina)
+{
+ CHeli *heli;
+ CVector heliPos;
+ int i;
+
+ if(catalina)
+ heli = new CHeli(MI_ESCAPE, PERMANENT_VEHICLE);
+ else
+ heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE);
+
+ if(catalina)
+ heliPos = CVector(-224.0f, 201.0f, 83.0f);
+ else{
+ heliPos = FindPlayerCoors();
+ float angle = (float)(CGeneral::GetRandomNumber() & 0xFF)/0xFF * 6.28f;
+ heliPos.x += 250.0f*Sin(angle);
+ heliPos.y += 250.0f*Cos(angle);
+ if(heliPos.x < -2000.0f || heliPos.x > 2000.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){
+ // directly above player
+ heliPos.x -= 250.0f*Sin(angle);
+ heliPos.y -= 250.0f*Cos(angle);
+ }
+ heliPos.z += 50.0f;
+ }
+ heli->GetMatrix().SetTranslate(heliPos);
+ if(catalina)
+ heli->GetMatrix().SetRotateZOnly(DEGTORAD(270.0f)); // game actually uses 3.14 here
+
+ heli->m_status = STATUS_ABANDONED;
+
+ int id = -1;
+ bool found = false;
+ while(!found){
+ id++;
+ found = true;
+ for(i = 0; i < 4; i++)
+ if(CHeli::pHelis[i] && CHeli::pHelis[i]->m_nHeliId == id)
+ found = false;
+ }
+ heli->m_nHeliId = id;
+
+ CWorld::Add(heli);
+
+ return heli;
+}
+
+void
+CHeli::UpdateHelis(void)
+{
+ int i, j;
+
+ // Spawn new police helis
+ int numHelisRequired = FindPlayerPed()->m_pWanted->NumOfHelisRequired();
+ if(CStreaming::HasModelLoaded(MI_CHOPPER) && CTimer::GetTimeInMilliseconds() > TestForNewRandomHelisTimer){
+ // Spawn a police heli
+ TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 15000;
+ if(NumRandomHelis < numHelisRequired){
+ NumRandomHelis++;
+ CHeli *heli = GenerateHeli(false);
+ heli->m_heliType = HELI_TYPE_RANDOM;
+ if(pHelis[HELI_RANDOM0] == nil)
+ pHelis[HELI_RANDOM0] = heli;
+ else if(pHelis[HELI_RANDOM1] == nil)
+ pHelis[HELI_RANDOM1] = heli;
+ else
+ assert(0 && "too many helis");
+ }
+ }
+
+ // Handle script heli
+ if(ScriptHeliOn){
+ if(CStreaming::HasModelLoaded(MI_CHOPPER) && pHelis[HELI_SCRIPT] == nil){
+ pHelis[HELI_SCRIPT] = GenerateHeli(false);
+ pHelis[HELI_SCRIPT]->m_heliType = HELI_TYPE_SCRIPT;
+ }else
+ CStreaming::RequestModel(MI_CHOPPER, 0);
+ }else{
+ if(pHelis[HELI_SCRIPT])
+ pHelis[HELI_SCRIPT]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Handle Catalina's heli
+ if(CatalinaHeliOn){
+ if(CStreaming::HasModelLoaded(MI_ESCAPE) && pHelis[HELI_CATALINA] == nil){
+ pHelis[HELI_CATALINA] = GenerateHeli(true);
+ pHelis[HELI_CATALINA]->m_heliType = HELI_TYPE_CATALINA;
+ }else
+ CStreaming::RequestModel(MI_ESCAPE, STREAMFLAGS_DONT_REMOVE);
+ }else{
+ if(pHelis[HELI_CATALINA])
+ pHelis[HELI_CATALINA]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Delete helis that we no longer need
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_FLY_AWAY && pHelis[i]->GetPosition().z > 150.0f){
+ CWorld::Remove(pHelis[i]);
+ delete pHelis[i];
+ pHelis[i] = nil;
+ if(i != HELI_SCRIPT && i != HELI_CATALINA)
+ NumRandomHelis--;
+ }
+
+ // Handle explosions
+ for(i = 0; i < NUM_HELIS; i++){
+ if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds() > pHelis[i]->m_nExplosionTimer){
+ // Second part of explosion
+ static int nFrameGen;
+ CRGBA colors[8];
+
+ TheCamera.CamShake(0.7f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z);
+
+ colors[0] = CRGBA(0, 0, 0, 255);
+ colors[1] = CRGBA(224, 230, 238, 255);
+ colors[2] = CRGBA(0, 0, 0, 255);
+ colors[3] = CRGBA(0, 0, 0, 255);
+ colors[4] = CRGBA(66, 162, 252, 255);
+ colors[5] = CRGBA(0, 0, 0, 255);
+ colors[6] = CRGBA(0, 0, 0, 255);
+ colors[7] = CRGBA(0, 0, 0, 255);
+
+ CVector pos = pHelis[i]->GetPosition();
+ CVector dir;
+ for(j = 0; j < 40; j++){
+ 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, pos, dir,
+ nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
+ colors[nFrameGen], rotSpeed, 0, f, 0);
+ }
+
+ CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0);
+
+ pHelis[i]->SpawnFlyingComponent(HELI_SKID_LEFT);
+ pHelis[i]->SpawnFlyingComponent(HELI_SKID_RIGHT);
+ pHelis[i]->SpawnFlyingComponent(HELI_TOPROTOR);
+
+ CDarkel::RegisterCarBlownUpByPlayer(pHelis[i]);
+ CWorld::Remove(pHelis[i]);
+ delete pHelis[i];
+ pHelis[i] = nil;
+ if(i != HELI_SCRIPT && i != HELI_CATALINA)
+ NumRandomHelis--;
+ if(i == HELI_CATALINA)
+ CatalinaHasBeenShotDown = true;
+
+ CStats::HelisDestroyed++;
+ CStats::PeopleKilledByOthers += 2;
+ CStats::PedsKilledOfThisType[PEDTYPE_COP] += 2;
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250;
+ pos = CWorld::Players[CWorld::PlayerInFocus].m_pPed->GetPosition();
+ CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_SHOOT_HELI,
+ pos, i + 19843, false);
+
+ TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 50000;
+ }else if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds()+7000 > pHelis[i]->m_nExplosionTimer){
+ // First part of explosion
+ if(CTimer::GetPreviousTimeInMilliseconds()+7000 < pHelis[i]->m_nExplosionTimer){
+ pHelis[i]->SpawnFlyingComponent(HELI_BACKROTOR);
+ pHelis[i]->SpawnFlyingComponent(HELI_TAIL);
+ pHelis[i]->m_fAngularSpeed *= -2.5f;
+ pHelis[i]->bRenderScorched = true;
+
+ TheCamera.CamShake(0.4f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z);
+
+ CVector pos = pHelis[i]->GetPosition() - 2.5f*pHelis[i]->GetUp();
+ CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0);
+ }else
+ pHelis[i]->m_fAngularSpeed *= 1.03f;
+ }
+ }
+
+ // Find police helis to remove
+ for(i = 0; i < 2; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_FLY_AWAY){
+ if(numHelisRequired > 0)
+ numHelisRequired--;
+ else
+ pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+ }
+
+ // Remove all helis if in a tunnel
+ if(FindPlayerCoors().z < - 2.0f)
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_SHOT_DOWN)
+ pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY;
+}
+
+void
+CHeli::SpecialHeliPreRender(void)
+{
+ int i;
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i])
+ pHelis[i]->PreRenderAlways();
+}
+
+bool
+CHeli::TestRocketCollision(CVector *rocketPos)
+{
+ int i;
+ bool hit = false;
+
+ for(i = 0; i < NUM_HELIS; i++){
+ if(pHelis[i] && !pHelis[i]->bExplosionProof && (*rocketPos - pHelis[i]->GetPosition()).MagnitudeSqr() < sq(8.0f)){
+ pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f;
+ pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
+ pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000;
+ hit = true;
+ }
+ }
+ return hit;
+}
+
+bool
+CHeli::TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage)
+{
+ int i;
+ bool hit = false;
+
+ for(i = 0; i < NUM_HELIS; i++)
+ if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pHelis[i]->GetPosition()) < 5.0f){
+ // Find bullet position
+ float distToHeli = (pHelis[i]->GetPosition() - *line0).Magnitude();
+ CVector line = (*line1 - *line0);
+ float lineLength = line.Magnitude();
+ *bulletPos = *line0 + line*max(1.0f, distToHeli-5.0f);
+
+ pHelis[i]->m_nBulletDamage += damage;
+
+ if(pHelis[i]->m_heliType == HELI_CATALINA && pHelis[i]->m_nBulletDamage > 400 ||
+ pHelis[i]->m_heliType != HELI_CATALINA && pHelis[i]->m_nBulletDamage > 700){
+ pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f;
+ pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
+ pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000;
+ }
+
+ hit = true;
+ }
+ return hit;
+}
+
+void CHeli::StartCatalinaFlyBy(void)
+{
+ CatalinaHeliOn = true;
+ CatalinaHasBeenShotDown = false;
+}
+
+void
+CHeli::RemoveCatalinaHeli(void)
+{
+ CatalinaHeliOn = false;
+ if(pHelis[HELI_CATALINA]){
+ CWorld::Remove(pHelis[HELI_CATALINA]);
+ delete pHelis[HELI_CATALINA];
+ pHelis[HELI_CATALINA] = nil;
+ }
+}
+
+CHeli *CHeli::FindPointerToCatalinasHeli(void) { return pHelis[HELI_CATALINA]; }
+void CHeli::CatalinaTakeOff(void) { pHelis[HELI_CATALINA]->m_pathState = 8; }
+void CHeli::MakeCatalinaHeliFlyAway(void) { pHelis[HELI_CATALINA]->m_pathState = 12; }
+bool CHeli::HasCatalinaBeenShotDown(void) { return CatalinaHasBeenShotDown; }
+
+void CHeli::ActivateHeli(bool activate) { ScriptHeliOn = activate; }
+
class CHeli_ : public CHeli
{
public:
+ void ctor(int32 id, uint8 CreatedBy) { ::new (this) CHeli(id, CreatedBy); }
void dtor(void) { CHeli::~CHeli(); }
};
STARTPATCHES
+ InjectHook(0x547220, &CHeli_::ctor, PATCH_JUMP);
InjectHook(0x5474A0, &CHeli_::dtor, PATCH_JUMP);
+ InjectHook(0x54AE50, &CHeli::SpawnFlyingComponent, PATCH_JUMP);
+ InjectHook(0x549970, CHeli::InitHelis, PATCH_JUMP);
+ InjectHook(0x5499F0, CHeli::UpdateHelis, PATCH_JUMP);
+ InjectHook(0x54AE10, CHeli::SpecialHeliPreRender, PATCH_JUMP);
+ InjectHook(0x54AA30, CHeli::TestRocketCollision, PATCH_JUMP);
+ InjectHook(0x54AB30, CHeli::TestBulletCollision, PATCH_JUMP);
+ InjectHook(0x54A640, GenerateHeli, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Heli.h b/src/vehicles/Heli.h
index db873ae2..bb10345d 100644
--- a/src/vehicles/Heli.h
+++ b/src/vehicles/Heli.h
@@ -2,15 +2,98 @@
#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
- uint8 stuff[180];
+ 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(int, uint8);
+ CHeli(int32 id, uint8 CreatedBy);
CHeli* ctor(int, uint8);
+ // 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 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);
+ 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);
};
static_assert(sizeof(CHeli) == 0x33C, "CHeli: error");
diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp
index 8e0e313d..775cf572 100644
--- a/src/vehicles/Plane.cpp
+++ b/src/vehicles/Plane.cpp
@@ -137,15 +137,16 @@ CPlane::ProcessControl(void)
colors[6] = CRGBA(0, 0, 0, 255);
colors[7] = CRGBA(224, 230, 238, 255);
+ CVector dir;
for(i = 0; i < 40; i++){
- int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f);
+ 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),
- CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
- CGeneral::GetRandomNumberInRange(-2.0f, 2.0f),
- CGeneral::GetRandomNumberInRange(0.0f, 2.0f)),
+ CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir,
nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f),
colors[nFrameGen], rotSpeed, 0, f, 0);
}
diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp
index dada1b21..4c07a63a 100644
--- a/src/vehicles/Train.cpp
+++ b/src/vehicles/Train.cpp
@@ -67,7 +67,7 @@ CTrain::SetModelIndex(uint32 id)
int i;
CVehicle::SetModelIndex(id);
- for(i = 0; i < 3; i++)
+ for(i = 0; i < NUM_TRAIN_NODES; i++)
m_aTrainNodes[i] = nil;
CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes);
}
diff --git a/src/vehicles/Train.h b/src/vehicles/Train.h
index 84b53537..bf541250 100644
--- a/src/vehicles/Train.h
+++ b/src/vehicles/Train.h
@@ -20,7 +20,8 @@ enum
enum eTrainNodes
{
TRAIN_DOOR_LHS = 1,
- TRAIN_DOOR_RHS
+ TRAIN_DOOR_RHS,
+ NUM_TRAIN_NODES
};
enum eTrainPositions
@@ -66,7 +67,7 @@ public:
uint32 m_nDoorTimer;
int16 m_nDoorState;
CTrainDoor Doors[2];
- RwFrame *m_aTrainNodes[3];
+ RwFrame *m_aTrainNodes[NUM_TRAIN_NODES];
// unused
static CVector aStationCoors[3];
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index aaf5926f..4fc79cca 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -30,6 +30,9 @@ void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehicleP
void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
void CVehicle::operator delete(void *p, int handle) { CPools::GetVehiclePool()->Delete((CVehicle*)p); }
+// or Weapon.cpp?
+WRAPPER void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage) { EAXJMP(0x563B00); }
+
CVehicle::CVehicle(uint8 CreatedBy)
{
int i;
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 07893782..4681c711 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -106,6 +106,9 @@ enum eFlightModel
FLIGHT_MODEL_SEAPLANE
};
+// Or Weapon.h?
+void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage);
+
class CVehicle : public CPhysical
{
public: