summaryrefslogtreecommitdiffstats
path: root/src/control
diff options
context:
space:
mode:
Diffstat (limited to 'src/control')
-rw-r--r--src/control/Cranes.cpp4
-rw-r--r--src/control/Garages.cpp2
-rw-r--r--src/control/Pickups.cpp469
-rw-r--r--src/control/Pickups.h25
-rw-r--r--src/control/Record.cpp526
-rw-r--r--src/control/Record.h90
-rw-r--r--src/control/TrafficLights.cpp4
7 files changed, 1062 insertions, 58 deletions
diff --git a/src/control/Cranes.cpp b/src/control/Cranes.cpp
index 021a5c19..33385dae 100644
--- a/src/control/Cranes.cpp
+++ b/src/control/Cranes.cpp
@@ -410,6 +410,8 @@ void CCrane::FindCarInSectorList(CPtrList* pList)
if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 ||
pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2)
continue;
+ if (pVehicle->pDriver)
+ continue;
if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD ||
Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD ||
Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD)
@@ -674,4 +676,4 @@ void CranesLoad(uint8* buf, uint32 size)
STARTPATCHES
InjectHook(0x5454D0, CranesLoad, PATCH_JUMP); // GenericLoad
-ENDPATCHES \ No newline at end of file
+ENDPATCHES
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 84d49bee..d971d453 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -2108,7 +2108,7 @@ void CGarages::CloseHideOutGaragesBeforeSave()
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_eGarageType != GARAGE_HIDEOUT_ONE || !aGarages[i].IsAnyCarBlockingDoor())) {
aGarages[i].m_eGarageState = GS_FULLYCLOSED;
switch (aGarages[i].m_eGarageType) {
case GARAGE_HIDEOUT_ONE:
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index 774abd8c..eb561670 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -54,23 +54,6 @@ uint8 aWeaponGreens[] = { 0, 255, 128, 255, 0, 255, 128, 255, 0, 255, 255, 0, 25
uint8 aWeaponBlues[] = { 0, 0, 255, 0, 255, 255, 0, 128, 255, 0, 255, 0, 128, 255, 0, 0 };
float aWeaponScale[] = { 1.0f, 2.0f, 1.5f, 1.0f, 1.0f, 1.5f, 1.0f, 2.0f, 1.0f, 2.0f, 2.5f, 1.0f, 1.0f, 1.0f, 1.0f };
-WRAPPER void CPacManPickups::Init(void) { EAXJMP(0x432760); }
-WRAPPER void CPacManPickups::Update(void) { EAXJMP(0x432800); }
-WRAPPER void CPacManPickups::GeneratePMPickUps(CVector, float, int16, uint8) { EAXJMP(0x432AE0); }
-WRAPPER void CPacManPickups::GeneratePMPickUpsForRace(int32) { EAXJMP(0x432D50); }
-WRAPPER void CPacManPickups::GenerateOnePMPickUp(CVector) { EAXJMP(0x432F20); }
-WRAPPER void CPacManPickups::Render(void) { EAXJMP(0x432F60); }
-WRAPPER void CPacManPickups::DoCleanUpPacManStuff(void) { EAXJMP(0x433150); }
-WRAPPER void CPacManPickups::StartPacManRace(int32) { EAXJMP(0x433340); }
-WRAPPER void CPacManPickups::StartPacManRecord(void) { EAXJMP(0x433360); }
-WRAPPER uint32 CPacManPickups::QueryPowerPillsEatenInRace(void) { EAXJMP(0x4333A0); }
-WRAPPER void CPacManPickups::ResetPowerPillsEatenInRace(void) { EAXJMP(0x4333B0); }
-WRAPPER void CPacManPickups::CleanUpPacManStuff(void) { EAXJMP(0x4333C0); }
-WRAPPER void CPacManPickups::StartPacManScramble(CVector, float, int16) { EAXJMP(0x4333D0); }
-WRAPPER uint32 CPacManPickups::QueryPowerPillsCarriedByPlayer(void) { EAXJMP(0x4333F0); }
-WRAPPER void CPacManPickups::ResetPowerPillsCarriedByPlayer(void) { EAXJMP(0x433410); }
-
-
void
CPickup::RemoveKeepType()
{
@@ -111,7 +94,7 @@ CPickup::GiveUsAPickUpObject(int32 handle)
object->bUsesCollision = false;
object->bIsPickup = true;
- object->field_172 = m_eModelIndex == MI_PICKUP_BONUS ? m_nQuantity : 0;
+ object->m_nBonusValue = m_eModelIndex == MI_PICKUP_BONUS ? m_nQuantity : 0;
switch (m_eType)
{
@@ -289,13 +272,6 @@ CPickup::Update(CPlayerPed *player, CVehicle *vehicle, int playerId)
Remove();
DMAudio.PlayFrontEndSound(SOUND_PICKUP_MONEY, 0);
return true;
- //case PICKUP_IN_SHOP_OUT_OF_STOCK:
- //case PICKUP_MINE_INACTIVE:
- //case PICKUP_MINE_ARMED:
- //case PICKUP_NAUTICAL_MINE_INACTIVE:
- //case PICKUP_NAUTICAL_MINE_ARMED:
- //case PICKUP_FLOATINGPACKAGE:
- //case PICKUP_FLOATINGPACKAGE_FLOATING:
default:
break;
}
@@ -530,14 +506,12 @@ CPickups::GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quan
}
}
- if (!bFreeFound)
- {
+ if (!bFreeFound) {
for (slot = 0; slot < NUMGENERALPICKUPS; slot++) {
if (aPickUps[slot].m_eType == PICKUP_MONEY) break;
}
- if (slot >= NUMGENERALPICKUPS)
- {
+ if (slot >= NUMGENERALPICKUPS) {
for (slot = 0; slot < NUMGENERALPICKUPS; slot++) {
if (aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT) break;
}
@@ -671,9 +645,9 @@ void
CPickups::DoPickUpEffects(CEntity *entity)
{
if (entity->GetModelIndex() == MI_PICKUP_KILLFRENZY)
- entity->m_flagD80 = CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame;
+ entity->bDoNotRender = CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame;
- if (!entity->m_flagD80) {
+ if (!entity->bDoNotRender) {
float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x7FF) * DEGTORAD(360.0f / 0x800));
float modifiedSin = 0.3f * (s + 1.0f);
@@ -716,7 +690,7 @@ CPickups::DoPickUpEffects(CEntity *entity)
size, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
CObject *object = (CObject*)entity;
- if (object->m_obj_flag2 || object->bOutOfStock || object->field_172) {
+ if (object->m_obj_flag2 || object->bOutOfStock || object->m_nBonusValue) {
float dist = (TheCamera.GetPosition() - pos).Magnitude();
const float MAXDIST = 12.0f;
@@ -734,7 +708,7 @@ CPickups::DoPickUpEffects(CEntity *entity)
aMessages[NumMessages].m_color.blue = aWeaponBlues[colorId];
aMessages[NumMessages].m_color.alpha = (1.0f - dist / MAXDIST) * 128.0f;
aMessages[NumMessages].m_bOutOfStock = object->bOutOfStock;
- aMessages[NumMessages].m_quantity = object->field_172;
+ aMessages[NumMessages].m_quantity = object->m_nBonusValue;
NumMessages++;
}
}
@@ -1021,6 +995,418 @@ INITSAVEBUF
VALIDATESAVEBUF(*size)
}
+void
+CPacManPickup::Update()
+{
+ if (FindPlayerVehicle() == nil) return;
+
+ CVehicle *veh = FindPlayerVehicle();
+
+ if (DistanceSqr2D(FindPlayerVehicle()->GetPosition(), m_vecPosn.x, m_vecPosn.y) < 100.0f && veh->IsSphereTouchingVehicle(m_vecPosn.x, m_vecPosn.y, m_vecPosn.z, 1.5f)) {
+ switch (m_eType)
+ {
+ case PACMAN_SCRAMBLE:
+ {
+ veh->m_nPacManPickupsCarried++;
+ veh->m_vecMoveSpeed *= 0.65f;
+ float massMult = (veh->m_fMass + 250.0f) / veh->m_fMass;
+ veh->m_fMass *= massMult;
+ veh->m_fTurnMass *= massMult;
+ veh->m_fForceMultiplier *= massMult;
+ FindPlayerPed()->m_pWanted->m_nChaos += 10;
+ FindPlayerPed()->m_pWanted->UpdateWantedLevel();
+ DMAudio.PlayFrontEndSound(SOUND_PICKUP_PACMAN_PACKAGE, 0);
+ break;
+ }
+ case PACMAN_RACE:
+ CPacManPickups::PillsEatenInRace++;
+ DMAudio.PlayFrontEndSound(SOUND_PICKUP_PACMAN_PILL, 0);
+ break;
+ default:
+ break;
+ }
+ m_eType = PACMAN_NONE;
+ if (m_pObject != nil) {
+ CWorld::Remove(m_pObject);
+ delete m_pObject;
+ m_pObject = nil;
+ }
+ }
+}
+
+int32 CollectGameState;
+int16 ThingsToCollect;
+
+CPacManPickup CPacManPickups::aPMPickUps[NUMPACMANPICKUPS];
+CVector CPacManPickups::LastPickUpCoors;
+int32 CPacManPickups::PillsEatenInRace;
+bool CPacManPickups::bPMActive;
+
+void
+CPacManPickups::Init()
+{
+ for (int i = 0; i < NUMPACMANPICKUPS; i++)
+ aPMPickUps[i].m_eType = PACMAN_NONE;
+ bPMActive = false;
+}
+
+void
+CPacManPickups::Update()
+{
+ if (FindPlayerVehicle()) {
+ float dist = Distance(FindPlayerCoors(), CVector(1072.0f, -948.0f, 14.5f));
+ switch (CollectGameState) {
+ case 1:
+ if (dist < 10.0f) {
+ ThingsToCollect -= FindPlayerVehicle()->m_nPacManPickupsCarried;
+ FindPlayerVehicle()->m_nPacManPickupsCarried = 0;
+ FindPlayerVehicle()->m_fMass /= FindPlayerVehicle()->m_fForceMultiplier;
+ FindPlayerVehicle()->m_fTurnMass /= FindPlayerVehicle()->m_fForceMultiplier;
+ FindPlayerVehicle()->m_fForceMultiplier = 1.0f;
+ }
+ if (ThingsToCollect <= 0) {
+ CollectGameState = 2;
+ ClearPMPickUps();
+ }
+ break;
+ case 2:
+ if (dist > 11.0f)
+ CollectGameState = 0;
+ break;
+ case 20:
+ if (Distance(FindPlayerCoors(), LastPickUpCoors) > 30.0f) {
+ LastPickUpCoors = FindPlayerCoors();
+ printf("%f, %f, %f,\n", LastPickUpCoors.x, LastPickUpCoors.y, LastPickUpCoors.z);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (bPMActive) {
+#define PACMANPICKUPS_FRAME_SPAN (4)
+ for (uint32 i = (CTimer::GetFrameCounter() % PACMANPICKUPS_FRAME_SPAN) * (NUMPACMANPICKUPS / PACMANPICKUPS_FRAME_SPAN); i < ((CTimer::GetFrameCounter() % PACMANPICKUPS_FRAME_SPAN) + 1) * (NUMPACMANPICKUPS / PACMANPICKUPS_FRAME_SPAN); i++) {
+ if (aPMPickUps[i].m_eType != PACMAN_NONE)
+ aPMPickUps[i].Update();
+ }
+#undef PACMANPICKUPS_FRAME_SPAN
+ }
+}
+
+void
+CPacManPickups::GeneratePMPickUps(CVector pos, float scrambleMult, int16 count, uint8 type)
+{
+ int i = 0;
+ while (count > 0) {
+ while (aPMPickUps[i].m_eType != PACMAN_NONE)
+ i++;
+
+ bool bPickupCreated = false;
+ while (!bPickupCreated) {
+ CVector newPos = pos;
+ CColPoint colPoint;
+ CEntity *pRoad;
+ uint16 nRand = CGeneral::GetRandomNumber();
+ newPos.x += ((nRand & 0xFF) - 128) * scrambleMult / 128.0f;
+ newPos.y += (((nRand >> 8) & 0xFF) - 128) * scrambleMult / 128.0f;
+ newPos.z = 1000.0f;
+ if (CWorld::ProcessVerticalLine(newPos, -1000.0f, colPoint, pRoad, true, false, false, false, true, false, nil) && pRoad->IsBuilding() && ((CBuilding*)pRoad)->GetIsATreadable()) {
+ newPos.z = 0.7f + colPoint.point.z;
+ aPMPickUps[i].m_eType = type;
+ aPMPickUps[i].m_vecPosn = newPos;
+ CObject *obj = new CObject(MI_BULLION, true);
+ if (obj != nil) {
+ obj->ObjectCreatedBy = MISSION_OBJECT;
+ obj->GetPosition() = aPMPickUps[i].m_vecPosn;
+ obj->SetOrientation(0.0f, 0.0f, -HALFPI);
+ obj->GetMatrix().UpdateRW();
+ obj->UpdateRwFrame();
+
+ obj->bAffectedByGravity = false;
+ obj->bExplosionProof = true;
+ obj->bUsesCollision = false;
+ obj->bIsPickup = false;
+ CWorld::Add(obj);
+ }
+ aPMPickUps[i].m_pObject = obj;
+ bPickupCreated = true;
+ }
+ }
+ count--;
+ }
+ bPMActive = true;
+}
+
+// diablo porn mission pickups
+static const CVector aRacePoints1[] = {
+ CVector(913.62219f, -155.13692f, 4.9699469f),
+ CVector(913.92401f, -124.12943f, 4.9692569f),
+ CVector(913.27899f, -93.524231f, 7.4325991f),
+ CVector(912.60852f, -63.15905f, 7.4533591f),
+ CVector(934.22144f, -42.049122f, 7.4511471f),
+ CVector(958.88092f, -23.863735f, 7.4652338f),
+ CVector(978.50812f, -0.78458798f, 5.13515f),
+ CVector(1009.4175f, -2.1041219f, 2.4461579f),
+ CVector(1040.6313f, -2.0793829f, 2.293175f),
+ CVector(1070.7863f, -2.084095f, 2.2789791f),
+ CVector(1100.5773f, -8.468729f, 5.3248072f),
+ CVector(1119.9341f, -31.738031f, 7.1913071f),
+ CVector(1122.1664f, -62.762737f, 7.4703908f),
+ CVector(1122.814f, -93.650566f, 8.5577497f),
+ CVector(1125.8253f, -124.26616f, 9.9803305f),
+ CVector(1153.8727f, -135.47169f, 14.150617f),
+ CVector(1184.0831f, -135.82845f, 14.973998f),
+ CVector(1192.0432f, -164.57816f, 19.18627f),
+ CVector(1192.7761f, -194.28871f, 24.799675f),
+ CVector(1215.1527f, -215.0714f, 25.74975f),
+ CVector(1245.79f, -215.39304f, 28.70726f),
+ CVector(1276.2477f, -216.39485f, 33.71236f),
+ CVector(1306.5535f, -216.71007f, 39.711472f),
+ CVector(1335.0244f, -224.59329f, 46.474979f),
+ CVector(1355.4879f, -246.27664f, 49.934841f),
+ CVector(1362.6003f, -276.47064f, 49.96265f),
+ CVector(1363.027f, -307.30847f, 49.969173f),
+ CVector(1365.343f, -338.08609f, 49.967789f),
+ CVector(1367.5957f, -368.01105f, 50.092304f),
+ CVector(1368.2749f, -398.38049f, 50.061268f),
+ CVector(1366.9034f, -429.98483f, 50.057545f),
+ CVector(1356.8534f, -459.09259f, 50.035545f),
+ CVector(1335.5819f, -481.13544f, 47.217903f),
+ CVector(1306.7552f, -491.07443f, 40.202629f),
+ CVector(1275.5978f, -491.33194f, 33.969223f),
+ CVector(1244.702f, -491.46451f, 29.111021f),
+ CVector(1213.2222f, -491.8754f, 25.771168f),
+ CVector(1182.7729f, -492.19995f, 24.749964f),
+ CVector(1152.6874f, -491.42221f, 21.70038f),
+ CVector(1121.5352f, -491.94604f, 20.075182f),
+ CVector(1090.7056f, -492.63751f, 17.585758f),
+ CVector(1059.6008f, -491.65762f, 14.848632f),
+ CVector(1029.113f, -489.66031f, 14.918498f),
+ CVector(998.20679f, -486.78107f, 14.945688f),
+ CVector(968.00555f, -484.91266f, 15.001229f),
+ CVector(937.74939f, -492.09015f, 14.958629f),
+ CVector(927.17352f, -520.97736f, 14.972308f),
+ CVector(929.29749f, -552.08643f, 14.978855f),
+ CVector(950.69525f, -574.47778f, 14.972788f),
+ CVector(974.02826f, -593.56024f, 14.966445f),
+ CVector(989.04779f, -620.12854f, 14.951016f),
+ CVector(1014.1639f, -637.3905f, 14.966736f),
+ CVector(1017.5961f, -667.3736f, 14.956415f),
+ CVector(1041.9735f, -685.94391f, 15.003841f),
+ CVector(1043.3064f, -716.11298f, 14.974236f),
+ CVector(1043.5337f, -746.63855f, 14.96919f),
+ CVector(1044.142f, -776.93823f, 14.965424f),
+ CVector(1044.2657f, -807.29395f, 14.97171f),
+ CVector(1017.0797f, -820.1076f, 14.975431f),
+ CVector(986.23865f, -820.37103f, 14.972883f),
+ CVector(956.10065f, -820.23291f, 14.981133f),
+ CVector(925.86914f, -820.19049f, 14.976553f),
+ CVector(897.69702f, -831.08734f, 14.962709f),
+ CVector(868.06586f, -835.99237f, 14.970685f),
+ CVector(836.93054f, -836.84387f, 14.965049f),
+ CVector(811.63586f, -853.7915f, 15.067576f),
+ CVector(811.46344f, -884.27368f, 12.247812f),
+ CVector(811.60651f, -914.70959f, 9.2393751f),
+ CVector(811.10425f, -945.16272f, 5.817255f),
+ CVector(816.54584f, -975.64587f, 4.998558f),
+ CVector(828.2951f, -1003.3685f, 5.0471172f),
+ CVector(852.28839f, -1021.5963f, 4.9371028f),
+ CVector(882.50067f, -1025.4459f, 5.14077f),
+ CVector(912.84821f, -1026.7874f, 8.3415451f),
+ CVector(943.68274f, -1026.6914f, 11.341879f),
+ CVector(974.4129f, -1027.3682f, 14.410345f),
+ CVector(1004.1079f, -1036.0778f, 14.92961f),
+ CVector(1030.1144f, -1051.1224f, 14.850387f),
+ CVector(1058.7585f, -1060.342f, 14.821624f),
+ CVector(1087.7797f, -1068.3263f, 14.800561f),
+ CVector(1099.8807f, -1095.656f, 11.877907f),
+ CVector(1130.0005f, -1101.994f, 11.853914f),
+ CVector(1160.3809f, -1101.6355f, 11.854824f),
+ CVector(1191.8524f, -1102.1577f, 11.853843f),
+ CVector(1223.3307f, -1102.7448f, 11.852233f),
+ CVector(1253.564f, -1098.1045f, 11.853944f),
+ CVector(1262.0203f, -1069.1785f, 14.8147f),
+ CVector(1290.9998f, -1059.1882f, 14.816016f),
+ CVector(1316.246f, -1041.0635f, 14.81109f),
+ CVector(1331.7539f, -1013.835f, 14.81207f),
+ CVector(1334.0579f, -983.55402f, 14.827253f),
+ CVector(1323.2429f, -954.23083f, 14.954678f),
+ CVector(1302.7495f, -932.21216f, 14.962917f),
+ CVector(1317.418f, -905.89325f, 14.967506f),
+ CVector(1337.9503f, -883.5025f, 14.969675f),
+ CVector(1352.6929f, -855.96954f, 14.967854f),
+ CVector(1357.2388f, -826.26971f, 14.97295f),
+ CVector(1384.8668f, -812.47693f, 12.907736f),
+ CVector(1410.8983f, -795.39056f, 12.052228f),
+ CVector(1433.901f, -775.55811f, 11.96265f),
+ CVector(1443.8615f, -746.92511f, 11.976114f),
+ CVector(1457.7015f, -720.00903f, 11.971177f),
+ CVector(1481.5685f, -701.30237f, 11.977908f),
+ CVector(1511.4004f, -696.83295f, 11.972709f),
+ CVector(1542.1796f, -695.61676f, 11.970441f),
+ CVector(1570.3301f, -684.6239f, 11.969202f),
+ CVector(0.0f, 0.0f, 0.0f),
+};
+
+void
+CPacManPickups::GeneratePMPickUpsForRace(int32 race)
+{
+ const CVector *pPos = nil;
+ int i = 0;
+
+ if (race == 0) pPos = aRacePoints1; // there's only one available
+ assert(pPos != nil);
+
+ while (!pPos->IsZero()) {
+ while (aPMPickUps[i].m_eType != PACMAN_NONE)
+ i++;
+
+ aPMPickUps[i].m_eType = PACMAN_RACE;
+ aPMPickUps[i].m_vecPosn = *(pPos++);
+ if (race == 0) {
+ CObject* obj = new CObject(MI_DONKEYMAG, true);
+ if (obj != nil) {
+ obj->ObjectCreatedBy = MISSION_OBJECT;
+
+ obj->GetPosition() = aPMPickUps[i].m_vecPosn;
+ obj->SetOrientation(0.0f, 0.0f, -HALFPI);
+ obj->GetMatrix().UpdateRW();
+ obj->UpdateRwFrame();
+
+ obj->bAffectedByGravity = false;
+ obj->bExplosionProof = true;
+ obj->bUsesCollision = false;
+ obj->bIsPickup = false;
+
+ CWorld::Add(obj);
+ }
+ aPMPickUps[i].m_pObject = obj;
+ } else
+ aPMPickUps[i].m_pObject = nil;
+ }
+ bPMActive = true;
+}
+
+void
+CPacManPickups::GenerateOnePMPickUp(CVector pos)
+{
+ bPMActive = true;
+ aPMPickUps[0].m_eType = PACMAN_RACE;
+ aPMPickUps[0].m_vecPosn = pos;
+}
+
+void
+CPacManPickups::Render()
+{
+ if (!bPMActive) return;
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[6]));
+
+ RwV3d pos;
+ float w, h;
+
+ for (int i = 0; i < NUMPACMANPICKUPS; i++) {
+ switch (aPMPickUps[i].m_eType)
+ {
+ case PACMAN_SCRAMBLE:
+ case PACMAN_RACE:
+ if (CSprite::CalcScreenCoors(aPMPickUps[i].m_vecPosn, &pos, &w, &h, true) && pos.z < 100.0f) {
+ if (aPMPickUps[i].m_pObject != nil) {
+ aPMPickUps[i].m_pObject->GetMatrix().SetRotateZOnly((CTimer::GetTimeInMilliseconds() % 1024) * TWOPI / 1024.0f);
+ aPMPickUps[i].m_pObject->GetMatrix().UpdateRW();
+ aPMPickUps[i].m_pObject->UpdateRwFrame();
+ }
+ float fsin = Sin((CTimer::GetTimeInMilliseconds() % 1024) * 6.28f / 1024.0f); // yes, it is 6.28f when it was TWOPI just now...
+ CSprite::RenderOneXLUSprite(pos.x, pos.y, pos.z, 0.8f * w * fsin, 0.8f * h, 100, 50, 5, 255, 1.0f / pos.z, 255);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE);
+}
+
+void
+CPacManPickups::ClearPMPickUps()
+{
+ bPMActive = false;
+
+ for (int i = 0; i < NUMPACMANPICKUPS; i++) {
+ if (aPMPickUps[i].m_pObject != nil) {
+ CWorld::Remove(aPMPickUps[i].m_pObject);
+ delete aPMPickUps[i].m_pObject;
+ aPMPickUps[i].m_pObject = nil;
+ }
+ aPMPickUps[i].m_eType = PACMAN_NONE;
+ }
+}
+
+void
+CPacManPickups::StartPacManRace(int32 race)
+{
+ GeneratePMPickUpsForRace(race);
+ PillsEatenInRace = 0;
+}
+
+void
+CPacManPickups::StartPacManRecord()
+{
+ CollectGameState = 20;
+ LastPickUpCoors = FindPlayerCoors();
+}
+
+uint32
+CPacManPickups::QueryPowerPillsEatenInRace()
+{
+ return PillsEatenInRace;
+}
+
+void
+CPacManPickups::ResetPowerPillsEatenInRace()
+{
+ PillsEatenInRace = 0;
+}
+
+void
+CPacManPickups::CleanUpPacManStuff()
+{
+ ClearPMPickUps();
+}
+
+void
+CPacManPickups::StartPacManScramble(CVector pos, float scrambleMult, int16 count)
+{
+ GeneratePMPickUps(pos, scrambleMult, count, PACMAN_SCRAMBLE);
+}
+
+uint32
+CPacManPickups::QueryPowerPillsCarriedByPlayer()
+{
+ if (FindPlayerVehicle())
+ return FindPlayerVehicle()->m_nPacManPickupsCarried;
+ return 0;
+}
+
+void
+CPacManPickups::ResetPowerPillsCarriedByPlayer()
+{
+ if (FindPlayerVehicle() != nil) {
+ FindPlayerVehicle()->m_nPacManPickupsCarried = 0;
+ FindPlayerVehicle()->m_fMass /= FindPlayerVehicle()->m_fForceMultiplier;
+ FindPlayerVehicle()->m_fTurnMass /= FindPlayerVehicle()->m_fForceMultiplier;
+ FindPlayerVehicle()->m_fForceMultiplier = 1.0f;
+ }
+}
+
STARTPATCHES
InjectHook(0x430220, CPickups::Init, PATCH_JUMP);
InjectHook(0x4303D0, CPickups::Update, PATCH_JUMP);
@@ -1046,4 +1432,21 @@ STARTPATCHES
InjectHook(0x433E40, CPickups::Save, PATCH_JUMP);
InjectHook(0x433BA0, &CPickup::GiveUsAPickUpObject, PATCH_JUMP);
InjectHook(0x430860, &CPickup::Update, PATCH_JUMP);
+ InjectHook(0x4331B0, &CPacManPickup::Update, PATCH_JUMP);
+ InjectHook(0x432760, CPacManPickups::Init, PATCH_JUMP);
+ InjectHook(0x432800, CPacManPickups::Update, PATCH_JUMP);
+ InjectHook(0x432AE0, CPacManPickups::GeneratePMPickUps, PATCH_JUMP);
+ InjectHook(0x432D50, CPacManPickups::GeneratePMPickUpsForRace, PATCH_JUMP);
+ InjectHook(0x432F20, CPacManPickups::GenerateOnePMPickUp, PATCH_JUMP);
+ InjectHook(0x432F60, CPacManPickups::Render, PATCH_JUMP);
+ InjectHook(0x433150, CPacManPickups::ClearPMPickUps, PATCH_JUMP);
+ InjectHook(0x433340, CPacManPickups::StartPacManRace, PATCH_JUMP);
+ InjectHook(0x433360, CPacManPickups::StartPacManRecord, PATCH_JUMP);
+ InjectHook(0x4333A0, CPacManPickups::QueryPowerPillsEatenInRace, PATCH_JUMP);
+ InjectHook(0x4333B0, CPacManPickups::ResetPowerPillsEatenInRace, PATCH_JUMP);
+ InjectHook(0x4333C0, CPacManPickups::CleanUpPacManStuff, PATCH_JUMP);
+ InjectHook(0x4333D0, CPacManPickups::StartPacManScramble, PATCH_JUMP);
+ InjectHook(0x4333F0, CPacManPickups::QueryPowerPillsCarriedByPlayer, PATCH_JUMP);
+ InjectHook(0x433410, CPacManPickups::ResetPowerPillsCarriedByPlayer, PATCH_JUMP);
+
ENDPATCHES
diff --git a/src/control/Pickups.h b/src/control/Pickups.h
index 3e075b24..b5b4f396 100644
--- a/src/control/Pickups.h
+++ b/src/control/Pickups.h
@@ -102,8 +102,31 @@ extern uint16 AmmoForWeapon[20];
extern uint16 AmmoForWeapon_OnStreet[20];
extern uint16 CostOfWeapon[20];
+enum ePacmanPickupType
+{
+ PACMAN_NONE,
+ PACMAN_SCRAMBLE,
+ PACMAN_RACE,
+};
+
+class CPacManPickup
+{
+public:
+ CVector m_vecPosn;
+ CObject *m_pObject;
+ uint8 m_eType;
+
+ void Update();
+};
+
class CPacManPickups
{
+ friend CPacManPickup;
+
+ static CPacManPickup aPMPickUps[NUMPACMANPICKUPS];
+ static CVector LastPickUpCoors;
+ static int PillsEatenInRace;
+ static bool bPMActive;
public:
static void Init(void);
static void Update(void);
@@ -111,11 +134,11 @@ public:
static void GeneratePMPickUpsForRace(int32);
static void GenerateOnePMPickUp(CVector);
static void Render(void);
- static void DoCleanUpPacManStuff(void);
static void StartPacManRace(int32);
static void StartPacManRecord(void);
static uint32 QueryPowerPillsEatenInRace(void);
static void ResetPowerPillsEatenInRace(void);
+ static void ClearPMPickUps(void);
static void CleanUpPacManStuff(void);
static void StartPacManScramble(CVector, float, int16);
static uint32 QueryPowerPillsCarriedByPlayer(void);
diff --git a/src/control/Record.cpp b/src/control/Record.cpp
index 7c330311..ca4128e3 100644
--- a/src/control/Record.cpp
+++ b/src/control/Record.cpp
@@ -2,18 +2,522 @@
#include "patcher.h"
#include "Record.h"
+#include "FileMgr.h"
+#include "Pad.h"
+#include "Pools.h"
+#include "Streaming.h"
+#include "Timer.h"
+#include "VehicleModelInfo.h"
+#include "World.h"
+
uint16 &CRecordDataForGame::RecordingState = *(uint16*)0x95CC24;
+uint8*& CRecordDataForGame::pDataBuffer = *(uint8**)0x8F1B70;
+uint8*& CRecordDataForGame::pDataBufferPointer = *(uint8**)0x8F1AB0;
+int& CRecordDataForGame::FId = *(int*)0x885BA4;
+tGameBuffer& CRecordDataForGame::pDataBufferForFrame = *(tGameBuffer*)0x72CED0;
+
+#define MEMORY_FOR_GAME_RECORD (150000)
+
+void CRecordDataForGame::Init(void)
+{
+ RecordingState = STATE_NONE;
+ delete[] pDataBuffer;
+ pDataBufferPointer = nil;
+ pDataBuffer = nil;
+#ifndef GTA_PS2 // this stuff is not present on PS2
+ FId = CFileMgr::OpenFile("playback.dat", "r");
+ if (FId <= 0) {
+ if ((FId = CFileMgr::OpenFile("record.dat", "r")) <= 0)
+ RecordingState = STATE_NONE;
+ else {
+ CFileMgr::CloseFile(FId);
+ FId = CFileMgr::OpenFileForWriting("record.dat");
+ RecordingState = STATE_RECORD;
+ }
+ }
+ else {
+ RecordingState = STATE_PLAYBACK;
+ }
+ if (RecordingState == STATE_PLAYBACK) {
+ pDataBufferPointer = new uint8[MEMORY_FOR_GAME_RECORD];
+ pDataBuffer = pDataBufferPointer;
+ pDataBuffer[CFileMgr::Read(FId, (char*)pDataBufferPointer, MEMORY_FOR_GAME_RECORD) + 8] = (uint8)-1;
+ CFileMgr::CloseFile(FId);
+ }
+#else
+ RecordingState = STATE_NONE; // second time to make sure
+#endif
+}
+
+void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void)
+{
+ switch (RecordingState) {
+ case STATE_RECORD:
+ {
+ pDataBufferForFrame.m_fTimeStep = CTimer::GetTimeStep();
+ pDataBufferForFrame.m_nTimeInMilliseconds = CTimer::GetTimeInMilliseconds();
+ pDataBufferForFrame.m_nSizeOfPads[0] = 0;
+ pDataBufferForFrame.m_nSizeOfPads[1] = 0;
+ pDataBufferForFrame.m_nChecksum = CalcGameChecksum();
+ uint8* pController1 = PackCurrentPadValues(pDataBufferForFrame.m_ControllerBuffer, &CPad::GetPad(0)->OldState, &CPad::GetPad(0)->NewState);
+ pDataBufferForFrame.m_nSizeOfPads[0] = (pController1 - pDataBufferForFrame.m_ControllerBuffer) / 2;
+ uint8* pController2 = PackCurrentPadValues(pController1, &CPad::GetPad(1)->OldState, &CPad::GetPad(1)->NewState);
+ pDataBufferForFrame.m_nSizeOfPads[1] = (pController2 - pController1) / 2;
+ uint8* pEndPtr = pController2;
+ if ((pDataBufferForFrame.m_nSizeOfPads[0] + pDataBufferForFrame.m_nSizeOfPads[1]) & 1)
+ pEndPtr += 2;
+ CFileMgr::Write(FId, (char*)&pDataBufferForFrame, pEndPtr - (uint8*)&pDataBufferForFrame);
+ break;
+ }
+ case STATE_PLAYBACK:
+ if (pDataBufferPointer[8] == (uint8)-1)
+ CPad::GetPad(0)->NewState.Clear();
+ else {
+ tGameBuffer* pData = (tGameBuffer*)pDataBufferPointer;
+ CTimer::SetTimeInMilliseconds(pData->m_nTimeInMilliseconds);
+ CTimer::SetTimeStep(pData->m_fTimeStep);
+ uint8 size1 = pData->m_nSizeOfPads[0];
+ uint8 size2 = pData->m_nSizeOfPads[1];
+ pDataBufferPointer = (uint8*)&pData->m_ControllerBuffer;
+ pDataBufferPointer = UnPackCurrentPadValues(pDataBufferPointer, size1, &CPad::GetPad(0)->NewState);
+ pDataBufferPointer = UnPackCurrentPadValues(pDataBufferPointer, size2, &CPad::GetPad(1)->NewState);
+ if ((size1 + size2) & 1)
+ pDataBufferPointer += 2;
+ if (pData->m_nChecksum != CalcGameChecksum())
+ printf("Playback out of sync\n");
+ }
+ }
+}
+
+#define PROCESS_BUTTON_STATE_STORE(buf, os, ns, field, id) \
+ do { \
+ if (os->field != ns->field){ \
+ *buf++ = id; \
+ *buf++ = ns->field; \
+ } \
+ } while (0);
+
+uint8* CRecordDataForGame::PackCurrentPadValues(uint8* buf, CControllerState* os, CControllerState* ns)
+{
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftStickX, 0);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftStickY, 1);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightStickX, 2);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightStickY, 3);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShoulder1, 4);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShoulder2, 5);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShoulder1, 6);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShoulder2, 7);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadUp, 8);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadDown, 9);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadLeft, 10);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadRight, 11);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Start, 12);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Select, 13);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Square, 14);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Triangle, 15);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Cross, 16);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Circle, 17);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShock, 18);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShock, 19);
+ return buf;
+}
+#undef PROCESS_BUTTON_STATE_STORE
+
+#define PROCESS_BUTTON_STATE_RESTORE(buf, state, field, id) case id: state->field = *buf++; break;
+
+uint8* CRecordDataForGame::UnPackCurrentPadValues(uint8* buf, uint8 total, CControllerState* state)
+{
+ for (uint8 i = 0; i < total; i++) {
+ switch (*buf++) {
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftStickX, 0);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftStickY, 1);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightStickX, 2);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightStickY, 3);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShoulder1, 4);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShoulder2, 5);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShoulder1, 6);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShoulder2, 7);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadUp, 8);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadDown, 9);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadLeft, 10);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadRight, 11);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Start, 12);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Select, 13);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Square, 14);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Triangle, 15);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Cross, 16);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Circle, 17);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShock, 18);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShock, 19);
+ }
+ }
+ return buf;
+}
+
+#undef PROCESS_BUTTON_STATE_RESTORE
+
+uint16 CRecordDataForGame::CalcGameChecksum(void)
+{
+ uint32 checksum = 0;
+ int i = CPools::GetPedPool()->GetSize();
+ while (i--) {
+ CPed* pPed = CPools::GetPedPool()->GetSlot(i);
+ if (!pPed)
+ continue;
+ checksum ^= pPed->GetModelIndex() ^ *(uint32*)&pPed->GetPosition().z ^ *(uint32*)&pPed->GetPosition().y ^ *(uint32*)&pPed->GetPosition().x;
+ }
+ i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ checksum ^= pVehicle->GetModelIndex() ^ *(uint32*)&pVehicle->GetPosition().z ^ *(uint32*)&pVehicle->GetPosition().y ^ *(uint32*)&pVehicle->GetPosition().x;
+ }
+ return checksum ^ checksum >> 16;
+}
+
+uint8& CRecordDataForChase::Status = *(uint8*)0x95CDCE;
+int& CRecordDataForChase::PositionChanges = *(int*)0x8F59C8;
+uint8& CRecordDataForChase::CurrentCar = *(uint8*)0x95CDC9;
+CAutomobile* (&CRecordDataForChase::pChaseCars)[NUM_CHASE_CARS] = *(CAutomobile * (*)[NUM_CHASE_CARS])*(uintptr*)0x6F46A8;
+uint32& CRecordDataForChase::AnimStartTime = *(uint32*)0x8F1AEC;
+float& CRecordDataForChase::AnimTime = *(float*)0x880F88;
+CCarStateEachFrame* (&CRecordDataForChase::pBaseMemForCar)[NUM_CHASE_CARS] = *(CCarStateEachFrame * (*)[NUM_CHASE_CARS])*(uintptr*)0x70EA18;
+float& CRecordDataForChase::TimeMultiplier = *(float*)0x8E2A94;
+int& CRecordDataForChase::FId2 = *(int*)0x8E2C18;
+
+#define CHASE_SCENE_LENGTH_IN_SECONDS (80)
+#define CHASE_SCENE_FRAMES_PER_SECOND (15) // skipping every second frame
+#define CHASE_SCENE_FRAMES_IN_RECORDING (CHASE_SCENE_LENGTH_IN_SECONDS * CHASE_SCENE_FRAMES_PER_SECOND)
+#define CHASE_SCENE_LENGTH_IN_FRAMES (CHASE_SCENE_FRAMES_IN_RECORDING * 2)
+
+void CRecordDataForChase::Init(void)
+{
+ Status = STATE_NONE;
+ PositionChanges = 0;
+ CurrentCar = 0;
+ for (int i = 0; i < NUM_CHASE_CARS; i++)
+ pChaseCars[i] = nil;
+ AnimStartTime = 0;
+}
+
+void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void)
+{
+ switch (Status) {
+ case STATE_NONE:
+ return;
+ case STATE_RECORD:
+ {
+ if ((CTimer::GetFrameCounter() & 1) == 0)
+ StoreInfoForCar(pChaseCars[CurrentCar], &pBaseMemForCar[CurrentCar][CTimer::GetFrameCounter() / 2]);
+ if (CTimer::GetFrameCounter() < CHASE_SCENE_LENGTH_IN_FRAMES * 2)
+ return;
+ CFileMgr::SetDir("data\\paths");
+ sprintf(gString, "chase%d.dat", CurrentCar);
+ int fid = CFileMgr::OpenFileForWriting(gString);
+ uint32 fs = CHASE_SCENE_LENGTH_IN_FRAMES * sizeof(CCarStateEachFrame);
+ printf("FileSize:%d\n", fs);
+ CFileMgr::Write(fid, (char*)pBaseMemForCar[CurrentCar], fs);
+ CFileMgr::CloseFile(fid);
+ CFileMgr::SetDir("");
+ sprintf(gString, "car%d.max", CurrentCar);
+ int fid2 = CFileMgr::OpenFileForWriting(gString);
+ for (int i = 0; i < CHASE_SCENE_FRAMES_IN_RECORDING; i++) {
+ // WTF? Was it ever used?
+#ifdef FIX_BUGS
+ CCarStateEachFrame* pState = pBaseMemForCar[CurrentCar];
+#else
+ CCarStateEachFrame* pState = (CCarStateEachFrame*)pChaseCars[CurrentCar];
+#endif
+ CVector right = CVector(pState->rightX, pState->rightY, pState->rightZ) / INT8_MAX;
+ CVector forward = CVector(pState->forwardX, pState->forwardY, pState->forwardZ) / INT8_MAX;
+ CVector up = CrossProduct(right, forward);
+ sprintf(gString, "%f %f %f\n", pState->pos.x, pState->pos.y, pState->pos.z);
+ CFileMgr::Write(fid2, gString, strlen(gString) - 1);
+ sprintf(gString, "%f %f %f\n", right.x, right.y, right.z);
+ CFileMgr::Write(fid2, gString, strlen(gString) - 1);
+ sprintf(gString, "%f %f %f\n", forward.x, forward.y, forward.z);
+ CFileMgr::Write(fid2, gString, strlen(gString) - 1);
+ sprintf(gString, "%f %f %f\n", up.x, up.y, up.z);
+ CFileMgr::Write(fid2, gString, strlen(gString) - 1);
+ }
+ CFileMgr::CloseFile(fid2);
+ }
+ case STATE_PLAYBACK:
+ case STATE_PLAYBACK_BEFORE_RECORDING:
+ case STATE_PLAYBACK_INIT:
+ break;
+ }
+}
+
+struct tCoors {
+ CVector pos;
+ float angle;
+};
+
+// I guess developer was filling this with actual data before running the game
+tCoors NewCoorsForRecordedCars[7];
+
+void CRecordDataForChase::SaveOrRetrieveCarPositions(void)
+{
+ switch (Status) {
+ case STATE_NONE:
+ return;
+ case STATE_RECORD:
+ case STATE_PLAYBACK_BEFORE_RECORDING:
+ for (int i = 0; i < NUM_CHASE_CARS; i++) {
+ if (i != CurrentCar && CTimer::GetFrameCounter()) {
+ RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][CTimer::GetFrameCounter() / 2], false);
+ pChaseCars[i]->GetMatrix().UpdateRW();
+ pChaseCars[i]->UpdateRwFrame();
+ }
+ }
+ if (Status == STATE_PLAYBACK_BEFORE_RECORDING && CTimer::GetFrameCounter()) {
+ RestoreInfoForCar(pChaseCars[CurrentCar], &pBaseMemForCar[CurrentCar][CTimer::GetFrameCounter() / 2], false);
+ pChaseCars[CurrentCar]->GetMatrix().UpdateRW();
+ pChaseCars[CurrentCar]->UpdateRwFrame();
+ }
+ if (CPad::GetPad(0)->GetLeftShockJustDown() && CPad::GetPad(0)->GetRightShockJustDown()) {
+ if (!CPad::GetPad(0)->GetRightShockJustDown()) {
+ pChaseCars[CurrentCar]->GetPosition() = NewCoorsForRecordedCars[PositionChanges].pos;
+ pChaseCars[CurrentCar]->SetMoveSpeed(0.0f, 0.0f, 0.0f);
+ pChaseCars[CurrentCar]->GetMatrix().SetRotateZOnly(DEGTORAD(NewCoorsForRecordedCars[PositionChanges].angle));
+ ++PositionChanges;
+ }
+ if (Status == STATE_PLAYBACK_BEFORE_RECORDING) {
+ Status = STATE_RECORD;
+ pChaseCars[CurrentCar]->m_status = STATUS_PLAYER;
+ }
+ }
+ break;
+ case STATE_PLAYBACK_INIT:
+ Status = STATE_PLAYBACK;
+ break;
+ case STATE_PLAYBACK:
+ {
+ TimeMultiplier += CTimer::GetTimeStepNonClippedInSeconds();
+ float EndOfFrameTime = CHASE_SCENE_FRAMES_PER_SECOND * min(CHASE_SCENE_LENGTH_IN_SECONDS, TimeMultiplier);
+ for (int i = 0; i < NUM_CHASE_CARS; i++) {
+ if (!pBaseMemForCar[i])
+ continue;
+ if (!pChaseCars[i])
+ continue;
+ if (EndOfFrameTime < CHASE_SCENE_FRAMES_IN_RECORDING - 1) {
+ int FlooredEOFTime = EndOfFrameTime;
+ RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][FlooredEOFTime], false);
+ CMatrix tmp;
+ float dp = EndOfFrameTime - FlooredEOFTime;
+ RestoreInfoForMatrix(tmp, &pBaseMemForCar[i][FlooredEOFTime + 1]);
+ pChaseCars[i]->GetRight() += (tmp.GetRight() - pChaseCars[i]->GetRight()) * dp;
+ pChaseCars[i]->GetForward() += (tmp.GetForward() - pChaseCars[i]->GetForward()) * dp;
+ pChaseCars[i]->GetUp() += (tmp.GetUp() - pChaseCars[i]->GetUp()) * dp;
+ pChaseCars[i]->GetPosition() += (tmp.GetPosition() - pChaseCars[i]->GetPosition()) * dp;
+ }
+ else{
+ RestoreInfoForCar(pChaseCars[i], &pBaseMemForCar[i][CHASE_SCENE_FRAMES_IN_RECORDING - 1], true);
+ if (i == 0)
+ pChaseCars[i]->GetPosition().z += 0.2f;
+ }
+ pChaseCars[i]->GetMatrix().UpdateRW();
+ pChaseCars[i]->UpdateRwFrame();
+ pChaseCars[i]->RemoveAndAdd();
+ }
+ break;
+ }
+ }
+}
+
+void CRecordDataForChase::StoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState)
+{
+ pState->rightX = INT8_MAX * pCar->GetRight().x;
+ pState->rightY = INT8_MAX * pCar->GetRight().y;
+ pState->rightZ = INT8_MAX * pCar->GetRight().z;
+ pState->forwardX = INT8_MAX * pCar->GetForward().x;
+ pState->forwardY = INT8_MAX * pCar->GetForward().y;
+ pState->forwardZ = INT8_MAX * pCar->GetForward().z;
+ pState->pos = pCar->GetPosition();
+ pState->velX = 0.5f * INT16_MAX * pCar->GetMoveSpeed().x;
+ pState->velY = 0.5f * INT16_MAX * pCar->GetMoveSpeed().y;
+ pState->velZ = 0.5f * INT16_MAX * pCar->GetMoveSpeed().z;
+ pState->wheel = 20 * pCar->m_fSteerAngle;
+ pState->gas = 100 * pCar->m_fGasPedal;
+ pState->brake = 100 * pCar->m_fBrakePedal;
+ pState->handbrake = pCar->bIsHandbrakeOn;
+}
+
+void CRecordDataForChase::RestoreInfoForMatrix(CMatrix& matrix, CCarStateEachFrame* pState)
+{
+ matrix.GetRight() = CVector(pState->rightX, pState->rightY, pState->rightZ) / INT8_MAX;
+ matrix.GetForward() = CVector(pState->forwardX, pState->forwardY, pState->forwardZ) / INT8_MAX;
+ matrix.GetUp() = CrossProduct(matrix.GetRight(), matrix.GetForward());
+ matrix.GetPosition() = pState->pos;
+}
+
+void CRecordDataForChase::RestoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState, bool stop)
+{
+ CVector oldPos = pCar->GetPosition();
+ RestoreInfoForMatrix(pCar->GetMatrix(), pState);
+ pCar->SetMoveSpeed(CVector(pState->velX, pState->velY, pState->velZ) / INT16_MAX / 0.5f);
+ pCar->SetTurnSpeed(0.0f, 0.0f, 0.0f);
+ pCar->m_fSteerAngle = pState->wheel / 20.0f;
+ pCar->m_fGasPedal = pState->gas / 100.0f;
+ pCar->m_fBrakePedal = pState->brake / 100.0f;
+ pCar->bIsHandbrakeOn = pState->handbrake;
+ if ((oldPos - pCar->GetPosition()).Magnitude() > 15.0f) {
+ if (pCar == pChaseCars[14]) {
+ pCar->m_currentColour1 = 58;
+ pCar->m_currentColour2 = 1;
+ }
+ else
+ pCar->GetModelInfo()->ChooseVehicleColour(pCar->m_currentColour1, pCar->m_currentColour2);
+ }
+ pCar->m_fHealth = min(pCar->m_fHealth, 500.0f);
+ if (stop) {
+ pCar->m_fGasPedal = 0.0f;
+ pCar->m_fBrakePedal = 0.0f;
+ pCar->SetMoveSpeed(0.0f, 0.0f, 0.0f);
+ pCar->bIsHandbrakeOn = false;
+ }
+}
+
+void CRecordDataForChase::ProcessControlCars(void)
+{
+ if (Status != STATE_PLAYBACK)
+ return;
+ for (int i = 0; i < NUM_CHASE_CARS; i++) {
+ if (pChaseCars[i])
+ pChaseCars[i]->ProcessControl();
+ }
+}
+
+#if (defined(GTA_PS2) || defined(FIX_BUGS))
+bool CRecordDataForChase::ShouldThisPadBeLeftAlone(uint8 pad)
+{
+ // may be wrong
+ if (Status == STATE_NONE || Status == STATE_PLAYBACK)
+ return false;
+ return pad != 0;
+}
+#endif
+
+void CRecordDataForChase::GiveUsACar(int32 mi, CVector pos, float angle, CAutomobile** ppCar, uint8 colour1, uint8 colour2)
+{
+ CStreaming::RequestModel(mi, STREAMFLAGS_DEPENDENCY);
+ CStreaming::LoadAllRequestedModels(false);
+ if (!CStreaming::HasModelLoaded(mi))
+ return;
+ CAutomobile* pCar = new CAutomobile(mi, MISSION_VEHICLE);
+ pCar->GetPosition() = pos;
+ pCar->m_status = STATUS_PLAYER_PLAYBACKFROMBUFFER;
+ pCar->GetMatrix().SetRotateZOnly(DEGTORAD(angle));
+ pCar->pDriver = nil;
+ pCar->m_currentColour1 = colour1;
+ pCar->m_currentColour2 = colour2;
+ CWorld::Add(pCar);
+ *ppCar = pCar;
+}
+
+void RemoveUnusedCollision(void)
+{
+ static const char* dontDeleteArray[] = {
+ "rd_SrRoad2A50", "rd_SrRoad2A20", "rd_CrossRda1w22", "rd_CrossRda1rw22",
+ "road_broadway02", "road_broadway01", "com_21way5", "com_21way50",
+ "cm1waycrosscom", "com_21way20", "com_21way10", "road_broadway04",
+ "com_rvroads52", "com_roadsrv", "com_roadkb23", "com_roadkb22"
+ };
+ for (int i = 0; i < ARRAY_SIZE(dontDeleteArray); i++)
+ CModelInfo::GetModelInfo(dontDeleteArray[i], nil)->GetColModel()->level = LEVEL_NONE;
+ CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_NONE);
+ for (int i = 0; i < ARRAY_SIZE(dontDeleteArray); i++)
+ CModelInfo::GetModelInfo(dontDeleteArray[i], nil)->GetColModel()->level = LEVEL_COMMERCIAL;
+}
+
+void CRecordDataForChase::StartChaseScene(float startTime)
+{
+ char filename[28];
+ SetUpCarsForChaseScene();
+ Status = STATE_PLAYBACK;
+ AnimTime = startTime;
+ AnimStartTime = CTimer::GetTimeInMilliseconds();
+ RemoveUnusedCollision();
+ CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN);
+ CGame::TidyUpMemory(true, true);
+ CStreaming::ImGonnaUseStreamingMemory();
+ CFileMgr::SetDir("data\\paths");
+ for (int i = 0; i < NUM_CHASE_CARS; i++) {
+ if (!pChaseCars[i]) {
+ pBaseMemForCar[i] = nil;
+ continue;
+ }
+ sprintf(filename, "chase%d.dat", i);
+ FId2 = CFileMgr::OpenFile(filename, "rb");
+ if (FId2 <= 0) {
+ pBaseMemForCar[i] = nil;
+ continue;
+ }
+ pBaseMemForCar[i] = new CCarStateEachFrame[CHASE_SCENE_FRAMES_IN_RECORDING];
+ for (int j = 0; j < CHASE_SCENE_FRAMES_IN_RECORDING; j++) {
+ CFileMgr::Read(FId2, (char*)&pBaseMemForCar[i][j], sizeof(CCarStateEachFrame));
+ CFileMgr::Seek(FId2, sizeof(CCarStateEachFrame), 1);
+ }
+ CFileMgr::CloseFile(FId2);
+ }
+ CFileMgr::SetDir("");
+ CStreaming::IHaveUsedStreamingMemory();
+ TimeMultiplier = 0.0f;
+}
+
+void CRecordDataForChase::CleanUpChaseScene(void)
+{
+ if (Status != STATE_PLAYBACK_INIT && Status != STATE_PLAYBACK)
+ return;
+ Status = STATE_NONE;
+ CleanUpCarsForChaseScene();
+ for (int i = 0; i < NUM_CHASE_CARS; i++) {
+ if (pBaseMemForCar[i]) {
+ delete[] pBaseMemForCar[i];
+ pBaseMemForCar[i] = nil;
+ }
+ }
+}
+
+void CRecordDataForChase::SetUpCarsForChaseScene(void)
+{
+ GiveUsACar(MI_POLICE, CVector(273.54221f, -1167.1907f, 24.880601f), 63.0f, &pChaseCars[0], 2, 1);
+ GiveUsACar(MI_ENFORCER, CVector(231.1783f, -1388.8322f, 25.978201f), 90.0f, &pChaseCars[1], 2, 1);
+ GiveUsACar(MI_TAXI, CVector(184.3156f, -1473.251f, 25.978201f), 0.0f, &pChaseCars[4], 6, 6);
+ GiveUsACar(MI_CHEETAH, CVector(173.8868f, -1377.6514f, 25.978201f), 0.0f, &pChaseCars[6], 4, 5);
+ GiveUsACar(MI_STINGER, CVector(102.5946f, -943.93628f, 25.9781f), 270.0f, &pChaseCars[7], 53, 53);
+ GiveUsACar(MI_CHEETAH, CVector(-177.7157f, -862.18652f, 25.978201f), 155.0f, &pChaseCars[10], 41, 1);
+ GiveUsACar(MI_STINGER, CVector(-170.56979f, -889.02362f, 25.978201f), 154.0f, &pChaseCars[11], 10, 10);
+ GiveUsACar(MI_KURUMA, CVector(402.60809f, -917.49628f, 37.381001f), 90.0f, &pChaseCars[14], 34, 1);
+ GiveUsACar(MI_TAXI, CVector(-33.496201f, -938.4563f, 25.9781f), 266.0f, &pChaseCars[16], 6, 6);
+ GiveUsACar(MI_KURUMA, CVector(49.363098f, -987.60498f, 25.9781f), 0.0f, &pChaseCars[18], 51, 1);
+ GiveUsACar(MI_TAXI, CVector(179.0049f, -1154.6686f, 25.9781f), 0.0f, &pChaseCars[19], 6, 76);
+ GiveUsACar(MI_RUMPO, CVector(-28.9762f, -1031.3367f, 25.990601f), 242.0f, &pChaseCars[2], 1, 75);
+ GiveUsACar(MI_PATRIOT, CVector(114.1564f, -796.69379f, 24.978201f), 180.0f, &pChaseCars[3], 0, 0);
+}
+
+void CRecordDataForChase::CleanUpCarsForChaseScene(void)
+{
+ for (int i = 0; i < NUM_CHASE_CARS; i++)
+ RemoveCarFromChase(i);
+}
-uint8 &CRecordDataForChase::Status = *(uint8*)0x95CDCE;
+void CRecordDataForChase::RemoveCarFromChase(int32 i)
+{
+ if (!pChaseCars[i])
+ return;
+ CWorld::Remove(pChaseCars[i]);
+ delete pChaseCars[i];
+ pChaseCars[i] = nil;
+}
-WRAPPER void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void) { EAXJMP(0x4341F0); }
-WRAPPER void CRecordDataForGame::Init(void) { EAXJMP(0x4340F0); }
+CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32 i)
+{
+ CVehicle* pVehicle = pChaseCars[i];
+ pChaseCars[i] = nil;
+ pVehicle->m_status = STATUS_PHYSICS;
+ return pVehicle;
+}
-WRAPPER void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) { EAXJMP(0x4347F0); }
-WRAPPER void CRecordDataForChase::ProcessControlCars(void) { EAXJMP(0x435540); }
-WRAPPER void CRecordDataForChase::SaveOrRetrieveCarPositions(void) { EAXJMP(0x434B20); }
-WRAPPER void CRecordDataForChase::StartChaseScene(float) { EAXJMP(0x435690); }
-WRAPPER void CRecordDataForChase::CleanUpChaseScene() { EAXJMP(0x4357C0); }
-WRAPPER void CRecordDataForChase::RemoveCarFromChase(int32) { EAXJMP(0x435BC0); }
-WRAPPER CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32) { EAXJMP(0x435C00); }
-WRAPPER void CRecordDataForChase::Init(void) { EAXJMP(0x434780); }
diff --git a/src/control/Record.h b/src/control/Record.h
index e52a623e..4abeb68a 100644
--- a/src/control/Record.h
+++ b/src/control/Record.h
@@ -1,34 +1,106 @@
#pragma once
+class CAutomobile;
class CVehicle;
+class CControllerState;
-enum {
- RECORDSTATE_0,
- RECORDSTATE_1,
- RECORDSTATE_2,
+class CCarStateEachFrame
+{
+public:
+ int16 velX;
+ int16 velY;
+ int16 velZ;
+ int8 rightX;
+ int8 rightY;
+ int8 rightZ;
+ int8 forwardX;
+ int8 forwardY;
+ int8 forwardZ;
+ int8 wheel;
+ uint8 gas;
+ uint8 brake;
+ bool handbrake;
+ CVector pos;
};
+extern char* gString;
+
class CRecordDataForChase
{
-public:
+ enum {
+ NUM_CHASE_CARS = 20
+ };
+ enum {
+ STATE_NONE = 0,
+ STATE_RECORD = 1,
+ STATE_PLAYBACK_INIT = 2,
+ STATE_PLAYBACK = 3,
+ STATE_PLAYBACK_BEFORE_RECORDING = 4
+ };
static uint8 &Status;
+ static int &PositionChanges;
+ static uint8 &CurrentCar;
+ static CAutomobile*(&pChaseCars)[NUM_CHASE_CARS];
+ static float &AnimTime;
+ static uint32 &AnimStartTime;
+ static CCarStateEachFrame* (&pBaseMemForCar)[NUM_CHASE_CARS];
+ static float &TimeMultiplier;
+ static int &FId2;
+public:
+
+ static bool IsRecording(void) { return Status == STATE_RECORD; }
+ static void Init(void);
static void SaveOrRetrieveDataForThisFrame(void);
- static void ProcessControlCars(void);
static void SaveOrRetrieveCarPositions(void);
+ static void StoreInfoForCar(CAutomobile*, CCarStateEachFrame*);
+ static void RestoreInfoForMatrix(CMatrix&, CCarStateEachFrame*);
+ static void RestoreInfoForCar(CAutomobile*, CCarStateEachFrame*, bool);
+ static void ProcessControlCars(void);
+#if (defined(GTA_PS2) || defined(FIX_BUGS))
+ static bool ShouldThisPadBeLeftAlone(uint8 pad);
+#endif
+ static void GiveUsACar(int32, CVector, float, CAutomobile**, uint8, uint8);
static void StartChaseScene(float);
- static void CleanUpChaseScene();
+ static void CleanUpChaseScene(void);
+ static void SetUpCarsForChaseScene(void);
+ static void CleanUpCarsForChaseScene(void);
static void RemoveCarFromChase(int32);
static CVehicle* TurnChaseCarIntoScriptCar(int32);
- static void Init(void);
+
};
+struct tGameBuffer
+{
+ float m_fTimeStep;
+ uint32 m_nTimeInMilliseconds;
+ uint8 m_nSizeOfPads[2];
+ uint16 m_nChecksum;
+ uint8 m_ControllerBuffer[116];
+};
class CRecordDataForGame
{
+ enum {
+ STATE_NONE = 0,
+ STATE_RECORD = 1,
+ STATE_PLAYBACK = 2,
+ };
+ static uint16& RecordingState;
+ static uint8* &pDataBuffer;
+ static uint8* &pDataBufferPointer;
+ static int &FId;
+ static tGameBuffer &pDataBufferForFrame;
+
public:
- static uint16 &RecordingState;
+ static bool IsRecording() { return RecordingState == STATE_RECORD; }
+ static bool IsPlayingBack() { return RecordingState == STATE_PLAYBACK; }
static void SaveOrRetrieveDataForThisFrame(void);
static void Init(void);
+
+private:
+ static uint16 CalcGameChecksum(void);
+ static uint8* PackCurrentPadValues(uint8*, CControllerState*, CControllerState*);
+ static uint8* UnPackCurrentPadValues(uint8*, uint8, CControllerState*);
};
diff --git a/src/control/TrafficLights.cpp b/src/control/TrafficLights.cpp
index e4416965..ab9cd92d 100644
--- a/src/control/TrafficLights.cpp
+++ b/src/control/TrafficLights.cpp
@@ -40,9 +40,9 @@ CTrafficLights::DisplayActualLight(CEntity *ent)
for(i = 1; i < 6; i++){
assert(mi->Get2dEffect(i));
yMin = min(yMin, mi->Get2dEffect(i)->pos.y);
- yMax = min(yMax, mi->Get2dEffect(i)->pos.y);
+ yMax = max(yMax, mi->Get2dEffect(i)->pos.y);
zMin = min(zMin, mi->Get2dEffect(i)->pos.z);
- zMax = min(zMax, mi->Get2dEffect(i)->pos.z);
+ zMax = max(zMax, mi->Get2dEffect(i)->pos.z);
}
CVector pos1, pos2;