diff options
Diffstat (limited to '')
38 files changed, 3201 insertions, 852 deletions
@@ -49,8 +49,6 @@ CCullZone CCullZones CExplosion CFallingGlassPane -CGarage -CGarages CGlass CMenuManager - WIP CMotionBlurStreaks diff --git a/gamefiles/JAPANESE.gxt b/gamefiles/JAPANESE.gxt Binary files differnew file mode 100644 index 00000000..d4b54383 --- /dev/null +++ b/gamefiles/JAPANESE.gxt diff --git a/gamefiles/fonts_j.txd b/gamefiles/fonts_j.txd Binary files differnew file mode 100644 index 00000000..437e13f9 --- /dev/null +++ b/gamefiles/fonts_j.txd diff --git a/gamefiles/fonts_r.txd b/gamefiles/fonts_r.txd Binary files differnew file mode 100644 index 00000000..4b89e449 --- /dev/null +++ b/gamefiles/fonts_r.txd diff --git a/gamefiles/russian.gxt b/gamefiles/russian.gxt Binary files differnew file mode 100644 index 00000000..41c4cec8 --- /dev/null +++ b/gamefiles/russian.gxt 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 
 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 |