summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/AudioScriptObject.cpp50
-rw-r--r--src/audio/AudioScriptObject.h251
-rw-r--r--src/audio/MusicManager.cpp4
-rw-r--r--src/control/Garages.cpp1153
-rw-r--r--src/control/Garages.h88
-rw-r--r--src/control/Pickups.cpp29
-rw-r--r--src/control/Script.cpp8
-rw-r--r--src/core/Cam.cpp673
-rw-r--r--src/core/Camera.h1
-rw-r--r--src/core/Frontend.cpp36
-rw-r--r--src/core/Frontend.h13
-rw-r--r--src/core/Game.cpp3
-rw-r--r--src/core/Game.h3
-rw-r--r--src/core/MenuScreens.h3
-rw-r--r--src/core/Radar.cpp5
-rw-r--r--src/core/Timer.cpp5
-rw-r--r--src/core/Timer.h1
-rw-r--r--src/core/config.h4
-rw-r--r--src/core/main.cpp75
-rw-r--r--src/core/re3.cpp6
-rw-r--r--src/core/timebars.cpp121
-rw-r--r--src/core/timebars.h6
-rw-r--r--src/peds/Ped.cpp12
-rw-r--r--src/peds/PlayerPed.cpp89
-rw-r--r--src/render/Font.cpp1151
-rw-r--r--src/render/Font.h38
-rw-r--r--src/render/Hud.cpp6
-rw-r--r--src/text/Text.cpp5
-rw-r--r--src/vehicles/Automobile.cpp18
-rw-r--r--src/vehicles/Vehicle.cpp188
-rw-r--r--src/vehicles/Vehicle.h3
-rw-r--r--src/weapons/Weapon.cpp1
-rw-r--r--src/weapons/Weapon.h2
33 files changed, 3201 insertions, 850 deletions
diff --git a/src/audio/AudioScriptObject.cpp b/src/audio/AudioScriptObject.cpp
index b5093c52..0ae3834a 100644
--- a/src/audio/AudioScriptObject.cpp
+++ b/src/audio/AudioScriptObject.cpp
@@ -4,12 +4,10 @@
#include "Pools.h"
#include "DMAudio.h"
-WRAPPER void cAudioScriptObject::SaveAllAudioScriptObjects(uint8 *buf, uint32 *size) { EAXJMP(0x57c460); }
-
void
cAudioScriptObject::Reset()
{
- AudioId = 125;
+ AudioId = SCRSOUND_INVALID;
Posn = CVector(0.0f, 0.0f, 0.0f);
AudioEntity = AEHANDLE_NONE;
}
@@ -19,16 +17,19 @@ cAudioScriptObject::operator new(size_t sz)
{
return CPools::GetAudioScriptObjectPool()->New();
}
+
void *
cAudioScriptObject::operator new(size_t sz, int handle)
{
return CPools::GetAudioScriptObjectPool()->New(handle);
}
+
void
cAudioScriptObject::operator delete(void *p, size_t sz)
{
CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p);
}
+
void
cAudioScriptObject::operator delete(void *p, int handle)
{
@@ -36,6 +37,47 @@ cAudioScriptObject::operator delete(void *p, int handle)
}
void
+cAudioScriptObject::LoadAllAudioScriptObjects(uint8 *buf, uint32 size)
+{
+ INITSAVEBUF
+
+ CheckSaveHeader(buf, 'A', 'U', 'D', '\0', size - SAVE_HEADER_SIZE);
+
+ int32 pool_size = ReadSaveBuf<int32>(buf);
+ for (int32 i = 0; i < pool_size; i++) {
+ int handle = ReadSaveBuf<int32>(buf);
+ cAudioScriptObject *p = new(handle) cAudioScriptObject;
+ assert(p != nil);
+ *p = ReadSaveBuf<cAudioScriptObject>(buf);
+ p->AudioEntity = DMAudio.CreateLoopingScriptObject(p);
+ }
+
+ VALIDATESAVEBUF(size);
+}
+
+void
+cAudioScriptObject::SaveAllAudioScriptObjects(uint8 *buf, uint32 *size)
+{
+ INITSAVEBUF
+
+ int32 pool_size = CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces();
+ *size = SAVE_HEADER_SIZE + pool_size * (sizeof(cAudioScriptObject) + sizeof(int32));
+ WriteSaveHeader(buf, 'A', 'U', 'D', '\0', *size - SAVE_HEADER_SIZE);
+ WriteSaveBuf(buf, pool_size);
+
+ int32 i = CPools::GetAudioScriptObjectPool()->GetSize();
+ while (i--) {
+ cAudioScriptObject *p = CPools::GetAudioScriptObjectPool()->GetSlot(i);
+ if (p != nil) {
+ WriteSaveBuf(buf, CPools::GetAudioScriptObjectPool()->GetIndex(p));
+ WriteSaveBuf(buf, *p);
+ }
+ }
+
+ VALIDATESAVEBUF(*size);
+}
+
+void
PlayOneShotScriptObject(uint8 id, CVector const &pos)
{
cAudioScriptObject *audioScriptObject = new cAudioScriptObject();
@@ -48,4 +90,6 @@ PlayOneShotScriptObject(uint8 id, CVector const &pos)
STARTPATCHES
InjectHook(0x57C430, &cAudioScriptObject::Reset, PATCH_JUMP);
InjectHook(0x57C5F0, &PlayOneShotScriptObject, PATCH_JUMP);
+InjectHook(0x57C560, &cAudioScriptObject::LoadAllAudioScriptObjects, PATCH_JUMP);
+InjectHook(0x57c460, &cAudioScriptObject::SaveAllAudioScriptObjects, PATCH_JUMP);
ENDPATCHES \ No newline at end of file
diff --git a/src/audio/AudioScriptObject.h b/src/audio/AudioScriptObject.h
index 1db19865..4308faee 100644
--- a/src/audio/AudioScriptObject.h
+++ b/src/audio/AudioScriptObject.h
@@ -2,130 +2,132 @@
enum
{
- SCRSOUND_TEST_1 = 0,
- _SCRSOUND_UNK_1 = 1,
- _SCRSOUND_UNK_2 = 2,
- _SCRSOUND_UNK_3 = 3,
- _SCRSOUND_CLUB_1_S = 4,
- _SCRSOUND_CLUB_1_L = 5,
- _SCRSOUND_CLUB_2_S = 6,
- _SCRSOUND_CLUB_2_L = 7,
- _SCRSOUND_CLUB_3_S = 8,
- _SCRSOUND_CLUB_3_L = 9,
- _SCRSOUND_CLUB_4_S = 10,
- _SCRSOUND_CLUB_4_L = 11,
- _SCRSOUND_CLUB_5_S = 12,
- _SCRSOUND_CLUB_5_L = 13,
- _SCRSOUND_CLUB_6_S = 14,
- _SCRSOUND_CLUB_6_L = 15,
- _SCRSOUND_CLUB_7_S = 16,
- _SCRSOUND_CLUB_7_L = 17,
- _SCRSOUND_CLUB_8_S = 18,
- _SCRSOUND_CLUB_8_L = 19,
- _SCRSOUND_CLUB_9_S = 20,
- _SCRSOUND_CLUB_9_L = 21,
- _SCRSOUND_CLUB_10_S = 22,
- _SCRSOUND_CLUB_10_L = 23,
- _SCRSOUND_CLUB_11_S = 24,
- _SCRSOUND_CLUB_11_L = 25,
- _SCRSOUND_CLUB_12_S = 26,
- _SCRSOUND_CLUB_12_L = 27,
- _SCRSOUND_CLUB_RAGGA_S = 28,
- _SCRSOUND_CLUB_RAGGA_L = 29,
- SCRSOUND_STRIP_CLUB_LOOP_1_S = 30,
- _SCRSOUND_STRIP_CLUB_LOOP_1_L = 31,
- SCRSOUND_STRIP_CLUB_LOOP_2_S = 32,
- _SCRSOUND_STRIP_CLUB_LOOP_2_L = 33,
- _SCRSOUND_SFX_WORKSHOP_1 = 34,
- _SCRSOUND_SFX_WORKSHOP_2 = 35,
- _SCRSOUND_SAWMILL_LOOP_S = 36,
- SCRSOUND_SAWMILL_LOOP_L = 37,
- _SCRSOUND_DOG_FOOD_FACTORY_S = 38,
- _SCRSOUND_DOG_FOOD_FACTORY_L = 39,
- _SCRSOUND_LAUNDERETTE_1 = 40,
- _SCRSOUND_LAUNDERETTE_2 = 41,
- _SCRSOUND_RESTAURANT_CHINATOWN_S = 42,
- _SCRSOUND_RESTAURANT_CHINATOWN_L = 43,
- _SCRSOUND_RESTAURANT_ITALY_S = 44,
- _SCRSOUND_RESTAURANT_ITALY_L = 45,
- _SCRSOUND_RESTAURANT_GENERIC_1_S = 46,
- _SCRSOUND_RESTAURANT_GENERIC_1_L = 47,
- _SCRSOUND_RESTAURANT_GENERIC_2_S = 48,
- _SCRSOUND_RESTAURANT_GENERIC_2_L = 49,
- _SCRSOUND_AIRPORT_ANNOUNCEMENT_S = 50,
- _SCRSOUND_AIRPORT_ANNOUNCEMENT_L = 51,
- _SCRSOUND_SHOP_LOOP_1 = 52,
- _SCRSOUND_SHOP_LOOP_2 = 53,
- _SCRSOUND_CINEMA_S = 54,
- _SCRSOUND_CINEMA_L = 55,
- _SCRSOUND_DOCKS_FOGHORN_S = 56,
- _SCRSOUND_DOCKS_FOGHORN_L = 57,
- _SCRSOUND_HOME_S = 58,
- _SCRSOUND_HOME_L = 59,
- _SCRSOUND_PIANO_BAR = 60,
- _SCRSOUND_CLUB = 61,
- SCRSOUND_PORN_CINEMA_1_S = 62,
- _SCRSOUND_PORN_CINEMA_1_L = 63,
- SCRSOUND_PORN_CINEMA_2_S = 64,
- _SCRSOUND_PORN_CINEMA_2_L = 65,
- SCRSOUND_PORN_CINEMA_3_S = 66,
- _SCRSOUND_PORN_CINEMA_3_L = 67,
- _SCRSOUND_BANK_ALARM_LOOP_S = 68,
- SCRSOUND_BANK_ALARM_LOOP_L = 69,
- _SCRSOUND_POLICE_BALL_LOOP_S = 70,
- SCRSOUND_POLICE_BALL_LOOP_L = 71,
- _SCRSOUND_RAVE_LOOP_INDUSTRIAL_S = 72,
- SCRSOUND_RAVE_LOOP_INDUSTRIAL_L = 73,
- _SCRSOUND_UNK_74 = 74,
- _SCRSOUND_UNK_75 = 75,
- _SCRSOUND_POLICE_CELL_BEATING_LOOP_S = 76,
- SCRSOUND_POLICE_CELL_BEATING_LOOP_L = 77,
- SCRSOUND_INJURED_PED_MALE_OUCH_S = 78,
- SCRSOUND_INJURED_PED_MALE_OUCH_L = 79,
- SCRSOUND_INJURED_PED_FEMALE_OUCH_S = 80,
- SCRSOUND_INJURED_PED_FEMALE_OUCH_L = 81,
- SCRSOUND_EVIDENCE_PICKUP = 82,
- SCRSOUND_UNLOAD_GOLD = 83,
- _SCRSOUND_RAVE_INDUSTRIAL_S = 84,
- _SCRSOUND_RAVE_INDUSTRIAL_L = 85,
- _SCRSOUND_RAVE_COMMERCIAL_S = 86,
- _SCRSOUND_RAVE_COMMERCIAL_L = 87,
- _SCRSOUND_RAVE_SUBURBAN_S = 88,
- _SCRSOUND_RAVE_SUBURBAN_L = 89,
- _SCRSOUND_GROAN_S = 90,
- _SCRSOUND_GROAN_L = 91,
- SCRSOUND_GATE_START_CLUNK = 92,
- SCRSOUND_GATE_STOP_CLUNK = 93,
- SCRSOUND_PART_MISSION_COMPLETE = 94,
- SCRSOUND_CHUNKY_RUN_SHOUT = 95,
- SCRSOUND_SECURITY_GUARD_RUN_AWAY_SHOUT = 96,
- SCRSOUND_RACE_START_1 = 97,
- SCRSOUND_RACE_START_2 = 98,
- SCRSOUND_RACE_START_3 = 99,
- SCRSOUND_RACE_START_GO = 100,
- SCRSOUND_SWAT_PED_SHOUT = 101,
- SCRSOUND_PRETEND_FIRE_LOOP = 102,
- SCRSOUND_AMMUNATION_CHAT_1 = 103,
- SCRSOUND_AMMUNATION_CHAT_2 = 104,
- SCRSOUND_AMMUNATION_CHAT_3 = 105,
- _SCRSOUND_BULLET_WALL_1 = 106,
- _SCRSOUND_BULLET_WALL_2 = 107,
- _SCRSOUND_BULLET_WALL_3 = 108,
- _SCRSOUND_UNK_109 = 109,
- _SCRSOUND_GLASSFX2_1 = 110,
- _SCRSOUND_GLASSFX2_2 = 111,
- _SCRSOUND_PHONE_RING = 112,
- _SCRSOUND_UNK_113 = 113,
- _SCRSOUND_GLASS_SMASH_1 = 114,
- _SCRSOUND_GLASS_SMASH_2 = 115,
- _SCRSOUND_GLASS_CRACK = 116,
- _SCRSOUND_GLASS_SHARD = 117,
- _SCRSOUND_WOODEN_BOX_SMASH = 118,
- _SCRSOUND_CARDBOARD_BOX_SMASH = 119,
- _SCRSOUND_COL_CAR = 120,
- _SCRSOUND_TYRE_BUMP = 121,
- _SCRSOUND_BULLET_SHELL_HIT_GROUND_1 = 122,
- _SCRSOUND_BULLET_SHELL_HIT_GROUND_2 = 123,
+ SCRSOUND_TEST_1,
+ _SCRSOUND_UNK_1,
+ _SCRSOUND_UNK_2,
+ _SCRSOUND_UNK_3,
+ _SCRSOUND_CLUB_1_S,
+ _SCRSOUND_CLUB_1_L,
+ _SCRSOUND_CLUB_2_S,
+ _SCRSOUND_CLUB_2_L,
+ _SCRSOUND_CLUB_3_S,
+ _SCRSOUND_CLUB_3_L,
+ _SCRSOUND_CLUB_4_S,
+ _SCRSOUND_CLUB_4_L,
+ _SCRSOUND_CLUB_5_S,
+ _SCRSOUND_CLUB_5_L,
+ _SCRSOUND_CLUB_6_S,
+ _SCRSOUND_CLUB_6_L,
+ _SCRSOUND_CLUB_7_S,
+ _SCRSOUND_CLUB_7_L,
+ _SCRSOUND_CLUB_8_S,
+ _SCRSOUND_CLUB_8_L,
+ _SCRSOUND_CLUB_9_S,
+ _SCRSOUND_CLUB_9_L,
+ _SCRSOUND_CLUB_10_S,
+ _SCRSOUND_CLUB_10_L,
+ _SCRSOUND_CLUB_11_S,
+ _SCRSOUND_CLUB_11_L,
+ _SCRSOUND_CLUB_12_S,
+ _SCRSOUND_CLUB_12_L,
+ _SCRSOUND_CLUB_RAGGA_S,
+ _SCRSOUND_CLUB_RAGGA_L,
+ SCRSOUND_STRIP_CLUB_LOOP_1_S,
+ _SCRSOUND_STRIP_CLUB_LOOP_1_L,
+ SCRSOUND_STRIP_CLUB_LOOP_2_S,
+ _SCRSOUND_STRIP_CLUB_LOOP_2_L,
+ _SCRSOUND_SFX_WORKSHOP_1,
+ _SCRSOUND_SFX_WORKSHOP_2,
+ _SCRSOUND_SAWMILL_LOOP_S,
+ SCRSOUND_SAWMILL_LOOP_L,
+ _SCRSOUND_DOG_FOOD_FACTORY_S,
+ _SCRSOUND_DOG_FOOD_FACTORY_L,
+ _SCRSOUND_LAUNDERETTE_1,
+ _SCRSOUND_LAUNDERETTE_2,
+ _SCRSOUND_RESTAURANT_CHINATOWN_S,
+ _SCRSOUND_RESTAURANT_CHINATOWN_L,
+ _SCRSOUND_RESTAURANT_ITALY_S,
+ _SCRSOUND_RESTAURANT_ITALY_L,
+ _SCRSOUND_RESTAURANT_GENERIC_1_S,
+ _SCRSOUND_RESTAURANT_GENERIC_1_L,
+ _SCRSOUND_RESTAURANT_GENERIC_2_S,
+ _SCRSOUND_RESTAURANT_GENERIC_2_L,
+ _SCRSOUND_AIRPORT_ANNOUNCEMENT_S,
+ _SCRSOUND_AIRPORT_ANNOUNCEMENT_L,
+ _SCRSOUND_SHOP_LOOP_1,
+ _SCRSOUND_SHOP_LOOP_2,
+ _SCRSOUND_CINEMA_S,
+ _SCRSOUND_CINEMA_L,
+ _SCRSOUND_DOCKS_FOGHORN_S,
+ _SCRSOUND_DOCKS_FOGHORN_L,
+ _SCRSOUND_HOME_S,
+ _SCRSOUND_HOME_L,
+ _SCRSOUND_PIANO_BAR,
+ _SCRSOUND_CLUB,
+ SCRSOUND_PORN_CINEMA_1_S,
+ _SCRSOUND_PORN_CINEMA_1_L,
+ SCRSOUND_PORN_CINEMA_2_S,
+ _SCRSOUND_PORN_CINEMA_2_L,
+ SCRSOUND_PORN_CINEMA_3_S,
+ _SCRSOUND_PORN_CINEMA_3_L,
+ _SCRSOUND_BANK_ALARM_LOOP_S,
+ SCRSOUND_BANK_ALARM_LOOP_L,
+ _SCRSOUND_POLICE_BALL_LOOP_S,
+ SCRSOUND_POLICE_BALL_LOOP_L,
+ _SCRSOUND_RAVE_LOOP_INDUSTRIAL_S,
+ SCRSOUND_RAVE_LOOP_INDUSTRIAL_L,
+ _SCRSOUND_UNK_74,
+ _SCRSOUND_UNK_75,
+ _SCRSOUND_POLICE_CELL_BEATING_LOOP_S,
+ SCRSOUND_POLICE_CELL_BEATING_LOOP_L,
+ SCRSOUND_INJURED_PED_MALE_OUCH_S,
+ SCRSOUND_INJURED_PED_MALE_OUCH_L,
+ SCRSOUND_INJURED_PED_FEMALE_OUCH_S,
+ SCRSOUND_INJURED_PED_FEMALE_OUCH_L,
+ SCRSOUND_EVIDENCE_PICKUP,
+ SCRSOUND_UNLOAD_GOLD,
+ _SCRSOUND_RAVE_INDUSTRIAL_S,
+ _SCRSOUND_RAVE_INDUSTRIAL_L,
+ _SCRSOUND_RAVE_COMMERCIAL_S,
+ _SCRSOUND_RAVE_COMMERCIAL_L,
+ _SCRSOUND_RAVE_SUBURBAN_S,
+ _SCRSOUND_RAVE_SUBURBAN_L,
+ _SCRSOUND_GROAN_S,
+ _SCRSOUND_GROAN_L,
+ SCRSOUND_GATE_START_CLUNK,
+ SCRSOUND_GATE_STOP_CLUNK,
+ SCRSOUND_PART_MISSION_COMPLETE,
+ SCRSOUND_CHUNKY_RUN_SHOUT,
+ SCRSOUND_SECURITY_GUARD_RUN_AWAY_SHOUT,
+ SCRSOUND_RACE_START_1,
+ SCRSOUND_RACE_START_2,
+ SCRSOUND_RACE_START_3,
+ SCRSOUND_RACE_START_GO,
+ SCRSOUND_SWAT_PED_SHOUT,
+ SCRSOUND_PRETEND_FIRE_LOOP,
+ SCRSOUND_AMMUNATION_CHAT_1,
+ SCRSOUND_AMMUNATION_CHAT_2,
+ SCRSOUND_AMMUNATION_CHAT_3,
+ _SCRSOUND_BULLET_WALL_1,
+ _SCRSOUND_BULLET_WALL_2,
+ _SCRSOUND_BULLET_WALL_3,
+ _SCRSOUND_UNK_109,
+ _SCRSOUND_GLASSFX2_1,
+ _SCRSOUND_GLASSFX2_2,
+ _SCRSOUND_PHONE_RING,
+ _SCRSOUND_UNK_113,
+ _SCRSOUND_GLASS_SMASH_1,
+ _SCRSOUND_GLASS_SMASH_2,
+ _SCRSOUND_GLASS_CRACK,
+ _SCRSOUND_GLASS_SHARD,
+ _SCRSOUND_WOODEN_BOX_SMASH,
+ _SCRSOUND_CARDBOARD_BOX_SMASH,
+ _SCRSOUND_COL_CAR,
+ _SCRSOUND_TYRE_BUMP,
+ _SCRSOUND_BULLET_SHELL_HIT_GROUND_1,
+ _SCRSOUND_BULLET_SHELL_HIT_GROUND_2,
+ TOTAL_SCRSOUNDS,
+ SCRSOUND_INVALID
};
class cAudioScriptObject
@@ -142,6 +144,7 @@ public:
static void operator delete(void*, size_t);
static void operator delete(void*, int);
+ static void LoadAllAudioScriptObjects(uint8 *buf, uint32 size);
static void SaveAllAudioScriptObjects(uint8 *buf, uint32 *size);
};
diff --git a/src/audio/MusicManager.cpp b/src/audio/MusicManager.cpp
index 1f1c343a..d840c57b 100644
--- a/src/audio/MusicManager.cpp
+++ b/src/audio/MusicManager.cpp
@@ -17,8 +17,6 @@
cMusicManager &MusicManager = *(cMusicManager *)0x8F3964;
int32 &gNumRetunePresses = *(int32 *)0x650B80;
-wchar *pCurrentStation = (wchar *)0x650B9C;
-uint8 &cDisplay = *(uint8 *)0x650BA1;
int32 &gRetuneCounter = *(int32*)0x650B84;
bool& bHasStarted = *(bool*)0x650B7C;
@@ -72,6 +70,8 @@ cMusicManager::DisplayRadioStationName()
int8 pRetune;
int8 gStreamedSound;
int8 gRetuneCounter;
+ static wchar *pCurrentStation = nil;
+ static uint8 cDisplay = 0;
if(!CTimer::GetIsPaused() && !TheCamera.m_WideScreenOn && PlayerInCar() &&
!CReplay::IsPlayingBack()) {
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index b4d7a703..68d58b10 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -3,6 +3,9 @@
#include "Garages.h"
#include "main.h"
+#ifdef FIX_BUGS
+#include "Boat.h"
+#endif
#include "DMAudio.h"
#include "General.h"
#include "Font.h"
@@ -15,6 +18,7 @@
#include "PlayerPed.h"
#include "Replay.h"
#include "Stats.h"
+#include "Streaming.h"
#include "Text.h"
#include "Timer.h"
#include "Vehicle.h"
@@ -53,6 +57,8 @@
#define DISTANCE_TO_OPEN_HIDEOUT_GARAGE_IN_CAR (10.0f)
#define DISTANCE_TO_SHOW_HIDEOUT_MESSAGE (5.0f)
+#define DISTANCE_TO_CONSIDER_DOOR_FOR_GARAGE (20.0f)
+
// Time
#define TIME_TO_RESPRAY (2000)
#define TIME_TO_SETUP_BOMB (2000)
@@ -62,6 +68,7 @@
// Respray stuff
#define FREE_RESPRAY_HEALTH_THRESHOLD (970.0f)
#define NUM_PARTICLES_IN_RESPRAY (200)
+#define RESPRAY_CENTERING_COEFFICIENT (0.75f)
// Bomb stuff
#define KGS_OF_EXPLOSIVES_IN_BOMB (10)
@@ -74,6 +81,8 @@
// Collect cars stuff
#define MAX_SPEED_TO_SHOW_COLLECTED_MESSAGE (0.03f)
+#define IMPORT_REWARD (1000)
+#define IMPORT_ALLCARS_REWARD (200000)
// Crusher stuff
#define CRUSHER_VEHICLE_TEST_SPAN (8)
@@ -85,30 +94,44 @@
#define MAX_STORED_CARS_IN_INDUSTRIAL (1)
#define MAX_STORED_CARS_IN_COMMERCIAL (NUM_GARAGE_STORED_CARS)
#define MAX_STORED_CARS_IN_SUBURBAN (NUM_GARAGE_STORED_CARS)
+#define LIMIT_CARS_IN_INDUSTRIAL (1)
+#define LIMIT_CARS_IN_COMMERCIAL (2)
+#define LIMIT_CARS_IN_SUBURBAN (3)
#define HIDEOUT_DOOR_SPEED_COEFFICIENT (1.7f)
#define TIME_BETWEEN_HIDEOUT_MESSAGES (18000)
-int32 &CGarages::BankVansCollected = *(int32 *)0x8F1B34;
-bool &CGarages::BombsAreFree = *(bool *)0x95CD7A;
-bool &CGarages::RespraysAreFree = *(bool *)0x95CD1D;
-int32 &CGarages::CarsCollected = *(int32 *)0x880E18;
-int32 (&CGarages::CarTypesCollected)[TOTAL_COLLECTCARS_GARAGES] = *(int32 (*)[TOTAL_COLLECTCARS_GARAGES])*(uintptr*)0x8E286C;
-int32 &CGarages::CrushedCarId = *(int32 *)0x943060;
-uint32 &CGarages::LastTimeHelpMessage = *(uint32 *)0x8F1B58;
-int32 &CGarages::MessageNumberInString = *(int32 *)0x885BA8;
-const char *CGarages::MessageIDString = (const char *)0x878358;
-int32 &CGarages::MessageNumberInString2 = *(int32 *)0x8E2C14;
-uint32 &CGarages::MessageStartTime = *(uint32 *)0x8F2530;
-uint32 &CGarages::MessageEndTime = *(uint32 *)0x8F597C;
-uint32 &CGarages::NumGarages = *(uint32 *)0x8F29F4;
-bool &CGarages::PlayerInGarage = *(bool *)0x95CD83;
-int32 &CGarages::PoliceCarsCollected = *(int32 *)0x941444;
-uint32 &CGarages::GarageToBeTidied = *(uint32 *)0x623570;
-CStoredCar(&CGarages::aCarsInSafeHouse1)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS])*(uintptr*)0x6FA210;
-CStoredCar(&CGarages::aCarsInSafeHouse2)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS])*(uintptr*)0x6FA300;
-CStoredCar(&CGarages::aCarsInSafeHouse3)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS])*(uintptr*)0x6FA3F0;
+// Camera stuff
+#define MARGIN_FOR_CAMERA_COLLECTCARS (1.3f)
+#define MARGIN_FOR_CAMERA_DEFAULT (4.0f)
+
+const int32 gaCarsToCollectInCraigsGarages[TOTAL_COLLECTCARS_GARAGES][TOTAL_COLLECTCARS_CARS] =
+{
+ { MI_SECURICA, MI_MOONBEAM, MI_COACH, MI_FLATBED, MI_LINERUN, MI_TRASH, MI_PATRIOT, MI_MRWHOOP, MI_BLISTA, MI_MULE, MI_YANKEE, MI_BOBCAT, MI_DODO, MI_BUS, MI_RUMPO, MI_PONY },
+ { MI_SENTINEL, MI_CHEETAH, MI_BANSHEE, MI_IDAHO, MI_INFERNUS, MI_TAXI, MI_KURUMA, MI_STRETCH, MI_PEREN, MI_STINGER, MI_MANANA, MI_LANDSTAL, MI_STALLION, MI_BFINJECT, MI_CABBIE, MI_ESPERANT },
+ { MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_LANDSTAL, MI_CHEETAH, MI_TAXI, MI_ESPERANT, MI_SENTINEL, MI_IDAHO }
+};
+
+int32& CGarages::BankVansCollected = *(int32*)0x8F1B34;
+bool& CGarages::BombsAreFree = *(bool*)0x95CD7A;
+bool& CGarages::RespraysAreFree = *(bool*)0x95CD1D;
+int32& CGarages::CarsCollected = *(int32*)0x880E18;
+int32(&CGarages::CarTypesCollected)[TOTAL_COLLECTCARS_GARAGES] = *(int32(*)[TOTAL_COLLECTCARS_GARAGES]) * (uintptr*)0x8E286C;
+int32& CGarages::CrushedCarId = *(int32*)0x943060;
+uint32& CGarages::LastTimeHelpMessage = *(uint32*)0x8F1B58;
+int32& CGarages::MessageNumberInString = *(int32*)0x885BA8;
+char(&CGarages::MessageIDString)[MESSAGE_LENGTH] = *(char(*)[MESSAGE_LENGTH]) * (uintptr*)0x878358;
+int32& CGarages::MessageNumberInString2 = *(int32*)0x8E2C14;
+uint32& CGarages::MessageStartTime = *(uint32*)0x8F2530;
+uint32& CGarages::MessageEndTime = *(uint32*)0x8F597C;
+uint32& CGarages::NumGarages = *(uint32*)0x8F29F4;
+bool& CGarages::PlayerInGarage = *(bool*)0x95CD83;
+int32& CGarages::PoliceCarsCollected = *(int32*)0x941444;
+uint32& CGarages::GarageToBeTidied = *(uint32*)0x623570;
+CStoredCar(&CGarages::aCarsInSafeHouse1)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS]) * (uintptr*)0x6FA210;
+CStoredCar(&CGarages::aCarsInSafeHouse2)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS]) * (uintptr*)0x6FA300;
+CStoredCar(&CGarages::aCarsInSafeHouse3)[NUM_GARAGE_STORED_CARS] = *(CStoredCar(*)[NUM_GARAGE_STORED_CARS]) * (uintptr*)0x6FA3F0;
int32& CGarages::AudioEntity = *(int32*)0x5ECEA8;
-CGarage(&CGarages::aGarages)[NUM_GARAGES] = *(CGarage(*)[NUM_GARAGES])*(uintptr*)0x72BCD0;
+CGarage(&CGarages::aGarages)[NUM_GARAGES] = *(CGarage(*)[NUM_GARAGES]) * (uintptr*)0x72BCD0;
bool& CGarages::bCamShouldBeOutisde = *(bool*)0x95CDB2;
void CGarages::Init(void)
@@ -171,7 +194,7 @@ void CGarages::Update(void)
GarageToBeTidied = 0;
if (!aGarages[GarageToBeTidied].IsUsed())
return;
- if (aGarages[GarageToBeTidied].IsClose())
+ if (!aGarages[GarageToBeTidied].IsFar())
aGarages[GarageToBeTidied].TidyUpGarageClose();
else
aGarages[GarageToBeTidied].TidyUpGarage();
@@ -213,9 +236,9 @@ int16 CGarages::AddOne(float X1, float Y1, float Z1, float X2, float Y2, float Z
pGarage->m_fDoorPos = 0.0f;
pGarage->m_eGarageState = GS_FULLYCLOSED;
pGarage->m_nTimeToStartAction = 0;
- pGarage->field_2 = 0;
+ pGarage->field_2 = false;
pGarage->m_nTargetModelIndex = targetId;
- pGarage->field_96 = 0;
+ pGarage->field_96 = nil;
pGarage->m_bCollectedCarsState = 0;
pGarage->m_bDeactivated = false;
pGarage->m_bResprayHappened = false;
@@ -313,12 +336,14 @@ void CGarage::Update()
m_eGarageState = GS_CLOSING;
CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE);
FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true;
- } else {
+ }
+ else {
CGarages::TriggerMessage("GA_3", -1, 4000, -1); // No more freebies. $1000 to respray!
m_eGarageState = GS_OPENEDCONTAINSCAR;
DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1);
}
- } else {
+ }
+ else {
CGarages::TriggerMessage("GA_1", -1, 4000, -1); // Whoa! I don't touch nothing THAT hot!
m_eGarageState = GS_OPENEDCONTAINSCAR;
DMAudio.PlayFrontEndSound(SOUND_GARAGE_BAD_VEHICLE, 1);
@@ -464,7 +489,7 @@ void CGarage::Update()
DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB_ALREADY_SET, 1);
break;
}
- if (!CGarages::BombsAreFree && CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= BOMB_PRICE) {
+ if (!CGarages::BombsAreFree && CWorld::Players[CWorld::PlayerInFocus].m_nMoney < BOMB_PRICE) {
CGarages::TriggerMessage("GA_4", -1, 4000, -1); // "Car bombs are $1000 each"
m_eGarageState = GS_OPENEDCONTAINSCAR;
DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1);
@@ -546,8 +571,8 @@ void CGarage::Update()
if (IsPlayerOutsideGarage())
m_eGarageState = GS_OPENED;
break;
- //case GS_CLOSEDCONTAINSCAR:
- //case GS_AFTERDROPOFF:
+ //case GS_CLOSEDCONTAINSCAR:
+ //case GS_AFTERDROPOFF:
default:
break;
}
@@ -751,7 +776,7 @@ void CGarage::Update()
CalcSmallestDistToGarageDoorSquared(
FindPlayerVehicle()->GetPosition().x,
FindPlayerVehicle()->GetPosition().y
- ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) {
+ ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) {
if (DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) {
if (FindPlayerVehicle()->VehicleCreatedBy == MISSION_VEHICLE)
CGarages::TriggerMessage("GA_1A", -1, 5000, -1); // Come back when you're not so busy...
@@ -839,13 +864,13 @@ void CGarage::Update()
case GS_CLOSING:
if (m_pTarget) {
m_fDoorPos = max(0.0f, m_fDoorPos - CRUSHER_CRANE_SPEED * CTimer::GetTimeStep());
- if (m_fDoorPos < TWOPI/5) {
+ if (m_fDoorPos < TWOPI / 5) {
m_pTarget->bUsesCollision = false;
m_pTarget->bAffectedByGravity = false;
m_pTarget->SetMoveSpeed(0.0f, 0.0f, 0.0f);
}
else {
- m_pTarget->SetMoveSpeed(m_pTarget->GetMoveSpeed()* Pow(0.8f, CTimer::GetTimeStep()));
+ m_pTarget->SetMoveSpeed(m_pTarget->GetMoveSpeed() * Pow(0.8f, CTimer::GetTimeStep()));
}
if (m_fDoorPos == 0.0f) {
CGarages::CrushedCarId = CPools::GetVehiclePool()->GetIndex(m_pTarget);
@@ -883,7 +908,6 @@ void CGarage::Update()
//case GS_FULLYCLOSED:
//case GS_CLOSEDCONTAINSCAR:
//case GS_OPENEDCONTAINSCAR:
-
default:
break;
}
@@ -999,7 +1023,6 @@ void CGarage::Update()
break;
}
break;
-
case GARAGE_HIDEOUT_ONE:
case GARAGE_HIDEOUT_TWO:
case GARAGE_HIDEOUT_THREE:
@@ -1010,12 +1033,12 @@ void CGarage::Update()
// Close car doors either if player is far, or if he is in vehicle and garage is full,
// or if player is very very far so that we can remove whatever is blocking garage door without him noticing
if ((distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_IN_CAR) ||
- !FindPlayerVehicle() && distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT) &&
+ !FindPlayerVehicle() && distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT) &&
!IsAnyCarBlockingDoor()))
m_eGarageState = GS_CLOSING;
else if (FindPlayerVehicle() &&
CountCarsWithCenterPointWithinGarage(FindPlayerVehicle()) >=
- CGarages::FindMaxNumStoredCarsForGarage(m_eGarageType)) {
+ CGarages::FindMaxNumStoredCarsForGarage(m_eGarageType)) {
m_eGarageState = GS_CLOSING;
}
else if (distance > SQR(DISTANCE_TO_FORCE_CLOSE_HIDEOUT_GARAGE)) {
@@ -1025,15 +1048,9 @@ void CGarage::Update()
break;
}
case GS_CLOSING:
-#ifndef FIX_BUGS // TODO: check and replace with ifdef
- if (!IsPlayerOutsideGarage())
- m_eGarageState = GS_OPENING;
- m_fDoorPos = max(0.0f, m_fDoorPos - HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep());
-#else
m_fDoorPos = max(0.0f, m_fDoorPos - HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep());
if (!IsPlayerOutsideGarage())
m_eGarageState = GS_OPENING;
-#endif
else if (m_fDoorPos == 0.0f) {
DMAudio.PlayOneShot(CGarages::AudioEntity, SOUND_GARAGE_DOOR_CLOSED, 1.0f);
m_eGarageState = GS_FULLYCLOSED;
@@ -1132,27 +1149,235 @@ void CGarage::Update()
break;
}
break;
- //case GARAGE_COLLECTORSITEMS:
- //case GARAGE_60SECONDS:
+ //case GARAGE_COLLECTORSITEMS:
+ //case GARAGE_60SECONDS:
default:
break;
}
-
-}
-
-WRAPPER bool CGarage::IsStaticPlayerCarEntirelyInside() { EAXJMP(0x4251C0); }
-WRAPPER bool CGarage::IsEntityEntirelyInside(CEntity*) { EAXJMP(0x425370); }
-WRAPPER bool CGarage::IsEntityEntirelyInside3D(CEntity*, float) { EAXJMP(0x4254F0); }
-WRAPPER bool CGarage::IsEntityEntirelyOutside(CEntity*, float) { EAXJMP(0x425740); }
-WRAPPER bool CGarage::IsGarageEmpty() { EAXJMP(0x425890); }
-WRAPPER bool CGarage::IsPlayerOutsideGarage() { EAXJMP(0x425910); }
-WRAPPER bool CGarage::IsEntityTouching3D(CEntity*) { EAXJMP(0x425950); }
-WRAPPER bool CGarage::EntityHasASphereWayOutsideGarage(CEntity*, float) { EAXJMP(0x425B30); }
-WRAPPER bool CGarage::IsAnyOtherCarTouchingGarage(CVehicle* pException) { EAXJMP(0x425C90); }
-WRAPPER bool CGarage::IsAnyOtherPedTouchingGarage(CPed* pException) { EAXJMP(0x425E20); }
-WRAPPER bool CGarage::IsAnyCarBlockingDoor() { EAXJMP(0x425FB0); }
-WRAPPER int32 CGarage::CountCarsWithCenterPointWithinGarage(CEntity* pException) { EAXJMP(0x426130); }
-WRAPPER void CGarage::RemoveCarsBlockingDoorNotInside() { EAXJMP(0x4261F0); }
+}
+
+bool CGarage::IsStaticPlayerCarEntirelyInside()
+{
+ if (!FindPlayerVehicle())
+ return false;
+ if (!FindPlayerVehicle()->IsCar())
+ return false;
+ if (FindPlayerPed()->GetPedState() != PED_DRIVING)
+ return false;
+ if (FindPlayerPed()->m_objective == OBJECTIVE_LEAVE_VEHICLE)
+ return false;
+ CVehicle* pVehicle = FindPlayerVehicle();
+ if (pVehicle->GetPosition().x < m_fX1 || pVehicle->GetPosition().x > m_fX2 ||
+ pVehicle->GetPosition().y < m_fY1 || pVehicle->GetPosition().y > m_fY2)
+ return false;
+ if (Abs(pVehicle->GetSpeed().x) > 0.01f ||
+ Abs(pVehicle->GetSpeed().y) > 0.01f ||
+ Abs(pVehicle->GetSpeed().z) > 0.01f)
+ return false;
+ if (pVehicle->GetSpeed().MagnitudeSqr() > SQR(0.01f))
+ return false;
+ return IsEntityEntirelyInside3D(pVehicle, 0.0f);
+}
+
+bool CGarage::IsEntityEntirelyInside(CEntity * pEntity)
+{
+ if (pEntity->GetPosition().x < m_fX1 || pEntity->GetPosition().x > m_fX2 ||
+ pEntity->GetPosition().y < m_fY1 || pEntity->GetPosition().y > m_fY2)
+ return false;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pEntity->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x - radius < m_fX1 || pos.x + radius > m_fX2 ||
+ pos.y - radius < m_fY1 || pos.y + radius > m_fY2)
+ return false;
+ }
+ return true;
+}
+
+bool CGarage::IsEntityEntirelyInside3D(CEntity * pEntity, float fMargin)
+{
+ if (pEntity->GetPosition().x < m_fX1 - fMargin || pEntity->GetPosition().x > m_fX2 + fMargin ||
+ pEntity->GetPosition().y < m_fY1 - fMargin || pEntity->GetPosition().y > m_fY2 + fMargin ||
+ pEntity->GetPosition().z < m_fZ1 - fMargin || pEntity->GetPosition().z > m_fZ2 + fMargin)
+ return false;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pEntity->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius < m_fX1 - fMargin || pos.x - radius > m_fX2 + fMargin ||
+ pos.y + radius < m_fY1 - fMargin || pos.y - radius > m_fY2 + fMargin ||
+ pos.z + radius < m_fZ1 - fMargin || pos.z - radius > m_fZ2 + fMargin)
+ return false;
+ }
+ return true;
+}
+
+bool CGarage::IsEntityEntirelyOutside(CEntity * pEntity, float fMargin)
+{
+ if (pEntity->GetPosition().x > m_fX1 - fMargin && pEntity->GetPosition().x < m_fX2 + fMargin &&
+ pEntity->GetPosition().y > m_fY1 - fMargin && pEntity->GetPosition().y < m_fY2 + fMargin)
+ return false;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pEntity->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius > m_fX1 - fMargin && pos.x - radius < m_fX2 + fMargin &&
+ pos.y + radius > m_fY1 - fMargin && pos.y - radius < m_fY2 + fMargin)
+ return false;
+ }
+ return true;
+}
+
+bool CGarage::IsGarageEmpty()
+{
+ int16 num;
+ CWorld::FindObjectsIntersectingCube(CVector(m_fX1, m_fY1, m_fZ1), CVector(m_fX2, m_fY2, m_fZ2), &num, 2, nil, false, true, true, false, false);
+ return num == 0;
+}
+
+bool CGarage::IsPlayerOutsideGarage()
+{
+ if (FindPlayerVehicle())
+ return IsEntityEntirelyOutside(FindPlayerVehicle(), 0.0f);
+ return IsEntityEntirelyOutside(FindPlayerPed(), 0.0f);
+}
+
+bool CGarage::IsEntityTouching3D(CEntity * pEntity)
+{
+ float radius = pEntity->GetBoundRadius();
+ if (pEntity->GetPosition().x - radius < m_fX1 || pEntity->GetPosition().x + radius > m_fX2 ||
+ pEntity->GetPosition().y - radius < m_fY1 || pEntity->GetPosition().y + radius > m_fY2 ||
+ pEntity->GetPosition().z - radius < m_fZ1 || pEntity->GetPosition().z + radius > m_fZ2)
+ return false;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pEntity->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center;
+ radius = pColModel->spheres[i].radius;
+ if (pos.x + radius > m_fX1 && pos.x - radius < m_fX2 &&
+ pos.y + radius > m_fY1 && pos.y - radius < m_fY2 &&
+ pos.z + radius > m_fZ1 && pos.z - radius < m_fZ2)
+ return false;
+ }
+ return true;
+}
+
+bool CGarage::EntityHasASphereWayOutsideGarage(CEntity * pEntity, float fMargin)
+{
+ CColModel* pColModel = CModelInfo::GetModelInfo(pEntity->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius + fMargin < m_fX1 || pos.x - radius - fMargin > m_fX2 ||
+ pos.y + radius + fMargin < m_fY1 || pos.y - radius - fMargin > m_fY2 ||
+ pos.z + radius + fMargin < m_fZ1 || pos.z - radius - fMargin > m_fZ2)
+ return true;
+ }
+ return false;
+}
+
+bool CGarage::IsAnyOtherCarTouchingGarage(CVehicle * pException)
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle || pVehicle == pException)
+ continue;
+ if (!IsEntityTouching3D(pVehicle))
+ continue;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pVehicle->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius > m_fX1 && pos.x - radius < m_fX2 &&
+ pos.y + radius > m_fY1 && pos.y - radius < m_fY2 &&
+ pos.z + radius > m_fZ1 && pos.z - radius < m_fZ2)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CGarage::IsAnyOtherPedTouchingGarage(CPed * pException)
+{
+ uint32 i = CPools::GetPedPool()->GetSize();
+ while (i--) {
+ CPed* pPed = CPools::GetPedPool()->GetSlot(i);
+ if (!pPed || pPed == pException)
+ continue;
+ if (!IsEntityTouching3D(pPed))
+ continue;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pPed->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pPed->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius > m_fX1 && pos.x - radius < m_fX2 &&
+ pos.y + radius > m_fY1 && pos.y - radius < m_fY2 &&
+ pos.z + radius > m_fZ1 && pos.z - radius < m_fZ2)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CGarage::IsAnyCarBlockingDoor()
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ if (!IsEntityTouching3D(pVehicle))
+ continue;
+ CColModel* pColModel = CModelInfo::GetModelInfo(pVehicle->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius < m_fX1 || pos.x - radius > m_fX2 ||
+ pos.y + radius < m_fY1 || pos.y - radius > m_fY2 ||
+ pos.z + radius < m_fZ1 || pos.z - radius > m_fZ2)
+ return true;
+ }
+ }
+ return false;
+}
+
+int32 CGarage::CountCarsWithCenterPointWithinGarage(CEntity * pException)
+{
+ int32 total = 0;
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle || pVehicle == pException)
+ continue;
+ if (pVehicle->GetPosition().x > m_fX1 && pVehicle->GetPosition().x < m_fX2 &&
+ pVehicle->GetPosition().y > m_fY1 && pVehicle->GetPosition().y < m_fY2 &&
+ pVehicle->GetPosition().z > m_fZ1 && pVehicle->GetPosition().z < m_fZ2)
+ total++;
+ }
+ return total;
+}
+
+void CGarage::RemoveCarsBlockingDoorNotInside()
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ if (!IsEntityTouching3D(pVehicle))
+ continue;
+ if (pVehicle->GetPosition().x < m_fX1 || pVehicle->GetPosition().x > m_fX2 ||
+ pVehicle->GetPosition().y < m_fY1 || pVehicle->GetPosition().y > m_fY2 ||
+ pVehicle->GetPosition().z < m_fZ1 || pVehicle->GetPosition().z > m_fZ2) {
+ if (pVehicle->bIsLocked && pVehicle->CanBeDeleted()) {
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ return; // WHY?
+ }
+ }
+ }
+}
void CGarages::PrintMessages()
{
@@ -1166,8 +1391,11 @@ void CGarages::PrintMessages()
CFont::SetFontStyle(FONT_BANK);
CFont::SetColor(CRGBA(0, 0, 0, 255));
+#if defined(PS2) || defined (FIX_BUGS)
float y_offset = SCREEN_HEIGHT / 3; // THIS is PS2 calculation
- // y_offset = SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(84.0f); // This is PC and results in text being written over some HUD elements
+#else
+ float y_offset = SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(84.0f); // This is PC and results in text being written over some HUD elements
+#endif
if (MessageNumberInString2 < 0) {
if (MessageNumberInString < 0) {
@@ -1195,9 +1423,50 @@ void CGarages::PrintMessages()
}
}
-WRAPPER bool CGarages::IsCarSprayable(CVehicle*) { EAXJMP(0x426700); }
-WRAPPER void CGarage::UpdateDoorsHeight() { EAXJMP(0x426730); }
-WRAPPER void CGarage::BuildRotatedDoorMatrix(CEntity*, float) { EAXJMP(0x4267C0); }
+bool CGarages::IsCarSprayable(CVehicle * pVehicle)
+{
+ switch (pVehicle->GetModelIndex()) {
+ case MI_FIRETRUCK:
+ case MI_AMBULAN:
+ case MI_POLICE:
+ case MI_ENFORCER:
+ case MI_BUS:
+ case MI_RHINO:
+ case MI_BARRACKS:
+ case MI_DODO:
+ case MI_COACH:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+void CGarage::UpdateDoorsHeight()
+{
+ RefreshDoorPointers(false);
+ if (m_pDoor1) {
+ m_pDoor1->GetPosition().z = m_fDoorPos + m_fDoor1Z;
+ if (m_bRotatedDoor)
+ BuildRotatedDoorMatrix(m_pDoor1, m_fDoorPos / m_fDoorHeight);
+ m_pDoor1->GetMatrix().UpdateRW();
+ m_pDoor1->UpdateRwFrame();
+ }
+ if (m_pDoor2) {
+ m_pDoor2->GetPosition().z = m_fDoorPos + m_fDoor2Z;
+ if (m_bRotatedDoor)
+ BuildRotatedDoorMatrix(m_pDoor2, m_fDoorPos / m_fDoorHeight);
+ m_pDoor2->GetMatrix().UpdateRW();
+ m_pDoor2->UpdateRwFrame();
+ }
+}
+
+void CGarage::BuildRotatedDoorMatrix(CEntity * pDoor, float fPosition)
+{
+ float fAngle = -fPosition * HALFPI;
+ CVector r(-Sin(fAngle) * pDoor->GetForward().x, Sin(fAngle) * pDoor->GetForward().y, Cos(fAngle) * pDoor->GetForward().z);
+ pDoor->GetRight() = CrossProduct(r, pDoor->GetForward());
+}
void CGarage::UpdateCrusherAngle()
{
@@ -1207,10 +1476,25 @@ void CGarage::UpdateCrusherAngle()
m_pDoor2->UpdateRwFrame();
}
-WRAPPER void CGarage::UpdateCrusherShake(float, float) { EAXJMP(0x4268E0); }
+void CGarage::UpdateCrusherShake(float X, float Y)
+{
+ RefreshDoorPointers(false);
+ m_pDoor1->GetPosition().x += X;
+ m_pDoor1->GetPosition().y += Y;
+ m_pDoor1->GetMatrix().UpdateRW();
+ m_pDoor1->UpdateRwFrame();
+ m_pDoor1->GetPosition().x -= X;
+ m_pDoor1->GetPosition().y -= Y;
+ m_pDoor2->GetPosition().x += X;
+ m_pDoor2->GetPosition().y += Y;
+ m_pDoor2->GetMatrix().UpdateRW();
+ m_pDoor2->UpdateRwFrame();
+ m_pDoor2->GetPosition().x -= X;
+ m_pDoor2->GetPosition().y -= Y;
+}
// This is dumb but there is no way to avoid goto. What was there originally even?
-static bool DoINeedToRefreshPointer(CEntity* pDoor, bool bIsDummy, int8 nIndex)
+static bool DoINeedToRefreshPointer(CEntity * pDoor, bool bIsDummy, int8 nIndex)
{
bool bNeedToFindDoorEntities = false;
if (pDoor) {
@@ -1239,6 +1523,7 @@ void CGarage::RefreshDoorPointers(bool bCreate)
bool bNeedToFindDoorEntities = true;
if (!bCreate && !m_bRecreateDoorOnNextRefresh)
bNeedToFindDoorEntities = false;
+ m_bRecreateDoorOnNextRefresh = false;
if (DoINeedToRefreshPointer(m_pDoor1, m_bDoor1IsDummy, m_bDoor1PoolIndex))
bNeedToFindDoorEntities = true;
if (DoINeedToRefreshPointer(m_pDoor2, m_bDoor2IsDummy, m_bDoor2PoolIndex))
@@ -1247,11 +1532,53 @@ void CGarage::RefreshDoorPointers(bool bCreate)
FindDoorsEntities();
}
-WRAPPER void CGarages::TriggerMessage(const char* text, int16, uint16 time, int16) { EAXJMP(0x426B20); }
-WRAPPER void CGarages::SetTargetCarForMissonGarage(int16, CVehicle*) { EAXJMP(0x426BD0); }
-WRAPPER bool CGarages::HasCarBeenDroppedOffYet(int16) { EAXJMP(0x426C20); }
-WRAPPER void CGarages::DeActivateGarage(int16) { EAXJMP(0x426C40); }
-WRAPPER void CGarages::ActivateGarage(int16) { EAXJMP(0x426C60); }
+void CGarages::TriggerMessage(const char* text, int16 num1, uint16 time, int16 num2)
+{
+ if (strcmp(text, MessageIDString) == 0 &&
+ CTimer::GetTimeInMilliseconds() >= MessageStartTime &&
+ CTimer::GetTimeInMilliseconds() <= MessageEndTime) {
+ if (CTimer::GetTimeInMilliseconds() - MessageStartTime <= 500)
+ return;
+ MessageStartTime = CTimer::GetTimeInMilliseconds() - 500;
+ MessageEndTime = CTimer::GetTimeInMilliseconds() - 500 + time;
+ }
+ else {
+ strcpy(MessageIDString, text);
+ MessageStartTime = CTimer::GetTimeInMilliseconds();
+ MessageEndTime = CTimer::GetTimeInMilliseconds() + time;
+ }
+ MessageNumberInString = num1;
+ MessageNumberInString2 = num2;
+}
+
+void CGarages::SetTargetCarForMissonGarage(int16 garage, CVehicle * pVehicle)
+{
+ assert(garage >= 0 && garage < NUM_GARAGES);
+ if (pVehicle) {
+ aGarages[garage].m_pTarget = pVehicle;
+ if (aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR)
+ aGarages[garage].m_eGarageState = GS_FULLYCLOSED;
+ }
+ else
+ aGarages[garage].m_pTarget = nil;
+}
+
+bool CGarages::HasCarBeenDroppedOffYet(int16 garage)
+{
+ return aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR;
+}
+
+void CGarages::DeActivateGarage(int16 garage)
+{
+ aGarages[garage].m_bDeactivated = true;
+}
+
+void CGarages::ActivateGarage(int16 garage)
+{
+ aGarages[garage].m_bDeactivated = false;
+ if (aGarages[garage].m_eGarageType == GARAGE_FORCARTOCOMEOUTOF && aGarages[garage].m_eGarageState == GS_FULLYCLOSED)
+ aGarages[garage].m_eGarageState = GS_OPENING;
+}
int32 CGarages::QueryCarsCollected(int16 garage)
{
@@ -1260,7 +1587,7 @@ int32 CGarages::QueryCarsCollected(int16 garage)
bool CGarages::HasImportExportGarageCollectedThisCar(int16 garage, int8 car)
{
- return CarTypesCollected[GetCarsCollectedIndexForGarageType(aGarages[garage].m_eGarageType)] & (1 << car);
+ return CarTypesCollected[GetCarsCollectedIndexForGarageType(aGarages[garage].m_eGarageType)] & (BIT(car));
}
bool CGarages::IsGarageOpen(int16 garage)
@@ -1273,10 +1600,59 @@ bool CGarages::IsGarageClosed(int16 garage)
return aGarages[garage].IsClosed();
}
-WRAPPER bool CGarages::HasThisCarBeenCollected(int16 garage, uint8 id) { EAXJMP(0x426D50); }
-WRAPPER bool CGarage::DoesCraigNeedThisCar(int32) { EAXJMP(0x426D90); }
-WRAPPER bool CGarage::HasCraigCollectedThisCar(int32) { EAXJMP(0x426DF0); }
-WRAPPER void CGarage::MarkThisCarAsCollectedForCraig(int32) { EAXJMP(0x426E50); }
+bool CGarages::HasThisCarBeenCollected(int16 garage, uint8 id)
+{
+ return aGarages[garage].m_bCollectedCarsState & BIT(id);
+}
+
+bool CGarage::DoesCraigNeedThisCar(int32 mi)
+{
+ if (mi == MI_CORPSE)
+ mi = MI_MANANA;
+ int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType);
+ for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) {
+ if (mi == gaCarsToCollectInCraigsGarages[ct][i])
+ return (CGarages::CarTypesCollected[ct] & BIT(i)) == 0;
+ }
+ return false;
+}
+
+bool CGarage::HasCraigCollectedThisCar(int32 mi)
+{
+ if (mi == MI_CORPSE)
+ mi = MI_MANANA;
+ int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType);
+ for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) {
+ if (mi == gaCarsToCollectInCraigsGarages[ct][i])
+ return CGarages::CarTypesCollected[ct] & BIT(i);
+ }
+ return false;
+}
+
+bool CGarage::MarkThisCarAsCollectedForCraig(int32 mi)
+{
+ if (mi == MI_CORPSE)
+ mi = MI_MANANA;
+ int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType);
+ int index;
+ for (index = 0; index < TOTAL_COLLECTCARS_CARS; index++) {
+ if (mi == gaCarsToCollectInCraigsGarages[ct][index])
+ break;
+ }
+ if (index >= TOTAL_COLLECTCARS_CARS)
+ return false;
+ CGarages::CarTypesCollected[ct] |= BIT(index);
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_REWARD;
+ for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) {
+ if ((CGarages::CarTypesCollected[ct] & BIT(i)) == 0) {
+ CGarages::TriggerMessage("GA_13", -1, 5000, -1); // Delivered like a pro. Complete the list and there'll be a bonus for you.
+ return false;
+ }
+ }
+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_ALLCARS_REWARD;
+ CGarages::TriggerMessage("GA_14", -1, 5000, -1); // All the cars. NICE! Here's a little something.
+ return true;
+}
void CGarage::OpenThisGarage()
{
@@ -1290,11 +1666,132 @@ void CGarage::CloseThisGarage()
m_eGarageState = GS_CLOSING;
}
-WRAPPER float CGarage::CalcDistToGarageRectangleSquared(float, float) { EAXJMP(0x426F50); }
-WRAPPER float CGarage::CalcSmallestDistToGarageDoorSquared(float, float) { EAXJMP(0x426FE0); }
-WRAPPER void CGarage::FindDoorsEntities() { EAXJMP(0x427060); }
-WRAPPER void CGarage::FindDoorsEntitiesSectorList(CPtrList&, bool) { EAXJMP(0x427300); }
-WRAPPER bool CGarages::HasResprayHappened(int16 garage) { EAXJMP(0x4274F0); }
+float CGarage::CalcDistToGarageRectangleSquared(float X, float Y)
+{
+ float distX, distY;
+ if (X < m_fX1)
+ distX = m_fX1 - X;
+ else if (X > m_fX2)
+ distX = X - m_fX2;
+ else
+ distX = 0.0f;
+ if (Y < m_fY1)
+ distY = m_fY1 - X;
+ else if (Y > m_fY2)
+ distY = Y - m_fY2;
+ else
+ distY = 0.0f;
+ return SQR(distX) + SQR(distY);
+}
+
+float CGarage::CalcSmallestDistToGarageDoorSquared(float X, float Y)
+{
+ float dist1 = 10000000.0f;
+ float dist2 = 10000000.0f;
+ if (m_pDoor1)
+ dist1 = SQR(m_fDoor1X - X) + SQR(m_fDoor1Y - Y);
+ if (m_pDoor2)
+ dist2 = SQR(m_fDoor2X - X) + SQR(m_fDoor2Y - Y);
+ return min(dist1, dist2);
+}
+
+void CGarage::FindDoorsEntities()
+{
+ m_pDoor1 = false;
+ m_pDoor2 = false;
+ int xstart = max(0, CWorld::GetSectorIndexX(m_fX1));
+ int xend = min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fX2));
+ int ystart = max(0, CWorld::GetSectorIndexY(m_fY1));
+ int yend = min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fY2));
+ assert(xstart <= xend);
+ assert(ystart <= yend);
+
+ CWorld::AdvanceCurrentScanCode();
+
+ for (int y = ystart; y <= yend; y++) {
+ for (int x = xstart; x <= xend; x++) {
+ CSector* s = CWorld::GetSector(x, y);
+ FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS], false);
+ FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS_OVERLAP], false);
+ FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES], true);
+ FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES_OVERLAP], true);
+ }
+ }
+ if (!m_pDoor1 || !m_pDoor2)
+ return;
+ if (m_pDoor1->GetModelIndex() == MI_CRUSHERBODY || m_pDoor1->GetModelIndex() == MI_CRUSHERLID)
+ return;
+ CVector2D vecDoor1ToGarage(m_pDoor1->GetPosition().x - GetGarageCenterX(), m_pDoor1->GetPosition().y - GetGarageCenterY());
+ CVector2D vecDoor2ToGarage(m_pDoor2->GetPosition().x - GetGarageCenterX(), m_pDoor2->GetPosition().y - GetGarageCenterY());
+ if (DotProduct2D(vecDoor1ToGarage, vecDoor2ToGarage) > 0.0f) {
+ if (vecDoor1ToGarage.MagnitudeSqr() >= vecDoor2ToGarage.MagnitudeSqr()) {
+ m_pDoor1 = m_pDoor2;
+ m_bDoor1IsDummy = m_bDoor2IsDummy;
+ }
+ m_pDoor2 = nil;
+ m_bDoor2IsDummy = false;
+ }
+}
+
+void CGarage::FindDoorsEntitiesSectorList(CPtrList& list, bool dummy)
+{
+ CPtrNode* node;
+ for (node = list.first; node; node = node->next) {
+ CEntity* pEntity = (CEntity*)node->item;
+ if (pEntity->m_scanCode == CWorld::GetCurrentScanCode())
+ continue;
+ pEntity->m_scanCode = CWorld::GetCurrentScanCode();
+ if (!pEntity || !CGarages::IsModelIndexADoor(pEntity->GetModelIndex()))
+ continue;
+ if (Abs(pEntity->GetPosition().x - GetGarageCenterX()) >= DISTANCE_TO_CONSIDER_DOOR_FOR_GARAGE)
+ continue;
+ if (Abs(pEntity->GetPosition().y - GetGarageCenterY()) >= DISTANCE_TO_CONSIDER_DOOR_FOR_GARAGE)
+ continue;
+ if (pEntity->GetModelIndex() == MI_CRUSHERBODY) {
+ m_pDoor1 = pEntity;
+ m_bDoor1IsDummy = dummy;
+ // very odd pool operations, they could have used GetJustIndex
+ if (dummy)
+ m_bDoor1PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F;
+ else
+ m_bDoor1PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F;
+ continue;
+ }
+ if (pEntity->GetModelIndex() == MI_CRUSHERLID) {
+ m_pDoor2 = pEntity;
+ m_bDoor2IsDummy = dummy;
+ if (dummy)
+ m_bDoor2PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F;
+ else
+ m_bDoor2PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F;
+ continue;
+ }
+ if (!m_pDoor1) {
+ m_pDoor1 = pEntity;
+ m_bDoor1IsDummy = dummy;
+ if (dummy)
+ m_bDoor1PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F;
+ else
+ m_bDoor1PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F;
+ continue;
+ }
+ else {
+ m_pDoor2 = pEntity;
+ m_bDoor2IsDummy = dummy;
+ if (dummy)
+ m_bDoor2PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F;
+ else
+ m_bDoor2PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F;
+ }
+ }
+}
+
+bool CGarages::HasResprayHappened(int16 garage)
+{
+ bool result = aGarages[garage].m_bResprayHappened;
+ aGarages[garage].m_bResprayHappened = false;
+ return result;
+}
void CGarages::SetGarageDoorToRotate(int16 garage)
{
@@ -1310,19 +1807,149 @@ void CGarages::SetLeaveCameraForThisGarage(int16 garage)
aGarages[garage].m_bCameraFollowsPlayer = true;
}
-WRAPPER bool CGarages::IsThisCarWithinGarageArea(int16 garage, CEntity* pCar) { EAXJMP(0x427570); }
+bool CGarages::IsThisCarWithinGarageArea(int16 garage, CEntity * pCar)
+{
+ return aGarages[garage].IsEntityEntirelyInside3D(pCar, 0.0f);
+}
bool CGarages::HasCarBeenCrushed(int32 handle)
{
return CrushedCarId == handle;
}
-WRAPPER void CStoredCar::StoreCar(CVehicle*) { EAXJMP(0x4275C0); }
-WRAPPER CVehicle* CStoredCar::RestoreCar() { EAXJMP(0x427690); }
-WRAPPER void CGarage::StoreAndRemoveCarsForThisHideout(CStoredCar*, int32) { EAXJMP(0x427840); }
-WRAPPER bool CGarage::RestoreCarsForThisHideout(CStoredCar*) { EAXJMP(0x427A40); }
-WRAPPER bool CGarages::IsPointInAGarageCameraZone(CVector) { EAXJMP(0x427AB0); }
-WRAPPER bool CGarages::CameraShouldBeOutside() { EAXJMP(0x427BC0); }
+void CStoredCar::StoreCar(CVehicle* pVehicle)
+{
+ m_nModelIndex = pVehicle->GetModelIndex();
+ m_vecPos = pVehicle->GetPosition();
+ m_vecAngle = pVehicle->GetForward();
+ m_nPrimaryColor = pVehicle->m_currentColour1;
+ m_nSecondaryColor = pVehicle->m_currentColour2;
+ m_nRadioStation = pVehicle->m_nRadioStation;
+ m_nVariationA = pVehicle->m_aExtras[0];
+ m_nVariationB = pVehicle->m_aExtras[1];
+ m_bBulletproof = pVehicle->bBulletProof;
+ m_bFireproof = pVehicle->bFireProof;
+ m_bExplosionproof = pVehicle->bExplosionProof;
+ m_bCollisionproof = pVehicle->bCollisionProof;
+ m_bMeleeproof = pVehicle->bMeleeProof;
+ if (pVehicle->IsCar())
+ m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType;
+}
+
+CVehicle* CStoredCar::RestoreCar()
+{
+ CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY);
+ if (!CStreaming::HasModelLoaded(m_nModelIndex))
+ return nil;
+ CVehicleModelInfo::SetComponentsToUse(m_nVariationA, m_nVariationB);
+#ifdef FIX_BUGS
+ CVehicle* pVehicle;
+ if (CModelInfo::IsBoatModel(m_nModelIndex))
+ pVehicle = new CBoat(m_nModelIndex, RANDOM_VEHICLE);
+ else
+ pVehicle = new CAutomobile(m_nModelIndex, RANDOM_VEHICLE);
+#else
+ CVehicle* pVehicle = new CAutomobile(m_nModelIndex, RANDOM_VEHICLE);
+#endif
+ pVehicle->GetPosition() = m_vecPos;
+ pVehicle->m_status = STATUS_ABANDONED;
+ pVehicle->GetForward() = m_vecAngle;
+ pVehicle->GetRight() = CVector(m_vecAngle.y, -m_vecAngle.x, 0.0f);
+ pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f);
+ pVehicle->pDriver = nil;
+ pVehicle->m_currentColour1 = m_nPrimaryColor;
+ pVehicle->m_currentColour2 = m_nSecondaryColor;
+ pVehicle->m_nRadioStation = m_nRadioStation;
+ pVehicle->bFreebies = false;
+#ifdef FIX_BUGS
+ ((CAutomobile*)pVehicle)->m_bombType = m_nCarBombType;
+#endif
+ pVehicle->bHasBeenOwnedByPlayer = true;
+ pVehicle->m_nDoorLock = CARLOCK_UNLOCKED;
+ pVehicle->bBulletProof = m_bBulletproof;
+ pVehicle->bFireProof = m_bFireproof;
+ pVehicle->bExplosionProof = m_bExplosionproof;
+ pVehicle->bCollisionProof = m_bCollisionproof;
+ pVehicle->bMeleeProof = m_bMeleeproof;
+ return pVehicle;
+}
+
+void CGarage::StoreAndRemoveCarsForThisHideout(CStoredCar* aCars, int32 nMax)
+{
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++)
+ aCars[i].Clear();
+ int i = CPools::GetVehiclePool()->GetSize();
+ int index = 0;
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ if (pVehicle->GetPosition().x > m_fX1 && pVehicle->GetPosition().x < m_fX2 &&
+ pVehicle->GetPosition().y > m_fY1 && pVehicle->GetPosition().y < m_fY2 &&
+ pVehicle->GetPosition().z > m_fZ1 && pVehicle->GetPosition().z < m_fZ2) {
+ if (pVehicle->VehicleCreatedBy != MISSION_VEHICLE) {
+ if (index < max(NUM_GARAGE_STORED_CARS, nMax) && !EntityHasASphereWayOutsideGarage(pVehicle, 1.0f))
+ aCars[index++].StoreCar(pVehicle);
+ CWorld::Players[CWorld::PlayerInFocus].CancelPlayerEnteringCars(pVehicle);
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ }
+ }
+ }
+ // why?
+ for (i = index; i < NUM_GARAGE_STORED_CARS; i++)
+ aCars[i].Clear();
+}
+
+bool CGarage::RestoreCarsForThisHideout(CStoredCar* aCars)
+{
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) {
+ if (aCars[i].HasCar()) {
+ CVehicle* pVehicle = aCars[i].RestoreCar();
+ if (pVehicle) {
+ CWorld::Add(pVehicle);
+ aCars[i].Clear();
+ }
+ }
+ }
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) {
+ if (aCars[i].HasCar())
+ return false;
+ }
+ return true;
+}
+
+bool CGarages::IsPointInAGarageCameraZone(CVector point)
+{
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ switch (aGarages[i].m_eGarageType) {
+ case GARAGE_NONE:
+ break;
+ case GARAGE_COLLECTCARS_1:
+ case GARAGE_COLLECTCARS_2:
+ case GARAGE_COLLECTCARS_3:
+ if (aGarages[i].m_fX1 - MARGIN_FOR_CAMERA_COLLECTCARS <= point.x &&
+ aGarages[i].m_fX2 + MARGIN_FOR_CAMERA_COLLECTCARS >= point.x &&
+ aGarages[i].m_fY1 - MARGIN_FOR_CAMERA_COLLECTCARS <= point.y &&
+ aGarages[i].m_fY2 + MARGIN_FOR_CAMERA_COLLECTCARS >= point.y)
+ return true;
+ break;
+ default:
+ if (aGarages[i].m_fX1 - MARGIN_FOR_CAMERA_DEFAULT <= point.x &&
+ aGarages[i].m_fX2 + MARGIN_FOR_CAMERA_DEFAULT >= point.x &&
+ aGarages[i].m_fY1 - MARGIN_FOR_CAMERA_DEFAULT <= point.y &&
+ aGarages[i].m_fY2 + MARGIN_FOR_CAMERA_DEFAULT >= point.y)
+ return true;
+ break;
+ }
+ }
+ return false;
+}
+
+bool CGarages::CameraShouldBeOutside()
+{
+ return bCamShouldBeOutisde;
+}
void CGarages::GivePlayerDetonator()
{
@@ -1330,21 +1957,299 @@ void CGarages::GivePlayerDetonator()
FindPlayerPed()->GetWeapon(FindPlayerPed()->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY;
}
-WRAPPER float CGarages::FindDoorHeightForMI(int32) { EAXJMP(0x427C10); }
-WRAPPER void CGarage::TidyUpGarage() { EAXJMP(0x427C30); }
-WRAPPER void CGarage::TidyUpGarageClose() { EAXJMP(0x427D90); }
-WRAPPER void CGarages::PlayerArrestedOrDied() { EAXJMP(0x427F60); }
-WRAPPER void CGarage::PlayerArrestedOrDied() { EAXJMP(0x427FC0); }
-WRAPPER void CGarage::CenterCarInGarage(CVehicle*) { EAXJMP(0x428000); }
-WRAPPER void CGarages::CloseHideOutGaragesBeforeSave() { EAXJMP(0x428130); }
-WRAPPER int32 CGarages::CountCarsInHideoutGarage(eGarageType) { EAXJMP(0x4281E0); }
-WRAPPER int32 CGarages::FindMaxNumStoredCarsForGarage(eGarageType) { EAXJMP(0x428230); }
-WRAPPER bool CGarages::IsPointWithinHideOutGarage(CVector&) { EAXJMP(0x428260); }
-WRAPPER bool CGarages::IsPointWithinAnyGarage(CVector&) { EAXJMP(0x428320); }
-WRAPPER void CGarages::SetAllDoorsBackToOriginalHeight() { EAXJMP(0x4283D0); }
-WRAPPER void CGarages::Save(uint8* buf, uint32* size) { EAXJMP(0x4284E0); }
+float CGarages::FindDoorHeightForMI(int32 mi)
+{
+ return CModelInfo::GetModelInfo(mi)->GetColModel()->boundingBox.max.z - CModelInfo::GetModelInfo(mi)->GetColModel()->boundingBox.min.z - 0.1f;
+}
+
+void CGarage::TidyUpGarage()
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle || !pVehicle->IsCar())
+ continue;
+ if (pVehicle->GetPosition().x > m_fX1 && pVehicle->GetPosition().x < m_fX2 &&
+ pVehicle->GetPosition().y > m_fY1 && pVehicle->GetPosition().y < m_fY2 &&
+ pVehicle->GetPosition().z > m_fZ1 && pVehicle->GetPosition().z < m_fZ2) {
+ if (pVehicle->m_status == STATUS_WRECKED || pVehicle->GetUp().z < 0.5f) {
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ }
+ }
+ }
+}
-CStoredCar::CStoredCar(const CStoredCar& other)
+void CGarage::TidyUpGarageClose()
+{
+ uint32 i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle || !pVehicle->IsCar())
+ continue;
+ if (!pVehicle->IsCar() || pVehicle->m_status != STATUS_WRECKED || !IsEntityTouching3D(pVehicle))
+ continue;
+ bool bRemove = false;
+ if (m_eGarageState != GS_FULLYCLOSED) {
+ CColModel* pColModel = CModelInfo::GetModelInfo(pVehicle->GetModelIndex())->GetColModel();
+ for (int i = 0; i < pColModel->numSpheres; i++) {
+ CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center;
+ float radius = pColModel->spheres[i].radius;
+ if (pos.x + radius < m_fX1 || pos.x - radius > m_fX2 ||
+ pos.y + radius < m_fY1 || pos.y - radius > m_fY2 ||
+ pos.z + radius < m_fZ1 || pos.z - radius > m_fZ2) {
+ bRemove = true;
+ }
+ }
+ }
+ else
+ bRemove = true;
+ if (bRemove) {
+ // no MISSION_VEHICLE check???
+ CWorld::Remove(pVehicle);
+ delete pVehicle;
+ }
+ }
+}
+
+void CGarages::PlayerArrestedOrDied()
+{
+ static int GarageToBeTidied = 0; // lol
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ if (aGarages[i].m_eGarageType != GARAGE_NONE)
+ aGarages[i].PlayerArrestedOrDied();
+ }
+ MessageEndTime = 0;
+ MessageStartTime = 0;
+}
+
+void CGarage::PlayerArrestedOrDied()
+{
+ switch (m_eGarageType) {
+ case GARAGE_MISSION:
+ case GARAGE_COLLECTORSITEMS:
+ case GARAGE_COLLECTSPECIFICCARS:
+ case GARAGE_COLLECTCARS_1:
+ case GARAGE_COLLECTCARS_2:
+ case GARAGE_COLLECTCARS_3:
+ case GARAGE_FORCARTOCOMEOUTOF:
+ case GARAGE_60SECONDS:
+ case GARAGE_MISSION_KEEPCAR:
+ case GARAGE_FOR_SCRIPT_TO_OPEN:
+ case GARAGE_HIDEOUT_ONE:
+ case GARAGE_HIDEOUT_TWO:
+ case GARAGE_HIDEOUT_THREE:
+ case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE:
+ case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR:
+ case GARAGE_MISSION_KEEPCAR_REMAINCLOSED:
+ switch (m_eGarageState) {
+ case GS_OPENED:
+ case GS_CLOSING:
+ case GS_OPENING:
+ m_eGarageState = GS_CLOSING;
+ break;
+ default:
+ break;
+ }
+ break;
+ case GARAGE_BOMBSHOP1:
+ case GARAGE_BOMBSHOP2:
+ case GARAGE_BOMBSHOP3:
+ case GARAGE_RESPRAY:
+ case GARAGE_CRUSHER:
+ switch (m_eGarageState) {
+ case GS_FULLYCLOSED:
+ case GS_CLOSING:
+ case GS_OPENING:
+ m_eGarageState = GS_OPENING;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void CGarage::CenterCarInGarage(CVehicle* pVehicle)
+{
+ if (IsAnyOtherCarTouchingGarage(FindPlayerVehicle()))
+ return;
+ if (IsAnyOtherPedTouchingGarage(FindPlayerPed()))
+ return;
+ CVector pos = pVehicle->GetPosition();
+ float garageX = GetGarageCenterX();
+ float garageY = GetGarageCenterY();
+ float offsetX = garageX - pos.x;
+ float offsetY = garageY - pos.y;
+ float offsetZ = pos.z - pos.z;
+ float distance = CVector(offsetX, offsetY, offsetZ).Magnitude();
+ if (distance < RESPRAY_CENTERING_COEFFICIENT) {
+ pVehicle->GetPosition().x = GetGarageCenterX();
+ pVehicle->GetPosition().y = GetGarageCenterY();
+ }
+ else {
+ pVehicle->GetPosition().x += offsetX * RESPRAY_CENTERING_COEFFICIENT / distance;
+ pVehicle->GetPosition().y += offsetY * RESPRAY_CENTERING_COEFFICIENT / distance;
+ }
+ if (!IsEntityEntirelyInside3D(pVehicle, 0.1f))
+ pVehicle->GetPosition() = pos;
+}
+
+void CGarages::CloseHideOutGaragesBeforeSave()
+{
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ if (aGarages[i].m_eGarageType != GARAGE_HIDEOUT_ONE &&
+ aGarages[i].m_eGarageType != GARAGE_HIDEOUT_TWO &&
+ aGarages[i].m_eGarageType != GARAGE_HIDEOUT_THREE)
+ continue;
+ if (aGarages[i].m_eGarageState != GS_FULLYCLOSED &&
+ aGarages[i].m_eGarageType != GARAGE_HIDEOUT_ONE || !aGarages[i].IsAnyCarBlockingDoor()) {
+ aGarages[i].m_eGarageState = GS_FULLYCLOSED;
+ switch (aGarages[i].m_eGarageType) {
+ case GARAGE_HIDEOUT_ONE:
+ aGarages[i].StoreAndRemoveCarsForThisHideout(aCarsInSafeHouse1, NUM_GARAGE_STORED_CARS);
+ aGarages[i].RemoveCarsBlockingDoorNotInside();
+ break;
+ case GARAGE_HIDEOUT_TWO:
+ aGarages[i].StoreAndRemoveCarsForThisHideout(aCarsInSafeHouse2, NUM_GARAGE_STORED_CARS);
+ aGarages[i].RemoveCarsBlockingDoorNotInside();
+ break;
+ case GARAGE_HIDEOUT_THREE:
+ aGarages[i].StoreAndRemoveCarsForThisHideout(aCarsInSafeHouse3, NUM_GARAGE_STORED_CARS);
+ aGarages[i].RemoveCarsBlockingDoorNotInside();
+ break;
+ default:
+ break;
+ }
+ }
+ aGarages[i].m_fDoorPos = 0.0f;
+ aGarages[i].UpdateDoorsHeight();
+ }
+}
+
+int32 CGarages::CountCarsInHideoutGarage(eGarageType type)
+{
+ int32 total = 0;
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) {
+ switch (type) {
+ case GARAGE_HIDEOUT_ONE:
+ total += (aCarsInSafeHouse1[i].HasCar());
+ break;
+ case GARAGE_HIDEOUT_TWO:
+ total += (aCarsInSafeHouse2[i].HasCar());
+ break;
+ case GARAGE_HIDEOUT_THREE:
+ total += (aCarsInSafeHouse3[i].HasCar());
+ break;
+ }
+ }
+ return total;
+}
+
+int32 CGarages::FindMaxNumStoredCarsForGarage(eGarageType type)
+{
+ switch (type) {
+ case GARAGE_HIDEOUT_ONE:
+ return LIMIT_CARS_IN_INDUSTRIAL;
+ case GARAGE_HIDEOUT_TWO:
+ return LIMIT_CARS_IN_COMMERCIAL;
+ case GARAGE_HIDEOUT_THREE:
+ return LIMIT_CARS_IN_SUBURBAN;
+ }
+ return 0;
+}
+
+bool CGarages::IsPointWithinHideOutGarage(CVector& point)
+{
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ switch (aGarages[i].m_eGarageType) {
+ case GARAGE_HIDEOUT_ONE:
+ case GARAGE_HIDEOUT_TWO:
+ case GARAGE_HIDEOUT_THREE:
+ if (point.x > aGarages[i].m_fX1 && point.x < aGarages[i].m_fX2 &&
+ point.y > aGarages[i].m_fY1 && point.y < aGarages[i].m_fY2 &&
+ point.z > aGarages[i].m_fZ1 && point.z < aGarages[i].m_fZ2)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CGarages::IsPointWithinAnyGarage(CVector& point)
+{
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ switch (aGarages[i].m_eGarageType) {
+ case GARAGE_NONE:
+ continue;
+ default:
+ if (point.x > aGarages[i].m_fX1 && point.x < aGarages[i].m_fX2 &&
+ point.y > aGarages[i].m_fY1 && point.y < aGarages[i].m_fY2 &&
+ point.z > aGarages[i].m_fZ1 && point.z < aGarages[i].m_fZ2)
+ return true;
+ }
+ }
+ return false;
+}
+
+void CGarages::SetAllDoorsBackToOriginalHeight()
+{
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ switch (aGarages[i].m_eGarageType) {
+ case GARAGE_NONE:
+ continue;
+ default:
+ aGarages[i].RefreshDoorPointers(true);
+ if (aGarages[i].m_pDoor1) {
+ aGarages[i].m_pDoor1->GetPosition().z = aGarages[i].m_fDoor1Z;
+ if (aGarages[i].m_pDoor1->IsObject())
+ ((CObject*)aGarages[i].m_pDoor1)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor1Z;
+ if (aGarages[i].m_bRotatedDoor)
+ aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor1, 0.0f);
+ aGarages[i].m_pDoor1->GetMatrix().UpdateRW();
+ aGarages[i].m_pDoor1->UpdateRwFrame();
+ }
+ if (aGarages[i].m_pDoor2) {
+ aGarages[i].m_pDoor2->GetPosition().z = aGarages[i].m_fDoor2Z;
+ if (aGarages[i].m_pDoor2->IsObject())
+ ((CObject*)aGarages[i].m_pDoor2)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor2Z;
+ if (aGarages[i].m_bRotatedDoor)
+ aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor2, 0.0f);
+ aGarages[i].m_pDoor2->GetMatrix().UpdateRW();
+ aGarages[i].m_pDoor2->UpdateRwFrame();
+ }
+ }
+ }
+}
+
+void CGarages::Save(uint8 * buf, uint32 * size)
+{
+#ifdef FIX_GARAGE_SIZE
+ *size = (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage));
+#else
+ * size = 5484;
+#endif
+ CloseHideOutGaragesBeforeSave();
+ WriteSaveBuf(buf, NumGarages);
+ WriteSaveBuf(buf, (uint32)BombsAreFree);
+ WriteSaveBuf(buf, (uint32)RespraysAreFree);
+ WriteSaveBuf(buf, CarsCollected);
+ WriteSaveBuf(buf, BankVansCollected);
+ WriteSaveBuf(buf, PoliceCarsCollected);
+ for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++)
+ WriteSaveBuf(buf, CarTypesCollected[i]);
+ WriteSaveBuf(buf, LastTimeHelpMessage);
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) {
+ WriteSaveBuf(buf, aCarsInSafeHouse1[i]);
+ WriteSaveBuf(buf, aCarsInSafeHouse2[i]);
+ WriteSaveBuf(buf, aCarsInSafeHouse3[i]);
+ }
+ for (int i = 0; i < NUM_GARAGES; i++)
+ WriteSaveBuf(buf, aGarages[i]);
+}
+
+CStoredCar::CStoredCar(const CStoredCar & other)
{
m_nModelIndex = other.m_nModelIndex;
m_vecPos = other.m_vecPos;
@@ -1362,7 +2267,42 @@ CStoredCar::CStoredCar(const CStoredCar& other)
m_nCarBombType = other.m_nCarBombType;
}
-WRAPPER void CGarages::Load(uint8* buf, uint32 size) { EAXJMP(0x428940); }
+void CGarages::Load(uint8* buf, uint32 size)
+{
+#ifdef FIX_GARAGE_SIZE
+ assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage));
+#else
+ assert(size == 5484);
+#endif
+ CloseHideOutGaragesBeforeSave();
+ NumGarages = ReadSaveBuf<uint32>(buf);
+ BombsAreFree = ReadSaveBuf<uint32>(buf);
+ RespraysAreFree = ReadSaveBuf<uint32>(buf);
+ CarsCollected = ReadSaveBuf<int32>(buf);
+ BankVansCollected = ReadSaveBuf<int32>(buf);
+ PoliceCarsCollected = ReadSaveBuf<int32>(buf);
+ for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++)
+ CarTypesCollected[i] = ReadSaveBuf<uint32>(buf);
+ LastTimeHelpMessage = ReadSaveBuf<uint32>(buf);
+ for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) {
+ aCarsInSafeHouse1[i] = ReadSaveBuf<CStoredCar>(buf);
+ aCarsInSafeHouse2[i] = ReadSaveBuf<CStoredCar>(buf);
+ aCarsInSafeHouse3[i] = ReadSaveBuf<CStoredCar>(buf);
+ }
+ for (int i = 0; i < NUM_GARAGES; i++) {
+ aGarages[i] = ReadSaveBuf<CGarage>(buf);
+ aGarages[i].m_pDoor1 = nil;
+ aGarages[i].m_pDoor2 = nil;
+ aGarages[i].m_pTarget = nil;
+ aGarages[i].field_96 = nil;
+ aGarages[i].m_bRecreateDoorOnNextRefresh = true;
+ aGarages[i].RefreshDoorPointers(true);
+ if (aGarages[i].m_eGarageType == GARAGE_CRUSHER)
+ aGarages[i].UpdateCrusherAngle();
+ else
+ aGarages[i].UpdateDoorsHeight();
+ }
+}
bool
CGarages::IsModelIndexADoor(uint32 id)
@@ -1404,9 +2344,8 @@ CGarages::IsModelIndexADoor(uint32 id)
STARTPATCHES
- InjectHook(0x421C60, CGarages::Init, PATCH_JUMP);
-#ifndef PS2
- InjectHook(0x421E10, CGarages::Shutdown, PATCH_JUMP);
-#endif
- InjectHook(0x421E40, CGarages::Update, PATCH_JUMP);
+ InjectHook(0x426B20, CGarages::TriggerMessage, PATCH_JUMP); // CCrane::Update, CCrane::FindCarInSectorList
+ InjectHook(0x427AB0, CGarages::IsPointInAGarageCameraZone, PATCH_JUMP); // CCamera::CamControl
+ InjectHook(0x427BC0, CGarages::CameraShouldBeOutside, PATCH_JUMP); // CCamera::CamControl
+ InjectHook(0x428940, CGarages::Load, PATCH_JUMP); // GenericLoad
ENDPATCHES \ No newline at end of file
diff --git a/src/control/Garages.h b/src/control/Garages.h
index e39a81fa..3f471555 100644
--- a/src/control/Garages.h
+++ b/src/control/Garages.h
@@ -45,7 +45,8 @@ enum eGarageType : int8
enum
{
- TOTAL_COLLECTCARS_GARAGES = GARAGE_COLLECTCARS_3 - GARAGE_COLLECTCARS_1 + 1
+ TOTAL_COLLECTCARS_GARAGES = GARAGE_COLLECTCARS_3 - GARAGE_COLLECTCARS_1 + 1,
+ TOTAL_COLLECTCARS_CARS = 16
};
class CStoredCar
@@ -66,6 +67,8 @@ class CStoredCar
int8 m_nCarBombType;
public:
void Init() { m_nModelIndex = 0; }
+ void Clear() { m_nModelIndex = 0; }
+ bool HasCar() { return m_nModelIndex != 0; }
CStoredCar(const CStoredCar& other);
void StoreCar(CVehicle*);
CVehicle* RestoreCar();
@@ -77,10 +80,9 @@ static_assert(sizeof(CStoredCar) == 0x28, "CStoredCar");
class CGarage
{
-public:
eGarageType m_eGarageType;
eGarageState m_eGarageState;
- char field_2;
+ bool field_2; // unused
bool m_bClosingWithoutTargetCar;
bool m_bDeactivated;
bool m_bResprayHappened;
@@ -109,13 +111,10 @@ public:
float m_fDoor1Z;
float m_fDoor2Z;
uint32 m_nTimeToStartAction;
- char m_bCollectedCarsState;
- char field_89;
- char field_90;
- char field_91;
+ uint8 m_bCollectedCarsState;
CVehicle *m_pTarget;
- int field_96;
- CStoredCar m_sStoredCar;
+ void* field_96; // unused
+ CStoredCar m_sStoredCar; // not needed
void OpenThisGarage();
void CloseThisGarage();
@@ -125,7 +124,7 @@ public:
void Update();
float GetGarageCenterX() { return (m_fX1 + m_fX2) / 2; }
float GetGarageCenterY() { return (m_fY1 + m_fY2) / 2; }
- bool IsClose()
+ bool IsFar()
{
#ifdef FIX_BUGS
return Abs(TheCamera.GetPosition().x - GetGarageCenterX()) > SWITCH_GARAGE_DISTANCE_CLOSE ||
@@ -151,7 +150,7 @@ public:
bool IsAnyCarBlockingDoor();
void CenterCarInGarage(CVehicle*);
bool DoesCraigNeedThisCar(int32);
- void MarkThisCarAsCollectedForCraig(int32);
+ bool MarkThisCarAsCollectedForCraig(int32);
bool HasCraigCollectedThisCar(int32);
bool IsGarageEmpty();
void UpdateCrusherShake(float, float);
@@ -166,13 +165,18 @@ public:
void FindDoorsEntities();
void FindDoorsEntitiesSectorList(CPtrList&, bool);
void PlayerArrestedOrDied();
+
+ friend class CGarages;
+ friend class cAudioManager;
};
static_assert(sizeof(CGarage) == 140, "CGarage");
class CGarages
{
-public:
+ enum {
+ MESSAGE_LENGTH = 8
+ };
static int32 &BankVansCollected;
static bool &BombsAreFree;
static bool &RespraysAreFree;
@@ -181,7 +185,7 @@ public:
static int32 &CrushedCarId;
static uint32 &LastTimeHelpMessage;
static int32 &MessageNumberInString;
- static const char *MessageIDString;
+ static char(&MessageIDString)[MESSAGE_LENGTH];
static int32 &MessageNumberInString2;
static uint32 &MessageStartTime;
static uint32 &MessageEndTime;
@@ -195,50 +199,56 @@ public:
static CStoredCar(&aCarsInSafeHouse3)[NUM_GARAGE_STORED_CARS];
static int32 &AudioEntity;
static bool &bCamShouldBeOutisde;
+
public:
static void Init(void);
#ifndef PS2
static void Shutdown(void);
#endif
- static int16 AddOne(float X1, float Y1, float Z1, float X2, float Y2, float Z2, eGarageType type, int32 targetId);
+ static void Update(void);
- static bool IsModelIndexADoor(uint32 id);
- static void TriggerMessage(const char *text, int16, uint16 time, int16);
+ static int16 AddOne(float X1, float Y1, float Z1, float X2, float Y2, float Z2, eGarageType type, int32 targetId);
+ static void ChangeGarageType(int16, eGarageType, int32);
static void PrintMessages(void);
- static bool HasCarBeenCrushed(int32);
- static bool IsPointWithinHideOutGarage(CVector&);
- static bool IsPointWithinAnyGarage(CVector&);
- static void PlayerArrestedOrDied();
-
- static void Update(void);
- static void Load(uint8 *buf, uint32 size);
- static void Save(uint8 *buf, uint32 *size);
+ static void TriggerMessage(const char* text, int16, uint16 time, int16);
static void SetTargetCarForMissonGarage(int16, CVehicle*);
static bool HasCarBeenDroppedOffYet(int16);
- static void ActivateGarage(int16);
static void DeActivateGarage(int16);
+ static void ActivateGarage(int16);
static int32 QueryCarsCollected(int16);
- static bool HasThisCarBeenCollected(int16, uint8);
- static void ChangeGarageType(int16, eGarageType, int32);
- static bool HasResprayHappened(int16);
- static void GivePlayerDetonator();
+ static bool HasImportExportGarageCollectedThisCar(int16, int8);
static bool IsGarageOpen(int16);
static bool IsGarageClosed(int16);
+ static bool HasThisCarBeenCollected(int16, uint8);
+ static void OpenGarage(int16 garage) { aGarages[garage].OpenThisGarage(); }
+ static void CloseGarage(int16 garage) { aGarages[garage].CloseThisGarage(); }
+ static bool HasResprayHappened(int16);
static void SetGarageDoorToRotate(int16);
- static bool HasImportExportGarageCollectedThisCar(int16, int8);
static void SetLeaveCameraForThisGarage(int16);
static bool IsThisCarWithinGarageArea(int16, CEntity*);
- static bool IsCarSprayable(CVehicle*);
- static int32 FindMaxNumStoredCarsForGarage(eGarageType);
- static int32 CountCarsInHideoutGarage(eGarageType);
+ static bool HasCarBeenCrushed(int32);
static bool IsPointInAGarageCameraZone(CVector);
- static bool CameraShouldBeOutside();
- static void CloseHideOutGaragesBeforeSave();
- static void SetAllDoorsBackToOriginalHeight();
-
- static int GetBombTypeForGarageType(eGarageType type) { return type - GARAGE_BOMBSHOP1 + 1; }
- static int GetCarsCollectedIndexForGarageType(eGarageType type) { return type - GARAGE_COLLECTCARS_1; }
+ static bool CameraShouldBeOutside(void);
+ static void GivePlayerDetonator(void);
+ static void PlayerArrestedOrDied(void);
+ static bool IsPointWithinHideOutGarage(CVector&);
+ static bool IsPointWithinAnyGarage(CVector&);
+ static void SetAllDoorsBackToOriginalHeight(void);
+ static void Save(uint8* buf, uint32* size);
+ static void Load(uint8* buf, uint32 size);
+ static bool IsModelIndexADoor(uint32 id);
+ static void SetFreeBombs(bool bValue) { BombsAreFree = bValue; }
+ static void SetFreeResprays(bool bValue) { RespraysAreFree = bValue; }
private:
+ static bool IsCarSprayable(CVehicle*);
static float FindDoorHeightForMI(int32);
+ static void CloseHideOutGaragesBeforeSave(void);
+ static int32 CountCarsInHideoutGarage(eGarageType);
+ static int32 FindMaxNumStoredCarsForGarage(eGarageType);
+ static int32 GetBombTypeForGarageType(eGarageType type) { return type - GARAGE_BOMBSHOP1 + 1; }
+ static int32 GetCarsCollectedIndexForGarageType(eGarageType type) { return type - GARAGE_COLLECTCARS_1; }
+
+ friend class cAudioManager;
+ friend class CGarage;
};
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index b1832f0e..3e3c2a48 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -20,6 +20,9 @@
#include "Fire.h"
#include "PointLights.h"
#include "Pools.h"
+#ifdef FIX_BUGS
+#include "Replay.h"
+#endif
#include "Script.h"
#include "Shadows.h"
#include "SpecialFX.h"
@@ -642,32 +645,26 @@ CPickups::AddToCollectedPickupsArray(int32 index)
void
CPickups::Update()
{
-#ifndef FIX_BUGS
- // BUG: this code can only reach 318 out of 320 pickups
+#ifdef FIX_BUGS // RIP speedrunning (solution from SA)
+ if (CReplay::IsPlayingBack())
+ return;
+#endif
#define PICKUPS_FRAME_SPAN (6)
-#define PICKUPS_PER_FRAME (NUMGENERALPICKUPS/PICKUPS_FRAME_SPAN)
-
- for (uint32 i = PICKUPS_PER_FRAME * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < PICKUPS_PER_FRAME * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) {
- if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
- AddToCollectedPickupsArray(i);
- }
- }
-
- for (uint32 i = NUMGENERALPICKUPS; i < NUMPICKUPS; i++) {
+#ifdef FIX_BUGS
+ for (uint32 i = NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN) / PICKUPS_FRAME_SPAN; i < NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1) / PICKUPS_FRAME_SPAN; i++) {
+#else // BUG: this code can only reach 318 out of 320 pickups
+ for (uint32 i = NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) {
+#endif
if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
AddToCollectedPickupsArray(i);
}
}
-
#undef PICKUPS_FRAME_SPAN
-#undef PICKUPS_PER_FRAME
-#else
- for (uint32 i = 0; i < NUMPICKUPS; i++) {
+ for (uint32 i = NUMGENERALPICKUPS; i < NUMPICKUPS; i++) {
if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) {
AddToCollectedPickupsArray(i);
}
}
-#endif
}
void
diff --git a/src/control/Script.cpp b/src/control/Script.cpp
index 2ea5ee24..8a79ba1d 100644
--- a/src/control/Script.cpp
+++ b/src/control/Script.cpp
@@ -4671,7 +4671,7 @@ int8 CRunningScript::ProcessCommands500To599(int32 command)
return 0;
case COMMAND_SET_FREE_BOMBS:
CollectParameters(&m_nIp, 1);
- CGarages::BombsAreFree = (ScriptParams[0] != 0);
+ CGarages::SetFreeBombs(ScriptParams[0] != 0);
return 0;
#ifdef GTA_PS2
case COMMAND_SET_POWERPOINT:
@@ -6662,7 +6662,7 @@ int8 CRunningScript::ProcessCommands800To899(int32 command)
}
case COMMAND_SET_FREE_RESPRAYS:
CollectParameters(&m_nIp, 1);
- CGarages::RespraysAreFree = (ScriptParams[0] != 0);
+ CGarages::SetFreeResprays(ScriptParams[0] != 0);
return 0;
case COMMAND_SET_PLAYER_VISIBLE:
{
@@ -7110,13 +7110,13 @@ int8 CRunningScript::ProcessCommands800To899(int32 command)
case COMMAND_OPEN_GARAGE:
{
CollectParameters(&m_nIp, 1);
- CGarages::aGarages[ScriptParams[0]].OpenThisGarage();
+ CGarages::OpenGarage(ScriptParams[0]);
return 0;
}
case COMMAND_CLOSE_GARAGE:
{
CollectParameters(&m_nIp, 1);
- CGarages::aGarages[ScriptParams[0]].CloseThisGarage();
+ CGarages::CloseGarage(ScriptParams[1]);
return 0;
}
case COMMAND_WARP_CHAR_FROM_CAR_TO_COORD:
diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp
index 5844b61a..dd1b5ce2 100644
--- a/src/core/Cam.cpp
+++ b/src/core/Cam.cpp
@@ -23,12 +23,18 @@
#include "SceneEdit.h"
#include "Debug.h"
#include "Camera.h"
+#include "DMAudio.h"
const float DefaultFOV = 70.0f; // beta: 80.0f
bool PrintDebugCode = false;
int16 &DebugCamMode = *(int16*)0x95CCF2;
-bool bFreeCam = false;
+
+#ifdef FREE_CAM
+bool bFreePadCam = false;
+bool bFreeMouseCam = false;
+int nPreviousMode = -1;
+#endif
void
CCam::Init(void)
@@ -140,7 +146,7 @@ CCam::Process(void)
Process_FollowPedWithMouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
else
#ifdef FREE_CAM
- if(bFreeCam)
+ if(bFreePadCam)
Process_FollowPed_Rotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
else
#endif
@@ -180,7 +186,12 @@ CCam::Process(void)
Process_FlyBy(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_CAM_ON_A_STRING:
- Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+#ifdef FREE_CAM
+ if(bFreeMouseCam || bFreePadCam)
+ Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+ else
+#endif
+ Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_REACTION:
Process_ReactionCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
@@ -192,7 +203,12 @@ CCam::Process(void)
Process_Chris_With_Binding_PlusRotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_BEHINDBOAT:
- Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+#ifdef FREE_CAM
+ if (bFreeMouseCam || bFreePadCam)
+ Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+ else
+#endif
+ Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_PLAYER_FALLEN_WATER:
Process_Player_Fallen_Water(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
@@ -244,6 +260,9 @@ CCam::Process(void)
Up = CVector(0.0f, 0.0f, 1.0f);
}
+#ifdef FREE_CAM
+ nPreviousMode = Mode;
+#endif
CVector TargetToCam = Source - m_cvecTargetCoorsForFudgeInter;
float DistOnGround = TargetToCam.Magnitude2D();
m_fTrueBeta = CGeneral::GetATanOfXY(TargetToCam.x, TargetToCam.y);
@@ -1530,7 +1549,12 @@ CCam::Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrient
TargetCoors.z += fTranslateCamUp;
TargetCoors = DoAverageOnVector(TargetCoors);
+ // SA code
+#ifdef FREE_CAM
+ if((bFreeMouseCam && Alpha > 0.0f) || (!bFreeMouseCam && Alpha > fBaseDist))
+#else
if(Alpha > fBaseDist) // comparing an angle against a distance?
+#endif
CamDist = fBaseDist + Cos(min(Alpha*fFalloff, HALFPI))*fAngleDist;
else
CamDist = fBaseDist + Cos(Alpha)*fAngleDist;
@@ -4441,7 +4465,7 @@ CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrient
float MouseX = CPad::GetPad(0)->GetMouseX();
float MouseY = CPad::GetPad(0)->GetMouseY();
float LookLeftRight, LookUpDown;
- if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){
+ if(bFreeMouseCam && (MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){
UseMouse = true;
LookLeftRight = -2.5f*MouseX;
LookUpDown = 4.0f*MouseY;
@@ -4572,6 +4596,645 @@ CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrient
GetVectorsReadyForRW();
}
+
+// LCS cam hehe
+void
+CCam::Process_FollowCar_SA(const CVector& CameraTarget, float TargetOrientation, float, float)
+{
+ // Missing things on III CCam
+ static CVector m_aTargetHistoryPosOne;
+ static CVector m_aTargetHistoryPosTwo;
+ static CVector m_aTargetHistoryPosThree;
+ static int m_nCurrentHistoryPoints = 0;
+ static float lastBeta = -9999.0f;
+ static float lastAlpha = -9999.0f;
+ static float stepsLeftToChangeBetaByMouse;
+ static float dontCollideWithCars;
+ static bool alphaCorrected;
+ static float heightIncreaseMult;
+
+ if (!CamTargetEntity->IsVehicle())
+ return;
+
+ CVehicle* car = (CVehicle*)CamTargetEntity;
+ CVector TargetCoors = CameraTarget;
+ uint8 camSetArrPos = 0;
+
+ // We may need those later
+ bool isPlane = car->m_modelIndex == MI_DODO;
+ bool isHeli = false;
+ bool isBike = false;
+ bool isCar = car->IsCar() && !isPlane && !isHeli && !isBike;
+
+ CPad* pad = CPad::GetPad(0);
+
+ // Next direction is non-existent in III
+ uint8 nextDirectionIsForward = !(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()) &&
+ DirectionWasLooking == LOOKING_FORWARD;
+
+ if (car->m_modelIndex == MI_FIRETRUCK) {
+ camSetArrPos = 7;
+ } else if (car->m_modelIndex == MI_RCBANDIT) {
+ camSetArrPos = 5;
+ } else if (car->IsBoat()) {
+ camSetArrPos = 4;
+ } else if (isBike) {
+ camSetArrPos = 1;
+ } else if (isPlane) {
+ camSetArrPos = 3;
+ } else if (isHeli) {
+ camSetArrPos = 2;
+ }
+
+ // LCS one but index 1(firetruck) moved to last
+ float CARCAM_SET[][15] = {
+ {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // cars
+ {1.1f, 1.0f, 0.1f, 10.0f, 11.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.75f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // bike
+ {1.1f, 1.0f, 0.2f, 10.0f, 15.0f, 0.05f, 0.05f, 0.0f, 0.9f, 0.05f, 0.01f, 0.05f, 1.0f, DEGTORAD(10.0f), DEGTORAD(70.0f)}, // heli (SA values)
+ {1.1f, 3.5f, 0.2f, 10.0f, 25.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(89.0f), DEGTORAD(89.0f)}, // plane (SA values)
+ {0.9f, 1.0f, 0.1f, 10.0f, 15.0f, 0.5f, 1.0f, 0.0f, 0.9f, 0.05f, 0.005f, 0.05f, 1.0f, -0.2f, DEGTORAD(70.0f)}, // boat
+ {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // rc cars
+ {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(20.0f), DEGTORAD(70.0f)}, // rc heli/planes
+ {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, -0.18f, DEGTORAD(40.0f)}, // firetruck...
+ };
+
+ // RC Heli/planes use same alpha values with heli/planes (LCS firetruck will fallback to 0)
+ uint8 alphaArrPos = (camSetArrPos > 4 ? (isPlane ? 3 : (isHeli ? 2 : 0)) : camSetArrPos);
+ float zoomModeAlphaOffset = 0.0f;
+ static float ZmOneAlphaOffsetLCS[] = { 0.12f, 0.08f, 0.15f, 0.08f, 0.08f };
+ static float ZmTwoAlphaOffsetLCS[] = { 0.1f, 0.08f, 0.3f, 0.08f, 0.08f };
+ static float ZmThreeAlphaOffsetLCS[] = { 0.065f, 0.05f, 0.15f, 0.06f, 0.08f };
+
+ if (isHeli && car->m_status == STATUS_PLAYER_REMOTE)
+ zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos];
+ else {
+ switch ((int)TheCamera.CarZoomIndicator) {
+ // near
+ case 1:
+ zoomModeAlphaOffset = ZmOneAlphaOffsetLCS[alphaArrPos];
+ break;
+ // mid
+ case 2:
+ zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos];
+ break;
+ // far
+ case 3:
+ zoomModeAlphaOffset = ZmThreeAlphaOffsetLCS[alphaArrPos];
+ break;
+ default:
+ break;
+ }
+ }
+
+ CColModel* carCol = (CColModel*)car->GetColModel();
+ float colMaxZ = carCol->boundingBox.max.z; // As opposed to LCS and SA, VC does this: carCol->boundingBox.max.z - carCol->boundingBox.min.z;
+ float approxCarLength = 2.0f * Abs(carCol->boundingBox.min.y); // SA taxi min.y = -2.95, max.z = 0.883502f
+
+ float newDistance = TheCamera.CarZoomValueSmooth + CARCAM_SET[camSetArrPos][1] + approxCarLength;
+
+ float minDistForThisCar = approxCarLength * CARCAM_SET[camSetArrPos][3];
+
+ if (!isHeli || car->m_status == STATUS_PLAYER_REMOTE) {
+ float radiusToStayOutside = colMaxZ * CARCAM_SET[camSetArrPos][0] - CARCAM_SET[camSetArrPos][2];
+ if (radiusToStayOutside > 0.0f) {
+ TargetCoors.z += radiusToStayOutside;
+ newDistance += radiusToStayOutside;
+ zoomModeAlphaOffset += 0.3f / newDistance * radiusToStayOutside;
+ }
+ } else {
+ // 0.6f = fTestShiftHeliCamTarget
+ TargetCoors.x += 0.6f * car->GetUp().x * colMaxZ;
+ TargetCoors.y += 0.6f * car->GetUp().y * colMaxZ;
+ TargetCoors.z += 0.6f * car->GetUp().z * colMaxZ;
+ }
+
+ float minDistForVehType = CARCAM_SET[camSetArrPos][4];
+
+ if ((int)TheCamera.CarZoomIndicator == 1 && (camSetArrPos < 2 || camSetArrPos == 7)) {
+ minDistForVehType = minDistForVehType * 0.65f;
+ }
+
+ float nextDistance = max(newDistance, minDistForVehType);
+
+ CA_MAX_DISTANCE = newDistance;
+ CA_MIN_DISTANCE = 3.5f;
+
+ if (ResetStatics) {
+ FOV = DefaultFOV;
+
+ // GTA 3 has this in veh. camera
+ if (TheCamera.m_bIdleOn)
+ TheCamera.m_uiTimeWeEnteredIdle = CTimer::GetTimeInMilliseconds();
+ } else {
+ if (isCar || isBike) {
+ // 0.4f: CAR_FOV_START_SPEED
+ if (DotProduct(car->GetForward(), car->m_vecMoveSpeed) > 0.4f)
+ FOV += (DotProduct(car->GetForward(), car->m_vecMoveSpeed) - 0.4f) * CTimer::GetTimeStep();
+ }
+
+ if (FOV > DefaultFOV)
+ // 0.98f: CAR_FOV_FADE_MULT
+ FOV = pow(0.98f, CTimer::GetTimeStep()) * (FOV - DefaultFOV) + DefaultFOV;
+
+ if (FOV <= DefaultFOV + 30.0f) {
+ if (FOV < DefaultFOV)
+ FOV = DefaultFOV;
+ } else
+ FOV = DefaultFOV + 30.0f;
+ }
+
+ // WORKAROUND: I still don't know how looking behind works (m_bCamDirectlyInFront is unused in III, they seem to use m_bUseTransitionBeta)
+ if (pad->GetLookBehindForCar())
+ if (DirectionWasLooking == LOOKING_FORWARD || !LookingBehind)
+ TheCamera.m_bCamDirectlyInFront = true;
+
+ // Taken from RotCamIfInFrontCar, because we don't call it anymore
+ if (!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()))
+ if (DirectionWasLooking != LOOKING_FORWARD)
+ TheCamera.m_bCamDirectlyBehind = true;
+
+ // Called when we just entered the car, just started to look behind or returned back from looking left, right or behind
+ if (ResetStatics || TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront) {
+ ResetStatics = false;
+ Rotating = false;
+ m_bCollisionChecksOn = true;
+ // TheCamera.m_bResetOldMatrix = 1;
+
+ // Garage exit cam is not working well in III...
+ // if (!TheCamera.m_bJustCameOutOfGarage) // && !sthForScript)
+ // {
+ Alpha = 0.0f;
+ Beta = car->GetForward().Heading() - HALFPI;
+ if (TheCamera.m_bCamDirectlyInFront) {
+ Beta += PI;
+ }
+ // }
+
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ Distance = 1000.0;
+
+ Front.x = -(cos(Beta) * cos(Alpha));
+ Front.y = -(sin(Beta) * cos(Alpha));
+ Front.z = sin(Alpha);
+
+ m_aTargetHistoryPosOne = TargetCoors - nextDistance * Front;
+
+ m_aTargetHistoryPosTwo = TargetCoors - newDistance * Front;
+
+ m_nCurrentHistoryPoints = 0;
+ if (!TheCamera.m_bJustCameOutOfGarage) // && !sthForScript)
+ Alpha = -zoomModeAlphaOffset;
+ }
+
+ Front = TargetCoors - m_aTargetHistoryPosOne;
+ Front.Normalise();
+
+ // Code that makes cam rotate around the car
+ float camRightHeading = Front.Heading() - HALFPI;
+ if (camRightHeading < -PI)
+ camRightHeading = camRightHeading + TWOPI;
+
+ float velocityRightHeading;
+ if (car->m_vecMoveSpeed.Magnitude2D() <= 0.02f)
+ velocityRightHeading = camRightHeading;
+ else
+ velocityRightHeading = car->m_vecMoveSpeed.Heading() - HALFPI;
+
+ if (velocityRightHeading < camRightHeading - PI)
+ velocityRightHeading = velocityRightHeading + TWOPI;
+ else if (velocityRightHeading > camRightHeading + PI)
+ velocityRightHeading = velocityRightHeading - TWOPI;
+
+ float betaChangeMult1 = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][10];
+ float betaChangeLimit = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][11];
+
+ float betaChangeMult2 = (car->m_vecMoveSpeed - DotProduct(car->m_vecMoveSpeed, Front) * Front).Magnitude();
+
+ float betaChange = min(1.0f, betaChangeMult1 * betaChangeMult2) * (velocityRightHeading - camRightHeading);
+ if (betaChange <= betaChangeLimit) {
+ if (betaChange < -betaChangeLimit)
+ betaChange = -betaChangeLimit;
+ } else {
+ betaChange = betaChangeLimit;
+ }
+ float targetBeta = camRightHeading + betaChange;
+
+ if (targetBeta < Beta - HALFPI)
+ targetBeta += TWOPI;
+ else if (targetBeta > Beta + PI)
+ targetBeta -= TWOPI;
+
+ float carPosChange = (TargetCoors - m_aTargetHistoryPosTwo).Magnitude();
+ if (carPosChange < newDistance && newDistance > minDistForThisCar) {
+ newDistance = max(minDistForThisCar, carPosChange);
+ }
+ float maxAlphaAllowed = CARCAM_SET[camSetArrPos][13];
+
+ // Originally this is to prevent camera enter into car while we're stopping, but what about moving???
+ // This is also original LCS and SA bug, or some attempt to fix lag. We'll never know
+
+ // if (car->m_vecMoveSpeed.MagnitudeSqr() < sq(0.2f))
+ if (car->m_modelIndex != MI_FIRETRUCK) {
+ // if (!isBike || GetMysteriousWheelRelatedThingBike(car) > 3)
+ // if (!isHeli && (!isPlane || car->GetWheelsOnGround())) {
+
+ CVector left = CrossProduct(car->GetForward(), CVector(0.0f, 0.0f, 1.0f));
+ left.Normalise();
+ CVector up = CrossProduct(left, car->GetForward());
+ up.Normalise();
+ float lookingUp = DotProduct(up, Front);
+ if (lookingUp > 0.0f) {
+ float v88 = Asin(Abs(Sin(Beta - (car->GetForward().Heading() - HALFPI))));
+ float v200;
+ if (v88 <= Atan2(carCol->boundingBox.max.x, -carCol->boundingBox.min.y)) {
+ v200 = (1.5f - carCol->boundingBox.min.y) / Cos(v88);
+ } else {
+ float a6g = 1.2f + carCol->boundingBox.max.x;
+ v200 = a6g / Cos(max(0.0f, HALFPI - v88));
+ }
+ maxAlphaAllowed = Cos(Beta - (car->GetForward().Heading() - HALFPI)) * Atan2(car->GetForward().z, car->GetForward().Magnitude2D())
+ + Atan2(TargetCoors.z - car->GetPosition().z + car->GetHeightAboveRoad(), v200 * 1.2f);
+
+ if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 && Abs(DotProduct(car->m_vecTurnSpeed, car->GetForward())) < 0.05f) {
+ maxAlphaAllowed += Cos(Beta - (car->GetForward().Heading() - HALFPI) + HALFPI) * Atan2(car->GetRight().z, car->GetRight().Magnitude2D());
+ }
+ }
+ }
+
+ float targetAlpha = Asin(clamp(Front.z, -1.0f, 1.0f)) - zoomModeAlphaOffset;
+ if (targetAlpha <= maxAlphaAllowed) {
+ if (targetAlpha < -CARCAM_SET[camSetArrPos][14])
+ targetAlpha = -CARCAM_SET[camSetArrPos][14];
+ } else {
+ targetAlpha = maxAlphaAllowed;
+ }
+ float maxAlphaBlendAmount = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][6];
+ float targetAlphaBlendAmount = (1.0f - pow(CARCAM_SET[camSetArrPos][5], CTimer::GetTimeStep())) * (targetAlpha - Alpha);
+ if (targetAlphaBlendAmount <= maxAlphaBlendAmount) {
+ if (targetAlphaBlendAmount < -maxAlphaBlendAmount)
+ targetAlphaBlendAmount = -maxAlphaBlendAmount;
+ } else {
+ targetAlphaBlendAmount = maxAlphaBlendAmount;
+ }
+
+ // Using GetCarGun(LR/UD) will give us same unprocessed RightStick value as SA
+ float stickX = -(pad->GetCarGunLeftRight());
+ float stickY = pad->GetCarGunUpDown();
+
+ // In SA this checks for m_bUseMouse3rdPerson so num2/num8 do not move camera when Keyboard & Mouse controls are used.
+ if (CCamera::m_bUseMouse3rdPerson)
+ stickY = 0.0f;
+
+ float xMovement = Abs(stickX) * (FOV / 80.0f * 5.f / 70.f) * stickX * 0.007f * 0.007f;
+ float yMovement = Abs(stickY) * (FOV / 80.0f * 3.f / 70.f) * stickY * 0.007f * 0.007f;
+
+ bool correctAlpha = true;
+ // if (SA checks if we aren't in work car, why?) {
+ if (!isCar || car->m_modelIndex != MI_YARDIE) {
+ correctAlpha = false;
+ }
+ else {
+ xMovement = 0.0f;
+ yMovement = 0.0f;
+ }
+ // } else
+ // yMovement = 0.0;
+
+ if (!nextDirectionIsForward) {
+ yMovement = 0.0;
+ xMovement = 0.0;
+ }
+
+ if (camSetArrPos == 0 || camSetArrPos == 7) {
+ // This is not working on cars as SA
+ // Because III/VC doesn't have any buttons tied to LeftStick if you're not in Classic Configuration, using Dodo or using GInput/Pad, so :shrug:
+ if (Abs(pad->GetSteeringUpDown()) > 120.0f) {
+ if (car->pDriver && car->pDriver->m_objective != OBJECTIVE_LEAVE_VEHICLE) {
+ yMovement += Abs(pad->GetSteeringUpDown()) * (FOV / 80.0f * 3.f / 70.f) * pad->GetSteeringUpDown() * 0.007f * 0.007f * 0.5;
+ }
+ }
+ }
+
+ if (yMovement > 0.0)
+ yMovement = yMovement * 0.5;
+
+ bool mouseChangesBeta = false;
+
+ // FIX: Disable mouse movement in drive-by, it's buggy. Original SA bug.
+ if (bFreeMouseCam && CCamera::m_bUseMouse3rdPerson && !pad->ArePlayerControlsDisabled() && nextDirectionIsForward) {
+ float mouseY = pad->GetMouseY() * 2.0f;
+ float mouseX = pad->GetMouseX() * -2.0f;
+
+ // If you want an ability to toggle free cam while steering with mouse, you can add an OR after DisableMouseSteering.
+ // There was a pad->NewState.m_bVehicleMouseLook in SA, which doesn't exists in III.
+
+ if ((mouseX != 0.0 || mouseY != 0.0) && (CVehicle::m_bDisableMouseSteering)) {
+ yMovement = mouseY * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; // Same as SA, horizontal sensitivity.
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ xMovement = mouseX * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl;
+ targetAlpha = Alpha;
+ stepsLeftToChangeBetaByMouse = 1.0f * 50.0f;
+ mouseChangesBeta = true;
+ } else if (stepsLeftToChangeBetaByMouse > 0.0f) {
+ // Finish rotation by decreasing speed when we stopped moving mouse
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ yMovement = 0.0;
+ xMovement = 0.0;
+ targetAlpha = Alpha;
+ stepsLeftToChangeBetaByMouse = max(0.0f, stepsLeftToChangeBetaByMouse - CTimer::GetTimeStep());
+ mouseChangesBeta = true;
+ }
+ }
+
+ if (correctAlpha) {
+ if (nPreviousMode != MODE_CAM_ON_A_STRING)
+ alphaCorrected = false;
+
+ if (!alphaCorrected && Abs(zoomModeAlphaOffset + Alpha) > 0.05f) {
+ yMovement = (-zoomModeAlphaOffset - Alpha) * 0.05f;
+ } else
+ alphaCorrected = true;
+ }
+ float alphaSpeedFromStickY = yMovement * CARCAM_SET[camSetArrPos][12];
+ float betaSpeedFromStickX = xMovement * CARCAM_SET[camSetArrPos][12];
+
+ float newAngleSpeedMaxBlendAmount = CARCAM_SET[camSetArrPos][9];
+ float angleChangeStep = pow(CARCAM_SET[camSetArrPos][8], CTimer::GetTimeStep());
+ float targetBetaWithStickBlendAmount = betaSpeedFromStickX + (targetBeta - Beta) / max(CTimer::GetTimeStep(), 1.0f);
+
+ if (targetBetaWithStickBlendAmount < -newAngleSpeedMaxBlendAmount)
+ targetBetaWithStickBlendAmount = -newAngleSpeedMaxBlendAmount;
+ else if (targetBetaWithStickBlendAmount > newAngleSpeedMaxBlendAmount)
+ targetBetaWithStickBlendAmount = newAngleSpeedMaxBlendAmount;
+
+ float angleChangeStepLeft = 1.0f - angleChangeStep;
+ BetaSpeed = targetBetaWithStickBlendAmount * angleChangeStepLeft + angleChangeStep * BetaSpeed;
+ if (Abs(BetaSpeed) < 0.0001f)
+ BetaSpeed = 0.0f;
+
+ float betaChangePerFrame;
+ if (mouseChangesBeta)
+ betaChangePerFrame = betaSpeedFromStickX;
+ else
+ betaChangePerFrame = CTimer::GetTimeStep() * BetaSpeed;
+ Beta = betaChangePerFrame + Beta;
+
+ if (TheCamera.m_bJustCameOutOfGarage) {
+ float invHeading = Atan2(Front.y, Front.x);
+ if (invHeading < 0.0f)
+ invHeading += TWOPI;
+
+ Beta = invHeading + PI;
+ }
+
+ Beta = CGeneral::LimitRadianAngle(Beta);
+ if (Beta < 0.0f)
+ Beta += TWOPI;
+
+ if ((camSetArrPos <= 1 || camSetArrPos == 7) && targetAlpha < Alpha && carPosChange >= newDistance) {
+ if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1)
+ // || isBike && GetMysteriousWheelRelatedThingBike(car) > 1)
+ alphaSpeedFromStickY += (targetAlpha - Alpha) * 0.075f;
+ }
+
+ AlphaSpeed = angleChangeStepLeft * alphaSpeedFromStickY + angleChangeStep * AlphaSpeed;
+ float maxAlphaSpeed = newAngleSpeedMaxBlendAmount;
+ if (alphaSpeedFromStickY > 0.0f)
+ maxAlphaSpeed = maxAlphaSpeed * 0.5;
+
+ if (AlphaSpeed <= maxAlphaSpeed) {
+ float minAlphaSpeed = -maxAlphaSpeed;
+ if (AlphaSpeed < minAlphaSpeed)
+ AlphaSpeed = minAlphaSpeed;
+ } else {
+ AlphaSpeed = maxAlphaSpeed;
+ }
+
+ if (Abs(AlphaSpeed) < 0.0001f)
+ AlphaSpeed = 0.0f;
+
+ float alphaWithSpeedAccounted;
+ if (mouseChangesBeta) {
+ alphaWithSpeedAccounted = alphaSpeedFromStickY + targetAlpha;
+ Alpha += alphaSpeedFromStickY;
+ } else {
+ alphaWithSpeedAccounted = CTimer::GetTimeStep() * AlphaSpeed + targetAlpha;
+ Alpha += targetAlphaBlendAmount;
+ }
+
+ if (Alpha <= maxAlphaAllowed) {
+ float minAlphaAllowed = -CARCAM_SET[camSetArrPos][14];
+ if (minAlphaAllowed > Alpha) {
+ Alpha = minAlphaAllowed;
+ AlphaSpeed = 0.0f;
+ }
+ } else {
+ Alpha = maxAlphaAllowed;
+ AlphaSpeed = 0.0f;
+ }
+
+ // Prevent unsignificant angle changes
+ if (Abs(lastAlpha - Alpha) < 0.0001f)
+ Alpha = lastAlpha;
+
+ lastAlpha = Alpha;
+
+ if (Abs(lastBeta - Beta) < 0.0001f)
+ Beta = lastBeta;
+
+ lastBeta = Beta;
+
+ Front.x = -(cos(Beta) * cos(Alpha));
+ Front.y = -(sin(Beta) * cos(Alpha));
+ Front.z = sin(Alpha);
+ GetVectorsReadyForRW();
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+
+ Source = TargetCoors - newDistance * Front;
+
+ m_cvecTargetCoorsForFudgeInter = TargetCoors;
+ m_aTargetHistoryPosThree = m_aTargetHistoryPosOne;
+ float nextAlpha = alphaWithSpeedAccounted + zoomModeAlphaOffset;
+ float nextFrontX = -(cos(Beta) * cos(nextAlpha));
+ float nextFrontY = -(sin(Beta) * cos(nextAlpha));
+ float nextFrontZ = sin(nextAlpha);
+
+ m_aTargetHistoryPosOne.x = TargetCoors.x - nextFrontX * nextDistance;
+ m_aTargetHistoryPosOne.y = TargetCoors.y - nextFrontY * nextDistance;
+ m_aTargetHistoryPosOne.z = TargetCoors.z - nextFrontZ * nextDistance;
+
+ m_aTargetHistoryPosTwo.x = TargetCoors.x - nextFrontX * newDistance;
+ m_aTargetHistoryPosTwo.y = TargetCoors.y - nextFrontY * newDistance;
+ m_aTargetHistoryPosTwo.z = TargetCoors.z - nextFrontZ * newDistance;
+
+ // SA calls SetColVarsVehicle in here
+ if (nextDirectionIsForward) {
+
+ // This is new in LCS!
+ float timestepFactor = Pow(0.99f, CTimer::GetTimeStep());
+ dontCollideWithCars = (timestepFactor * dontCollideWithCars) + ((1.0f - timestepFactor) * car->m_vecMoveSpeed.Magnitude());
+
+ // Move cam if on collision
+ CColPoint foundCol;
+ CEntity* foundEnt;
+ CWorld::pIgnoreEntity = CamTargetEntity;
+ if (CWorld::ProcessLineOfSight(TargetCoors, Source, foundCol, foundEnt, true, dontCollideWithCars < 0.1f, false, true, false, true, false)) {
+ float obstacleTargetDist = (TargetCoors - foundCol.point).Magnitude();
+ float obstacleCamDist = newDistance - obstacleTargetDist;
+ if (!foundEnt->IsPed() || obstacleCamDist <= 1.0f) {
+ Source = foundCol.point;
+ if (obstacleTargetDist < 1.2f) {
+ RwCameraSetNearClipPlane(Scene.camera, max(0.05f, obstacleTargetDist - 0.3f));
+ }
+ } else {
+ if (!CWorld::ProcessLineOfSight(foundCol.point, Source, foundCol, foundEnt, true, dontCollideWithCars < 0.1f, false, true, false, true, false)) {
+ float lessClip = obstacleCamDist - 0.35f;
+ if (lessClip <= 0.9f)
+ RwCameraSetNearClipPlane(Scene.camera, lessClip);
+ else
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+ } else {
+ obstacleTargetDist = (TargetCoors - foundCol.point).Magnitude();
+ Source = foundCol.point;
+ if (obstacleTargetDist < 1.2f) {
+ float lessClip = obstacleTargetDist - 0.3f;
+ if (lessClip >= 0.05f)
+ RwCameraSetNearClipPlane(Scene.camera, lessClip);
+ else
+ RwCameraSetNearClipPlane(Scene.camera, 0.05f);
+ }
+ }
+ }
+ }
+ CWorld::pIgnoreEntity = nil;
+ float nearClip = RwCameraGetNearClipPlane(Scene.camera);
+ float radius = Tan(DEGTORAD(FOV * 0.5f)) * CDraw::GetAspectRatio() * 1.1f;
+
+ // If we're seeing blue hell due to camera intersects some surface, fix it.
+ // SA and LCS have this unrolled.
+ for (int i = 0;
+ i <= 5 && CWorld::TestSphereAgainstWorld((nearClip * Front) + Source, radius * nearClip, nil, true, true, false, true, false, false);
+ i++) {
+
+ CVector surfaceCamDist = gaTempSphereColPoints->point - Source;
+ CVector frontButInvertedIfTouchesSurface = DotProduct(surfaceCamDist, Front) * Front;
+ float newNearClip = (surfaceCamDist - frontButInvertedIfTouchesSurface).Magnitude() / radius;
+
+ if (newNearClip > nearClip)
+ newNearClip = nearClip;
+ if (newNearClip < 0.1f)
+ newNearClip = 0.1f;
+ if (nearClip > newNearClip)
+ RwCameraSetNearClipPlane(Scene.camera, newNearClip);
+
+ if (newNearClip == 0.1f)
+ Source += (TargetCoors - Source) * 0.3f;
+
+ nearClip = RwCameraGetNearClipPlane(Scene.camera);
+ radius = Tan(DEGTORAD(FOV * 0.5f)) * CDraw::GetAspectRatio() * 1.1f;
+ }
+ }
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+
+ // ------- LCS specific part starts
+
+ if (camSetArrPos == 5 && Source.z < 1.0f) // RC Bandit and Baron
+ Source.z = 1.0f;
+
+ // Obviously some specific place in LC
+ if (Source.x > 11.0f && Source.x < 91.0f) {
+ if (Source.y > -680.0f && Source.y < -600.0f && Source.z < 24.4f)
+ Source.z = 24.4f;
+ }
+
+ // CCam::FixSourceAboveWaterLevel
+ if (CameraTarget.z >= -2.0f) {
+ float level = -6000.0;
+ // +0.5f is needed for III
+ if (CWaterLevel::GetWaterLevelNoWaves(Source.x, Source.y, Source.z, &level)) {
+ if (Source.z < level + 0.5f)
+ Source.z = level + 0.5f;
+ }
+ }
+ Front = TargetCoors - Source;
+
+ // -------- LCS specific part ends
+
+ GetVectorsReadyForRW();
+ // SA
+ // gTargetCoordsForLookingBehind = TargetCoors;
+
+ // SA code from CAutomobile::TankControl/FireTruckControl.
+ if (car->m_modelIndex == MI_RHINO || car->m_modelIndex == MI_FIRETRUCK) {
+
+ float &carGunLR = ((CAutomobile*)car)->m_fCarGunLR;
+ CVector hi = Multiply3x3(Front, car->GetMatrix());
+
+ // III/VC's firetruck turret angle is reversed
+ float angleToFace = (car->m_modelIndex == MI_FIRETRUCK ? -hi.Heading() : hi.Heading());
+
+ if (angleToFace <= carGunLR + PI) {
+ if (angleToFace < carGunLR - PI)
+ angleToFace = angleToFace + TWOPI;
+ } else {
+ angleToFace = angleToFace - TWOPI;
+ }
+
+ float neededTurn = angleToFace - carGunLR;
+ float turnPerFrame = CTimer::GetTimeStep() * (car->m_modelIndex == MI_FIRETRUCK ? 0.05f : 0.015f);
+ if (neededTurn <= turnPerFrame) {
+ if (neededTurn < -turnPerFrame)
+ angleToFace = carGunLR - turnPerFrame;
+ } else {
+ angleToFace = turnPerFrame + carGunLR;
+ }
+
+ if (car->m_modelIndex == MI_RHINO && carGunLR != angleToFace) {
+ DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(angleToFace - carGunLR));
+ }
+ carGunLR = angleToFace;
+
+ if (carGunLR < -PI) {
+ carGunLR += TWOPI;
+ } else if (carGunLR > PI) {
+ carGunLR -= TWOPI;
+ }
+
+ // Because firetruk turret also has Y movement
+ if (car->m_modelIndex == MI_FIRETRUCK) {
+ float &carGunUD = ((CAutomobile*)car)->m_fCarGunUD;
+
+ float alphaToFace = Atan2(hi.z, hi.Magnitude2D()) + DEGTORAD(15.0f);
+ float neededAlphaTurn = alphaToFace - carGunUD;
+ float alphaTurnPerFrame = CTimer::GetTimeStep() * 0.02f;
+
+ if (neededAlphaTurn > alphaTurnPerFrame) {
+ neededTurn = alphaTurnPerFrame;
+ carGunUD = neededTurn + carGunUD;
+ } else {
+ if (neededAlphaTurn >= -alphaTurnPerFrame) {
+ carGunUD = alphaToFace;
+ } else {
+ carGunUD = carGunUD - alphaTurnPerFrame;
+ }
+ }
+
+ float turretMinY = -DEGTORAD(20.0f);
+ float turretMaxY = DEGTORAD(20.0f);
+ if (turretMinY <= carGunUD) {
+ if (carGunUD > turretMaxY)
+ carGunUD = turretMaxY;
+ } else {
+ carGunUD = turretMinY;
+ }
+ }
+ }
+}
#endif
STARTPATCHES
diff --git a/src/core/Camera.h b/src/core/Camera.h
index 3dc74fe7..f3e3e661 100644
--- a/src/core/Camera.h
+++ b/src/core/Camera.h
@@ -224,6 +224,7 @@ struct CCam
// custom stuff
void Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float);
+ void Process_FollowCar_SA(const CVector &CameraTarget, float TargetOrientation, float, float);
};
static_assert(sizeof(CCam) == 0x1A4, "CCam: wrong size");
static_assert(offsetof(CCam, Alpha) == 0xA8, "CCam: error");
diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp
index a469a215..4fefe9a9 100644
--- a/src/core/Frontend.cpp
+++ b/src/core/Frontend.cpp
@@ -64,7 +64,6 @@ bool &CMenuManager::m_bShutDownFrontEndRequested = *(bool*)0x95CD6A;
int8 &CMenuManager::m_PrefsUseWideScreen = *(int8*)0x95CD23;
int8 &CMenuManager::m_PrefsRadioStation = *(int8*)0x95CDA4;
-int8 &CMenuManager::m_bDisableMouseSteering = *(int8*)0x60252C; // 1
int32 &CMenuManager::m_PrefsBrightness = *(int32*)0x5F2E50; // 256
float &CMenuManager::m_PrefsLOD = *(float*)0x8F42C4;
int8 &CMenuManager::m_bFrontEnd_ReloadObrTxtGxt = *(int8*)0x628CFC;
@@ -97,6 +96,11 @@ int32 &JoyButtonJustClicked = *(int32*)0x628D10;
bool &holdingScrollBar = *(bool*)0x628D59;
//int32 *pControlTemp = 0;
+#ifndef MASTER
+bool CMenuManager::m_PrefsMarketing = false;
+bool CMenuManager::m_PrefsDisableTutorials = false;
+#endif // !MASTER
+
// 0x5F311C
const char* FrontendFilenames[][2] = {
{"fe2_mainpanel_ul", "" },
@@ -968,7 +972,7 @@ void CMenuManager::Draw()
rightText = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF");
break;
case MENUACTION_MOUSESTEER:
- rightText = TheText.Get(m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON");
+ rightText = TheText.Get(CVehicle::m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON");
break;
}
@@ -1727,6 +1731,17 @@ void CMenuManager::InitialiseChangedLanguageSettings()
CTimer::Update();
CGame::frenchGame = false;
CGame::germanGame = false;
+#ifdef MORE_LANGUAGES
+ switch (CMenuManager::m_PrefsLanguage) {
+ case LANGUAGE_RUSSIAN:
+ CFont::ReloadFonts(FONT_LANGSET_RUSSIAN);
+ break;
+ default:
+ CFont::ReloadFonts(FONT_LANGSET_EFIGS);
+ break;
+ }
+#endif
+
switch (CMenuManager::m_PrefsLanguage) {
case LANGUAGE_FRENCH:
CGame::frenchGame = true;
@@ -1734,6 +1749,11 @@ void CMenuManager::InitialiseChangedLanguageSettings()
case LANGUAGE_GERMAN:
CGame::germanGame = true;
break;
+#ifdef MORE_LANGUAGES
+ case LANGUAGE_RUSSIAN:
+ CGame::russianGame = true;
+ break;
+#endif
default:
break;
}
@@ -2935,6 +2955,14 @@ CMenuManager::ProcessButtonPresses(void)
CMenuManager::InitialiseChangedLanguageSettings();
SaveSettings();
break;
+#ifdef MORE_LANGUAGES
+ case MENUACTION_LANG_RUS:
+ m_PrefsLanguage = LANGUAGE_RUSSIAN;
+ m_bFrontEnd_ReloadObrTxtGxt = true;
+ CMenuManager::InitialiseChangedLanguageSettings();
+ SaveSettings();
+ break;
+#endif
case MENUACTION_POPULATESLOTS_CHANGEMENU:
PcSaveHelper.PopulateSlotInfo();
@@ -3141,7 +3169,7 @@ CMenuManager::ProcessButtonPresses(void)
CMenuManager::m_ControlMethod = CONTROL_STANDART;
MousePointerStateHelper.bInvertVertically = false;
TheCamera.m_fMouseAccelHorzntl = 0.0025f;
- m_bDisableMouseSteering = true;
+ CVehicle::m_bDisableMouseSteering = true;
TheCamera.m_bHeadBob = false;
SaveSettings();
}
@@ -3460,7 +3488,7 @@ void CMenuManager::ProcessOnOffMenuOptions()
SaveSettings();
break;
case MENUACTION_MOUSESTEER:
- m_bDisableMouseSteering = !m_bDisableMouseSteering;
+ CVehicle::m_bDisableMouseSteering = !CVehicle::m_bDisableMouseSteering;
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SUCCESS, 0);
SaveSettings();
break;
diff --git a/src/core/Frontend.h b/src/core/Frontend.h
index 3dbed164..30e4f652 100644
--- a/src/core/Frontend.h
+++ b/src/core/Frontend.h
@@ -51,6 +51,9 @@ enum eLanguages
LANGUAGE_GERMAN,
LANGUAGE_ITALIAN,
LANGUAGE_SPANISH,
+#ifdef MORE_LANGUAGES
+ LANGUAGE_RUSSIAN,
+#endif
};
enum eFrontendSprites
@@ -301,6 +304,9 @@ enum eMenuAction
MENUACTION_UNK108,
MENUACTION_UNK109,
MENUACTION_UNK110,
+#ifdef MORE_LANGUAGES
+ MENUACTION_LANG_RUS,
+#endif
};
enum eCheckHover
@@ -473,7 +479,6 @@ public:
static int32 &m_ControlMethod;
static int8 &m_PrefsDMA;
static int32 &m_PrefsLanguage;
- static int8 &m_bDisableMouseSteering;
static int32 &m_PrefsBrightness;
static float &m_PrefsLOD;
static int8 &m_bFrontEnd_ReloadObrTxtGxt;
@@ -492,6 +497,12 @@ public:
static int32 &sthWithButtons;
static int32 &sthWithButtons2;
+#ifndef MASTER
+ static bool m_PrefsMarketing;
+ static bool m_PrefsDisableTutorials;
+#endif // !MASTER
+
+
public:
static void BuildStatLine(char *text, void *stat, uint8 aFloat, void *stat2);
static void CentreMousePointer();
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index 3306277c..8571e93e 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -99,6 +99,9 @@ bool &CGame::germanGame = *(bool*)0x95CD1E;
bool &CGame::noProstitutes = *(bool*)0x95CDCF;
bool &CGame::playingIntro = *(bool*)0x95CDC2;
char *CGame::aDatFile = (char*)0x773A48;
+#ifdef MORE_LANGUAGES
+bool CGame::russianGame = false;
+#endif
int &gameTxdSlot = *(int*)0x628D88;
diff --git a/src/core/Game.h b/src/core/Game.h
index b6728a2f..318ff54b 100644
--- a/src/core/Game.h
+++ b/src/core/Game.h
@@ -16,6 +16,9 @@ public:
static bool &nastyGame;
static bool &frenchGame;
static bool &germanGame;
+#ifdef MORE_LANGUAGES
+ static bool russianGame;
+#endif
static bool &noProstitutes;
static bool &playingIntro;
static char *aDatFile; //[32];
diff --git a/src/core/MenuScreens.h b/src/core/MenuScreens.h
index 427d01bf..ace6a719 100644
--- a/src/core/MenuScreens.h
+++ b/src/core/MenuScreens.h
@@ -65,6 +65,9 @@ const CMenuScreen aScreens[] = {
MENUACTION_LANG_GER, "FEL_GER", SAVESLOT_NONE, MENUPAGE_NONE,
MENUACTION_LANG_ITA, "FEL_ITA", SAVESLOT_NONE, MENUPAGE_NONE,
MENUACTION_LANG_SPA, "FEL_SPA", SAVESLOT_NONE, MENUPAGE_NONE,
+#ifdef MORE_LANGUAGES
+ MENUACTION_LANG_RUS, "FEL_RUS", SAVESLOT_NONE, MENUPAGE_NONE,
+#endif
MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE,
},
diff --git a/src/core/Radar.cpp b/src/core/Radar.cpp
index 1c634760..f1d8ec96 100644
--- a/src/core/Radar.cpp
+++ b/src/core/Radar.cpp
@@ -1106,8 +1106,11 @@ void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D
// Radar space goes from -1.0 to 1.0 in x and y, top right is (1.0, 1.0)
void CRadar::TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in)
{
- // FIX? scale RADAR_LEFT here somehow
+#ifdef FIX_BUGS
+ out.x = (in.x + 1.0f)*0.5f*SCREEN_SCALE_X(RADAR_WIDTH) + SCREEN_SCALE_X(RADAR_LEFT);
+#else
out.x = (in.x + 1.0f)*0.5f*SCREEN_SCALE_X(RADAR_WIDTH) + RADAR_LEFT;
+#endif
out.y = (1.0f - in.y)*0.5f*SCREEN_SCALE_Y(RADAR_HEIGHT) + SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT);
}
diff --git a/src/core/Timer.cpp b/src/core/Timer.cpp
index a46e1d8b..18d6b6a3 100644
--- a/src/core/Timer.cpp
+++ b/src/core/Timer.cpp
@@ -214,6 +214,11 @@ void CTimer::EndUserPause(void)
m_UserPause = false;
}
+uint32 CTimer::GetCyclesPerFrame()
+{
+ return 20;
+}
+
#if 1
STARTPATCHES
InjectHook(0x4ACE60, CTimer::Initialise, PATCH_JUMP);
diff --git a/src/core/Timer.h b/src/core/Timer.h
index ef525be7..2498ec8a 100644
--- a/src/core/Timer.h
+++ b/src/core/Timer.h
@@ -34,6 +34,7 @@ public:
static void SetPreviousTimeInMilliseconds(uint32 t) { m_snPreviousTimeInMilliseconds = t; }
static const float &GetTimeScale(void) { return ms_fTimeScale; }
static void SetTimeScale(float ts) { ms_fTimeScale = ts; }
+ static uint32 GetCyclesPerFrame();
static bool GetIsPaused() { return m_UserPause || m_CodePause; }
static bool GetIsUserPaused() { return m_UserPause; }
diff --git a/src/core/config.h b/src/core/config.h
index 926cb863..4d66eef5 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -171,12 +171,14 @@ enum Config {
// not in any game
# define NASTY_GAME // nasty game for all languages
# define NO_MOVIES // disable intro videos
-# define NO_CDCHECK
+# define NO_CDCHECK
# define CHATTYSPLASH // print what the game is loading
+//# define TIMEBARS // print debug timers
#endif
#define FIX_BUGS // fixes bugs that we've came across during reversing, TODO: use this more
#define TOGGLEABLE_BETA_FEATURES // toggleable from debug menu. not too many things
+#define MORE_LANGUAGES // Add more translations to the game
// Pad
#define XINPUT
diff --git a/src/core/main.cpp b/src/core/main.cpp
index 50543b1e..674527f5 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -51,6 +51,7 @@
#include "Script.h"
#include "Debug.h"
#include "Console.h"
+#include "timebars.h"
#define DEFAULT_VIEWWINDOW (Tan(DEGTORAD(CDraw::GetFOV() * 0.5f)))
@@ -141,6 +142,11 @@ Idle(void *arg)
#endif
CTimer::Update();
+
+#ifdef TIMEBARS
+ tbInit();
+#endif
+
CSprite2d::InitPerFrame();
CFont::InitPerFrame();
@@ -155,16 +161,39 @@ Idle(void *arg)
FrontEndMenuManager.Process();
} else {
CPointLights::InitPerFrame();
+#ifdef TIMEBARS
+ tbStartTimer(0, "CGame::Process");
+#endif
CGame::Process();
+#ifdef TIMEBARS
+ tbEndTimer("CGame::Process");
+ tbStartTimer(0, "DMAudio.Service");
+#endif
DMAudio.Service();
+
+#ifdef TIMEBARS
+ tbEndTimer("DMAudio.Service");
+#endif
}
if (RsGlobal.quit)
return;
#else
CPointLights::InitPerFrame();
+#ifdef TIMEBARS
+ tbStartTimer(0, "CGame::Process");
+#endif
CGame::Process();
+#ifdef TIMEBARS
+ tbEndTimer("CGame::Process");
+ tbStartTimer(0, "DMAudio.Service");
+#endif
+
DMAudio.Service();
+
+#ifdef TIMEBARS
+ tbEndTimer("DMAudio.Service");
+#endif
#endif
if(CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()){
@@ -192,8 +221,18 @@ Idle(void *arg)
RsMouseSetPos(&pos);
}
#endif
+#ifdef TIMEBARS
+ tbStartTimer(0, "CnstrRenderList");
+#endif
CRenderer::ConstructRenderList();
+#ifdef TIMEBARS
+ tbEndTimer("CnstrRenderList");
+ tbStartTimer(0, "PreRender");
+#endif
CRenderer::PreRender();
+#ifdef TIMEBARS
+ tbEndTimer("PreRender");
+#endif
if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
if(!DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255))
@@ -211,16 +250,31 @@ Idle(void *arg)
RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip());
RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart());
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderScene");
+#endif
RenderScene();
+#ifdef TIMEBARS
+ tbEndTimer("RenderScene");
+#endif
RenderDebugShit();
RenderEffects();
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderMotionBlur");
+#endif
if((TheCamera.m_BlurType == MBLUR_NONE || TheCamera.m_BlurType == MBLUR_NORMAL) &&
TheCamera.m_ScreenReductionPercentage > 0.0f)
TheCamera.SetMotionBlurAlpha(150);
TheCamera.RenderMotionBlur();
-
+#ifdef TIMEBARS
+ tbEndTimer("RenderMotionBlur");
+ tbStartTimer(0, "Render2dStuff");
+#endif
Render2dStuff();
+#ifdef TIMEBARS
+ tbEndTimer("Render2dStuff");
+#endif
}else{
float viewWindow = DEFAULT_VIEWWINDOW;
#ifdef ASPECT_RATIO_SCALE
@@ -238,10 +292,29 @@ Idle(void *arg)
if (FrontEndMenuManager.m_bMenuActive)
DefinedState();
#endif
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderMenus");
+#endif
RenderMenus();
+#ifdef TIMEBARS
+ tbEndTimer("RenderMenus");
+ tbStartTimer(0, "DoFade");
+#endif
DoFade();
+#ifdef TIMEBARS
+ tbEndTimer("DoFade");
+ tbStartTimer(0, "Render2dStuff-Fade");
+#endif
Render2dStuffAfterFade();
+#ifdef TIMEBARS
+ tbEndTimer("Render2dStuff-Fade");
+#endif
CCredits::Render();
+
+#ifdef TIMEBARS
+ tbDisplay();
+#endif
+
DoRWStuffEndOfFrame();
// if(g_SlowMode)
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 500bf230..05d28167 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -373,8 +373,10 @@ DebugMenuPopulate(void)
extern bool PrintDebugCode;
extern int16 &DebugCamMode;
#ifdef FREE_CAM
- extern bool bFreeCam;
- DebugMenuAddVarBool8("Cam", "Free Cam", (int8*)&bFreeCam, nil);
+ extern bool bFreePadCam;
+ extern bool bFreeMouseCam;
+ DebugMenuAddVarBool8("Cam", "Free Gamepad Cam", (int8*)&bFreePadCam, nil);
+ DebugMenuAddVarBool8("Cam", "Free Mouse Cam", (int8*)&bFreeMouseCam, nil);
#endif
DebugMenuAddVarBool8("Cam", "Print Debug Code", (int8*)&PrintDebugCode, nil);
DebugMenuAddVar("Cam", "Cam Mode", &DebugCamMode, nil, 1, 0, CCam::MODE_EDITOR, nil);
diff --git a/src/core/timebars.cpp b/src/core/timebars.cpp
new file mode 100644
index 00000000..30421731
--- /dev/null
+++ b/src/core/timebars.cpp
@@ -0,0 +1,121 @@
+#ifndef MASTER
+#include "common.h"
+#include "Font.h"
+#include "Frontend.h"
+#include "Timer.h"
+#include "Text.h"
+
+#define MAX_TIMERS (50)
+#define MAX_MS_COLLECTED (40)
+
+// enables frame time output
+#define FRAMETIME
+
+struct sTimeBar
+{
+ char name[20];
+ float startTime;
+ float endTime;
+ int32 unk;
+};
+
+struct
+{
+ sTimeBar Timers[MAX_TIMERS];
+ uint32 count;
+} TimerBar;
+float MaxTimes[MAX_TIMERS];
+float MaxFrameTime;
+
+uint32 curMS;
+uint32 msCollected[MAX_MS_COLLECTED];
+#ifdef FRAMETIME
+float FrameInitTime;
+#endif
+
+void tbInit()
+{
+ TimerBar.count = 0;
+ uint32 i = CTimer::GetFrameCounter() & 0x7F;
+ if (i == 0) {
+ do
+ MaxTimes[i++] = 0.0f;
+ while (i != MAX_TIMERS);
+#ifdef FRAMETIME
+ MaxFrameTime = 0.0f;
+#endif
+ }
+#ifdef FRAMETIME
+ FrameInitTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+#endif
+}
+
+void tbStartTimer(int32 unk, char *name)
+{
+ strcpy(TimerBar.Timers[TimerBar.count].name, name);
+ TimerBar.Timers[TimerBar.count].unk = unk;
+ TimerBar.Timers[TimerBar.count].startTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+ TimerBar.count++;
+}
+
+void tbEndTimer(char* name)
+{
+ uint32 n = 1500;
+ for (uint32 i = 0; i < TimerBar.count; i++) {
+ if (strcmp(name, TimerBar.Timers[i].name) == 0)
+ n = i;
+ }
+ assert(n != 1500);
+ TimerBar.Timers[n].endTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+}
+
+float Diag_GetFPS()
+{
+ return 39000.0f / (msCollected[(curMS - 1) % MAX_MS_COLLECTED] - msCollected[curMS % MAX_MS_COLLECTED]);
+}
+
+void tbDisplay()
+{
+ char temp[200];
+ wchar wtemp[200];
+
+#ifdef FRAMETIME
+ float FrameEndTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+#endif
+
+ msCollected[(curMS++) % MAX_MS_COLLECTED] = RsTimer();
+ CFont::SetBackgroundOff();
+ CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128));
+ CFont::SetScale(0.48f, 1.12f);
+ CFont::SetCentreOff();
+ CFont::SetJustifyOff();
+ CFont::SetWrapx(640.0f);
+ CFont::SetRightJustifyOff();
+ CFont::SetPropOn();
+ CFont::SetFontStyle(FONT_BANK);
+ sprintf(temp, "FPS: %.2f", Diag_GetFPS());
+ AsciiToUnicode(temp, wtemp);
+ CFont::SetColor(CRGBA(255, 255, 255, 255));
+ if (!CMenuManager::m_PrefsMarketing || !CMenuManager::m_PrefsDisableTutorials) {
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * (4.0f / DEFAULT_SCREEN_HEIGHT), wtemp);
+
+#ifndef FINAL
+ // Timers output (my own implementation)
+ for (uint32 i = 0; i < TimerBar.count; i++) {
+ MaxTimes[i] = max(MaxTimes[i], TimerBar.Timers[i].endTime - TimerBar.Timers[i].startTime);
+ sprintf(temp, "%s: %.2f", &TimerBar.Timers[i].name[0], MaxTimes[i]);
+ AsciiToUnicode(temp, wtemp);
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (i + 2)) / DEFAULT_SCREEN_HEIGHT), wtemp);
+ }
+
+#ifdef FRAMETIME
+ MaxFrameTime = max(MaxFrameTime, FrameEndTime - FrameInitTime);
+ sprintf(temp, "Frame Time: %.2f", MaxFrameTime);
+ AsciiToUnicode(temp, wtemp);
+
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (TimerBar.count + 4)) / DEFAULT_SCREEN_HEIGHT), wtemp);
+#endif // FRAMETIME
+#endif // !FINAL
+ }
+}
+#endif // !MASTER \ No newline at end of file
diff --git a/src/core/timebars.h b/src/core/timebars.h
new file mode 100644
index 00000000..8ffccd8e
--- /dev/null
+++ b/src/core/timebars.h
@@ -0,0 +1,6 @@
+#pragma once
+
+void tbInit();
+void tbStartTimer(int32, char*);
+void tbEndTimer(char*);
+void tbDisplay(); \ No newline at end of file
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index f43feae5..264fa669 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -59,6 +59,10 @@
#define CAN_SEE_ENTITY_ANGLE_THRESHOLD DEGTORAD(60.0f)
+#ifdef FREE_CAM
+extern bool bFreeMouseCam;
+#endif
+
CPed *gapTempPedList[50];
uint16 gnNumTempPedList;
@@ -807,6 +811,10 @@ CPed::IsPedInControl(void)
bool
CPed::CanStrafeOrMouseControl(void)
{
+#ifdef FREE_CAM
+ if (bFreeMouseCam)
+ return false;
+#endif
return m_nPedState == PED_NONE || m_nPedState == PED_IDLE || m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY ||
m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT || m_nPedState == PED_AIM_GUN || m_nPedState == PED_JUMP;
}
@@ -6984,7 +6992,11 @@ CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg)
#endif
) {
+#ifdef FREE_CAM
+ if (TheCamera.Cams[0].Using3rdPersonMouseCam() && !bFreeMouseCam) {
+#else
if (TheCamera.Cams[0].Using3rdPersonMouseCam()) {
+#endif
float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur);
ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(fpsAngle);
ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(fpsAngle);
diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp
index 5275f716..cd2cac23 100644
--- a/src/peds/PlayerPed.cpp
+++ b/src/peds/PlayerPed.cpp
@@ -18,6 +18,10 @@
#define PAD_MOVE_TO_GAME_WORLD_MOVE 60.0f
+#ifdef FREE_CAM
+extern bool bFreeMouseCam;
+#endif
+
CPlayerPed::~CPlayerPed()
{
delete m_pWanted;
@@ -65,7 +69,7 @@ void CPlayerPed::ClearWeaponTarget()
TheCamera.ClearPlayerWeaponMode();
CWeaponEffects::ClearCrossHair();
}
- ClearPointGunAt();
+ ClearPointGunAt();
}
void
@@ -688,7 +692,14 @@ CPlayerPed::PlayerControl1stPersonRunAround(CPad *padUsed)
float padMove = CVector2D(leftRight, upDown).Magnitude();
float padMoveInGameUnit = padMove / PAD_MOVE_TO_GAME_WORLD_MOVE;
if (padMoveInGameUnit > 0.0f) {
+#ifdef FREE_CAM
+ if (!bFreeMouseCam)
+ m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation);
+ else
+ m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown) - TheCamera.Orientation;
+#else
m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation);
+#endif
m_fMoveSpeed = min(padMoveInGameUnit, 0.07f * CTimer::GetTimeStep() + m_fMoveSpeed);
} else {
m_fMoveSpeed = 0.0f;
@@ -981,6 +992,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
if (padUsed->TargetJustDown()) {
SetStoredState();
m_nPedState = PED_SNIPER_MODE;
+#ifdef FREE_CAM
+ if (bFreeMouseCam && TheCamera.Cams[0].Using3rdPersonMouseCam()) {
+ m_fRotationCur = CGeneral::LimitRadianAngle(-TheCamera.Orientation);
+ SetHeading(m_fRotationCur);
+ }
+#endif
if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER)
TheCamera.SetNewPlayerWeaponMode(CCam::MODE_ROCKETLAUNCHER, 0, 0);
else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE)
@@ -1000,7 +1017,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
if (padUsed->GetWeapon() && m_nMoveState != PEDMOVE_SPRINT) {
if (m_nSelectedWepSlot == m_currentWeapon) {
if (m_pPointGunAt) {
- SetAttack(m_pPointGunAt);
+#ifdef FREE_CAM
+ if (bFreeMouseCam && weaponInfo->m_eWeaponFire == WEAPON_FIRE_MELEE && m_fMoveSpeed < 1.0f)
+ StartFightAttack(padUsed->GetWeapon());
+ else
+#endif
+ SetAttack(m_pPointGunAt);
} else if (m_currentWeapon != WEAPONTYPE_UNARMED) {
if (m_nPedState == PED_ATTACK) {
if (padUsed->WeaponJustDown()) {
@@ -1027,11 +1049,65 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
bIsAttacking = false;
}
}
+
+#ifdef FREE_CAM
+ // Rotate player/arm when shooting. We don't have auto-rotation anymore
+ if (CCamera::m_bUseMouse3rdPerson && bFreeMouseCam &&
+ m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) {
+
+ // Weapons except throwable and melee ones
+ if (weaponInfo->m_bCanAim || weaponInfo->m_b1stPerson || weaponInfo->m_bExpands) {
+ if ((padUsed->GetTarget() && weaponInfo->m_bCanAimWithArm) || padUsed->GetWeapon()) {
+ float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation);
+
+ // On this one we can rotate arm.
+ if (weaponInfo->m_bCanAimWithArm) {
+ if (!padUsed->GetWeapon()) { // making this State != ATTACK still stops it after attack. Re-start it immediately!
+ SetPointGunAt(nil);
+ bIsPointingGunAt = false; // to not stop after attack
+ }
+
+ SetLookFlag(limitedCam, true);
+ SetAimFlag(limitedCam);
+#ifdef VC_PED_PORTS
+ SetLookTimer(INT_MAX); // removing this makes head move for real, but I experinced some bugs.
+#endif
+ } else {
+ m_fRotationDest = limitedCam;
+ m_headingRate = 50.0f;
+
+ // Anim. fix for shotgun, ak47 and m16 (we must finish rot. it quickly)
+ if (weaponInfo->m_bCanAim && padUsed->WeaponJustDown()) {
+ m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur);
+ float limitedRotDest = m_fRotationDest;
+
+ if (m_fRotationCur - PI > m_fRotationDest) {
+ limitedRotDest += 2 * PI;
+ } else if (PI + m_fRotationCur < m_fRotationDest) {
+ limitedRotDest -= 2 * PI;
+ }
+
+ m_fRotationCur += (limitedRotDest - m_fRotationCur) / 2;
+ }
+ }
+ } else if (weaponInfo->m_bCanAimWithArm)
+ ClearPointGunAt();
+ else
+ RestoreHeadingRate();
+ }
+ }
+#endif
+
if (padUsed->GetTarget() && m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) {
if (m_pPointGunAt) {
// what??
if (!m_pPointGunAt
- || CCamera::m_bUseMouse3rdPerson || m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->bInVehicle) {
+#ifdef FREE_CAM
+ || (!bFreeMouseCam && CCamera::m_bUseMouse3rdPerson)
+#else
+ || CCamera::m_bUseMouse3rdPerson
+#endif
+ || m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->bInVehicle) {
ClearWeaponTarget();
return;
}
@@ -1047,7 +1123,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
}
TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SYPHON, 0, 0);
TheCamera.UpdateAimingCoors(m_pPointGunAt->GetPosition());
- } else if (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson) {
+ }
+#ifdef FREE_CAM
+ else if ((bFreeMouseCam && weaponInfo->m_eWeaponFire == WEAPON_FIRE_MELEE) || (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson)) {
+#else
+ else if (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson) {
+#endif
if (padUsed->TargetJustDown())
FindWeaponLockOnTarget();
}
diff --git a/src/render/Font.cpp b/src/render/Font.cpp
index d7b4b5d8..6f336f1e 100644
--- a/src/render/Font.cpp
+++ b/src/render/Font.cpp
@@ -1,515 +1,636 @@
-#include "common.h"
-#include "patcher.h"
-#include "Sprite2d.h"
-#include "TxdStore.h"
-#include "Font.h"
-
-CFontDetails &CFont::Details = *(CFontDetails*)0x8F317C;
-int16 &CFont::NewLine = *(int16*)0x95CC94;
-CSprite2d *CFont::Sprite = (CSprite2d*)0x95CC04;
-
-int16 CFont::Size[3][193] = {
- {
- 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17, 13, 31,
- 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10, 35, 26, 26, 26, 26,
- 30, 26, 24, 23, 24, 22, 21, 24, 26, 10, 20, 26, 22, 29, 26, 25,
- 23, 25, 24, 24, 22, 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37,
- 35, 21, 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21, 21,
- 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 33, 33, 33, 33, 35,
- 27, 27, 27, 27, 32, 24, 23, 23, 23, 23, 11, 11, 11, 11, 26, 26,
- 26, 26, 26, 26, 26, 25, 26, 21, 21, 21, 21, 32, 23, 22, 22, 22,
- 22, 11, 11, 11, 11, 22, 22, 22, 22, 22, 22, 22, 22, 26, 21, 24,
- 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 18, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 20
- },
-
- {
- 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11, 17, 13, 33,
- 28, 14, 22, 21, 24, 23, 23, 21, 23, 22, 10, 35, 13, 35, 13, 33,
- 5, 25, 22, 23, 24, 21, 21, 24, 24, 9, 20, 24, 21, 27, 25, 25,
- 22, 25, 23, 20, 23, 23, 23, 31, 23, 23, 23, 37, 33, 37, 35, 37,
- 35, 21, 19, 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21,
- 20, 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33, 33, 35,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 16
- },
-
- {
- 15, 14, 16, 25, 19, 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27,
- 19, 18, 19, 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
- 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29, 19, 20,
- 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19, 33, 31, 39, 37, 39,
- 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23, 23,
- 20, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35, 35, 35, 35, 37,
- 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, 9, 9, 9, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 30, 19, 19, 19, 19,
- 19, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35,
- 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 11, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19
- }
-};
-
-uint16 foreign_table[128] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175,
- 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
- 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150,
- 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
- 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0,
-};
-
-void
-CFont::Initialise(void)
-{
- int slot;
-
- slot = CTxdStore::AddTxdSlot("fonts");
- CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
- CTxdStore::AddRef(slot);
- CTxdStore::PushCurrentTxd();
- CTxdStore::SetCurrentTxd(slot);
- Sprite[0].SetTexture("font2", "font2_mask");
- Sprite[1].SetTexture("pager", "pager_mask");
- Sprite[2].SetTexture("font1", "font1_mask");
- SetScale(1.0f, 1.0f);
- SetSlantRefPoint(SCREEN_WIDTH, 0.0f);
- SetSlant(0.0f);
- SetColor(CRGBA(0xFF, 0xFF, 0xFF, 0));
- SetJustifyOff();
- SetCentreOff();
- SetWrapx(640.0f);
- SetCentreSize(640.0f);
- SetBackgroundOff();
- SetBackgroundColor(CRGBA(0x80, 0x80, 0x80, 0x80));
- SetBackGroundOnlyTextOff();
- SetPropOn();
- SetFontStyle(FONT_BANK);
- SetRightJustifyWrap(0.0f);
- SetAlphaFade(255.0f);
- SetDropShadowPosition(0);
- CTxdStore::PopCurrentTxd();
-}
-
-void
-CFont::Shutdown(void)
-{
- Sprite[0].Delete();
- Sprite[1].Delete();
- Sprite[2].Delete();
- CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts"));
-}
-
-void
-CFont::InitPerFrame(void)
-{
- Details.bank = CSprite2d::GetBank(30, Sprite[0].m_pTexture);
- CSprite2d::GetBank(15, Sprite[1].m_pTexture);
- CSprite2d::GetBank(15, Sprite[2].m_pTexture);
- SetDropShadowPosition(0);
- NewLine = 0;
-}
-
-void
-CFont::PrintChar(float x, float y, uint16 c)
-{
- if(x <= 0.0f || x > SCREEN_WIDTH ||
- y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again
- return;
-
- float w = GetCharacterWidth(c) / 32.0f;
- float xoff = c & 0xF;
- float yoff = c >> 4;
-
- if(Details.style == FONT_BANK || Details.style == FONT_HEADING){
- if(Details.dropShadowPosition != 0){
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition),
- y + SCREEN_SCALE_Y(Details.dropShadowPosition),
- x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f,
- y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY * 0.5f),
- Details.dropColor,
- xoff/16.0f, yoff/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
- xoff/16.0f, (yoff+1.0f)/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.0001f);
- }
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x, y,
- x + 32.0f * Details.scaleX * 1.0f,
- y + 40.0f * Details.scaleY * 0.5f),
- Details.color,
- xoff/16.0f, yoff/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
- xoff/16.0f, (yoff+1.0f)/12.8f - 0.002f,
- (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.002f);
- }else{
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x, y,
- x + 32.0f * Details.scaleX * w,
- y + 32.0f * Details.scaleY * 0.5f),
- Details.color,
- xoff/16.0f, yoff/16.0f,
- (xoff+w)/16.0f, yoff/16.0f,
- xoff/16.0f, (yoff+1.0f)/16.0f,
- (xoff+w)/16.0f - 0.0001f, (yoff+1.0f)/16.0f - 0.0001f);
- }
-}
-
-void
-CFont::PrintString(float xstart, float ystart, uint16 *s)
-{
- CRect rect;
- int numSpaces;
- float lineLength;
- float x, y;
- bool first;
- uint16 *start, *t;
-
- if(*s == '*')
- return;
-
- if(Details.background){
- GetNumberLines(xstart, ystart, s); // BUG: result not used
- GetTextRect(&rect, xstart, ystart, s);
- CSprite2d::DrawRect(rect, Details.backgroundColor);
- }
-
- lineLength = 0.0f;
- numSpaces = 0;
- first = true;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
- start = s;
-
- // This is super ugly, I blame R*
- for(;;){
- for(;;){
- for(;;){
- if(*s == '\0')
- return;
- int xend = Details.centre ? Details.centreSize :
- Details.rightJustify ? xstart - Details.rightJustifyWrap :
- Details.wrapX;
- if(x + GetStringWidth(s) > xend && !first){
- // flush line
- float spaceWidth = !Details.justify || Details.centre ? 0.0f :
- (Details.wrapX - lineLength) / numSpaces;
- float xleft = Details.centre ? xstart - x/2 :
- Details.rightJustify ? xstart - x :
- xstart;
- PrintString(xleft, y, start, s, spaceWidth);
- // reset things
- lineLength = 0.0f;
- numSpaces = 0;
- first = true;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- start = s;
- }else
- break;
- }
- // advance by one word
- t = GetNextSpace(s);
- if(t[0] == '\0' ||
- t[0] == ' ' && t[1] == '\0')
- break;
- if(!first)
- numSpaces++;
- first = false;
- x += GetStringWidth(s) + GetCharacterSize(*t - ' ');
- lineLength = x;
- s = t+1;
- }
- // print rest
- if(t[0] == ' ' && t[1] == '\0')
- t[0] = '\0';
- x += GetStringWidth(s);
- s = t;
- float xleft = Details.centre ? xstart - x/2 :
- Details.rightJustify ? xstart - x :
- xstart;
- PrintString(xleft, y, start, s, 0.0f);
- }
-}
-
-int
-CFont::GetNumberLines(float xstart, float ystart, uint16 *s)
-{
- int n;
- float x, y;
- uint16 *t;
-
- n = 0;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
-
- while(*s){
- if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
- // reached end of line
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- n++;
- // Why even?
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- }else{
- // still space in current line
- t = GetNextSpace(s);
- if(*t == '\0'){
- // end of string
- x += GetStringWidth(s);
- n++;
- s = t;
- }else{
- x += GetStringWidth(s);
- x += GetCharacterSize(*t - ' ');
- s = t+1;
- }
- }
- }
-
- return n;
-}
-
-void
-CFont::GetTextRect(CRect *rect, float xstart, float ystart, uint16 *s)
-{
- int numLines;
- float x, y;
- int16 maxlength;
- uint16 *t;
-
- maxlength = 0;
- numLines = 0;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
-
- while(*s){
- if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
- // reached end of line
- if(x > maxlength)
- maxlength = x;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- numLines++;
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- }else{
- // still space in current line
- t = GetNextSpace(s);
- if(*t == '\0'){
- // end of string
- x += GetStringWidth(s);
- if(x > maxlength)
- maxlength = x;
- numLines++;
- s = t;
- }else{
- x += GetStringWidth(s);
- x += GetCharacterSize(*t - ' ');
- s = t+1;
- }
- }
- }
-
- if(Details.centre){
- if(Details.backgroundOnlyText){
- rect->left = xstart - maxlength/2 - 4.0f;
- rect->right = xstart + maxlength/2 + 4.0f;
- rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f;
- rect->top = ystart - 2.0f;
- }else{
- rect->left = xstart - Details.centreSize*0.5f - 4.0f;
- rect->right = xstart + Details.centreSize*0.5f + 4.0f;
- rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f;
- rect->top = ystart - 2.0f;
- }
- }else{
- rect->left = xstart - 4.0f;
- rect->right = Details.wrapX;
- // WTF?
- rect->bottom = ystart - 4.0f + 4.0f;
- rect->top = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f + 2.0f;
- }
-}
-
-void
-CFont::PrintString(float x, float y, uint16 *start, uint16 *end, float spwidth)
-{
- uint16 *s, c, unused;
-
- for(s = start; s < end; s++){
- if(*s == '~')
- s = ParseToken(s, &unused);
- c = *s - ' ';
- if(Details.slant != 0.0f)
- y = (Details.slantRefX - x)*Details.slant + Details.slantRefY;
- PrintChar(x, y, c);
- x += GetCharacterSize(c);
- if(c == 0) // space
- x += spwidth;
- }
-}
-
-float
-CFont::GetCharacterWidth(uint16 c)
-{
- if(Details.proportional)
- return Size[Details.style][c];
- else
- return Size[Details.style][192];
-}
-
-float
-CFont::GetCharacterSize(uint16 c)
-{
- if(Details.proportional)
- return Size[Details.style][c] * Details.scaleX;
- else
- return Size[Details.style][192] * Details.scaleX;
-}
-
-float
-CFont::GetStringWidth(uint16 *s, bool spaces)
-{
- float w;
-
- w = 0.0f;
- for(; (*s != ' ' || spaces) && *s != '\0'; s++){
- if(*s == '~'){
- s++;
- while(*s != '~') s++;
- s++;
- if(*s == ' ' && !spaces)
- break;
- }
- w += GetCharacterSize(*s - ' ');
- }
- return w;
-}
-
-uint16*
-CFont::GetNextSpace(uint16 *s)
-{
- for(; *s != ' ' && *s != '\0'; s++)
- if(*s == '~'){
- s++;
- while(*s != '~') s++;
- s++;
- if(*s == ' ')
- break;
- }
- return s;
-}
-
-uint16*
-CFont::ParseToken(uint16 *s, uint16*)
-{
- s++;
- if(Details.color.r || Details.color.g || Details.color.b)
- switch(*s){
- case 'N':
- case 'n':
- NewLine = 1;
- break;
- case 'b': SetColor(CRGBA(0x80, 0xA7, 0xF3, 0xFF)); break;
- case 'g': SetColor(CRGBA(0x5F, 0xA0, 0x6A, 0xFF)); break;
- case 'h': SetColor(CRGBA(0xE1, 0xE1, 0xE1, 0xFF)); break;
- case 'l': SetColor(CRGBA(0x00, 0x00, 0x00, 0xFF)); break;
- case 'p': SetColor(CRGBA(0xA8, 0x6E, 0xFC, 0xFF)); break;
- case 'r': SetColor(CRGBA(0x71, 0x2B, 0x49, 0xFF)); break;
- case 'w': SetColor(CRGBA(0xAF, 0xAF, 0xAF, 0xFF)); break;
- case 'y': SetColor(CRGBA(0xD2, 0xC4, 0x6A, 0xFF)); break;
- }
- while(*s != '~') s++;
- return s+1;
-}
-
-void
-CFont::DrawFonts(void)
-{
- CSprite2d::DrawBank(Details.bank);
- CSprite2d::DrawBank(Details.bank+1);
- CSprite2d::DrawBank(Details.bank+2);
-}
-
-uint16
-CFont::character_code(uint8 c)
-{
- if(c < 128)
- return c;
- return foreign_table[c-128];
-}
-
-STARTPATCHES
-
- InjectHook(0x500A40, CFont::Initialise, PATCH_JUMP);
- InjectHook(0x500BA0, CFont::Shutdown, PATCH_JUMP);
- InjectHook(0x500BE0, CFont::InitPerFrame, PATCH_JUMP);
- InjectHook(0x500C30, CFont::PrintChar, PATCH_JUMP);
- InjectHook(0x500F50, (void (*)(float, float, uint16*))CFont::PrintString, PATCH_JUMP);
- InjectHook(0x501260, CFont::GetNumberLines, PATCH_JUMP);
- InjectHook(0x5013B0, CFont::GetTextRect, PATCH_JUMP);
- InjectHook(0x501730, (void (*)(float, float, uint16*, uint16*, float))CFont::PrintString, PATCH_JUMP);
- InjectHook(0x5017E0, CFont::GetCharacterWidth, PATCH_JUMP);
- InjectHook(0x501840, CFont::GetCharacterSize, PATCH_JUMP);
- InjectHook(0x5018A0, CFont::GetStringWidth, PATCH_JUMP);
- InjectHook(0x501960, CFont::GetNextSpace, PATCH_JUMP);
- InjectHook(0x5019A0, CFont::ParseToken, PATCH_JUMP);
- InjectHook(0x501B50, CFont::DrawFonts, PATCH_JUMP);
- InjectHook(0x501E80, CFont::character_code, PATCH_JUMP);
-
- InjectHook(0x501B80, CFont::SetScale, PATCH_JUMP);
- InjectHook(0x501BA0, CFont::SetSlantRefPoint, PATCH_JUMP);
- InjectHook(0x501BC0, CFont::SetSlant, PATCH_JUMP);
- InjectHook(0x501BD0, CFont::SetColor, PATCH_JUMP);
- InjectHook(0x501C60, CFont::SetJustifyOn, PATCH_JUMP);
- InjectHook(0x501C80, CFont::SetJustifyOff, PATCH_JUMP);
- InjectHook(0x501C90, CFont::SetCentreOn, PATCH_JUMP);
- InjectHook(0x501CB0, CFont::SetCentreOff, PATCH_JUMP);
- InjectHook(0x501CC0, CFont::SetWrapx, PATCH_JUMP);
- InjectHook(0x501CD0, CFont::SetCentreSize, PATCH_JUMP);
- InjectHook(0x501CE0, CFont::SetBackgroundOn, PATCH_JUMP);
- InjectHook(0x501CF0, CFont::SetBackgroundOff, PATCH_JUMP);
- InjectHook(0x501D00, CFont::SetBackgroundColor, PATCH_JUMP);
- InjectHook(0x501D30, CFont::SetBackGroundOnlyTextOn, PATCH_JUMP);
- InjectHook(0x501D40, CFont::SetBackGroundOnlyTextOff, PATCH_JUMP);
- InjectHook(0x501D50, CFont::SetRightJustifyOn, PATCH_JUMP);
- InjectHook(0x501D70, CFont::SetRightJustifyOff, PATCH_JUMP);
- InjectHook(0x501D90, CFont::SetPropOff, PATCH_JUMP);
- InjectHook(0x501DA0, CFont::SetPropOn, PATCH_JUMP);
- InjectHook(0x501DB0, CFont::SetFontStyle, PATCH_JUMP);
- InjectHook(0x501DC0, CFont::SetRightJustifyWrap, PATCH_JUMP);
- InjectHook(0x501DD0, CFont::SetAlphaFade, PATCH_JUMP);
- InjectHook(0x501DE0, CFont::SetDropColor, PATCH_JUMP);
- InjectHook(0x501E70, CFont::SetDropShadowPosition, PATCH_JUMP);
-
-ENDPATCHES
+#include "common.h"
+#include "patcher.h"
+#include "Sprite2d.h"
+#include "TxdStore.h"
+#include "Font.h"
+
+CFontDetails &CFont::Details = *(CFontDetails*)0x8F317C;
+int16 &CFont::NewLine = *(int16*)0x95CC94;
+CSprite2d *CFont::Sprite = (CSprite2d*)0x95CC04;
+
+#ifdef MORE_LANGUAGES
+uint8 CFont::LanguageSet = FONT_LANGSET_EFIGS;
+int32 CFont::Slot = -1;
+
+int16 CFont::Size[2][3][193] = {
+ {
+#else
+int16 CFont::Size[3][193] = {
+#endif
+ {
+ 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17, 13, 31,
+ 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10, 35, 26, 26, 26, 26,
+ 30, 26, 24, 23, 24, 22, 21, 24, 26, 10, 20, 26, 22, 29, 26, 25,
+ 23, 25, 24, 24, 22, 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37,
+ 35, 21, 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21, 21,
+ 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 33, 33, 33, 33, 35,
+ 27, 27, 27, 27, 32, 24, 23, 23, 23, 23, 11, 11, 11, 11, 26, 26,
+ 26, 26, 26, 26, 26, 25, 26, 21, 21, 21, 21, 32, 23, 22, 22, 22,
+ 22, 11, 11, 11, 11, 22, 22, 22, 22, 22, 22, 22, 22, 26, 21, 24,
+ 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 18, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 20
+ },
+
+ {
+ 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11, 17, 13, 33,
+ 28, 14, 22, 21, 24, 23, 23, 21, 23, 22, 10, 35, 13, 35, 13, 33,
+ 5, 25, 22, 23, 24, 21, 21, 24, 24, 9, 20, 24, 21, 27, 25, 25,
+ 22, 25, 23, 20, 23, 23, 23, 31, 23, 23, 23, 37, 33, 37, 35, 37,
+ 35, 21, 19, 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21,
+ 20, 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33, 33, 35,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 16
+ },
+
+ {
+ 15, 14, 16, 25, 19, 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27,
+ 19, 18, 19, 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
+ 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29, 19, 20,
+ 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19, 33, 31, 39, 37, 39,
+ 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23, 23,
+ 20, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35, 35, 35, 35, 37,
+ 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, 9, 9, 9, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 30, 19, 19, 19, 19,
+ 19, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35,
+ 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 11, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19
+ }
+#ifdef MORE_LANGUAGES
+ },
+ {
+ { 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17,
+ 13, 31, 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10,
+ 35, 26, 26, 26, 26, 30, 26, 24, 23, 24, 22, 21, 24,
+ 26, 10, 20, 26, 22, 29, 26, 25, 23, 25, 24, 24, 22,
+ 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37, 35, 21,
+ 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21,
+ 21, 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 13,
+ 33, 13, 13, 13, 24, 22, 22, 19, 26, 21, 30, 20, 23,
+ 23, 21, 24, 26, 23, 22, 23, 21, 22, 20, 20, 26, 25,
+ 24, 22, 31, 32, 23, 30, 22, 22, 32, 23, 19, 18, 18,
+ 15, 22, 19, 27, 19, 20, 20, 18, 22, 24, 20, 19, 19,
+ 20, 19, 16, 19, 28, 20, 20, 18, 26, 27, 19, 26, 18,
+ 19, 27, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 18, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 20 },
+ { 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11,
+ 17, 13, 33, 28, 14, 22, 21, 24, 23, 23, 21, 23, 22,
+ 10, 35, 13, 35, 13, 33, 5, 25, 22, 23, 24, 21, 21, 24,
+ 24, 9, 20, 24, 21, 27, 25, 25, 22, 25, 23, 20, 23, 23,
+ 23, 31, 23, 23, 23, 37, 33, 37, 35, 37, 35, 21, 19,
+ 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21, 20,
+ 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33,
+ 33, 35, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 16, },
+ { 15, 14, 16, 25, 19,
+ 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27, 19, 18, 19,
+ 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
+ 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29,
+ 19, 20, 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19,
+ 33, 31, 39, 37, 39, 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23,
+ 21, 21, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35,
+ 35, 35, 35, 37, 19, 19, 19, 19, 19, 19, 29, 19, 19,
+ 19, 20, 22, 31, 19, 19, 19, 19, 19, 29, 19, 29, 19,
+ 21, 19, 30, 31, 21, 29, 19, 19, 29, 19, 21, 23, 32,
+ 21, 21, 30, 31, 22, 21, 32, 33, 23, 32, 21, 21, 32,
+ 21, 19, 19, 30, 31, 22, 22, 21, 32, 33, 23, 32, 21,
+ 21, 32, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 11, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 },
+ }
+#endif
+};
+
+wchar foreign_table[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175,
+ 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150,
+ 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0,
+};
+
+void
+CFont::Initialise(void)
+{
+ int slot;
+
+ slot = CTxdStore::AddTxdSlot("fonts");
+#ifdef MORE_LANGUAGES
+ Slot = slot;
+ switch (LanguageSet)
+ {
+ case FONT_LANGSET_EFIGS:
+ default:
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
+ break;
+ case FONT_LANGSET_RUSSIAN:
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS_R.TXD");
+ break;
+ }
+#else
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
+#endif
+ CTxdStore::AddRef(slot);
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::SetCurrentTxd(slot);
+ Sprite[0].SetTexture("font2", "font2_mask");
+ Sprite[1].SetTexture("pager", "pager_mask");
+ Sprite[2].SetTexture("font1", "font1_mask");
+ SetScale(1.0f, 1.0f);
+ SetSlantRefPoint(SCREEN_WIDTH, 0.0f);
+ SetSlant(0.0f);
+ SetColor(CRGBA(0xFF, 0xFF, 0xFF, 0));
+ SetJustifyOff();
+ SetCentreOff();
+ SetWrapx(640.0f);
+ SetCentreSize(640.0f);
+ SetBackgroundOff();
+ SetBackgroundColor(CRGBA(0x80, 0x80, 0x80, 0x80));
+ SetBackGroundOnlyTextOff();
+ SetPropOn();
+ SetFontStyle(FONT_BANK);
+ SetRightJustifyWrap(0.0f);
+ SetAlphaFade(255.0f);
+ SetDropShadowPosition(0);
+ CTxdStore::PopCurrentTxd();
+}
+
+#ifdef MORE_LANGUAGES
+void
+CFont::ReloadFonts(uint8 set)
+{
+ if (Slot != -1 && LanguageSet != set) {
+ Sprite[0].Delete();
+ Sprite[1].Delete();
+ Sprite[2].Delete();
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::RemoveTxd(Slot);
+ switch (set)
+ {
+ case FONT_LANGSET_EFIGS:
+ default:
+ CTxdStore::LoadTxd(Slot, "MODELS/FONTS.TXD");
+ break;
+ case FONT_LANGSET_RUSSIAN:
+ CTxdStore::LoadTxd(Slot, "MODELS/FONTS_R.TXD");
+ break;
+ }
+ CTxdStore::SetCurrentTxd(Slot);
+ Sprite[0].SetTexture("font2", "font2_mask");
+ Sprite[1].SetTexture("pager", "pager_mask");
+ Sprite[2].SetTexture("font1", "font1_mask");
+ CTxdStore::PopCurrentTxd();
+ }
+ LanguageSet = set;
+}
+#endif
+
+void
+CFont::Shutdown(void)
+{
+ Sprite[0].Delete();
+ Sprite[1].Delete();
+ Sprite[2].Delete();
+#ifdef MORE_LANGUAGES
+ CTxdStore::RemoveTxdSlot(Slot);
+ Slot = -1;
+#else
+ CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts"));
+#endif
+}
+
+void
+CFont::InitPerFrame(void)
+{
+ Details.bank = CSprite2d::GetBank(30, Sprite[0].m_pTexture);
+ CSprite2d::GetBank(15, Sprite[1].m_pTexture);
+ CSprite2d::GetBank(15, Sprite[2].m_pTexture);
+ SetDropShadowPosition(0);
+ NewLine = 0;
+}
+
+void
+CFont::PrintChar(float x, float y, wchar c)
+{
+ if(x <= 0.0f || x > SCREEN_WIDTH ||
+ y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again
+ return;
+
+ float w = GetCharacterWidth(c) / 32.0f;
+ float xoff = c & 0xF;
+ float yoff = c >> 4;
+
+ if(Details.style == FONT_BANK || Details.style == FONT_HEADING){
+ if(Details.dropShadowPosition != 0){
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition),
+ y + SCREEN_SCALE_Y(Details.dropShadowPosition),
+ x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f,
+ y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY * 0.5f),
+ Details.dropColor,
+ xoff/16.0f, yoff/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
+ xoff/16.0f, (yoff+1.0f)/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.0001f);
+ }
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x, y,
+ x + 32.0f * Details.scaleX * 1.0f,
+ y + 40.0f * Details.scaleY * 0.5f),
+ Details.color,
+ xoff/16.0f, yoff/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
+ xoff/16.0f, (yoff+1.0f)/12.8f - 0.002f,
+ (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.002f);
+ }else{
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x, y,
+ x + 32.0f * Details.scaleX * w,
+ y + 32.0f * Details.scaleY * 0.5f),
+ Details.color,
+ xoff/16.0f, yoff/16.0f,
+ (xoff+w)/16.0f, yoff/16.0f,
+ xoff/16.0f, (yoff+1.0f)/16.0f,
+ (xoff+w)/16.0f - 0.0001f, (yoff+1.0f)/16.0f - 0.0001f);
+ }
+}
+
+void
+CFont::PrintString(float xstart, float ystart, wchar *s)
+{
+ CRect rect;
+ int numSpaces;
+ float lineLength;
+ float x, y;
+ bool first;
+ wchar *start, *t;
+
+ if(*s == '*')
+ return;
+
+ if(Details.background){
+ GetNumberLines(xstart, ystart, s); // BUG: result not used
+ GetTextRect(&rect, xstart, ystart, s);
+ CSprite2d::DrawRect(rect, Details.backgroundColor);
+ }
+
+ lineLength = 0.0f;
+ numSpaces = 0;
+ first = true;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+ start = s;
+
+ // This is super ugly, I blame R*
+ for(;;){
+ for(;;){
+ for(;;){
+ if(*s == '\0')
+ return;
+ int xend = Details.centre ? Details.centreSize :
+ Details.rightJustify ? xstart - Details.rightJustifyWrap :
+ Details.wrapX;
+ if(x + GetStringWidth(s) > xend && !first){
+ // flush line
+ float spaceWidth = !Details.justify || Details.centre ? 0.0f :
+ (Details.wrapX - lineLength) / numSpaces;
+ float xleft = Details.centre ? xstart - x/2 :
+ Details.rightJustify ? xstart - x :
+ xstart;
+ PrintString(xleft, y, start, s, spaceWidth);
+ // reset things
+ lineLength = 0.0f;
+ numSpaces = 0;
+ first = true;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ start = s;
+ }else
+ break;
+ }
+ // advance by one word
+ t = GetNextSpace(s);
+ if(t[0] == '\0' ||
+ t[0] == ' ' && t[1] == '\0')
+ break;
+ if(!first)
+ numSpaces++;
+ first = false;
+ x += GetStringWidth(s) + GetCharacterSize(*t - ' ');
+ lineLength = x;
+ s = t+1;
+ }
+ // print rest
+ if(t[0] == ' ' && t[1] == '\0')
+ t[0] = '\0';
+ x += GetStringWidth(s);
+ s = t;
+ float xleft = Details.centre ? xstart - x/2 :
+ Details.rightJustify ? xstart - x :
+ xstart;
+ PrintString(xleft, y, start, s, 0.0f);
+ }
+}
+
+int
+CFont::GetNumberLines(float xstart, float ystart, wchar *s)
+{
+ int n;
+ float x, y;
+ wchar *t;
+
+ n = 0;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+
+ while(*s){
+ if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
+ // reached end of line
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ n++;
+ // Why even?
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ }else{
+ // still space in current line
+ t = GetNextSpace(s);
+ if(*t == '\0'){
+ // end of string
+ x += GetStringWidth(s);
+ n++;
+ s = t;
+ }else{
+ x += GetStringWidth(s);
+ x += GetCharacterSize(*t - ' ');
+ s = t+1;
+ }
+ }
+ }
+
+ return n;
+}
+
+void
+CFont::GetTextRect(CRect *rect, float xstart, float ystart, wchar *s)
+{
+ int numLines;
+ float x, y;
+ int16 maxlength;
+ wchar *t;
+
+ maxlength = 0;
+ numLines = 0;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+
+ while(*s){
+ if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
+ // reached end of line
+ if(x > maxlength)
+ maxlength = x;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ numLines++;
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ }else{
+ // still space in current line
+ t = GetNextSpace(s);
+ if(*t == '\0'){
+ // end of string
+ x += GetStringWidth(s);
+ if(x > maxlength)
+ maxlength = x;
+ numLines++;
+ s = t;
+ }else{
+ x += GetStringWidth(s);
+ x += GetCharacterSize(*t - ' ');
+ s = t+1;
+ }
+ }
+ }
+
+ if(Details.centre){
+ if(Details.backgroundOnlyText){
+ rect->left = xstart - maxlength/2 - 4.0f;
+ rect->right = xstart + maxlength/2 + 4.0f;
+ rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f;
+ rect->top = ystart - 2.0f;
+ }else{
+ rect->left = xstart - Details.centreSize*0.5f - 4.0f;
+ rect->right = xstart + Details.centreSize*0.5f + 4.0f;
+ rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f;
+ rect->top = ystart - 2.0f;
+ }
+ }else{
+ rect->left = xstart - 4.0f;
+ rect->right = Details.wrapX;
+ // WTF?
+ rect->bottom = ystart - 4.0f + 4.0f;
+ rect->top = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f + 2.0f;
+ }
+}
+
+void
+CFont::PrintString(float x, float y, wchar *start, wchar *end, float spwidth)
+{
+ wchar *s, c, unused;
+
+ for(s = start; s < end; s++){
+ if(*s == '~')
+ s = ParseToken(s, &unused);
+ c = *s - ' ';
+ if(Details.slant != 0.0f)
+ y = (Details.slantRefX - x)*Details.slant + Details.slantRefY;
+ PrintChar(x, y, c);
+ x += GetCharacterSize(c);
+ if(c == 0) // space
+ x += spwidth;
+ }
+}
+
+float
+CFont::GetCharacterWidth(wchar c)
+{
+#ifdef MORE_LANGUAGES
+ if (Details.proportional)
+ return Size[LanguageSet][Details.style][c];
+ else
+ return Size[LanguageSet][Details.style][192];
+#else
+ if (Details.proportional)
+ return Size[Details.style][c];
+ else
+ return Size[Details.style][192];
+#endif // MORE_LANGUAGES
+}
+
+float
+CFont::GetCharacterSize(wchar c)
+{
+#ifdef MORE_LANGUAGES
+ if(Details.proportional)
+ return Size[LanguageSet][Details.style][c] * Details.scaleX;
+ else
+ return Size[LanguageSet][Details.style][192] * Details.scaleX;
+#else
+ if (Details.proportional)
+ return Size[Details.style][c] * Details.scaleX;
+ else
+ return Size[Details.style][192] * Details.scaleX;
+#endif // MORE_LANGUAGES
+}
+
+float
+CFont::GetStringWidth(wchar *s, bool spaces)
+{
+ float w;
+
+ w = 0.0f;
+ for(; (*s != ' ' || spaces) && *s != '\0'; s++){
+ if(*s == '~'){
+ s++;
+ while(*s != '~') s++;
+ s++;
+ if(*s == ' ' && !spaces)
+ break;
+ }
+ w += GetCharacterSize(*s - ' ');
+ }
+ return w;
+}
+
+wchar*
+CFont::GetNextSpace(wchar *s)
+{
+ for(; *s != ' ' && *s != '\0'; s++)
+ if(*s == '~'){
+ s++;
+ while(*s != '~') s++;
+ s++;
+ if(*s == ' ')
+ break;
+ }
+ return s;
+}
+
+wchar*
+CFont::ParseToken(wchar *s, wchar*)
+{
+ s++;
+ if(Details.color.r || Details.color.g || Details.color.b)
+ switch(*s){
+ case 'N':
+ case 'n':
+ NewLine = 1;
+ break;
+ case 'b': SetColor(CRGBA(0x80, 0xA7, 0xF3, 0xFF)); break;
+ case 'g': SetColor(CRGBA(0x5F, 0xA0, 0x6A, 0xFF)); break;
+ case 'h': SetColor(CRGBA(0xE1, 0xE1, 0xE1, 0xFF)); break;
+ case 'l': SetColor(CRGBA(0x00, 0x00, 0x00, 0xFF)); break;
+ case 'p': SetColor(CRGBA(0xA8, 0x6E, 0xFC, 0xFF)); break;
+ case 'r': SetColor(CRGBA(0x71, 0x2B, 0x49, 0xFF)); break;
+ case 'w': SetColor(CRGBA(0xAF, 0xAF, 0xAF, 0xFF)); break;
+ case 'y': SetColor(CRGBA(0xD2, 0xC4, 0x6A, 0xFF)); break;
+ }
+ while(*s != '~') s++;
+ return s+1;
+}
+
+void
+CFont::DrawFonts(void)
+{
+ CSprite2d::DrawBank(Details.bank);
+ CSprite2d::DrawBank(Details.bank+1);
+ CSprite2d::DrawBank(Details.bank+2);
+}
+
+wchar
+CFont::character_code(uint8 c)
+{
+ if(c < 128)
+ return c;
+ return foreign_table[c-128];
+}
+
+STARTPATCHES
+
+ InjectHook(0x500A40, CFont::Initialise, PATCH_JUMP);
+ InjectHook(0x500BA0, CFont::Shutdown, PATCH_JUMP);
+ InjectHook(0x500BE0, CFont::InitPerFrame, PATCH_JUMP);
+ InjectHook(0x500C30, CFont::PrintChar, PATCH_JUMP);
+ InjectHook(0x500F50, (void (*)(float, float, wchar*))CFont::PrintString, PATCH_JUMP);
+ InjectHook(0x501260, CFont::GetNumberLines, PATCH_JUMP);
+ InjectHook(0x5013B0, CFont::GetTextRect, PATCH_JUMP);
+ InjectHook(0x501730, (void (*)(float, float, wchar*, wchar*, float))CFont::PrintString, PATCH_JUMP);
+ InjectHook(0x5017E0, CFont::GetCharacterWidth, PATCH_JUMP);
+ InjectHook(0x501840, CFont::GetCharacterSize, PATCH_JUMP);
+ InjectHook(0x5018A0, CFont::GetStringWidth, PATCH_JUMP);
+ InjectHook(0x501960, CFont::GetNextSpace, PATCH_JUMP);
+ InjectHook(0x5019A0, CFont::ParseToken, PATCH_JUMP);
+ InjectHook(0x501B50, CFont::DrawFonts, PATCH_JUMP);
+ InjectHook(0x501E80, CFont::character_code, PATCH_JUMP);
+
+ InjectHook(0x501B80, CFont::SetScale, PATCH_JUMP);
+ InjectHook(0x501BA0, CFont::SetSlantRefPoint, PATCH_JUMP);
+ InjectHook(0x501BC0, CFont::SetSlant, PATCH_JUMP);
+ InjectHook(0x501BD0, CFont::SetColor, PATCH_JUMP);
+ InjectHook(0x501C60, CFont::SetJustifyOn, PATCH_JUMP);
+ InjectHook(0x501C80, CFont::SetJustifyOff, PATCH_JUMP);
+ InjectHook(0x501C90, CFont::SetCentreOn, PATCH_JUMP);
+ InjectHook(0x501CB0, CFont::SetCentreOff, PATCH_JUMP);
+ InjectHook(0x501CC0, CFont::SetWrapx, PATCH_JUMP);
+ InjectHook(0x501CD0, CFont::SetCentreSize, PATCH_JUMP);
+ InjectHook(0x501CE0, CFont::SetBackgroundOn, PATCH_JUMP);
+ InjectHook(0x501CF0, CFont::SetBackgroundOff, PATCH_JUMP);
+ InjectHook(0x501D00, CFont::SetBackgroundColor, PATCH_JUMP);
+ InjectHook(0x501D30, CFont::SetBackGroundOnlyTextOn, PATCH_JUMP);
+ InjectHook(0x501D40, CFont::SetBackGroundOnlyTextOff, PATCH_JUMP);
+ InjectHook(0x501D50, CFont::SetRightJustifyOn, PATCH_JUMP);
+ InjectHook(0x501D70, CFont::SetRightJustifyOff, PATCH_JUMP);
+ InjectHook(0x501D90, CFont::SetPropOff, PATCH_JUMP);
+ InjectHook(0x501DA0, CFont::SetPropOn, PATCH_JUMP);
+ InjectHook(0x501DB0, CFont::SetFontStyle, PATCH_JUMP);
+ InjectHook(0x501DC0, CFont::SetRightJustifyWrap, PATCH_JUMP);
+ InjectHook(0x501DD0, CFont::SetAlphaFade, PATCH_JUMP);
+ InjectHook(0x501DE0, CFont::SetDropColor, PATCH_JUMP);
+ InjectHook(0x501E70, CFont::SetDropShadowPosition, PATCH_JUMP);
+
+ENDPATCHES
diff --git a/src/render/Font.h b/src/render/Font.h
index 132ad168..0659dda1 100644
--- a/src/render/Font.h
+++ b/src/render/Font.h
@@ -39,9 +39,23 @@ enum {
ALIGN_RIGHT,
};
+#ifdef MORE_LANGUAGES
+enum
+{
+ FONT_LANGSET_EFIGS,
+ FONT_LANGSET_RUSSIAN
+};
+#endif
+
class CFont
{
+#ifdef MORE_LANGUAGES
+ static int16 Size[2][3][193];
+ static uint8 LanguageSet;
+ static int32 Slot;
+#else
static int16 Size[3][193];
+#endif
static int16 &NewLine;
static CSprite2d *Sprite; //[3]
public:
@@ -50,18 +64,18 @@ public:
static void Initialise(void);
static void Shutdown(void);
static void InitPerFrame(void);
- static void PrintChar(float x, float y, uint16 c);
- static void PrintString(float x, float y, uint16 *s);
- static int GetNumberLines(float xstart, float ystart, uint16 *s);
- static void GetTextRect(CRect *rect, float xstart, float ystart, uint16 *s);
- static void PrintString(float x, float y, uint16 *start, uint16 *end, float spwidth);
- static float GetCharacterWidth(uint16 c);
- static float GetCharacterSize(uint16 c);
- static float GetStringWidth(uint16 *s, bool spaces = false);
- static uint16 *GetNextSpace(uint16 *s);
- static uint16 *ParseToken(uint16 *s, uint16*);
+ static void PrintChar(float x, float y, wchar c);
+ static void PrintString(float x, float y, wchar *s);
+ static int GetNumberLines(float xstart, float ystart, wchar *s);
+ static void GetTextRect(CRect *rect, float xstart, float ystart, wchar *s);
+ static void PrintString(float x, float y, wchar *start, wchar *end, float spwidth);
+ static float GetCharacterWidth(wchar c);
+ static float GetCharacterSize(wchar c);
+ static float GetStringWidth(wchar *s, bool spaces = false);
+ static wchar *GetNextSpace(wchar *s);
+ static wchar *ParseToken(wchar *s, wchar*);
static void DrawFonts(void);
- static uint16 character_code(uint8 c);
+ static wchar character_code(uint8 c);
static CFontDetails GetDetails() { return Details; }
static void SetScale(float x, float y) { Details.scaleX = x; Details.scaleY = y; }
@@ -136,4 +150,6 @@ public:
if(Details.alphaFade < 255.0f)
Details.dropColor.a *= Details.alphaFade/255.0f;
}
+
+ static void ReloadFonts(uint8 set);
};
diff --git a/src/render/Hud.cpp b/src/render/Hud.cpp
index 2f523e17..c4aca8e4 100644
--- a/src/render/Hud.cpp
+++ b/src/render/Hud.cpp
@@ -793,8 +793,11 @@ void CHud::Draw()
if (m_ItemToFlash == ITEM_RADAR && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_RADAR) {
CRadar::DrawMap();
CRect rect(0.0f, 0.0f, SCREEN_SCALE_X(RADAR_WIDTH), SCREEN_SCALE_Y(RADAR_HEIGHT));
- // FIX? scale RADAR_LEFT here somehow
+#ifdef FIX_BUGS
+ rect.Translate(SCREEN_SCALE_X(RADAR_LEFT), SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT));
+#else
rect.Translate(RADAR_LEFT, SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT));
+#endif
rect.Grow(4.0f);
Sprites[HUD_RADARDISC].Draw(rect, CRGBA(0, 0, 0, 255));
CRadar::DrawBlips();
@@ -1094,7 +1097,6 @@ void CHud::DrawAfterFade()
CFont::SetColor(CRGBA(175, 175, 175, 255));
CFont::PrintString(SCREEN_SCALE_X(26.0f), SCREEN_SCALE_Y(28.0f + (150.0f - PagerXOffset) * 0.6f), CHud::m_HelpMessageToPrint);
CFont::SetAlphaFade(255.0f);
- CFont::DrawFonts();
}
}
else
diff --git a/src/text/Text.cpp b/src/text/Text.cpp
index 8bffa7e1..d0cdb310 100644
--- a/src/text/Text.cpp
+++ b/src/text/Text.cpp
@@ -43,6 +43,11 @@ CText::Load(void)
case LANGUAGE_SPANISH:
sprintf(filename, "SPANISH.GXT");
break;
+#ifdef MORE_LANGUAGES
+ case LANGUAGE_RUSSIAN:
+ sprintf(filename, "RUSSIAN.GXT");
+ break;
+#endif
}
length = CFileMgr::LoadFile(filename, filedata, 0x40000, "rb");
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index e709a87f..aca96aa3 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -2339,10 +2339,17 @@ CAutomobile::FireTruckControl(void)
if(this == FindPlayerVehicle()){
if(!CPad::GetPad(0)->GetWeapon())
return;
- m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight()*0.00025f*CTimer::GetTimeStep();
- m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown()*0.0001f*CTimer::GetTimeStep();
+#ifdef FREE_CAM
+ extern bool bFreeMouseCam;
+ if (!bFreeMouseCam)
+#endif
+ {
+ m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight() * 0.00025f * CTimer::GetTimeStep();
+ m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown() * 0.0001f * CTimer::GetTimeStep();
+ }
m_fCarGunUD = clamp(m_fCarGunUD, 0.05f, 0.3f);
+
CVector cannonPos(0.0f, 1.5f, 1.9f);
cannonPos = GetMatrix() * cannonPos;
CVector cannonDir(
@@ -2408,7 +2415,12 @@ CAutomobile::TankControl(void)
// Rotate turret
float prevAngle = m_fCarGunLR;
- m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep();
+#ifdef FREE_CAM
+ extern bool bFreeMouseCam;
+ if(!bFreeMouseCam)
+#endif
+ m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep();
+
if(m_fCarGunLR < 0.0f)
m_fCarGunLR += TWOPI;
if(m_fCarGunLR > TWOPI)
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 1fe02953..adeba19e 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -31,10 +31,17 @@ 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); }
-WRAPPER bool CVehicle::ShufflePassengersToMakeSpace(void) { EAXJMP(0x5528A0); }
-// or Weapon.cpp?
-WRAPPER void FireOneInstantHitRound(CVector *shotSource, CVector *shotTarget, int32 damage) { EAXJMP(0x563B00); }
-WRAPPER void CVehicle::InflictDamage(CEntity *damagedBy, uint32 weaponType, float damage) { EAXJMP(0x551950); }
+#ifdef FIX_BUGS
+// I think they meant that
+#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (MYRAND_MAX * 35 / 100)
+#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (MYRAND_MAX * 70 / 100)
+#else
+#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (35000)
+#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (70000)
+#endif
+#define DAMAGE_HEALTH_TO_FLEE_ALWAYS (200.0f)
+#define DAMAGE_HEALTH_TO_CATCH_FIRE (250.0f)
+
CVehicle::CVehicle(uint8 CreatedBy)
{
@@ -362,6 +369,119 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec
}
void
+CVehicle::InflictDamage(CEntity* damagedBy, eWeaponType weaponType, float damage)
+{
+ if (!bCanBeDamaged)
+ return;
+ if (bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle()))
+ return;
+ bool bFrightensDriver = false;
+ switch (weaponType) {
+ case WEAPONTYPE_UNARMED:
+ case WEAPONTYPE_BASEBALLBAT:
+ if (bMeleeProof)
+ return;
+ break;
+ case WEAPONTYPE_COLT45:
+ case WEAPONTYPE_UZI:
+ case WEAPONTYPE_SHOTGUN:
+ case WEAPONTYPE_AK47:
+ case WEAPONTYPE_M16:
+ case WEAPONTYPE_SNIPERRIFLE:
+ case WEAPONTYPE_TOTAL_INVENTORY_WEAPONS:
+ case WEAPONTYPE_UZI_DRIVEBY:
+ if (bBulletProof)
+ return;
+ bFrightensDriver = true;
+ break;
+ case WEAPONTYPE_ROCKETLAUNCHER:
+ case WEAPONTYPE_MOLOTOV:
+ case WEAPONTYPE_GRENADE:
+ case WEAPONTYPE_EXPLOSION:
+ if (bExplosionProof)
+ return;
+ bFrightensDriver = true;
+ break;
+ case WEAPONTYPE_FLAMETHROWER:
+ if (bFireProof)
+ return;
+ break;
+ case WEAPONTYPE_RAMMEDBYCAR:
+ if (bCollisionProof)
+ return;
+ break;
+ default:
+ break;
+ }
+ if (m_fHealth > 0.0f) {
+ if (VehicleCreatedBy == RANDOM_VEHICLE && pDriver &&
+ (m_status == STATUS_SIMPLE || m_status == STATUS_PHYSICS) &&
+ AutoPilot.m_nCarMission == MISSION_CRUISE) {
+ if (m_randomSeed < DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE) {
+ CCarCtrl::SwitchVehicleToRealPhysics(this);
+ AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
+ AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * pHandling->Transmission.fUnkMaxVelocity;
+ m_status = STATUS_PHYSICS;
+ }
+ }
+ m_nLastWeaponDamage = weaponType;
+ float oldHealth = m_fHealth;
+ if (m_fHealth > damage) {
+ m_fHealth -= damage;
+ if (VehicleCreatedBy == RANDOM_VEHICLE &&
+ (m_fHealth < DAMAGE_HEALTH_TO_FLEE_ALWAYS ||
+ bFrightensDriver && m_randomSeed > DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE)) {
+ switch (m_status) {
+ case STATUS_SIMPLE:
+ case STATUS_PHYSICS:
+ if (pDriver) {
+ m_status = STATUS_ABANDONED;
+ pDriver->bFleeAfterExitingCar = true;
+ pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this);
+ }
+ for (int i = 0; i < m_nNumMaxPassengers; i++) {
+ if (pPassengers[i]) {
+ pPassengers[i]->bFleeAfterExitingCar = true;
+ pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (oldHealth > DAMAGE_HEALTH_TO_CATCH_FIRE && m_fHealth < DAMAGE_HEALTH_TO_CATCH_FIRE) {
+ if (IsCar()) {
+ CAutomobile* pThisCar = (CAutomobile*)this;
+ pThisCar->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE);
+ pThisCar->m_pSetOnFireEntity = damagedBy;
+ if (damagedBy)
+ damagedBy->RegisterReference((CEntity**)&pThisCar->m_pSetOnFireEntity);
+ }
+ }
+ }
+ else {
+ m_fHealth = 0.0f;
+ if (weaponType == WEAPONTYPE_EXPLOSION) {
+ // between 1000 and 3047. Also not very nice: can't be saved by respray or cheat
+ m_nBombTimer = 1000 + CGeneral::GetRandomNumber() & 0x7FF;
+ m_pBlowUpEntity = damagedBy;
+ if (damagedBy)
+ damagedBy->RegisterReference((CEntity**)&m_pBlowUpEntity);
+ }
+ else
+ BlowUpCar(damagedBy);
+ }
+ }
+#ifdef FIX_BUGS // removing dumb case when shooting police car in player's own garage gives wanted level
+ if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && !bHasBeenOwnedByPlayer)
+#else
+ if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed())
+#endif
+ FindPlayerPed()->SetWantedLevelNoDrop(1);
+}
+
+void
CVehicle::ExtinguishCarFire(void)
{
m_fHealth = max(m_fHealth, 300.0f);
@@ -375,6 +495,65 @@ CVehicle::ExtinguishCarFire(void)
}
}
+bool
+CVehicle::ShufflePassengersToMakeSpace(void)
+{
+ if (m_nNumPassengers >= m_nNumMaxPassengers)
+ return false;
+ if (pPassengers[1] &&
+ !(m_nGettingInFlags & CAR_DOOR_FLAG_LR) &&
+ IsRoomForPedToLeaveCar(COMPONENT_DOOR_REAR_LEFT, nil)) {
+ if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) {
+ pPassengers[2] = pPassengers[1];
+ pPassengers[1] = nil;
+ pPassengers[2]->m_vehEnterType = CAR_DOOR_RR;
+ return true;
+ }
+ if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) {
+ pPassengers[0] = pPassengers[1];
+ pPassengers[1] = nil;
+ pPassengers[0]->m_vehEnterType = CAR_DOOR_RF;
+ return true;
+ }
+ return false;
+ }
+ if (pPassengers[2] &&
+ !(m_nGettingInFlags & CAR_DOOR_FLAG_RR) &&
+ IsRoomForPedToLeaveCar(COMPONENT_DOOR_REAR_RIGHT, nil)) {
+ if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) {
+ pPassengers[1] = pPassengers[2];
+ pPassengers[2] = nil;
+ pPassengers[1]->m_vehEnterType = CAR_DOOR_LR;
+ return true;
+ }
+ if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) {
+ pPassengers[0] = pPassengers[2];
+ pPassengers[2] = nil;
+ pPassengers[0]->m_vehEnterType = CAR_DOOR_RF;
+ return true;
+ }
+ return false;
+ }
+ if (pPassengers[0] &&
+ !(m_nGettingInFlags & CAR_DOOR_FLAG_RF) &&
+ IsRoomForPedToLeaveCar(COMPONENT_DOOR_FRONT_RIGHT, nil)) {
+ if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) {
+ pPassengers[1] = pPassengers[0];
+ pPassengers[0] = nil;
+ pPassengers[1]->m_vehEnterType = CAR_DOOR_LR;
+ return true;
+ }
+ if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) {
+ pPassengers[2] = pPassengers[0];
+ pPassengers[0] = nil;
+ pPassengers[2]->m_vehEnterType = CAR_DOOR_RR;
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
void
CVehicle::ProcessDelayedExplosion(void)
{
@@ -831,4 +1010,5 @@ STARTPATCHES
InjectHook(0x551EB0, &CVehicle::RemovePassenger, PATCH_JUMP);
InjectHook(0x5525A0, &CVehicle::ProcessCarAlarm, PATCH_JUMP);
InjectHook(0x552620, &CVehicle::IsSphereTouchingVehicle, PATCH_JUMP);
+ InjectHook(0x551950, &CVehicle::InflictDamage, PATCH_JUMP);
ENDPATCHES
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 2ca97841..4639f3e1 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -4,6 +4,7 @@
#include "AutoPilot.h"
#include "ModelIndices.h"
#include "AnimManager.h"
+#include "Weapon.h"
class CPed;
class CFire;
@@ -266,7 +267,7 @@ public:
void ProcessCarAlarm(void);
bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius);
bool ShufflePassengersToMakeSpace(void);
- void InflictDamage(CEntity *damagedBy, uint32 weaponType, float damage);
+ void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage);
bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; }
CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); }
diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp
index 09844c23..0f41264f 100644
--- a/src/weapons/Weapon.cpp
+++ b/src/weapons/Weapon.cpp
@@ -14,6 +14,7 @@ WRAPPER void CWeapon::AddGunshell(CEntity*, CVector const&, CVector2D const&, fl
WRAPPER void CWeapon::Update(int32 audioEntity) { EAXJMP(0x563A10); }
WRAPPER void CWeapon::DoTankDoomAiming(CEntity *playerVehicle, CEntity *playerPed, CVector *start, CVector *end) { EAXJMP(0x563200); }
WRAPPER void CWeapon::InitialiseWeapons(void) { EAXJMP(0x55C2D0); }
+WRAPPER void FireOneInstantHitRound(CVector* shotSource, CVector* shotTarget, int32 damage) { EAXJMP(0x563B00); }
void
CWeapon::Initialise(eWeaponType type, int ammo)
diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h
index 74145564..84760550 100644
--- a/src/weapons/Weapon.h
+++ b/src/weapons/Weapon.h
@@ -81,3 +81,5 @@ public:
static void UpdateWeapons(void);
};
static_assert(sizeof(CWeapon) == 0x18, "CWeapon: error");
+
+void FireOneInstantHitRound(CVector* shotSource, CVector* shotTarget, int32 damage); \ No newline at end of file