Initial commit

This commit is contained in:
Shinovon 2026-04-22 07:30:27 +05:00
commit 77cdaaf97e
827 changed files with 418745 additions and 0 deletions

128
src/control/AutoPilot.cpp Normal file
View file

@ -0,0 +1,128 @@
#include "common.h"
#include "AutoPilot.h"
#include "CarCtrl.h"
#include "Curves.h"
#include "PathFind.h"
#include "SaveBuf.h"
void CAutoPilot::ModifySpeed(float speed)
{
m_fMaxTrafficSpeed = Max(0.01f, speed);
float positionBetweenNodes = (float)(CTimer::GetTimeInMilliseconds() - m_nTimeEnteredCurve) / m_nTimeToSpendOnCurrentCurve;
CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo];
CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[m_nNextPathNodeInfo];
float currentPathLinkForwardX = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirX();
float currentPathLinkForwardY = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirY();
float nextPathLinkForwardX = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirX();
float nextPathLinkForwardY = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirY();
CVector positionOnCurrentLinkIncludingLane(
pCurrentLink->GetX() + ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardY,
pCurrentLink->GetY() - ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardX,
0.0f);
CVector positionOnNextLinkIncludingLane(
pNextLink->GetX() + ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardY,
pNextLink->GetY() - ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardX,
0.0f);
m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor(
&positionOnCurrentLinkIncludingLane,
&positionOnNextLinkIncludingLane,
currentPathLinkForwardX, currentPathLinkForwardY,
nextPathLinkForwardX, nextPathLinkForwardY
) * (1000.0f / m_fMaxTrafficSpeed);
#ifdef FIX_BUGS
/* Casting timer to float is very unwanted, and in this case even causes crashes. */
m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() -
(uint32)(positionBetweenNodes * m_nTimeToSpendOnCurrentCurve);
#else
m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - positionBetweenNodes * m_nTimeToSpendOnCurrentCurve;
#endif
}
void CAutoPilot::RemoveOnePathNode()
{
--m_nPathFindNodesCount;
for (int i = 0; i < m_nPathFindNodesCount; i++)
m_aPathFindNodesInfo[i] = m_aPathFindNodesInfo[i + 1];
}
#ifdef COMPATIBLE_SAVES
void CAutoPilot::Save(uint8*& buf)
{
WriteSaveBuf(buf, m_nCurrentRouteNode);
WriteSaveBuf(buf, m_nNextRouteNode);
WriteSaveBuf(buf, m_nPrevRouteNode);
WriteSaveBuf(buf, m_nTimeEnteredCurve);
WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve);
WriteSaveBuf(buf, m_nCurrentPathNodeInfo);
WriteSaveBuf(buf, m_nNextPathNodeInfo);
WriteSaveBuf(buf, m_nPreviousPathNodeInfo);
WriteSaveBuf(buf, m_nAntiReverseTimer);
WriteSaveBuf(buf, m_nTimeToStartMission);
WriteSaveBuf(buf, m_nPreviousDirection);
WriteSaveBuf(buf, m_nCurrentDirection);
WriteSaveBuf(buf, m_nNextDirection);
WriteSaveBuf(buf, m_nCurrentLane);
WriteSaveBuf(buf, m_nNextLane);
WriteSaveBuf(buf, m_nDrivingStyle);
WriteSaveBuf(buf, m_nCarMission);
WriteSaveBuf(buf, m_nTempAction);
WriteSaveBuf(buf, m_nTimeTempAction);
WriteSaveBuf(buf, m_fMaxTrafficSpeed);
WriteSaveBuf(buf, m_nCruiseSpeed);
uint8 flags = 0;
if (m_bSlowedDownBecauseOfCars) flags |= BIT(0);
if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1);
if (m_bStayInCurrentLevel) flags |= BIT(2);
if (m_bStayInFastLane) flags |= BIT(3);
if (m_bIgnorePathfinding) flags |= BIT(4);
WriteSaveBuf(buf, flags);
ZeroSaveBuf(buf, 2);
WriteSaveBuf(buf, m_vecDestinationCoors.x);
WriteSaveBuf(buf, m_vecDestinationCoors.y);
WriteSaveBuf(buf, m_vecDestinationCoors.z);
ZeroSaveBuf(buf, 32);
WriteSaveBuf(buf, m_nPathFindNodesCount);
ZeroSaveBuf(buf, 6);
}
void CAutoPilot::Load(uint8*& buf)
{
ReadSaveBuf(&m_nCurrentRouteNode, buf);
ReadSaveBuf(&m_nNextRouteNode, buf);
ReadSaveBuf(&m_nPrevRouteNode, buf);
ReadSaveBuf(&m_nTimeEnteredCurve, buf);
ReadSaveBuf(&m_nTimeToSpendOnCurrentCurve, buf);
ReadSaveBuf(&m_nCurrentPathNodeInfo, buf);
ReadSaveBuf(&m_nNextPathNodeInfo, buf);
ReadSaveBuf(&m_nPreviousPathNodeInfo, buf);
ReadSaveBuf(&m_nAntiReverseTimer, buf);
ReadSaveBuf(&m_nTimeToStartMission, buf);
ReadSaveBuf(&m_nPreviousDirection, buf);
ReadSaveBuf(&m_nCurrentDirection, buf);
ReadSaveBuf(&m_nNextDirection, buf);
ReadSaveBuf(&m_nCurrentLane, buf);
ReadSaveBuf(&m_nNextLane, buf);
ReadSaveBuf(&m_nDrivingStyle, buf);
ReadSaveBuf(&m_nCarMission, buf);
ReadSaveBuf(&m_nTempAction, buf);
ReadSaveBuf(&m_nTimeTempAction, buf);
ReadSaveBuf(&m_fMaxTrafficSpeed, buf);
ReadSaveBuf(&m_nCruiseSpeed, buf);
uint8 flags;
ReadSaveBuf(&flags, buf);
m_bSlowedDownBecauseOfCars = !!(flags & BIT(0));
m_bSlowedDownBecauseOfPeds = !!(flags & BIT(1));
m_bStayInCurrentLevel = !!(flags & BIT(2));
m_bStayInFastLane = !!(flags & BIT(3));
m_bIgnorePathfinding = !!(flags & BIT(4));
SkipSaveBuf(buf, 2);
ReadSaveBuf(&m_vecDestinationCoors.x, buf);
ReadSaveBuf(&m_vecDestinationCoors.y, buf);
ReadSaveBuf(&m_vecDestinationCoors.z, buf);
SkipSaveBuf(buf, 32);
ReadSaveBuf(&m_nPathFindNodesCount, buf);
SkipSaveBuf(buf, 6);
}
#endif

123
src/control/AutoPilot.h Normal file
View file

@ -0,0 +1,123 @@
#pragma once
#include "Timer.h"
class CVehicle;
struct CPathNode;
enum eCarMission
{
MISSION_NONE,
MISSION_CRUISE,
MISSION_RAMPLAYER_FARAWAY,
MISSION_RAMPLAYER_CLOSE,
MISSION_BLOCKPLAYER_FARAWAY,
MISSION_BLOCKPLAYER_CLOSE,
MISSION_BLOCKPLAYER_HANDBRAKESTOP,
MISSION_WAITFORDELETION,
MISSION_GOTOCOORDS,
MISSION_GOTOCOORDS_STRAIGHT,
MISSION_EMERGENCYVEHICLE_STOP,
MISSION_STOP_FOREVER,
MISSION_GOTOCOORDS_ACCURATE,
MISSION_GOTO_COORDS_STRAIGHT_ACCURATE,
MISSION_GOTOCOORDS_ASTHECROWSWIMS,
MISSION_RAMCAR_FARAWAY,
MISSION_RAMCAR_CLOSE,
MISSION_BLOCKCAR_FARAWAY,
MISSION_BLOCKCAR_CLOSE,
MISSION_BLOCKCAR_HANDBRAKESTOP,
};
enum eCarTempAction
{
TEMPACT_NONE,
TEMPACT_WAIT,
TEMPACT_REVERSE,
TEMPACT_HANDBRAKETURNLEFT,
TEMPACT_HANDBRAKETURNRIGHT,
TEMPACT_HANDBRAKESTRAIGHT,
TEMPACT_TURNLEFT,
TEMPACT_TURNRIGHT,
TEMPACT_GOFORWARD,
TEMPACT_SWERVELEFT,
TEMPACT_SWERVERIGHT
};
enum eCarDrivingStyle
{
DRIVINGSTYLE_STOP_FOR_CARS,
DRIVINGSTYLE_SLOW_DOWN_FOR_CARS,
DRIVINGSTYLE_AVOID_CARS,
DRIVINGSTYLE_PLOUGH_THROUGH,
DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS
};
class CAutoPilot {
public:
int32 m_nCurrentRouteNode;
int32 m_nNextRouteNode;
int32 m_nPrevRouteNode;
int32 m_nTimeEnteredCurve;
int32 m_nTimeToSpendOnCurrentCurve;
uint32 m_nCurrentPathNodeInfo;
uint32 m_nNextPathNodeInfo;
uint32 m_nPreviousPathNodeInfo;
uint32 m_nAntiReverseTimer;
uint32 m_nTimeToStartMission;
int8 m_nPreviousDirection;
int8 m_nCurrentDirection;
int8 m_nNextDirection;
int8 m_nCurrentLane;
int8 m_nNextLane;
uint8 m_nDrivingStyle;
uint8 m_nCarMission;
uint8 m_nTempAction;
uint32 m_nTimeTempAction;
float m_fMaxTrafficSpeed;
uint8 m_nCruiseSpeed;
uint8 m_bSlowedDownBecauseOfCars : 1;
uint8 m_bSlowedDownBecauseOfPeds : 1;
uint8 m_bStayInCurrentLevel : 1;
uint8 m_bStayInFastLane : 1;
uint8 m_bIgnorePathfinding : 1;
CVector m_vecDestinationCoors;
CPathNode *m_aPathFindNodesInfo[NUM_PATH_NODES_IN_AUTOPILOT];
int16 m_nPathFindNodesCount;
CVehicle *m_pTargetCar;
CAutoPilot(void) {
m_nPrevRouteNode = 0;
m_nNextRouteNode = m_nPrevRouteNode;
m_nCurrentRouteNode = m_nNextRouteNode;
m_nTimeEnteredCurve = 0;
m_nTimeToSpendOnCurrentCurve = 1000;
m_nPreviousPathNodeInfo = 0;
m_nNextPathNodeInfo = m_nPreviousPathNodeInfo;
m_nCurrentPathNodeInfo = m_nNextPathNodeInfo;
m_nNextDirection = 1;
m_nCurrentDirection = m_nNextDirection;
m_nCurrentLane = m_nNextLane = 0;
m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
m_nCarMission = MISSION_NONE;
m_nTempAction = TEMPACT_NONE;
m_nCruiseSpeed = 10;
m_fMaxTrafficSpeed = 10.0f;
m_bSlowedDownBecauseOfPeds = false;
m_bSlowedDownBecauseOfCars = false;
m_nPathFindNodesCount = 0;
m_pTargetCar = 0;
m_nTimeToStartMission = CTimer::GetTimeInMilliseconds();
m_nAntiReverseTimer = m_nTimeToStartMission;
m_bStayInFastLane = false;
}
void ModifySpeed(float);
void RemoveOnePathNode();
#ifdef COMPATIBLE_SAVES
void Save(uint8*& buf);
void Load(uint8*& buf);
#endif
};
VALIDATE_SIZE(CAutoPilot, 0x70);

149
src/control/Bridge.cpp Normal file
View file

@ -0,0 +1,149 @@
#include "common.h"
#include "Bridge.h"
#include "Pools.h"
#include "ModelIndices.h"
#include "PathFind.h"
#include "Stats.h"
CEntity *CBridge::pLiftRoad;
CEntity *CBridge::pLiftPart;
CEntity *CBridge::pWeight;
int CBridge::State;
int CBridge::OldState;
float CBridge::DefaultZLiftPart;
float CBridge::DefaultZLiftRoad;
float CBridge::DefaultZLiftWeight;
float CBridge::OldLift;
uint32 CBridge::TimeOfBridgeBecomingOperational;
void CBridge::Init()
{
FindBridgeEntities();
OldLift = -1.0f;
if (pLiftPart && pWeight)
{
DefaultZLiftPart = pLiftPart->GetPosition().z;
DefaultZLiftWeight = pWeight->GetPosition().z;
if (pLiftRoad)
DefaultZLiftRoad = pLiftRoad->GetPosition().z;
ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true);
}
}
void CBridge::Update()
{
if (!pLiftPart || !pWeight)
return;
OldState = State;
float liftHeight;
// Set bridge height and state
if (CStats::CommercialPassed)
{
if (TimeOfBridgeBecomingOperational == 0)
TimeOfBridgeBecomingOperational = CTimer::GetTimeInMilliseconds();
// Time remaining for bridge to become operational
// uint16, so after about a minute it overflows to 0 and the cycle repeats
uint16 timeElapsed = CTimer::GetTimeInMilliseconds() - TimeOfBridgeBecomingOperational;
// Calculate lift part height and bridge state
if (timeElapsed < 10000)
{
State = STATE_LIFT_PART_MOVING_DOWN;
liftHeight = 25.0f - timeElapsed / 10000.0f * 25.0f;
}
else if (timeElapsed < 40000)
{
liftHeight = 0.0f;
State = STATE_LIFT_PART_IS_DOWN;
}
else if (timeElapsed < 50000)
{
liftHeight = 0.0f;
State = STATE_LIFT_PART_ABOUT_TO_MOVE_UP;
}
else if (timeElapsed < 60000)
{
State = STATE_LIFT_PART_MOVING_UP;
liftHeight = (timeElapsed - 50000) / 10000.0f * 25.0f;
}
else
{
liftHeight = 25.0f;
State = STATE_LIFT_PART_IS_UP;
}
}
else
{
liftHeight = 25.0f;
TimeOfBridgeBecomingOperational = 0;
State = STATE_BRIDGE_LOCKED;
}
// Move bridge part
if (liftHeight != OldLift)
{
pLiftPart->GetMatrix().GetPosition().z = DefaultZLiftPart + liftHeight;
pLiftPart->GetMatrix().UpdateRW();
pLiftPart->UpdateRwFrame();
if (pLiftRoad)
{
pLiftRoad->GetMatrix().GetPosition().z = DefaultZLiftRoad + liftHeight;
pLiftRoad->GetMatrix().UpdateRW();
pLiftRoad->UpdateRwFrame();
}
pWeight->GetMatrix().GetPosition().z = DefaultZLiftWeight - liftHeight;
pWeight->GetMatrix().UpdateRW();
pWeight->UpdateRwFrame();
OldLift = liftHeight;
}
if (State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP && OldState == STATE_LIFT_PART_IS_DOWN)
ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true);
else if (State == STATE_LIFT_PART_IS_DOWN && OldState == STATE_LIFT_PART_MOVING_DOWN)
ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, false);
}
bool CBridge::ShouldLightsBeFlashing()
{
return State != STATE_LIFT_PART_IS_DOWN;
}
void CBridge::FindBridgeEntities()
{
pWeight = nil;
pLiftRoad = nil;
pLiftPart = nil;
for (int i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--) {
CBuilding* entry = CPools::GetBuildingPool()->GetSlot(i);
if (entry)
{
if (entry->GetModelIndex() == MI_BRIDGELIFT)
pLiftPart = entry;
else if (entry->GetModelIndex() == MI_BRIDGEROADSEGMENT)
pLiftRoad = entry;
else if (entry->GetModelIndex() == MI_BRIDGEWEIGHT)
pWeight = entry;
}
}
}
bool CBridge::ThisIsABridgeObjectMovingUp(int index)
{
if (index != MI_BRIDGEROADSEGMENT && index != MI_BRIDGELIFT)
return false;
return State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP || State == STATE_LIFT_PART_MOVING_UP;
}

28
src/control/Bridge.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
class CEntity;
enum bridgeStates {
STATE_BRIDGE_LOCKED,
STATE_LIFT_PART_IS_UP,
STATE_LIFT_PART_MOVING_DOWN,
STATE_LIFT_PART_IS_DOWN,
STATE_LIFT_PART_ABOUT_TO_MOVE_UP,
STATE_LIFT_PART_MOVING_UP
};
class CBridge
{
public:
static CEntity *pLiftRoad, *pLiftPart, *pWeight;
static int State, OldState;
static float DefaultZLiftPart, DefaultZLiftRoad, DefaultZLiftWeight;
static float OldLift;
static uint32 TimeOfBridgeBecomingOperational;
static void Init();
static void Update();
static bool ShouldLightsBeFlashing();
static void FindBridgeEntities();
static bool ThisIsABridgeObjectMovingUp(int);
};

666
src/control/CarAI.cpp Normal file
View file

@ -0,0 +1,666 @@
#include "common.h"
#include "CarAI.h"
#include "Accident.h"
#include "AutoPilot.h"
#include "CarCtrl.h"
#include "General.h"
#include "HandlingMgr.h"
#include "ModelIndices.h"
#include "PlayerPed.h"
#include "Wanted.h"
#include "DMAudio.h"
#include "Fire.h"
#include "Pools.h"
#include "Timer.h"
#include "TrafficLights.h"
#include "Vehicle.h"
#include "World.h"
#include "ZoneCull.h"
#define DISTANCE_TO_SWITCH_DISTANCE_GOTO 20.0f
float CCarAI::FindSwitchDistanceClose(CVehicle* pVehicle)
{
return 30.0f;
}
float CCarAI::FindSwitchDistanceFarNormalVehicle(CVehicle* pVehicle)
{
return FindSwitchDistanceClose(pVehicle) + 5.0f;
}
float CCarAI::FindSwitchDistanceFar(CVehicle* pVehicle)
{
if (pVehicle->bIsLawEnforcer)
return 50.0f;
return FindSwitchDistanceFarNormalVehicle(pVehicle);
}
void CCarAI::UpdateCarAI(CVehicle* pVehicle)
{
if (pVehicle->bIsLawEnforcer){
if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_FARAWAY ||
pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY ||
pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE ||
pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE)
pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle);
}
switch (pVehicle->GetStatus()){
case STATUS_PLAYER:
case STATUS_PLAYER_PLAYBACKFROMBUFFER:
case STATUS_TRAIN_MOVING:
case STATUS_TRAIN_NOT_MOVING:
case STATUS_HELI:
case STATUS_PLANE:
case STATUS_PLAYER_REMOTE:
case STATUS_PLAYER_DISABLED:
break;
case STATUS_SIMPLE:
case STATUS_PHYSICS:
switch (pVehicle->AutoPilot.m_nCarMission) {
case MISSION_RAMPLAYER_FARAWAY:
if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
pVehicle->AutoPilot.m_bIgnorePathfinding) {
pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_CLOSE;
if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
pVehicle->m_bSirenOrAlarm = true;
}
if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
(FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
pVehicle->m_bSirenOrAlarm = false;
if (CCullZones::NoPolice())
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
case MISSION_RAMPLAYER_CLOSE:
if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
pVehicle->AutoPilot.m_bIgnorePathfinding) {
if (FindPlayerVehicle()) {
if (pVehicle->GetHasCollidedWith(FindPlayerVehicle())) {
if (pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNLEFT && pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNRIGHT) {
if (FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) {
pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800;
}
else {
pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 50;
}
}
}
}
if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f)
#ifdef FIX_BUGS
pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds();
#else
pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep();
#endif
else
pVehicle->m_nTimeBlocked = 0;
if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() ||
FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) {
if (pVehicle->bIsLawEnforcer &&
(pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) &&
(FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) {
TellOccupantsToLeaveCar(pVehicle);
pVehicle->AutoPilot.m_nCruiseSpeed = 0;
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1)
pVehicle->m_bSirenOrAlarm = false;
}
}
}
else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)){
pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY;
pVehicle->m_bSirenOrAlarm = false;
pVehicle->m_nCarHornTimer = 0;
}
if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
(FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())){
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
pVehicle->m_bSirenOrAlarm = false;
if (CCullZones::NoPolice())
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
else if (pVehicle->bIsLawEnforcer)
MellowOutChaseSpeed(pVehicle);
break;
case MISSION_BLOCKPLAYER_FARAWAY:
if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
pVehicle->AutoPilot.m_bIgnorePathfinding) {
pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE;
if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
pVehicle->m_bSirenOrAlarm = true;
}
if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
(FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
pVehicle->m_bSirenOrAlarm = false;
if (CCullZones::NoPolice())
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
case MISSION_BLOCKPLAYER_CLOSE:
if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() ||
pVehicle->AutoPilot.m_bIgnorePathfinding) {
if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f)
#ifdef FIX_BUGS
pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds();
#else
pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep();
#endif
else
pVehicle->m_nTimeBlocked = 0;
if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() ||
FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) {
if (pVehicle->bIsLawEnforcer &&
(pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) &&
(FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) {
TellOccupantsToLeaveCar(pVehicle);
pVehicle->AutoPilot.m_nCruiseSpeed = 0;
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1)
pVehicle->m_bSirenOrAlarm = false;
}
}
}else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)) {
pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY;
pVehicle->m_bSirenOrAlarm = false;
pVehicle->m_nCarHornTimer = 0;
}
if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
(FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) {
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
pVehicle->m_bSirenOrAlarm = false;
if (CCullZones::NoPolice())
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
if (pVehicle->bIsLawEnforcer)
MellowOutChaseSpeed(pVehicle);
break;
case MISSION_GOTOCOORDS:
if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < DISTANCE_TO_SWITCH_DISTANCE_GOTO ||
pVehicle->AutoPilot.m_bIgnorePathfinding)
pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT;
break;
case MISSION_GOTOCOORDS_STRAIGHT:
{
float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D();
if ((pVehicle->bIsAmbulanceOnDuty || pVehicle->bIsFireTruckOnDuty) && distance < 20.0f)
pVehicle->AutoPilot.m_nCarMission = MISSION_EMERGENCYVEHICLE_STOP;
if (distance < 5.0f){
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
}
else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ?
MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS;
}
break;
}
case MISSION_EMERGENCYVEHICLE_STOP:
if (pVehicle->GetMoveSpeed().Magnitude2D() < 0.01f){
if (pVehicle->bIsAmbulanceOnDuty){
float distance = 30.0f;
if (gAccidentManager.FindNearestAccident(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)){
TellOccupantsToLeaveCar(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER;
}else{
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->m_bSirenOrAlarm = false;
pVehicle->AutoPilot.m_nCruiseSpeed = 17;
if (pVehicle->bIsAmbulanceOnDuty){
pVehicle->bIsAmbulanceOnDuty = false;
--CCarCtrl::NumAmbulancesOnDuty;
}
}
}
if (pVehicle->bIsFireTruckOnDuty) {
float distance = 30.0f;
if (gFireManager.FindNearestFire(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)) {
TellOccupantsToLeaveCar(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER;
}
else {
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->m_bSirenOrAlarm = false;
pVehicle->AutoPilot.m_nCruiseSpeed = 17;
if (pVehicle->bIsFireTruckOnDuty) {
pVehicle->bIsFireTruckOnDuty = false;
--CCarCtrl::NumFiretrucksOnDuty;
}
}
}
}
break;
case MISSION_GOTOCOORDS_ACCURATE:
if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < 20.0f ||
pVehicle->AutoPilot.m_bIgnorePathfinding)
pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE;
break;
case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE:
{
float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D();
if (distance < 1.0f) {
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
}
else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0) {
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ?
MISSION_GOTO_COORDS_STRAIGHT_ACCURATE : MISSION_GOTOCOORDS_ACCURATE;
}
break;
}
case MISSION_RAMCAR_FARAWAY:
if (pVehicle->AutoPilot.m_pTargetCar){
if ((pVehicle->GetPosition() - pVehicle->AutoPilot.m_pTargetCar->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) ||
pVehicle->AutoPilot.m_bIgnorePathfinding)
pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE;
}else{
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
case MISSION_RAMCAR_CLOSE:
if (pVehicle->AutoPilot.m_pTargetCar){
if
#ifdef FIX_BUGS
(FindPlayerVehicle() == pVehicle->AutoPilot.m_pTargetCar &&
#endif
(FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer &&
(FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice()))
#ifdef FIX_BUGS
)
#endif
{
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
pVehicle->m_bSirenOrAlarm = false;
if (CCullZones::NoPolice())
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() <= FindSwitchDistanceFar(pVehicle) ||
pVehicle->AutoPilot.m_bIgnorePathfinding){
if (pVehicle->GetHasCollidedWith(pVehicle->AutoPilot.m_pTargetCar)){
if (pVehicle->GetMoveSpeed().Magnitude() < 0.04f){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800;
}
}
}else{
pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY;
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
}
}else{
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
case MISSION_BLOCKCAR_FARAWAY:
if (pVehicle->AutoPilot.m_pTargetCar){
if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) ||
pVehicle->AutoPilot.m_bIgnorePathfinding){
pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_CLOSE;
if (pVehicle->UsesSiren(pVehicle->GetModelIndex()))
pVehicle->m_bSirenOrAlarm = true;
}
}else{
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
case MISSION_BLOCKCAR_CLOSE:
if (pVehicle->AutoPilot.m_pTargetCar){
if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() > FindSwitchDistanceFar(pVehicle) &&
!pVehicle->AutoPilot.m_bIgnorePathfinding){
pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY;
pVehicle->m_bSirenOrAlarm = false;
pVehicle->m_nCarHornTimer = 0;
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
}
}else{
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
}
break;
default:
if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && !CCullZones::NoPolice()){
if (ABS(FindPlayerCoors().x - pVehicle->GetPosition().x) > 10.0f ||
ABS(FindPlayerCoors().y - pVehicle->GetPosition().y) > 10.0f){
pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle);
pVehicle->SetStatus(STATUS_PHYSICS);
pVehicle->AutoPilot.m_nCarMission =
FindPoliceCarMissionForWantedLevel();
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
}else if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE){
pVehicle->SetStatus(STATUS_PHYSICS);
TellOccupantsToLeaveCar(pVehicle);
pVehicle->AutoPilot.m_nCruiseSpeed = 0;
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1)
pVehicle->m_bSirenOrAlarm = false;
}
}
break;
}
break;
case STATUS_ABANDONED:
case STATUS_WRECKED:
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
pVehicle->AutoPilot.m_nCruiseSpeed = 0;
break;
}
float flatSpeed = pVehicle->GetMoveSpeed().MagnitudeSqr2D();
if (flatSpeed > SQR(0.018f)){
pVehicle->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds();
pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
}
if (pVehicle->GetStatus() == STATUS_PHYSICS && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){
if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE){
if (pVehicle->AutoPilot.m_nCarMission != MISSION_STOP_FOREVER &&
pVehicle->AutoPilot.m_nCruiseSpeed != 0 &&
(pVehicle->VehicleCreatedBy != RANDOM_VEHICLE || pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE)){
if (pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS
) {
if (CTimer::GetTimeInMilliseconds() - pVehicle->m_nLastTimeCollided > 500)
pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
if (flatSpeed < SQR(0.018f) && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nAntiReverseTimer > 2000){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE &&
pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE || pVehicle->VehicleCreatedBy == MISSION_VEHICLE)
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1500;
else
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 750;
pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
if (pVehicle->VehicleCreatedBy == RANDOM_VEHICLE)
pVehicle->AutoPilot.m_nDrivingStyle = Max(DRIVINGSTYLE_AVOID_CARS, pVehicle->AutoPilot.m_nDrivingStyle);
pVehicle->PlayCarHorn();
}
}
}
}
}
if ((pVehicle->m_randomSeed & 7) == 0){
if (CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 30000 &&
CTimer::GetPreviousTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission <= 30000 &&
pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE &&
!CTrafficLights::ShouldCarStopForBridge(pVehicle)){
pVehicle->SetStatus(STATUS_PHYSICS);
CCarCtrl::SwitchVehicleToRealPhysics(pVehicle);
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 400;
}
}
if (pVehicle->GetUp().z < -0.7f){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000;
}
if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){
switch (pVehicle->AutoPilot.m_nCarMission){
case MISSION_RAMPLAYER_FARAWAY:
case MISSION_RAMPLAYER_CLOSE:
case MISSION_BLOCKPLAYER_FARAWAY:
case MISSION_BLOCKPLAYER_CLOSE:
if (FindPlayerVehicle() && FindPlayerSpeed().Magnitude() > pVehicle->GetMoveSpeed().Magnitude()){
if (FindPlayerSpeed().Magnitude() > 0.1f){
if (DotProduct2D(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.0f){
CVector2D dist = pVehicle->GetPosition() - FindPlayerCoors();
CVector2D speed = FindPlayerSpeed();
if (0.5f * dist.Magnitude() * speed.Magnitude() < DotProduct2D(dist, speed)){
if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() > 12.0f){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 500;
}
}
}
}
}
break;
default: break;
}
}
if (pVehicle->pDriver && pVehicle->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS){
if ((pVehicle->GetPosition() - FindPlayerCoors()).Magnitude() < 15.0f){
if (!FindPlayerVehicle() || pVehicle->GetHasCollidedWith(FindPlayerVehicle())){
pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000;
}
}
}
if (pVehicle->m_bSirenOrAlarm){
if ((uint8)(pVehicle->m_randomSeed ^ CGeneral::GetRandomNumber()) == 0xAD)
pVehicle->m_nCarHornTimer = 45;
}
}
void CCarAI::CarHasReasonToStop(CVehicle* pVehicle)
{
pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
}
float CCarAI::GetCarToGoToCoors(CVehicle* pVehicle, CVector* pTarget)
{
if (pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT){
pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS;
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
pVehicle->AutoPilot.m_nCruiseSpeed = 20;
pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds();
pVehicle->SetStatus(STATUS_PHYSICS);
pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, *pTarget, false)) ?
MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS;
}else if (Abs(pTarget->x - pVehicle->AutoPilot.m_vecDestinationCoors.x) > 2.0f ||
Abs(pTarget->y - pVehicle->AutoPilot.m_vecDestinationCoors.y) > 2.0f){
pVehicle->AutoPilot.m_vecDestinationCoors = *pTarget;
}
return (pVehicle->GetPosition() - *pTarget).Magnitude2D();
}
void CCarAI::AddPoliceCarOccupants(CVehicle* pVehicle)
{
if (pVehicle->bOccupantsHaveBeenGenerated)
return;
pVehicle->bOccupantsHaveBeenGenerated = true;
switch (pVehicle->GetModelIndex()){
case MI_FBICAR:
case MI_ENFORCER:
pVehicle->SetUpDriver();
for (int i = 0; i < 3; i++)
pVehicle->SetupPassenger(i);
return;
case MI_POLICE:
case MI_RHINO:
case MI_BARRACKS:
pVehicle->SetUpDriver();
if (FindPlayerPed()->m_pWanted->GetWantedLevel() > 1)
pVehicle->SetupPassenger(0);
return;
default:
return;
}
}
void CCarAI::AddAmbulanceOccupants(CVehicle* pVehicle)
{
pVehicle->SetUpDriver();
pVehicle->SetupPassenger(1);
}
void CCarAI::AddFiretruckOccupants(CVehicle* pVehicle)
{
pVehicle->SetUpDriver();
pVehicle->SetupPassenger(0);
}
void CCarAI::TellOccupantsToLeaveCar(CVehicle* pVehicle)
{
if (pVehicle->pDriver){
pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle);
switch (pVehicle->GetModelIndex()) {
case MI_FIRETRUCK:
case MI_FBICAR:
case MI_ENFORCER:
case MI_BARRACKS:
case MI_RHINO:
case MI_POLICE:
break;
case MI_AMBULAN:
pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE);
break;
}
}
int timer = 100;
for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++){
if (pVehicle->pPassengers[i]) {
pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle);
}
}
}
void CCarAI::TellCarToRamOtherCar(CVehicle* pVehicle, CVehicle* pTarget)
{
pVehicle->AutoPilot.m_pTargetCar = pTarget;
pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar);
pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY;
pVehicle->bEngineOn = true;
pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed);
}
void CCarAI::TellCarToBlockOtherCar(CVehicle* pVehicle, CVehicle* pTarget)
{
pVehicle->AutoPilot.m_pTargetCar = pTarget;
pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar);
pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY;
pVehicle->bEngineOn = true;
pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed);
}
uint8 CCarAI::FindPoliceCarMissionForWantedLevel()
{
switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()){
case 0:
case 1: return MISSION_BLOCKPLAYER_FARAWAY;
case 2: return (CGeneral::GetRandomNumber() & 3) >= 3 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
case 3: return (CGeneral::GetRandomNumber() & 3) >= 2 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
case 4:
case 5:
case 6: return (CGeneral::GetRandomNumber() & 3) >= 1 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY;
default: return MISSION_BLOCKPLAYER_FARAWAY;
}
}
int32 CCarAI::FindPoliceCarSpeedForWantedLevel(CVehicle* pVehicle)
{
switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) {
case 0: return CGeneral::GetRandomNumberInRange(12, 16);
case 1: return 25;
case 2: return 34;
case 3: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 0.9f;
case 4: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.2f;
case 5: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.25f;
case 6: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.3f;
default: return 0;
}
}
void CCarAI::MellowOutChaseSpeed(CVehicle* pVehicle)
{
if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 1){
float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude();
if (FindPlayerVehicle()){
if (distanceToPlayer < 10.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 15;
else if (distanceToPlayer < 20.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 22;
else
pVehicle->AutoPilot.m_nCruiseSpeed = 25;
}else{
if (distanceToPlayer < 20.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 5;
else if (distanceToPlayer < 40.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 13;
else
pVehicle->AutoPilot.m_nCruiseSpeed = 25;
}
}else if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 2){
float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude();
if (FindPlayerVehicle()) {
if (distanceToPlayer < 10.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 27;
else if (distanceToPlayer < 20.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 30;
else
pVehicle->AutoPilot.m_nCruiseSpeed = 34;
}
else {
if (distanceToPlayer < 20.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 5;
else if (distanceToPlayer < 40.0f)
pVehicle->AutoPilot.m_nCruiseSpeed = 18;
else
pVehicle->AutoPilot.m_nCruiseSpeed = 34;
}
}
}
void CCarAI::MakeWayForCarWithSiren(CVehicle *pVehicle)
{
float flatSpeed = pVehicle->GetMoveSpeed().Magnitude2D();
if (flatSpeed < 0.1f)
return;
CVector2D forward = pVehicle->GetMoveSpeed() / flatSpeed;
float projection = flatSpeed * 45 + 20;
int i = CPools::GetVehiclePool()->GetSize();
while (--i >= 0) {
CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i);
if (!vehicle)
continue;
if (!vehicle->IsCar() && !vehicle->IsBike())
continue;
if (vehicle->GetStatus() != STATUS_SIMPLE && vehicle->GetStatus() != STATUS_PHYSICS)
continue;
if (vehicle->VehicleCreatedBy != RANDOM_VEHICLE)
continue;
if (vehicle->bIsLawEnforcer || vehicle->bIsAmbulanceOnDuty || vehicle->bIsFireTruckOnDuty)
continue;
if (vehicle == pVehicle)
continue;
if (Abs(pVehicle->GetPosition().z - vehicle->GetPosition().z) >= 5.0f)
continue;
CVector2D distance = vehicle->GetPosition() - pVehicle->GetPosition();
if (distance.Magnitude() >= projection)
continue;
if (vehicle->GetMoveSpeed().Magnitude2D() <= 0.05f)
continue;
float correlation = DotProduct2D(forward, distance) / distance.Magnitude();
if (correlation <= 0.0f)
continue;
if (correlation > 0.8f && DotProduct2D(forward, vehicle->GetForward()) > 0.7f){
if (vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVELEFT && vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVERIGHT){
vehicle->AutoPilot.m_nTempAction = (distance.x * forward.y - distance.y * forward.x > 0.0f) ?
TEMPACT_SWERVELEFT : TEMPACT_SWERVERIGHT;
vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000;
}
vehicle->SetStatus(STATUS_PHYSICS);
}else{
if (DotProduct2D(vehicle->GetMoveSpeed(), distance) < 0.0f && vehicle->AutoPilot.m_nTempAction != TEMPACT_WAIT){
vehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT;
vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000;
}
}
}
}

26
src/control/CarAI.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include "AutoPilot.h"
class CVehicle;
class CCarAI
{
public:
static float FindSwitchDistanceClose(CVehicle*);
static float FindSwitchDistanceFarNormalVehicle(CVehicle*);
static float FindSwitchDistanceFar(CVehicle*);
static void UpdateCarAI(CVehicle*);
static void CarHasReasonToStop(CVehicle*);
static float GetCarToGoToCoors(CVehicle*, CVector*);
static void AddPoliceCarOccupants(CVehicle*);
static void AddAmbulanceOccupants(CVehicle*);
static void AddFiretruckOccupants(CVehicle*);
static void TellOccupantsToLeaveCar(CVehicle*);
static void TellCarToRamOtherCar(CVehicle*, CVehicle*);
static void TellCarToBlockOtherCar(CVehicle*, CVehicle*);
static uint8 FindPoliceCarMissionForWantedLevel();
static int32 FindPoliceCarSpeedForWantedLevel(CVehicle*);
static void MellowOutChaseSpeed(CVehicle*);
static void MakeWayForCarWithSiren(CVehicle *veh);
};

2825
src/control/CarCtrl.cpp Normal file

File diff suppressed because it is too large Load diff

143
src/control/CarCtrl.h Normal file
View file

@ -0,0 +1,143 @@
#pragma once
#include "PathFind.h"
#include "Boat.h"
#include "Vehicle.h"
#define GAME_SPEED_TO_METERS_PER_SECOND 50.0f
#define METERS_PER_SECOND_TO_GAME_SPEED (1.0f / GAME_SPEED_TO_METERS_PER_SECOND)
#define GAME_SPEED_TO_CARAI_SPEED 60.0f
#define TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING 2500
class CZoneInfo;
enum{
MAX_CARS_TO_KEEP = 2,
MAX_CAR_MODELS_IN_ARRAY = 256,
};
#define LANE_WIDTH 5.0f
#ifdef FIX_BUGS
#define FIX_PATHFIND_BUG
#endif
class CCarCtrl
{
public:
enum eCarClass {
POOR = 0,
RICH,
EXEC,
WORKER,
SPECIAL,
BIG,
TAXI,
TOTAL_CUSTOM_CLASSES,
MAFIA,
TRIAD,
DIABLO,
YAKUZA,
YARDIE,
COLOMB,
NINES,
GANG8,
GANG9,
COPS
};
static void SwitchVehicleToRealPhysics(CVehicle*);
static void AddToCarArray(int32 id, int32 vehclass);
static void UpdateCarCount(CVehicle*, bool);
static int32 ChooseCarModel(int32 vehclass);
static bool JoinCarWithRoadSystemGotoCoors(CVehicle*, CVector, bool);
static void JoinCarWithRoadSystem(CVehicle*);
static void UpdateCarOnRails(CVehicle*);
static bool MapCouldMoveInThisArea(float x, float y);
static void ScanForPedDanger(CVehicle *veh);
static void RemoveFromInterestingVehicleList(CVehicle*);
static void GenerateRandomCars(void);
static void GenerateOneRandomCar(void);
static void GenerateEmergencyServicesCar(void);
static int32 ChooseModel(CZoneInfo*, CVector*, int*);
static int32 ChoosePoliceCarModel(void);
static int32 ChooseGangCarModel(int32 gang);
static void RemoveDistantCars(void);
static void PossiblyRemoveVehicle(CVehicle*);
static bool IsThisVehicleInteresting(CVehicle*);
static void RegisterVehicleOfInterest(CVehicle*);
static int32 CountCarsOfType(int32 mi);
static void SlowCarOnRailsDownForTrafficAndLights(CVehicle*);
static bool PickNextNodeAccordingStrategy(CVehicle*);
static void DragCarToPoint(CVehicle*, CVector*);
static float FindMaximumSpeedForThisCarInTraffic(CVehicle*);
static void SlowCarDownForCarsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float);
static void SlowCarDownForPedsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float);
static void SlowCarDownForOtherCar(CEntity*, CVehicle*, float*, float);
static float TestCollisionBetween2MovingRects(CVehicle*, CVehicle*, float, float, CVector*, CVector*, uint8);
static float FindAngleToWeaveThroughTraffic(CVehicle*, CPhysical*, float, float);
static void WeaveThroughCarsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*);
static void WeaveForOtherCar(CEntity*, CVehicle*, float*, float*);
static void WeaveThroughPedsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*);
static void WeaveForPed(CEntity*, CVehicle*, float*, float*);
static void WeaveThroughObjectsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float*);
static void WeaveForObject(CEntity*, CVehicle*, float*, float*);
#ifdef FIX_PATHFIND_BUG
static void PickNextNodeToChaseCar(CVehicle*, float, float, float, CVehicle*);
#else
static void PickNextNodeToChaseCar(CVehicle*, float, float, CVehicle*);
#endif
static bool PickNextNodeToFollowPath(CVehicle*);
static void PickNextNodeRandomly(CVehicle*);
static uint8 FindPathDirection(int32, int32, int32);
static void Init(void);
static void ReInit(void);
static float FindSpeedMultiplier(float, float, float, float);
static void SteerAICarWithPhysics(CVehicle*);
static void SteerAICarWithPhysics_OnlyMission(CVehicle*, float*, float*, float*, bool*);
static void SteerAIBoatWithPhysics(CBoat*);
static float FindMaxSteerAngle(CVehicle*);
static void SteerAICarWithPhysicsFollowPath(CVehicle*, float*, float*, float*, bool*);
static void SteerAICarWithPhysicsHeadingForTarget(CVehicle*, CPhysical*, float, float, float*, float*, float*, bool*);
static void SteerAICarWithPhysicsTryingToBlockTarget(CVehicle*, float, float, float, float, float*, float*, float*, bool*);
static void SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle*, float, float, float, float, float*, float*, float*, bool*);
static void SteerAIBoatWithPhysicsHeadingForTarget(CBoat*, float, float, float*, float*, float*);
static bool ThisRoadObjectCouldMove(int16);
static void ClearInterestingVehicleList();
static void FindLinksToGoWithTheseNodes(CVehicle*);
static bool GenerateOneEmergencyServicesCar(uint32, CVector);
static float GetPositionAlongCurrentCurve(CVehicle* pVehicle)
{
uint32 timeInCurve = CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeEnteredCurve;
return (float)timeInCurve / pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve;
}
static float LimitRadianAngle(float angle)
{
while (angle < -PI)
angle += TWOPI;
while (angle > PI)
angle -= TWOPI;
return angle;
}
static int32 NumLawEnforcerCars;
static int32 NumAmbulancesOnDuty;
static int32 NumFiretrucksOnDuty;
static int32 NumRandomCars;
static int32 NumMissionCars;
static int32 NumParkedCars;
static int32 NumPermanentCars;
static bool bCarsGeneratedAroundCamera;
static float CarDensityMultiplier;
static int8 CountDownToCarsAtStart;
static int32 MaxNumberOfCarsInUse;
static uint32 LastTimeLawEnforcerCreated;
static uint32 LastTimeFireTruckCreated;
static uint32 LastTimeAmbulanceCreated;
static int32 TotalNumOfCarsOfRating[TOTAL_CUSTOM_CLASSES];
static int32 NextCarOfRating[TOTAL_CUSTOM_CLASSES];
static int32 CarArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY];
};
extern CVehicle* apCarsToKeep[MAX_CARS_TO_KEEP];

31
src/control/Curves.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "common.h"
#include "Curves.h"
float CCurves::CalcSpeedScaleFactor(CVector* pPoint1, CVector* pPoint2, float dir1X, float dir1Y, float dir2X, float dir2Y)
{
CVector2D dir1(dir1X, dir1Y);
CVector2D dir2(dir2X, dir2Y);
float distance = (*pPoint1 - *pPoint2).Magnitude2D();
float dp = DotProduct2D(dir1, dir2);
if (dp > 0.9f)
return distance + Abs((pPoint1->x * dir1Y - pPoint1->y * dir1X) - (pPoint2->x * dir1Y - pPoint2->y * dir1X));
else
return ((1.0f - dp) * 0.2f + 1.0f) * distance;
}
void CCurves::CalcCurvePoint(CVector* pPos1, CVector* pPos2, CVector* pDir1, CVector* pDir2, float between, int32 timeOnCurve, CVector* pOutPos, CVector* pOutDir)
{
float actualFactor = CalcSpeedScaleFactor(pPos1, pPos2, pDir1->x, pDir1->y, pDir2->x, pDir2->y);
CVector2D dir1 = *pDir1 * actualFactor;
CVector2D dir2 = *pDir2 * actualFactor;
float curveCoef = 0.5f - 0.5f * Cos(3.1415f * between);
*pOutPos = CVector(
(pPos1->x + between * dir1.x) * (1.0f - curveCoef) + (pPos2->x - (1 - between) * dir2.x) * curveCoef,
(pPos1->y + between * dir1.y) * (1.0f - curveCoef) + (pPos2->y - (1 - between) * dir2.y) * curveCoef,
0.0f);
*pOutDir = CVector(
(dir1.x * (1.0f - curveCoef) + dir2.x * curveCoef) / (timeOnCurve * 0.001f),
(dir1.y * (1.0f - curveCoef) + dir2.y * curveCoef) / (timeOnCurve * 0.001f),
0.0f);
}

9
src/control/Curves.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
class CVector;
class CCurves
{
public:
static float CalcSpeedScaleFactor(CVector*, CVector*, float, float, float, float);
static void CalcCurvePoint(CVector*, CVector*, CVector*, CVector*, float, int32, CVector*, CVector*);
};

448
src/control/Darkel.cpp Normal file
View file

@ -0,0 +1,448 @@
#include "common.h"
#include "main.h"
#include "Darkel.h"
#include "PlayerPed.h"
#include "Wanted.h"
#include "Timer.h"
#include "DMAudio.h"
#include "Population.h"
#include "Weapon.h"
#include "World.h"
#include "Stats.h"
#include "Font.h"
#include "Text.h"
#include "Vehicle.h"
#ifdef FIX_BUGS
#include "Replay.h"
#endif
#define FRENZY_ANY_PED -1
#define FRENZY_ANY_CAR -2
int32 CDarkel::TimeLimit;
int32 CDarkel::PreviousTime;
int32 CDarkel::TimeOfFrenzyStart;
int32 CDarkel::WeaponType;
int32 CDarkel::AmmoInterruptedWeapon;
int32 CDarkel::KillsNeeded;
int8 CDarkel::InterruptedWeapon;
/*
* bStandardSoundAndMessages is a completely beta thing,
* makes game handle sounds & messages instead of SCM (just like in GTA2)
* but it's never been used in the game. Has unused sliding text when frenzy completed etc.
*/
bool CDarkel::bStandardSoundAndMessages;
bool CDarkel::bNeedHeadShot;
bool CDarkel::bProperKillFrenzy;
uint16 CDarkel::Status;
uint16 CDarkel::RegisteredKills[NUM_DEFAULT_MODELS];
int32 CDarkel::ModelToKill;
int32 CDarkel::ModelToKill2;
int32 CDarkel::ModelToKill3;
int32 CDarkel::ModelToKill4;
wchar *CDarkel::pStartMessage;
uint8
CDarkel::CalcFade(uint32 time, uint32 start, uint32 end)
{
if (time >= start && time <= end) {
if (time >= start + 500) {
if (time <= end - 500)
return 255;
else
return 255 * (end - time) / 500;
} else
return 255 * (time - start) / 500;
} else
return 0;
}
// Screen positions taken from VC
void
CDarkel::DrawMessages()
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
switch (Status) {
case KILLFRENZY_ONGOING:
{
CFont::SetJustifyOff();
CFont::SetBackgroundOff();
#ifdef FIX_BUGS
CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 30));
#else
CFont::SetCentreSize(SCREEN_WIDTH - 30);
#endif
CFont::SetCentreOn();
CFont::SetPropOn();
uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - CDarkel::TimeOfFrenzyStart;
if (CDarkel::bStandardSoundAndMessages) {
if (timePassedSinceStart >= 3000 && timePassedSinceStart < 11000) {
#ifdef FIX_BUGS
CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f));
#else
CFont::SetScale(1.3f, 1.3f);
#endif
CFont::SetJustifyOff();
CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 3000, 11000)));
CFont::SetFontStyle(FONT_BANK);
if (pStartMessage) {
CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage);
}
}
} else {
if (timePassedSinceStart < 8000) {
#ifdef FIX_BUGS
CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f));
#else
CFont::SetScale(1.3f, 1.3f);
#endif
CFont::SetJustifyOff();
CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 0, 8000)));
CFont::SetFontStyle(FONT_BANK);
if (pStartMessage) {
CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage);
}
}
}
#ifdef FIX_BUGS
CFont::SetScale(SCREEN_SCALE_X(0.75f), SCREEN_SCALE_Y(1.5f));
#else
CFont::SetScale(0.75f, 1.5f);
#endif
CFont::SetCentreOff();
CFont::SetRightJustifyOn();
CFont::SetFontStyle(FONT_HEADING);
if (CDarkel::TimeLimit >= 0) {
uint32 timeLeft = CDarkel::TimeLimit - (CTimer::GetTimeInMilliseconds() - CDarkel::TimeOfFrenzyStart);
sprintf(gString, "%d:%02d", timeLeft / 60000, timeLeft % 60000 / 1000);
AsciiToUnicode(gString, gUString);
if (timeLeft > 4000 || CTimer::GetFrameCounter() & 1) {
CFont::SetColor(CRGBA(0, 0, 0, 255));
#if defined(PS2_HUD) || defined(FIX_BUGS)
#ifdef FIX_BUGS
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f - 1.0f), SCREEN_SCALE_Y(108.0f + 1.0f), gUString);
#else
CFont::PrintString(SCREEN_WIDTH-(34.0f - 1.0f), 108.0f + 1.0f, gUString);
#endif
#else
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f + 1.0f), SCREEN_SCALE_Y(108.0f + 1.0f), gUString);
#endif
CFont::SetColor(CRGBA(150, 100, 255, 255));
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(108.0f), gUString);
}
}
sprintf(gString, "%d", (CDarkel::KillsNeeded >= 0 ? CDarkel::KillsNeeded : 0));
AsciiToUnicode(gString, gUString);
CFont::SetColor(CRGBA(0, 0, 0, 255));
#ifdef FIX_BUGS
#define DARKEL_COUNTER_HEIGHT 143.0f
#else
#define DARKEL_COUNTER_HEIGHT 128.0f
#endif
#if defined(PS2_HUD) || defined(FIX_BUGS)
#ifdef FIX_BUGS
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f - 1.0f), SCREEN_SCALE_Y(DARKEL_COUNTER_HEIGHT + 1.0f), gUString);
#else
CFont::PrintString(SCREEN_WIDTH-(34.0f - 1.0f), DARKEL_COUNTER_HEIGHT + 1.0f, gUString);
#endif
#else
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f + 1.0f), SCREEN_SCALE_Y(DARKEL_COUNTER_HEIGHT + 1.0f), gUString);
#endif
CFont::SetColor(CRGBA(255, 128, 128, 255));
CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(DARKEL_COUNTER_HEIGHT), gUString);
#undef DARKEL_COUNTER_HEIGHT
break;
}
case KILLFRENZY_PASSED:
{
if (CDarkel::bStandardSoundAndMessages) {
uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - CDarkel::TimeOfFrenzyStart;
if (CTimer::GetTimeInMilliseconds() - CDarkel::TimeOfFrenzyStart < 5000) {
CFont::SetBackgroundOff();
#ifdef FIX_BUGS
CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20));
#else
CFont::SetCentreSize(SCREEN_WIDTH - 20);
#endif
CFont::SetCentreOn();
#ifdef FIX_BUGS
CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f));
#else
CFont::SetScale(1.5f, 1.5f);
#endif
CFont::SetJustifyOff();
CFont::SetColor(CRGBA(128, 255, 128, CalcFade(timePassedSinceStart, 0, 5000)));
CFont::SetFontStyle(FONT_BANK);
#ifdef FIX_BUGS
int y = SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(25.0f - timePassedSinceStart * 0.01f);
#else
int y = (SCREEN_HEIGHT / 2 + 25) - (timePassedSinceStart * 0.01f);
#endif
CFont::PrintString(SCREEN_WIDTH / 2, y, TheText.Get("KF_3"));
}
}
break;
}
default:
break;
}
}
void
CDarkel::Init()
{
Status = KILLFRENZY_NONE;
}
uint16
CDarkel::QueryModelsKilledByPlayer(int32 modelId)
{
return RegisteredKills[modelId];
}
bool
CDarkel::FrenzyOnGoing()
{
return Status == KILLFRENZY_ONGOING;
}
uint16
CDarkel::ReadStatus()
{
return Status;
}
void
CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle)
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
if (FrenzyOnGoing()) {
int32 model = vehicle->GetModelIndex();
if (ModelToKill == FRENZY_ANY_CAR || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) {
KillsNeeded--;
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_CAR_BLOWN, 0);
}
}
RegisteredKills[vehicle->GetModelIndex()]++;
CStats::CarsExploded++;
}
void
CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapon, bool headshot)
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
if (FrenzyOnGoing() && (weapon == WeaponType
|| weapon == WEAPONTYPE_EXPLOSION
|| weapon == WEAPONTYPE_UZI_DRIVEBY && WeaponType == WEAPONTYPE_UZI
|| weapon == WEAPONTYPE_RAMMEDBYCAR && WeaponType == WEAPONTYPE_RUNOVERBYCAR
|| weapon == WEAPONTYPE_RUNOVERBYCAR && WeaponType == WEAPONTYPE_RAMMEDBYCAR
|| weapon == WEAPONTYPE_FLAMETHROWER && WeaponType == WEAPONTYPE_MOLOTOV)) {
int32 model = victim->GetModelIndex();
if (ModelToKill == FRENZY_ANY_PED || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) {
if (!bNeedHeadShot || headshot) {
KillsNeeded--;
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_KILL, 0);
}
}
}
CStats::PeopleKilledByPlayer++;
RegisteredKills[victim->GetModelIndex()]++;
CStats::PedsKilledOfThisType[victim->bChrisCriminal ? PEDTYPE_CRIMINAL : victim->m_nPedType]++;
if (headshot)
CStats::HeadsPopped++;
CStats::KillsSinceLastCheckpoint++;
}
void
CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype)
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
CStats::PeopleKilledByOthers++;
}
void
CDarkel::ResetModelsKilledByPlayer()
{
for (int i = 0; i < NUM_DEFAULT_MODELS; i++)
RegisteredKills[i] = 0;
}
void
CDarkel::ResetOnPlayerDeath()
{
if (Status != KILLFRENZY_ONGOING)
return;
CPopulation::m_AllRandomPedsThisType = -1;
Status = KILLFRENZY_FAILED;
TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();
eWeaponType fixedWeapon;
if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
fixedWeapon = WEAPONTYPE_UZI;
else
fixedWeapon = (eWeaponType)WeaponType;
CPlayerPed *player = FindPlayerPed();
if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) {
player->m_nSelectedWepSlot = InterruptedWeapon;
player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal = CDarkel::AmmoInterruptedWeapon;
}
if (FindPlayerVehicle()) {
player->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nModelId);
player->m_currentWeapon = player->m_nSelectedWepSlot;
player->MakeChangesForNewWeapon(player->m_currentWeapon);
}
}
void
CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot)
{
eWeaponType fixedWeapon;
if (weaponType == WEAPONTYPE_UZI_DRIVEBY)
fixedWeapon = WEAPONTYPE_UZI;
else
fixedWeapon = weaponType;
WeaponType = weaponType;
Status = KILLFRENZY_ONGOING;
KillsNeeded = kill;
ModelToKill = modelId0;
ModelToKill2 = modelId2;
ModelToKill3 = modelId3;
ModelToKill4 = modelId4;
pStartMessage = text;
if (text == TheText.Get("PAGE_00")) {
CDarkel::bProperKillFrenzy = true;
CDarkel::pStartMessage = nil;
} else
bProperKillFrenzy = false;
bStandardSoundAndMessages = standardSound;
bNeedHeadShot = needHeadShot;
TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();
TimeLimit = time;
PreviousTime = time / 1000;
CPlayerPed *player = FindPlayerPed();
if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) {
InterruptedWeapon = player->m_currentWeapon;
player->GiveWeapon(fixedWeapon, 0);
AmmoInterruptedWeapon = player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal;
player->GiveWeapon(fixedWeapon, 30000);
player->m_nSelectedWepSlot = player->GetWeaponSlot(fixedWeapon);
player->MakeChangesForNewWeapon(player->m_nSelectedWepSlot);
if (FindPlayerVehicle()) {
player->m_currentWeapon = player->m_nSelectedWepSlot;
player->GetWeapon()->m_nAmmoInClip = Min(player->GetWeapon()->m_nAmmoTotal, CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition);
player->ClearWeaponTarget();
}
}
if (CDarkel::bStandardSoundAndMessages)
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_START, 0);
}
void
CDarkel::Update()
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
if (Status != KILLFRENZY_ONGOING)
return;
int32 FrameTime = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart);
if (FrameTime > 0 || TimeLimit < 0) {
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_ONGOING, FrameTime);
int32 PrevTime = FrameTime / 1000;
if (PrevTime != PreviousTime) {
if (PreviousTime < 12)
DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, PrevTime);
PreviousTime = PrevTime;
}
} else {
CPopulation::m_AllRandomPedsThisType = -1;
Status = KILLFRENZY_FAILED;
TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();
eWeaponType fixedWeapon;
if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
fixedWeapon = WEAPONTYPE_UZI;
else
fixedWeapon = (eWeaponType)WeaponType;
CPlayerPed *player = FindPlayerPed();
if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) {
player->m_nSelectedWepSlot = InterruptedWeapon;
player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal = CDarkel::AmmoInterruptedWeapon;
}
if (FindPlayerVehicle()) {
player->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nModelId);
player->m_currentWeapon = player->m_nSelectedWepSlot;
player->MakeChangesForNewWeapon(player->m_currentWeapon);
}
if (bStandardSoundAndMessages)
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_FAILED, 0);
}
if (KillsNeeded <= 0) {
CPopulation::m_AllRandomPedsThisType = -1;
Status = KILLFRENZY_PASSED;
if (bProperKillFrenzy)
CStats::AnotherKillFrenzyPassed();
TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds();
FindPlayerPed()->m_pWanted->SetWantedLevel(0);
eWeaponType fixedWeapon;
if (WeaponType == WEAPONTYPE_UZI_DRIVEBY)
fixedWeapon = WEAPONTYPE_UZI;
else
fixedWeapon = (eWeaponType)WeaponType;
CPlayerPed* player = FindPlayerPed();
if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) {
player->m_nSelectedWepSlot = InterruptedWeapon;
player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal = CDarkel::AmmoInterruptedWeapon;
}
if (FindPlayerVehicle()) {
player->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nModelId);
player->m_currentWeapon = player->m_nSelectedWepSlot;
player->MakeChangesForNewWeapon(player->m_currentWeapon);
}
if (bStandardSoundAndMessages)
DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_PASSED, 0);
}
}

53
src/control/Darkel.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
#include "ModelIndices.h"
#include "WeaponType.h"
class CVehicle;
class CPed;
enum
{
KILLFRENZY_NONE,
KILLFRENZY_ONGOING,
KILLFRENZY_PASSED,
KILLFRENZY_FAILED,
};
class CDarkel
{
private:
static int32 TimeLimit;
static int32 PreviousTime;
static int32 TimeOfFrenzyStart;
static int32 WeaponType;
static int32 AmmoInterruptedWeapon;
static int32 KillsNeeded;
static int8 InterruptedWeapon;
static bool bStandardSoundAndMessages;
static bool bNeedHeadShot;
static bool bProperKillFrenzy;
static uint16 Status;
static uint16 RegisteredKills[NUM_DEFAULT_MODELS];
static int32 ModelToKill;
static int32 ModelToKill2;
static int32 ModelToKill3;
static int32 ModelToKill4;
static wchar *pStartMessage;
public:
static uint8 CalcFade(uint32 time, uint32 min, uint32 max);
static void DrawMessages(void);
static bool FrenzyOnGoing();
static void Init();
static uint16 QueryModelsKilledByPlayer(int32 modelId);
static uint16 ReadStatus();
static void RegisterCarBlownUpByPlayer(CVehicle *vehicle);
static void RegisterKillByPlayer(CPed *victim, eWeaponType weapontype, bool headshot = false);
static void RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype);
static void ResetModelsKilledByPlayer();
static void ResetOnPlayerDeath();
static void StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot);
static void Update();
};

320
src/control/GameLogic.cpp Normal file
View file

@ -0,0 +1,320 @@
#include "common.h"
#include "GameLogic.h"
#include "Clock.h"
#include "Stats.h"
#include "Pickups.h"
#include "Timer.h"
#include "Streaming.h"
#include "CutsceneMgr.h"
#include "World.h"
#include "PlayerPed.h"
#include "Wanted.h"
#include "Camera.h"
#include "Messages.h"
#include "CarCtrl.h"
#include "Restart.h"
#include "Pad.h"
#include "References.h"
#include "Fire.h"
#include "Script.h"
#include "Garages.h"
#include "screendroplets.h"
uint8 CGameLogic::ActivePlayers;
void
CGameLogic::InitAtStartOfGame()
{
ActivePlayers = 1;
}
void
CGameLogic::PassTime(uint32 time)
{
int32 minutes, hours, days;
minutes = time + CClock::GetMinutes();
hours = CClock::GetHours();
for (; minutes >= 60; minutes -= 60)
hours++;
if (hours > 23) {
days = CStats::DaysPassed;
for (; hours >= 24; hours -= 24)
days++;
CStats::DaysPassed = days;
}
CClock::SetGameClock(hours, minutes);
CPickups::PassTime(time * 1000);
}
void
CGameLogic::SortOutStreamingAndMemory(const CVector &pos)
{
CTimer::Stop();
CStreaming::FlushRequestList();
CStreaming::DeleteRwObjectsAfterDeath(pos);
CStreaming::RemoveUnusedModelsInLoadedList();
CGame::DrasticTidyUpMemory(true);
CStreaming::LoadScene(pos);
CTimer::Update();
}
void
CGameLogic::Update()
{
CVector vecRestartPos;
float fRestartFloat;
if (CCutsceneMgr::IsCutsceneProcessing()) return;
CPlayerInfo &pPlayerInfo = CWorld::Players[CWorld::PlayerInFocus];
switch (pPlayerInfo.m_WBState) {
case WBSTATE_PLAYING:
if (pPlayerInfo.m_pPed->m_nPedState == PED_DEAD) {
pPlayerInfo.m_pPed->ClearAdrenaline();
pPlayerInfo.KillPlayer();
}
if (pPlayerInfo.m_pPed->m_nPedState == PED_ARRESTED) {
pPlayerInfo.m_pPed->ClearAdrenaline();
pPlayerInfo.ArrestPlayer();
}
break;
case WBSTATE_WASTED:
#ifdef MISSION_REPLAY
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) {
#else
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) {
#endif
TheCamera.SetFadeColour(200, 200, 200);
TheCamera.Fade(2.0f, FADE_OUT);
}
#ifdef MISSION_REPLAY
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) {
#else
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) {
#endif
pPlayerInfo.m_WBState = WBSTATE_PLAYING;
if (pPlayerInfo.m_bGetOutOfHospitalFree) {
pPlayerInfo.m_bGetOutOfHospitalFree = false;
} else {
pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - 1000);
pPlayerInfo.m_pPed->ClearWeapons();
}
if (pPlayerInfo.m_pPed->bInVehicle) {
CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle;
if (pVehicle != nil) {
if (pVehicle->pDriver == pPlayerInfo.m_pPed) {
pVehicle->pDriver = nil;
if (pVehicle->GetStatus() != STATUS_WRECKED)
pVehicle->SetStatus(STATUS_ABANDONED);
} else
pVehicle->RemovePassenger(pPlayerInfo.m_pPed);
}
}
CEventList::Initialise();
#ifdef SCREEN_DROPLETS
ScreenDroplets::Initialise();
#endif
CMessages::ClearMessages();
CCarCtrl::ClearInterestingVehicleList();
CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1);
CRestart::FindClosestHospitalRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat);
CRestart::OverrideHospitalLevel = LEVEL_GENERIC;
CRestart::OverridePoliceStationLevel = LEVEL_GENERIC;
PassTime(720);
RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat);
SortOutStreamingAndMemory(pPlayerInfo.GetPos());
TheCamera.m_fCamShakeForce = 0.0f;
TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE);
CPad::GetPad(0)->StopShaking(0);
CReferences::RemoveReferencesToPlayer();
CCarCtrl::CountDownToCarsAtStart = 2;
CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED;
if (CRestart::bFadeInAfterNextDeath) {
TheCamera.SetFadeColour(200, 200, 200);
TheCamera.Fade(4.0f, FADE_IN);
} else CRestart::bFadeInAfterNextDeath = true;
}
break;
case WBSTATE_BUSTED:
#ifdef MISSION_REPLAY
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) {
#else
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) {
#endif
TheCamera.SetFadeColour(0, 0, 0);
TheCamera.Fade(2.0f, FADE_OUT);
}
#ifdef MISSION_REPLAY
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) {
#else
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) {
#endif
pPlayerInfo.m_WBState = WBSTATE_PLAYING;
int takeMoney;
switch (pPlayerInfo.m_pPed->m_pWanted->GetWantedLevel()) {
case 0:
case 1:
takeMoney = 100;
break;
case 2:
takeMoney = 200;
break;
case 3:
takeMoney = 400;
break;
case 4:
takeMoney = 600;
break;
case 5:
takeMoney = 900;
break;
case 6:
takeMoney = 1500;
break;
}
if (pPlayerInfo.m_bGetOutOfJailFree) {
pPlayerInfo.m_bGetOutOfJailFree = false;
} else {
pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - takeMoney);
pPlayerInfo.m_pPed->ClearWeapons();
}
if (pPlayerInfo.m_pPed->bInVehicle) {
CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle;
if (pVehicle != nil) {
if (pVehicle->pDriver == pPlayerInfo.m_pPed) {
pVehicle->pDriver = nil;
if (pVehicle->GetStatus() != STATUS_WRECKED)
pVehicle->SetStatus(STATUS_ABANDONED);
}
else
pVehicle->RemovePassenger(pPlayerInfo.m_pPed);
}
}
CEventList::Initialise();
#ifdef SCREEN_DROPLETS
ScreenDroplets::Initialise();
#endif
CMessages::ClearMessages();
CCarCtrl::ClearInterestingVehicleList();
CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1);
CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat);
CRestart::OverrideHospitalLevel = LEVEL_GENERIC;
CRestart::OverridePoliceStationLevel = LEVEL_GENERIC;
PassTime(720);
RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat);
pPlayerInfo.m_pPed->ClearWeapons();
SortOutStreamingAndMemory(pPlayerInfo.GetPos());
TheCamera.m_fCamShakeForce = 0.0f;
TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE);
CPad::GetPad(0)->StopShaking(0);
CReferences::RemoveReferencesToPlayer();
CCarCtrl::CountDownToCarsAtStart = 2;
CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED;
if (CRestart::bFadeInAfterNextArrest) {
TheCamera.SetFadeColour(0, 0, 0);
TheCamera.Fade(4.0f, FADE_IN);
} else CRestart::bFadeInAfterNextArrest = true;
}
break;
case WBSTATE_FAILED_CRITICAL_MISSION:
#ifdef MISSION_REPLAY
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) {
#else
if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) {
#endif
TheCamera.SetFadeColour(0, 0, 0);
TheCamera.Fade(2.0f, FADE_OUT);
}
#ifdef MISSION_REPLAY
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) {
#else
if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) {
#endif
pPlayerInfo.m_WBState = WBSTATE_PLAYING;
if (pPlayerInfo.m_pPed->bInVehicle) {
CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle;
if (pVehicle != nil) {
if (pVehicle->pDriver == pPlayerInfo.m_pPed) {
pVehicle->pDriver = nil;
if (pVehicle->GetStatus() != STATUS_WRECKED)
pVehicle->SetStatus(STATUS_ABANDONED);
} else
pVehicle->RemovePassenger(pPlayerInfo.m_pPed);
}
}
CEventList::Initialise();
#ifdef SCREEN_DROPLETS
ScreenDroplets::Initialise();
#endif
CMessages::ClearMessages();
CCarCtrl::ClearInterestingVehicleList();
CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1);
CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat);
CRestart::OverridePoliceStationLevel = LEVEL_GENERIC;
CRestart::OverrideHospitalLevel = LEVEL_GENERIC;
RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat);
SortOutStreamingAndMemory(pPlayerInfo.GetPos());
TheCamera.m_fCamShakeForce = 0.0f;
TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE);
CPad::GetPad(0)->StopShaking(0);
CReferences::RemoveReferencesToPlayer();
CCarCtrl::CountDownToCarsAtStart = 2;
CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED;
TheCamera.SetFadeColour(0, 0, 0);
TheCamera.Fade(4.0f, FADE_IN);
}
break;
case 4:
return;
}
}
void
CGameLogic::RestorePlayerStuffDuringResurrection(CPlayerPed *pPlayerPed, CVector pos, float angle)
{
pPlayerPed->m_fHealth = 100.0f;
pPlayerPed->m_fArmour = 0.0f;
pPlayerPed->bIsVisible = true;
pPlayerPed->m_bloodyFootprintCountOrDeathTime = 0;
pPlayerPed->bDoBloodyFootprints = false;
pPlayerPed->ClearAdrenaline();
pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina;
if (pPlayerPed->m_pFire)
pPlayerPed->m_pFire->Extinguish();
pPlayerPed->bInVehicle = false;
pPlayerPed->m_pMyVehicle = nil;
pPlayerPed->m_pVehicleAnim = nil;
pPlayerPed->m_pWanted->Reset();
pPlayerPed->RestartNonPartialAnims();
pPlayerPed->GetPlayerInfoForThisPlayerPed()->MakePlayerSafe(false);
pPlayerPed->bRemoveFromWorld = false;
pPlayerPed->ClearWeaponTarget();
pPlayerPed->SetInitialState();
CCarCtrl::ClearInterestingVehicleList();
pos.z += 1.0f;
pPlayerPed->Teleport(pos);
pPlayerPed->SetMoveSpeed(CVector(0.0f, 0.0f, 0.0f));
pPlayerPed->m_fRotationCur = DEGTORAD(angle);
pPlayerPed->m_fRotationDest = pPlayerPed->m_fRotationCur;
pPlayerPed->SetHeading(pPlayerPed->m_fRotationCur);
CTheScripts::ClearSpaceForMissionEntity(pos, pPlayerPed);
CWorld::ClearExcitingStuffFromArea(pos, 4000.0, 1);
pPlayerPed->RestoreHeadingRate();
TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString();
CReferences::RemoveReferencesToPlayer();
CGarages::PlayerArrestedOrDied();
CStats::CheckPointReachedUnsuccessfully();
CWorld::Remove(pPlayerPed);
CWorld::Add(pPlayerPed);
}

13
src/control/GameLogic.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
class CGameLogic
{
public:
static void InitAtStartOfGame();
static void PassTime(uint32 time);
static void SortOutStreamingAndMemory(const CVector &pos);
static void Update();
static void RestorePlayerStuffDuringResurrection(class CPlayerPed *pPlayerPed, CVector pos, float angle);
static uint8 ActivePlayers;
};

2548
src/control/Garages.cpp Normal file

File diff suppressed because it is too large Load diff

263
src/control/Garages.h Normal file
View file

@ -0,0 +1,263 @@
#pragma once
#include "audio_enums.h"
#include "Camera.h"
#include "config.h"
#include "Lists.h"
class CVehicle;
enum eGarageState
{
GS_FULLYCLOSED,
GS_OPENED,
GS_CLOSING,
GS_OPENING,
GS_OPENEDCONTAINSCAR,
GS_CLOSEDCONTAINSCAR,
GS_AFTERDROPOFF,
};
enum eGarageType
{
GARAGE_NONE,
GARAGE_MISSION,
GARAGE_BOMBSHOP1,
GARAGE_BOMBSHOP2,
GARAGE_BOMBSHOP3,
GARAGE_RESPRAY,
GARAGE_COLLECTORSITEMS,
GARAGE_COLLECTSPECIFICCARS,
GARAGE_COLLECTCARS_1,
GARAGE_COLLECTCARS_2,
GARAGE_COLLECTCARS_3,
GARAGE_FORCARTOCOMEOUTOF,
GARAGE_60SECONDS,
GARAGE_CRUSHER,
GARAGE_MISSION_KEEPCAR,
GARAGE_FOR_SCRIPT_TO_OPEN,
GARAGE_HIDEOUT_ONE,
GARAGE_HIDEOUT_TWO,
GARAGE_HIDEOUT_THREE,
GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE,
GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR,
GARAGE_MISSION_KEEPCAR_REMAINCLOSED,
};
enum
{
TOTAL_COLLECTCARS_GARAGES = GARAGE_COLLECTCARS_3 - GARAGE_COLLECTCARS_1 + 1,
TOTAL_COLLECTCARS_CARS = 16
};
class CStoredCar
{
enum {
FLAG_BULLETPROOF = 0x1,
FLAG_FIREPROOF = 0x2,
FLAG_EXPLOSIONPROOF = 0x4,
FLAG_COLLISIONPROOF = 0x8,
FLAG_MELEEPROOF = 0x10,
};
int32 m_nModelIndex;
CVector m_vecPos;
CVector m_vecAngle;
int32 m_nFlags;
int8 m_nPrimaryColor;
int8 m_nSecondaryColor;
int8 m_nRadioStation;
int8 m_nVariationA;
int8 m_nVariationB;
int8 m_nCarBombType;
public:
void Init() { m_nModelIndex = 0; }
void Clear() { m_nModelIndex = 0; }
bool HasCar() { return m_nModelIndex != 0; }
const CStoredCar &operator=(const CStoredCar& other);
void StoreCar(CVehicle*);
CVehicle* RestoreCar();
};
VALIDATE_SIZE(CStoredCar, 0x28);
#define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f
#define CRUSHER_GARAGE_X1 (1135.5f)
#define CRUSHER_GARAGE_Y1 (57.0f)
#define CRUSHER_GARAGE_Z1 (-1.0f)
#define CRUSHER_GARAGE_X2 (1149.5f)
#define CRUSHER_GARAGE_Y2 (63.7f)
#define CRUSHER_GARAGE_Z2 (3.5f)
class CGarage
{
public:
uint8 m_eGarageType;
uint8 m_eGarageState;
bool field_2; // unused
bool m_bClosingWithoutTargetCar;
bool m_bDeactivated;
bool m_bResprayHappened;
int32 m_nTargetModelIndex;
CEntity *m_pDoor1;
CEntity *m_pDoor2;
uint8 m_bDoor1PoolIndex;
uint8 m_bDoor2PoolIndex;
bool m_bDoor1IsDummy;
bool m_bDoor2IsDummy;
bool m_bRecreateDoorOnNextRefresh;
bool m_bRotatedDoor;
bool m_bCameraFollowsPlayer;
float m_fX1;
float m_fX2;
float m_fY1;
float m_fY2;
float m_fZ1;
float m_fZ2;
float m_fDoorPos;
float m_fDoorHeight;
float m_fDoor1X;
float m_fDoor1Y;
float m_fDoor2X;
float m_fDoor2Y;
float m_fDoor1Z;
float m_fDoor2Z;
uint32 m_nTimeToStartAction;
uint8 m_bCollectedCarsState;
CVehicle *m_pTarget;
void* field_96; // unused
CStoredCar m_sStoredCar; // not needed
void OpenThisGarage();
void CloseThisGarage();
bool IsOpen() { return m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENEDCONTAINSCAR; }
bool IsClosed() { return m_eGarageState == GS_FULLYCLOSED; }
bool IsUsed() { return m_eGarageType != GARAGE_NONE; }
void Update();
float GetGarageCenterX() { return (m_fX1 + m_fX2) / 2; }
float GetGarageCenterY() { return (m_fY1 + m_fY2) / 2; }
bool IsFar()
{
#ifdef FIX_BUGS
return Abs(TheCamera.GetPosition().x - GetGarageCenterX()) > SWITCH_GARAGE_DISTANCE_CLOSE ||
Abs(TheCamera.GetPosition().y - GetGarageCenterY()) > SWITCH_GARAGE_DISTANCE_CLOSE;
#else
return Abs(TheCamera.GetPosition().x - m_fX1) > SWITCH_GARAGE_DISTANCE_CLOSE ||
Abs(TheCamera.GetPosition().y - m_fY1) > SWITCH_GARAGE_DISTANCE_CLOSE;
#endif
}
void TidyUpGarageClose();
void TidyUpGarage();
void RefreshDoorPointers(bool);
void UpdateCrusherAngle();
void UpdateDoorsHeight();
bool IsEntityEntirelyInside3D(CEntity*, float);
bool IsEntityEntirelyOutside(CEntity*, float);
bool IsEntityEntirelyInside(CEntity*);
float CalcDistToGarageRectangleSquared(float, float);
float CalcSmallestDistToGarageDoorSquared(float, float);
bool IsAnyOtherCarTouchingGarage(CVehicle* pException);
bool IsStaticPlayerCarEntirelyInside();
bool IsPlayerOutsideGarage();
bool IsAnyCarBlockingDoor();
void CenterCarInGarage(CVehicle*);
bool DoesCraigNeedThisCar(int32);
bool MarkThisCarAsCollectedForCraig(int32);
bool HasCraigCollectedThisCar(int32);
bool IsGarageEmpty();
void UpdateCrusherShake(float, float);
int32 CountCarsWithCenterPointWithinGarage(CEntity* pException);
void RemoveCarsBlockingDoorNotInside();
void StoreAndRemoveCarsForThisHideout(CStoredCar*, int32);
bool RestoreCarsForThisHideout(CStoredCar*);
bool IsEntityTouching3D(CEntity*);
bool EntityHasASphereWayOutsideGarage(CEntity*, float);
bool IsAnyOtherPedTouchingGarage(CPed* pException);
void BuildRotatedDoorMatrix(CEntity*, float);
void FindDoorsEntities();
void FindDoorsEntitiesSectorList(CPtrList&, bool);
void PlayerArrestedOrDied();
bool Does60SecondsNeedThisCarAtAll(int mi);
bool Does60SecondsNeedThisCar(int mi);
void MarkThisCarAsCollectedFor60Seconds(int mi);
bool IsPlayerEntirelyInsideGarage();
};
VALIDATE_SIZE(CGarage, 140);
class CGarages
{
enum {
MESSAGE_LENGTH = 8
};
public:
static int32 BankVansCollected;
static bool BombsAreFree;
static bool RespraysAreFree;
static int32 CarsCollected;
static int32 CarTypesCollected[TOTAL_COLLECTCARS_GARAGES];
static int32 CrushedCarId;
static uint32 LastTimeHelpMessage;
static int32 MessageNumberInString;
static char MessageIDString[MESSAGE_LENGTH];
static int32 MessageNumberInString2;
static uint32 MessageStartTime;
static uint32 MessageEndTime;
static uint32 NumGarages;
static bool PlayerInGarage;
static int32 PoliceCarsCollected;
static CGarage aGarages[NUM_GARAGES];
static CStoredCar aCarsInSafeHouse1[NUM_GARAGE_STORED_CARS];
static CStoredCar aCarsInSafeHouse2[NUM_GARAGE_STORED_CARS];
static CStoredCar aCarsInSafeHouse3[NUM_GARAGE_STORED_CARS];
static bool bCamShouldBeOutisde;
static void Init(void);
#ifndef PS2
static void Shutdown(void);
#endif
static void Update(void);
static int16 AddOne(CVector pos1, CVector pos2, uint8 type, int32 targetId);
static void ChangeGarageType(int16, uint8, int32);
static void PrintMessages(void);
static void TriggerMessage(const char* text, int16, uint16 time, int16);
static void SetTargetCarForMissonGarage(int16, CVehicle*);
static bool HasCarBeenDroppedOffYet(int16);
static void DeActivateGarage(int16);
static void ActivateGarage(int16);
static int32 QueryCarsCollected(int16);
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 void SetLeaveCameraForThisGarage(int16);
static bool IsThisCarWithinGarageArea(int16, CEntity*);
static bool HasCarBeenCrushed(int32);
static bool IsPointInAGarageCameraZone(CVector);
static bool CameraShouldBeOutside(void);
static void GivePlayerDetonator(void);
static void PlayerArrestedOrDied(void);
static bool IsPointWithinHideOutGarage(Const CVector&);
static bool IsPointWithinAnyGarage(Const 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; }
static void StopCarFromBlowingUp(CAutomobile*);
static bool IsCarSprayable(CVehicle*);
static float FindDoorHeightForMI(int32);
static void CloseHideOutGaragesBeforeSave(void);
static int32 CountCarsInHideoutGarage(uint8);
static int32 FindMaxNumStoredCarsForGarage(uint8);
static int32 GetBombTypeForGarageType(uint8 type) { return type - GARAGE_BOMBSHOP1 + 1; }
static int32 GetCarsCollectedIndexForGarageType(uint8 type) { return type - GARAGE_COLLECTCARS_1; }
};

87
src/control/NameGrid.cpp Normal file
View file

@ -0,0 +1,87 @@
#include "common.h"
#include "NameGrid.h"
// TODO: reverse mobile code
CPlayerName::CPlayerName()
{
// TODO
}
void
CPlayerName::DisplayName(int)
{
// TODO
}
CRow::CRow()
{
// TODO
}
void
CRow::SetLetter(int, wchar *)
{
// TODO
}
CGrid::CGrid()
{
// TODO
}
void
CGrid::ProcessAnyLeftJustDown()
{
unk_int2--;
}
void
CGrid::ProcessAnyRightJustDown()
{
unk_int2++;
}
void
CGrid::ProcessAnyUpJustDown()
{
unk_int1--;
}
void
CGrid::ProcessAnyDownJustDown()
{
unk_int1++;
}
void
CGrid::AllDoneMakePlayerName()
{
// TODO
}
void
CGrid::ProcessDPadCrossJustDown()
{
// TODO
}
void
CGrid::DisplayGrid()
{
// TODO
}
void
CGrid::ProcessControllerInput()
{
// TODO
}
void
CGrid::Process()
{
ProcessControllerInput();
DisplayGrid();
playerName.DisplayName(2 * playerName.unk_4c);
}

53
src/control/NameGrid.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
// TODO: reverse mobile code
class CPlayerName
{
friend class CGrid;
float x;
float y;
wchar unk_8[34];
int unk_4c;
public:
CPlayerName();
void DisplayName(int);
};
class CRow
{
friend class CGrid;
int unk_0;
int unk_4;
wchar unk_8[20];
int unk_30;
public:
CRow();
void SetLetter(int, wchar *);
};
class CGrid
{
CRow rows[5];
int unk_int1;
int unk_int2;
int unk_int3;
float unk_float1;
float unk_float2;
CPlayerName playerName;
char unk2[4];
char unk3[4];
public:
CGrid();
void ProcessAnyLeftJustDown();
void ProcessAnyRightJustDown();
void ProcessAnyUpJustDown();
void ProcessAnyDownJustDown();
void AllDoneMakePlayerName();
void ProcessDPadCrossJustDown();
void DisplayGrid();
void ProcessControllerInput();
void Process();
};

View file

@ -0,0 +1,159 @@
#include "common.h"
#include "DMAudio.h"
#include "Hud.h"
#include "Replay.h"
#include "Timer.h"
#include "Script.h"
#include "OnscreenTimer.h"
void
COnscreenTimer::Init()
{
m_bDisabled = false;
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
m_sEntries[i].m_nTimerOffset = 0;
m_sEntries[i].m_nCounterOffset = 0;
for(uint32 j = 0; j < 10; j++) {
m_sEntries[i].m_aTimerText[j] = '\0';
m_sEntries[i].m_aCounterText[j] = '\0';
}
m_sEntries[i].m_nType = COUNTER_DISPLAY_NUMBER;
m_sEntries[i].m_bTimerProcessed = false;
m_sEntries[i].m_bCounterProcessed = false;
}
}
void
COnscreenTimer::Process()
{
if(!CReplay::IsPlayingBack() && !m_bDisabled)
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++)
m_sEntries[i].Process();
}
void
COnscreenTimer::ProcessForDisplay()
{
if(CHud::m_Wants_To_Draw_Hud) {
m_bProcessed = false;
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++)
if(m_sEntries[i].ProcessForDisplay())
m_bProcessed = true;
}
}
void
COnscreenTimer::ClearCounter(uint32 offset)
{
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
if(offset == m_sEntries[i].m_nCounterOffset) {
m_sEntries[i].m_nCounterOffset = 0;
m_sEntries[i].m_aCounterText[0] = '\0';
m_sEntries[i].m_nType = COUNTER_DISPLAY_NUMBER;
m_sEntries[i].m_bCounterProcessed = false;
}
}
}
void
COnscreenTimer::ClearClock(uint32 offset)
{
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++)
if(offset == m_sEntries[i].m_nTimerOffset) {
m_sEntries[i].m_nTimerOffset = 0;
m_sEntries[i].m_aTimerText[0] = '\0';
m_sEntries[i].m_bTimerProcessed = false;
}
}
void
COnscreenTimer::AddCounter(uint32 offset, uint16 type, char* text)
{
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++)
if(m_sEntries[i].m_nCounterOffset == 0) {
m_sEntries[i].m_nCounterOffset = offset;
if (text)
strncpy(m_sEntries[i].m_aCounterText, text, 10);
else
m_sEntries[i].m_aCounterText[0] = '\0';
m_sEntries[i].m_nType = type;
break;
}
}
void
COnscreenTimer::AddClock(uint32 offset, char* text)
{
for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++)
if(m_sEntries[i].m_nTimerOffset == 0) {
m_sEntries[i].m_nTimerOffset = offset;
if (text)
strncpy(m_sEntries[i].m_aTimerText, text, 10);
else
m_sEntries[i].m_aTimerText[0] = '\0';
break;
}
}
void
COnscreenTimerEntry::Process()
{
if(m_nTimerOffset == 0)
return;
int32* timerPtr = CTheScripts::GetPointerToScriptVariable(m_nTimerOffset);
int32 oldTime = *timerPtr;
int32 newTime = oldTime - int32(CTimer::GetTimeStepInMilliseconds());
if(newTime < 0) {
*timerPtr = 0;
m_bTimerProcessed = false;
m_nTimerOffset = 0;
m_aTimerText[0] = '\0';
} else {
*timerPtr = newTime;
int32 oldTimeSeconds = oldTime / 1000;
if(oldTimeSeconds < 12 && newTime / 1000 != oldTimeSeconds) {
DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, newTime / 1000);
}
}
}
bool
COnscreenTimerEntry::ProcessForDisplay()
{
m_bTimerProcessed = false;
m_bCounterProcessed = false;
if(m_nTimerOffset == 0 && m_nCounterOffset == 0)
return false;
if(m_nTimerOffset != 0) {
m_bTimerProcessed = true;
ProcessForDisplayClock();
}
if(m_nCounterOffset != 0) {
m_bCounterProcessed = true;
ProcessForDisplayCounter();
}
return true;
}
void
COnscreenTimerEntry::ProcessForDisplayClock()
{
uint32 time = *CTheScripts::GetPointerToScriptVariable(m_nTimerOffset);
sprintf(m_bTimerBuffer, "%02d:%02d", time / 1000 / 60,
time / 1000 % 60);
}
void
COnscreenTimerEntry::ProcessForDisplayCounter()
{
uint32 counter = *CTheScripts::GetPointerToScriptVariable(m_nCounterOffset);
sprintf(m_bCounterBuffer, "%d", counter);
}

View file

@ -0,0 +1,49 @@
#pragma once
enum
{
COUNTER_DISPLAY_NUMBER,
COUNTER_DISPLAY_BAR,
};
class COnscreenTimerEntry
{
public:
uint32 m_nTimerOffset;
uint32 m_nCounterOffset;
char m_aTimerText[10];
char m_aCounterText[10];
uint16 m_nType;
char m_bCounterBuffer[42];
char m_bTimerBuffer[42];
bool m_bTimerProcessed;
bool m_bCounterProcessed;
void Process();
bool ProcessForDisplay();
void ProcessForDisplayClock();
void ProcessForDisplayCounter();
};
VALIDATE_SIZE(COnscreenTimerEntry, 0x74);
class COnscreenTimer
{
public:
COnscreenTimerEntry m_sEntries[NUMONSCREENTIMERENTRIES];
bool m_bProcessed;
bool m_bDisabled;
void Init();
void Process();
void ProcessForDisplay();
void ClearCounter(uint32 offset);
void ClearClock(uint32 offset);
void AddCounter(uint32 offset, uint16 type, char* text);
void AddClock(uint32 offset, char* text);
};
VALIDATE_SIZE(COnscreenTimer, 0x78);

1830
src/control/PathFind.cpp Normal file

File diff suppressed because it is too large Load diff

232
src/control/PathFind.h Normal file
View file

@ -0,0 +1,232 @@
#pragma once
#include "Treadable.h"
class CVehicle;
class CPtrList;
enum
{
NodeTypeExtern = 1,
NodeTypeIntern = 2,
UseInRoadBlock = 1,
ObjectEastWest = 2,
};
enum
{
PATH_CAR = 0,
PATH_PED = 1,
};
enum
{
SWITCH_OFF = 0,
SWITCH_ON = 1,
};
enum
{
ROUTE_ADD_BLOCKADE = 0,
ROUTE_NO_BLOCKADE = 1
};
struct CPedPathNode
{
bool bBlockade;
uint8 nodeIdX;
uint8 nodeIdY;
int16 id;
CPedPathNode* prev;
CPedPathNode* next;
};
VALIDATE_SIZE(CPedPathNode, 0x10);
class CPedPath {
public:
static bool CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints);
static void AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList);
static void RemoveNodeFromList(CPedPathNode *pNode);
static void AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList);
static void AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition);
static void AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition);
};
struct CPathNode
{
CVector pos;
CPathNode *prev;
CPathNode *next;
int16 distance; // in path search
int16 objectIndex;
int16 firstLink;
uint8 numLinks;
uint8 unkBits : 2;
uint8 bDeadEnd : 1;
uint8 bDisabled : 1;
uint8 bBetweenLevels : 1;
int8 group;
CVector &GetPosition(void) { return pos; }
void SetPosition(const CVector &p) { pos = p; }
float GetX(void) { return pos.x; }
float GetY(void) { return pos.y; }
float GetZ(void) { return pos.z; }
CPathNode *GetPrev(void) { return prev; }
CPathNode *GetNext(void) { return next; }
void SetPrev(CPathNode *node) { prev = node; }
void SetNext(CPathNode *node) { next = node; }
};
union CConnectionFlags
{
uint8 flags;
struct {
uint8 bCrossesRoad : 1;
uint8 bTrafficLight : 1;
};
};
struct CCarPathLink
{
CVector2D pos;
CVector2D dir;
int16 pathNodeIndex;
int8 numLeftLanes;
int8 numRightLanes;
uint8 trafficLightType;
uint8 bBridgeLights : 1;
// more?
CVector2D &GetPosition(void) { return pos; }
CVector2D &GetDirection(void) { return dir; }
float GetX(void) { return pos.x; }
float GetY(void) { return pos.y; }
float GetDirX(void) { return dir.x; }
float GetDirY(void) { return dir.y; }
float OneWayLaneOffset()
{
if (numLeftLanes == 0)
return 0.5f - 0.5f * numRightLanes;
if (numRightLanes == 0)
return 0.5f - 0.5f * numLeftLanes;
return 0.5f;
}
};
// This is what we're reading from the files, only temporary
struct CPathInfoForObject
{
int16 x;
int16 y;
int16 z;
int8 type;
int8 next;
int8 numLeftLanes;
int8 numRightLanes;
uint8 crossing : 1;
};
extern CPathInfoForObject *InfoForTileCars;
extern CPathInfoForObject *InfoForTilePeds;
struct CTempNode
{
CVector pos;
float dirX;
float dirY;
int16 link1;
int16 link2;
int8 numLeftLanes;
int8 numRightLanes;
int8 linkState;
};
struct CTempDetachedNode // unused
{
uint8 foo[20];
};
class CPathFind
{
public:
CPathNode m_pathNodes[NUM_PATHNODES];
CCarPathLink m_carPathLinks[NUM_CARPATHLINKS];
CTreadable *m_mapObjects[NUM_MAPOBJECTS];
uint8 m_objectFlags[NUM_MAPOBJECTS];
int16 m_connections[NUM_PATHCONNECTIONS];
int16 m_distances[NUM_PATHCONNECTIONS];
CConnectionFlags m_connectionFlags[NUM_PATHCONNECTIONS];
int16 m_carPathConnections[NUM_PATHCONNECTIONS];
int32 m_numPathNodes;
int32 m_numCarPathNodes;
int32 m_numPedPathNodes;
int16 m_numMapObjects;
int16 m_numConnections;
int32 m_numCarPathLinks;
int32 unk;
uint8 m_numGroups[2];
CPathNode m_searchNodes[512];
void Init(void);
void AllocatePathFindInfoMem(int16 numPathGroups);
void RegisterMapObject(CTreadable *mapObject);
void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, bool crossing);
void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, int16 width, int8 numLeft, int8 numRight);
void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out);
bool LoadPathFindData(void);
void PreparePathData(void);
void CountFloodFillGroups(uint8 type);
void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float maxdist, CTempDetachedNode *detachednodes, int32 numDetached);
bool IsPathObject(int id) { return id < PATHNODESIZE && (InfoForTileCars[id*12].type != 0 || InfoForTilePeds[id*12].type != 0); }
float CalcRoadDensity(float x, float y);
bool TestForPedTrafficLight(CPathNode *n1, CPathNode *n2);
bool TestCrossesRoad(CPathNode *n1, CPathNode *n2);
void AddNodeToList(CPathNode *node, int32 listId);
void RemoveNodeFromList(CPathNode *node);
void RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n);
void SetLinksBridgeLights(float, float, float, float, bool);
void SwitchOffNodeAndNeighbours(int32 nodeId, bool disable);
void SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable);
void SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable);
void SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable);
void MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId);
void MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2);
void PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2);
int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled = false, bool ignoreBetweenLevels = false);
int32 FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY);
float FindNodeOrientationForCarPlacement(int32 nodeId);
float FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards);
bool NewGenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled = false);
bool GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix);
CTreadable *FindRoadObjectClosestToCoors(CVector coors, uint8 type);
void FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*);
void DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *numNodes, int16 maxNumNodes, CVehicle *vehicle, float *dist, float distLimit, int32 forcedTargetNode);
bool TestCoorsCloseness(CVector target, uint8 type, CVector start);
void Save(uint8 *buf, uint32 *size);
void Load(uint8 *buf, uint32 size);
uint16 ConnectedNode(int id) { return m_connections[id]; }
bool ConnectionCrossesRoad(int id) { return m_connectionFlags[id].bCrossesRoad; }
bool ConnectionHasTrafficLight(int id) { return m_connectionFlags[id].bTrafficLight; }
void ConnectionSetTrafficLight(int id) { m_connectionFlags[id].bTrafficLight = true; }
void DisplayPathData(void);
};
VALIDATE_SIZE(CPathFind, 0x49bf4);
extern CPathFind ThePaths;
extern bool gbShowPedPaths;
extern bool gbShowCarPaths;
extern bool gbShowCarPathsLinks;

497
src/control/Phones.cpp Normal file
View file

@ -0,0 +1,497 @@
#include "common.h"
#include "Phones.h"
#include "Pools.h"
#include "ModelIndices.h"
#include "Ped.h"
#include "Pad.h"
#include "Messages.h"
#include "Camera.h"
#include "World.h"
#include "General.h"
#include "AudioScriptObject.h"
#include "RpAnimBlend.h"
#include "AnimBlendAssociation.h"
#include "soundlist.h"
#include "SaveBuf.h"
#ifdef FIX_BUGS
#include "Replay.h"
#endif
#ifdef COMPATIBLE_SAVES
#define PHONEINFO_SAVE_SIZE 0xA30
#else
#define PHONEINFO_SAVE_SIZE sizeof(CPhoneInfo)
#endif
CPhoneInfo gPhoneInfo;
bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up
uint32 CPhoneInfo::PhoneEnableControlsTimer;
CPhone *CPhoneInfo::pPhoneDisplayingMessages;
bool CPhoneInfo::bPickingUpPhone;
CPed *CPhoneInfo::pCallBackPed; // ped who picking up the phone (reset after pickup cb)
/*
Entering phonebooth cutscene, showing messages and triggering these things
by checking coordinates happens in here - blue mission marker is cosmetic.
Repeated message means after the script set the messages for a particular phone,
player can pick the phone again with the same messages appearing,
after 60 seconds of last phone pick-up.
*/
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
CPed* crimeReporters[NUMPHONES] = {};
bool
isPhoneAvailable(int m_phoneId)
{
return crimeReporters[m_phoneId] == nil || !crimeReporters[m_phoneId]->IsPointerValid() || crimeReporters[m_phoneId]->m_objective > OBJECTIVE_WAIT_ON_FOOT ||
(crimeReporters[m_phoneId]->m_nPedState != PED_MAKE_CALL && crimeReporters[m_phoneId]->m_nPedState != PED_FACE_PHONE && crimeReporters[m_phoneId]->m_nPedState != PED_SEEK_POS);
}
#endif
void
CPhoneInfo::Update(void)
{
#ifdef FIX_BUGS
if (CReplay::IsPlayingBack())
return;
#endif
CPlayerPed *player = FindPlayerPed();
CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus];
if (bDisplayingPhoneMessage && CTimer::GetTimeInMilliseconds() > PhoneEnableControlsTimer) {
playerInfo->MakePlayerSafe(false);
TheCamera.SetWideScreenOff();
pPhoneDisplayingMessages = nil;
bDisplayingPhoneMessage = false;
CAnimBlendAssociation *talkAssoc = RpAnimBlendClumpGetAssociation(player->GetClump(), ANIM_STD_PHONE_TALK);
if (talkAssoc && talkAssoc->blendAmount > 0.5f) {
CAnimBlendAssociation *endAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_OUT, 8.0f);
endAssoc->flags &= ~ASSOC_DELETEFADEDOUT;
endAssoc->SetFinishCallback(PhonePutDownCB, player);
} else {
CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE);
if (player->m_nPedState == PED_MAKE_CALL)
player->SetPedState(PED_IDLE);
}
}
bool notInCar;
CVector playerPos;
if (FindPlayerVehicle()) {
notInCar = false;
playerPos = FindPlayerVehicle()->GetPosition();
} else {
notInCar = true;
playerPos = player->GetPosition();
}
bool phoneRings = false;
bool scratchTheCabinet;
for(int phoneId = 0; phoneId < m_nScriptPhonesMax; phoneId++) {
if (m_aPhones[phoneId].m_visibleToCam) {
switch (m_aPhones[phoneId].m_nState) {
case PHONE_STATE_ONETIME_MESSAGE_SET:
case PHONE_STATE_REPEATED_MESSAGE_SET:
case PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE:
if (bPickingUpPhone) {
scratchTheCabinet = false;
phoneRings = false;
} else {
scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1;
phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1;
}
if (scratchTheCabinet) {
m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f;
if (!phoneRings)
PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition());
} else {
m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f;
}
m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW();
m_aPhones[phoneId].m_pEntity->UpdateRwFrame();
if (notInCar && !bPickingUpPhone && player->IsPedInControl()) {
CVector2D distToPhone = playerPos - m_aPhones[phoneId].m_vecPos;
if (Abs(distToPhone.x) < 1.0f && Abs(distToPhone.y) < 1.0f) {
if (DotProduct2D(distToPhone, m_aPhones[phoneId].m_pEntity->GetForward()) / distToPhone.Magnitude() < -0.85f) {
CVector2D distToPhoneObj = playerPos - m_aPhones[phoneId].m_pEntity->GetPosition();
float angleToFace = CGeneral::GetATanOfXY(distToPhoneObj.x, distToPhoneObj.y) + HALFPI;
if (angleToFace > TWOPI)
angleToFace = angleToFace - TWOPI;
player->m_fRotationCur = angleToFace;
player->m_fRotationDest = angleToFace;
player->SetHeading(angleToFace);
player->SetPedState(PED_MAKE_CALL);
CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_PHONE);
TheCamera.SetWideScreenOn();
playerInfo->MakePlayerSafe(true);
CAnimBlendAssociation *phonePickAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_IN, 4.0f);
phonePickAssoc->SetFinishCallback(PhonePickUpCB, &m_aPhones[phoneId]);
bPickingUpPhone = true;
pCallBackPed = player;
}
}
}
break;
case PHONE_STATE_REPEATED_MESSAGE_STARTED:
if (CTimer::GetTimeInMilliseconds() - m_aPhones[phoneId].m_repeatedMessagePickupStart > 60000)
m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE;
break;
case PHONE_STATE_9:
scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1;
phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1;
if (scratchTheCabinet) {
m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f;
if (!phoneRings)
PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition());
} else {
m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f;
}
m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW();
m_aPhones[phoneId].m_pEntity->UpdateRwFrame();
break;
default:
break;
}
if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() > sq(100.0f))
m_aPhones[phoneId].m_visibleToCam = false;
} else if (!((CTimer::GetFrameCounter() + m_aPhones[phoneId].m_pEntity->m_randomSeed) % 16)) {
if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() < sq(60.0f))
m_aPhones[phoneId].m_visibleToCam = true;
}
}
}
int
CPhoneInfo::FindNearestFreePhone(CVector *pos)
{
int nearestPhoneId = -1;
float nearestPhoneDist = 60.0f;
for (int phoneId = 0; phoneId < m_nMax; phoneId++) {
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
if (isPhoneAvailable(phoneId))
#else
if (gPhoneInfo.m_aPhones[phoneId].m_nState == PHONE_STATE_FREE)
#endif
{
float phoneDist = (m_aPhones[phoneId].m_vecPos - *pos).Magnitude2D();
if (phoneDist < nearestPhoneDist) {
nearestPhoneDist = phoneDist;
nearestPhoneId = phoneId;
}
}
}
return nearestPhoneId;
}
bool
CPhoneInfo::PhoneAtThisPosition(CVector pos)
{
for (int phoneId = 0; phoneId < m_nMax; phoneId++) {
if (pos.x == m_aPhones[phoneId].m_vecPos.x && pos.y == m_aPhones[phoneId].m_vecPos.y)
return true;
}
return false;
}
bool
CPhoneInfo::HasMessageBeenDisplayed(int phoneId)
{
if (bDisplayingPhoneMessage)
return false;
int state = m_aPhones[phoneId].m_nState;
return state == PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE ||
state == PHONE_STATE_ONETIME_MESSAGE_STARTED ||
state == PHONE_STATE_REPEATED_MESSAGE_STARTED;
}
bool
CPhoneInfo::IsMessageBeingDisplayed(int phoneId)
{
return pPhoneDisplayingMessages == &m_aPhones[phoneId];
}
#ifdef COMPATIBLE_SAVES
static inline void
LoadPhone(CPhone &phone, uint8 *&buf)
{
ReadSaveBuf(&phone.m_vecPos, buf);
SkipSaveBuf(buf, 6 * 4);
ReadSaveBuf<uint32>(&phone.m_repeatedMessagePickupStart, buf);
uint32 tmp;
ReadSaveBuf(&tmp, buf);
phone.m_pEntity = (CEntity*)(uintptr)tmp;
ReadSaveBuf<PhoneState>(&phone.m_nState, buf);
ReadSaveBuf<bool>(&phone.m_visibleToCam, buf);
SkipSaveBuf(buf, 3);
}
#endif
void
CPhoneInfo::Load(uint8 *buf, uint32 size)
{
INITSAVEBUF
int32 max, scriptPhonesMax;
ReadSaveBuf(&max, buf);
ReadSaveBuf(&scriptPhonesMax, buf);
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
m_nMax = Min(NUMPHONES, max);
m_nScriptPhonesMax = 0;
bool ignoreOtherPhones = false;
// We can do it without touching saves. We'll only load script phones, others are already loaded in Initialise
for (int i = 0; i < 50; i++) {
CPhone phoneToLoad;
#ifdef COMPATIBLE_SAVES
phoneToLoad.m_apMessages[0]=phoneToLoad.m_apMessages[1]=phoneToLoad.m_apMessages[2]=phoneToLoad.m_apMessages[3]=phoneToLoad.m_apMessages[4]=phoneToLoad.m_apMessages[5] = nil;
LoadPhone(phoneToLoad, buf);
#else
ReadSaveBuf(&phoneToLoad, buf);
#endif
if (ignoreOtherPhones)
continue;
if (i < scriptPhonesMax) {
if (i >= m_nMax) {
assert(0 && "Number of phones used by script exceeds the NUMPHONES or the stored phones in save file. Ignoring some phones");
ignoreOtherPhones = true;
continue;
}
SwapPhone(phoneToLoad.m_vecPos.x, phoneToLoad.m_vecPos.y, i);
m_aPhones[i] = phoneToLoad;
// It's saved as building pool index in save file, convert it to true entity
if (m_aPhones[i].m_pEntity) {
m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1);
}
} else
ignoreOtherPhones = true;
}
#else
m_nMax = max;
m_nScriptPhonesMax = scriptPhonesMax;
for (int i = 0; i < NUMPHONES; i++) {
#ifdef COMPATIBLE_SAVES
LoadPhone(m_aPhones[i], buf);
#else
ReadSaveBuf(&m_aPhones[i], buf);
#endif
// It's saved as building pool index in save file, convert it to true entity
if (m_aPhones[i].m_pEntity) {
m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1);
}
}
#endif
VALIDATESAVEBUF(size)
}
void
CPhoneInfo::SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6)
{
// If there is at least one message, it should be msg1.
if (msg1) {
m_aPhones[phoneId].m_apMessages[0] = msg1;
m_aPhones[phoneId].m_apMessages[1] = msg2;
m_aPhones[phoneId].m_apMessages[2] = msg3;
m_aPhones[phoneId].m_apMessages[3] = msg4;
m_aPhones[phoneId].m_apMessages[4] = msg5;
m_aPhones[phoneId].m_apMessages[5] = msg6;
m_aPhones[phoneId].m_nState = PHONE_STATE_ONETIME_MESSAGE_SET;
} else {
m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED;
}
}
void
CPhoneInfo::SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6)
{
// If there is at least one message, it should be msg1.
if (msg1) {
m_aPhones[phoneId].m_apMessages[0] = msg1;
m_aPhones[phoneId].m_apMessages[1] = msg2;
m_aPhones[phoneId].m_apMessages[2] = msg3;
m_aPhones[phoneId].m_apMessages[3] = msg4;
m_aPhones[phoneId].m_apMessages[4] = msg5;
m_aPhones[phoneId].m_apMessages[5] = msg6;
m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SET;
} else {
m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED;
}
}
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
void
CPhoneInfo::SwapPhone(float xPos, float yPos, int into)
{
// "into" should be in 0 - m_nScriptPhonesMax range
int nearestPhoneId = -1;
CVector pos(xPos, yPos, 0.0f);
float nearestPhoneDist = 1.0f;
for (int phoneId = m_nScriptPhonesMax; phoneId < m_nMax; phoneId++) {
float phoneDistance = (m_aPhones[phoneId].m_vecPos - pos).Magnitude2D();
if (phoneDistance < nearestPhoneDist) {
nearestPhoneDist = phoneDistance;
nearestPhoneId = phoneId;
}
}
m_aPhones[nearestPhoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED;
CPhone oldPhone = m_aPhones[into];
m_aPhones[into] = m_aPhones[nearestPhoneId];
m_aPhones[nearestPhoneId] = oldPhone;
m_nScriptPhonesMax++;
}
#endif
int
CPhoneInfo::GrabPhone(float xPos, float yPos)
{
// "Grab" doesn't mean picking up the phone, it means allocating some particular phone to
// whoever called the 024A opcode first with the position parameters closest to phone.
// Same phone won't be available on next run of this function.
int nearestPhoneId = -1;
CVector pos(xPos, yPos, 0.0f);
float nearestPhoneDist = 100.0f;
for (int phoneId = m_nScriptPhonesMax; phoneId < m_nMax; phoneId++) {
float phoneDistance = (m_aPhones[phoneId].m_vecPos - pos).Magnitude2D();
if (phoneDistance < nearestPhoneDist) {
nearestPhoneDist = phoneDistance;
nearestPhoneId = phoneId;
}
}
m_aPhones[nearestPhoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED;
CPhone oldFirstPhone = m_aPhones[m_nScriptPhonesMax];
m_aPhones[m_nScriptPhonesMax] = m_aPhones[nearestPhoneId];
m_aPhones[nearestPhoneId] = oldFirstPhone;
m_nScriptPhonesMax++;
return m_nScriptPhonesMax - 1;
}
void
CPhoneInfo::Initialise(void)
{
CBuildingPool *pool = CPools::GetBuildingPool();
pCallBackPed = nil;
bDisplayingPhoneMessage = false;
bPickingUpPhone = false;
pPhoneDisplayingMessages = nil;
m_nMax = 0;
m_nScriptPhonesMax = 0;
for (int i = pool->GetSize() - 1; i >= 0; i--) {
CBuilding *building = pool->GetSlot(i);
if (building) {
if (building->GetModelIndex() == MI_PHONEBOOTH1) {
assert(m_nMax < ARRAY_SIZE(m_aPhones) && "NUMPHONES should be increased");
CPhone *maxPhone = &m_aPhones[m_nMax];
maxPhone->m_nState = PHONE_STATE_FREE;
maxPhone->m_vecPos = building->GetPosition();
maxPhone->m_pEntity = building;
m_nMax++;
}
}
}
}
void
CPhoneInfo::Save(uint8 *buf, uint32 *size)
{
*size = PHONEINFO_SAVE_SIZE;
INITSAVEBUF
WriteSaveBuf(buf, m_nMax);
WriteSaveBuf(buf, m_nScriptPhonesMax);
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
for (int phoneId = 0; phoneId < 50; phoneId++) { // We can do it without touching saves
#else
for (int phoneId = 0; phoneId < NUMPHONES; phoneId++) {
#endif
#ifdef COMPATIBLE_SAVES
WriteSaveBuf(buf, m_aPhones[phoneId].m_vecPos);
ZeroSaveBuf(buf, 6 * 4);
WriteSaveBuf(buf, m_aPhones[phoneId].m_repeatedMessagePickupStart);
// Convert entity pointer to building pool index while saving
int32 tmp = m_aPhones[phoneId].m_pEntity ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)m_aPhones[phoneId].m_pEntity) + 1 : 0;
WriteSaveBuf(buf, tmp);
WriteSaveBuf(buf, m_aPhones[phoneId].m_nState);
WriteSaveBuf(buf, m_aPhones[phoneId].m_visibleToCam);
ZeroSaveBuf(buf, 3);
#else
CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]);
// Convert entity pointer to building pool index while saving
if (phone->m_pEntity) {
phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1);
}
#endif
}
VALIDATESAVEBUF(*size)
}
void
CPhoneInfo::Shutdown(void)
{
m_nMax = 0;
m_nScriptPhonesMax = 0;
}
void
PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg)
{
assoc->flags |= ASSOC_DELETEFADEDOUT;
assoc->blendDelta = -1000.0f;
CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE);
CPed *ped = (CPed*)arg;
if (assoc->blendAmount > 0.5f)
ped->bUpdateAnimHeading = true;
if (ped->m_nPedState == PED_MAKE_CALL)
ped->SetPedState(PED_IDLE);
}
void
PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg)
{
CPhone *phone = (CPhone*)arg;
int messagesDisplayTime = 0;
for(int i=0; i < 6; i++) {
wchar *msg = phone->m_apMessages[i];
if (msg) {
CMessages::AddMessage(msg, 3000, 0);
messagesDisplayTime += 3000;
}
}
CPhoneInfo::bPickingUpPhone = false;
CPhoneInfo::bDisplayingPhoneMessage = true;
CPhoneInfo::pPhoneDisplayingMessages = phone;
CPhoneInfo::PhoneEnableControlsTimer = CTimer::GetTimeInMilliseconds() + messagesDisplayTime;
if (phone->m_nState == PHONE_STATE_ONETIME_MESSAGE_SET) {
phone->m_nState = PHONE_STATE_ONETIME_MESSAGE_STARTED;
} else {
phone->m_nState = PHONE_STATE_REPEATED_MESSAGE_STARTED;
phone->m_repeatedMessagePickupStart = CTimer::GetTimeInMilliseconds();
}
CPed *ped = CPhoneInfo::pCallBackPed;
ped->m_nMoveState = PEDMOVE_STILL;
CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE, 8.0f);
if (assoc->blendAmount > 0.5f && ped)
CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 8.0f);
CPhoneInfo::pCallBackPed = nil;
}

77
src/control/Phones.h Normal file
View file

@ -0,0 +1,77 @@
#pragma once
#include "Physical.h"
class CPed;
class CAnimBlendAssociation;
enum PhoneState {
PHONE_STATE_FREE,
PHONE_STATE_REPORTING_CRIME, // CCivilianPed::ProcessControl sets it but unused
PHONE_STATE_2,
PHONE_STATE_MESSAGE_REMOVED,
PHONE_STATE_ONETIME_MESSAGE_SET,
PHONE_STATE_REPEATED_MESSAGE_SET,
PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE,
PHONE_STATE_ONETIME_MESSAGE_STARTED,
PHONE_STATE_REPEATED_MESSAGE_STARTED,
PHONE_STATE_9 // just rings, picking being handled via script. most of the time game uses this
};
class CPhone
{
public:
CVector m_vecPos;
wchar *m_apMessages[6];
uint32 m_repeatedMessagePickupStart;
CEntity *m_pEntity; // stored as building pool index in save files
PhoneState m_nState;
bool m_visibleToCam;
CPhone() { }
~CPhone() { }
};
VALIDATE_SIZE(CPhone, 0x34);
class CPhoneInfo {
public:
static bool bDisplayingPhoneMessage;
static uint32 PhoneEnableControlsTimer;
static CPhone *pPhoneDisplayingMessages;
static bool bPickingUpPhone;
static CPed *pCallBackPed;
int32 m_nMax;
int32 m_nScriptPhonesMax;
CPhone m_aPhones[NUMPHONES];
CPhoneInfo() { }
~CPhoneInfo() { }
int FindNearestFreePhone(CVector*);
bool PhoneAtThisPosition(CVector);
bool HasMessageBeenDisplayed(int);
bool IsMessageBeingDisplayed(int);
void Load(uint8 *buf, uint32 size);
void Save(uint8 *buf, uint32 *size);
void SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6);
void SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6);
int GrabPhone(float, float);
void Initialise(void);
void Shutdown(void);
void Update(void);
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
void SwapPhone(float xPos, float yPos, int into);
#endif
};
extern CPhoneInfo gPhoneInfo;
void PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg);
void PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg);
#ifdef PEDS_REPORT_CRIMES_ON_PHONE
extern CPed *crimeReporters[NUMPHONES];
bool isPhoneAvailable(int);
#endif

1568
src/control/Pickups.cpp Normal file

File diff suppressed because it is too large Load diff

146
src/control/Pickups.h Normal file
View file

@ -0,0 +1,146 @@
#pragma once
#include "Weapon.h"
enum ePickupType
{
PICKUP_NONE = 0,
PICKUP_IN_SHOP,
PICKUP_ON_STREET,
PICKUP_ONCE,
PICKUP_ONCE_TIMEOUT,
PICKUP_COLLECTABLE1,
PICKUP_IN_SHOP_OUT_OF_STOCK,
PICKUP_MONEY,
PICKUP_MINE_INACTIVE,
PICKUP_MINE_ARMED,
PICKUP_NAUTICAL_MINE_INACTIVE,
PICKUP_NAUTICAL_MINE_ARMED,
PICKUP_FLOATINGPACKAGE,
PICKUP_FLOATINGPACKAGE_FLOATING,
PICKUP_ON_STREET_SLOW,
PICKUP_NUMOFTYPES
};
class CEntity;
class CObject;
class CVehicle;
class CPlayerPed;
class CPickup
{
public:
uint8 m_eType;
bool m_bRemoved;
uint16 m_nQuantity;
CObject *m_pObject;
uint32 m_nTimer;
int16 m_eModelIndex;
uint16 m_nIndex;
CVector m_vecPos;
CObject *GiveUsAPickUpObject(int32 handle);
bool Update(CPlayerPed *player, CVehicle *vehicle, int playerId);
private:
inline bool IsMine() { return m_eType >= PICKUP_MINE_INACTIVE && m_eType <= PICKUP_FLOATINGPACKAGE_FLOATING; }
inline bool CanBePickedUp(CPlayerPed *player);
inline void Remove();
};
VALIDATE_SIZE(CPickup, 0x1C);
struct tPickupMessage
{
CVector2D m_pos;
eWeaponType m_weaponType;
CVector2D m_dist;
CRGBA m_color;
uint8 m_bOutOfStock : 1;
uint8 m_quantity;
};
class CPickups
{
static int32 aPickUpsCollected[NUMCOLLECTEDPICKUPS];
static int16 CollectedPickUpIndex;
static int16 NumMessages;
static tPickupMessage aMessages[NUMPICKUPMESSAGES];
public:
static void Init();
static void Update();
static void RenderPickUpText();
static void DoCollectableEffects(CEntity *ent);
static void DoMoneyEffects(CEntity *ent);
static void DoMineEffects(CEntity *ent);
static void DoPickUpEffects(CEntity *ent);
static int32 GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quantity);
static int32 GenerateNewOne_WeaponType(CVector pos, eWeaponType weaponType, uint8 type, uint32 quantity);
static void RemovePickUp(int32 pickupIndex);
static void RemoveAllFloatingPickups();
static void AddToCollectedPickupsArray(int32 index);
static bool IsPickUpPickedUp(int32 pickupId);
static int32 ModelForWeapon(eWeaponType weaponType);
static enum eWeaponType WeaponForModel(int32 model);
static int32 FindColourIndexForWeaponMI(int32 model);
static int32 GetActualPickupIndex(int32 index);
static int32 GetNewUniquePickupIndex(int32 slot);
static void PassTime(uint32 time);
static bool GivePlayerGoodiesWithPickUpMI(int16 modelIndex, int playerIndex);
static void Load(uint8 *buf, uint32 size);
static void Save(uint8 *buf, uint32 *size);
static CPickup aPickUps[NUMPICKUPS];
// unused
static bool bPickUpcamActivated;
static CVehicle *pPlayerVehicle;
static CVector StaticCamCoors;
static uint32 StaticCamStartTime;
};
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 class CPacManPickup;
static CPacManPickup aPMPickUps[NUMPACMANPICKUPS];
static CVector LastPickUpCoors;
static int PillsEatenInRace;
static bool bPMActive;
public:
static void Init(void);
static void Update(void);
static void GeneratePMPickUps(CVector, float, int16, uint8);
static void GeneratePMPickUpsForRace(int32);
static void GenerateOnePMPickUp(CVector);
static void Render(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);
static void ResetPowerPillsCarriedByPlayer(void);
};

View file

@ -0,0 +1,22 @@
#include "common.h"
#include "PowerPoints.h"
// Some cut beta feature
void CPowerPoint::Update()
{}
void CPowerPoints::Init()
{}
void CPowerPoints::Update()
{}
void CPowerPoints::GenerateNewOne(float, float, float, float, float, float, uint8)
{}
void CPowerPoints::Save(uint8**, uint32*)
{}
void CPowerPoints::Load(uint8*, uint32)
{}

26
src/control/PowerPoints.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
enum
{
POWERPOINT_NONE = 0,
POWERPOINT_HEALTH,
POWERPOINT_HIDEOUT_INDUSTRIAL,
POWERPOINT_HIDEOUT_COMMERCIAL,
POWERPOINT_HIDEOUT_SUBURBAN
};
class CPowerPoint
{
public:
void Update();
};
class CPowerPoints
{
public:
static void Init();
static void Update();
static void GenerateNewOne(float, float, float, float, float, float, uint8);
static void Save(uint8**, uint32*);
static void Load(uint8*, uint32);
};

529
src/control/Record.cpp Normal file
View file

@ -0,0 +1,529 @@
#include "common.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"
#include "Frontend.h"
uint16 CRecordDataForGame::RecordingState;
uint8* CRecordDataForGame::pDataBuffer;
uint8* CRecordDataForGame::pDataBufferPointer;
int CRecordDataForGame::FId;
tGameBuffer CRecordDataForGame::pDataBufferForFrame;
#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;
int CRecordDataForChase::PositionChanges;
uint8 CRecordDataForChase::CurrentCar;
CAutomobile* CRecordDataForChase::pChaseCars[NUM_CHASE_CARS];
uint32 CRecordDataForChase::AnimStartTime;
float CRecordDataForChase::AnimTime;
CCarStateEachFrame* CRecordDataForChase::pBaseMemForCar[NUM_CHASE_CARS];
float CRecordDataForChase::TimeMultiplier;
int CRecordDataForChase::FId2;
#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]->SetPosition(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]->SetStatus(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]->GetMatrix().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]->GetMatrix().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();
}
}
bool CRecordDataForChase::ShouldThisPadBeLeftAlone(uint8 pad)
{
// may be wrong
if (Status == STATE_PLAYBACK_INIT) // this is useless but ps2 def checks if it's STATE_PLAYBACK_INIT
return false;
if (Status == STATE_RECORD)
return pad != 0;
return false;
}
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->SetPosition(pos);
pCar->SetStatus(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_GENERIC;
CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC);
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();
#ifdef NO_ISLAND_LOADING
if (CMenuManager::m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_LOW)
#endif
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);
}
void CRecordDataForChase::RemoveCarFromChase(int32 i)
{
if (!pChaseCars[i])
return;
CWorld::Remove(pChaseCars[i]);
delete pChaseCars[i];
pChaseCars[i] = nil;
}
CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32 i)
{
CVehicle* pVehicle = pChaseCars[i];
pChaseCars[i] = nil;
pVehicle->SetStatus(STATUS_PHYSICS);
return pVehicle;
}

104
src/control/Record.h Normal file
View file

@ -0,0 +1,104 @@
#pragma once
class CAutomobile;
class CVehicle;
class CControllerState;
class CCarStateEachFrame
{
public:
int16 velX;
int16 velY;
int16 velZ;
int8 rightX;
int8 rightY;
int8 rightZ;
int8 forwardX;
int8 forwardY;
int8 forwardZ;
int8 wheel;
int8 gas;
int8 brake;
bool handbrake;
CVector pos;
};
extern char gString[256];
class CRecordDataForChase
{
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 SaveOrRetrieveCarPositions(void);
static void StoreInfoForCar(CAutomobile*, CCarStateEachFrame*);
static void RestoreInfoForMatrix(CMatrix&, CCarStateEachFrame*);
static void RestoreInfoForCar(CAutomobile*, CCarStateEachFrame*, bool);
static void ProcessControlCars(void);
static bool ShouldThisPadBeLeftAlone(uint8 pad);
static void GiveUsACar(int32, CVector, float, CAutomobile**, uint8, uint8);
static void StartChaseScene(float);
static void CleanUpChaseScene(void);
static void SetUpCarsForChaseScene(void);
static void CleanUpCarsForChaseScene(void);
static void RemoveCarFromChase(int32);
static CVehicle* TurnChaseCarIntoScriptCar(int32);
};
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 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*);
};

51
src/control/Remote.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "common.h"
#include "Automobile.h"
#include "CarCtrl.h"
#include "Camera.h"
#include "Remote.h"
#include "Timer.h"
#include "World.h"
#include "PlayerInfo.h"
#include "Vehicle.h"
void
CRemote::GivePlayerRemoteControlledCar(float x, float y, float z, float rot, uint16 model)
{
CAutomobile *car = new CAutomobile(model, MISSION_VEHICLE);
bool found;
z = car->GetDistanceFromCentreOfMassToBaseOfModel() + CWorld::FindGroundZFor3DCoord(x, y, z + 2.0f, &found);
car->GetMatrix().SetRotateZOnly(rot);
car->SetPosition(x, y, z);
car->SetStatus(STATUS_PLAYER_REMOTE);
car->bIsLocked = true;
CCarCtrl::JoinCarWithRoadSystem(car);
car->AutoPilot.m_nCarMission = MISSION_NONE;
car->AutoPilot.m_nTempAction = TEMPACT_NONE;
car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS;
car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f;
car->AutoPilot.m_nNextLane = car->AutoPilot.m_nCurrentLane = 0;
car->bEngineOn = true;
CWorld::Add(car);
if (FindPlayerVehicle() != nil)
FindPlayerVehicle()->SetStatus(STATUS_PLAYER_DISABLED);
CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle = car;
CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->RegisterReference((CEntity**)&CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle);
TheCamera.TakeControl(car, CCam::MODE_BEHINDCAR, INTERPOLATION, CAMCONTROL_SCRIPT);
}
void
CRemote::TakeRemoteControlledCarFromPlayer(void)
{
CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->VehicleCreatedBy = RANDOM_VEHICLE;
CCarCtrl::NumMissionCars--;
CCarCtrl::NumRandomCars++;
CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bIsLocked = false;
CWorld::Players[CWorld::PlayerInFocus].m_nTimeLostRemoteCar = CTimer::GetTimeInMilliseconds();
CWorld::Players[CWorld::PlayerInFocus].m_bInRemoteMode = true;
CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bRemoveFromWorld = true;
}

8
src/control/Remote.h Normal file
View file

@ -0,0 +1,8 @@
#pragma once
class CRemote
{
public:
static void GivePlayerRemoteControlledCar(float, float, float, float, uint16);
static void TakeRemoteControlledCarFromPlayer(void);
};

1635
src/control/Replay.cpp Normal file

File diff suppressed because it is too large Load diff

329
src/control/Replay.h Normal file
View file

@ -0,0 +1,329 @@
#pragma once
#include "Pools.h"
#include "World.h"
#ifdef FIX_BUGS
#ifndef DONT_FIX_REPLAY_BUGS
#define FIX_REPLAY_BUGS
#endif
#endif
class CVehicle;
struct CReference;
struct CAddressInReplayBuffer
{
uint32 m_nOffset;
uint8 *m_pBase;
uint8 m_bSlot;
};
struct CStoredAnimationState
{
uint8 animId;
uint8 time;
uint8 speed;
uint8 secAnimId;
uint8 secTime;
uint8 secSpeed;
uint8 blendAmount;
uint8 partAnimId;
uint8 partAnimTime;
uint8 partAnimSpeed;
uint8 partBlendAmount;
};
enum {
NUM_MAIN_ANIMS_IN_REPLAY = 3,
NUM_PARTIAL_ANIMS_IN_REPLAY = 6
};
struct CStoredDetailedAnimationState
{
uint8 aAnimId[NUM_MAIN_ANIMS_IN_REPLAY];
uint8 aCurTime[NUM_MAIN_ANIMS_IN_REPLAY];
uint8 aSpeed[NUM_MAIN_ANIMS_IN_REPLAY];
uint8 aBlendAmount[NUM_MAIN_ANIMS_IN_REPLAY];
#ifdef FIX_REPLAY_BUGS
int8 aBlendDelta[NUM_MAIN_ANIMS_IN_REPLAY];
#endif
uint8 aFunctionCallbackID[NUM_MAIN_ANIMS_IN_REPLAY];
uint16 aFlags[NUM_MAIN_ANIMS_IN_REPLAY];
uint8 aAnimId2[NUM_PARTIAL_ANIMS_IN_REPLAY];
uint8 aCurTime2[NUM_PARTIAL_ANIMS_IN_REPLAY];
uint8 aSpeed2[NUM_PARTIAL_ANIMS_IN_REPLAY];
uint8 aBlendAmount2[NUM_PARTIAL_ANIMS_IN_REPLAY];
#ifdef FIX_REPLAY_BUGS
int8 aBlendDelta2[NUM_PARTIAL_ANIMS_IN_REPLAY];
#endif
uint8 aFunctionCallbackID2[NUM_PARTIAL_ANIMS_IN_REPLAY];
uint16 aFlags2[NUM_PARTIAL_ANIMS_IN_REPLAY];
};
#ifdef GTA_REPLAY
#define REPLAY_STUB
#else
#define REPLAY_STUB {}
#endif
class CReplay
{
enum {
MODE_RECORD = 0,
MODE_PLAYBACK = 1
};
enum {
REPLAYCAMMODE_ASSTORED = 0,
REPLAYCAMMODE_TOPDOWN = 1,
REPLAYCAMMODE_FIXED = 2
};
enum {
REPLAYPACKET_END = 0,
REPLAYPACKET_VEHICLE = 1,
REPLAYPACKET_PED_HEADER = 2,
REPLAYPACKET_PED_UPDATE = 3,
REPLAYPACKET_GENERAL = 4,
REPLAYPACKET_CLOCK = 5,
REPLAYPACKET_WEATHER = 6,
REPLAYPACKET_ENDOFFRAME = 7,
REPLAYPACKET_TIMER = 8,
REPLAYPACKET_BULLET_TRACES = 9
};
enum {
REPLAYBUFFER_UNUSED = 0,
REPLAYBUFFER_PLAYBACK = 1,
REPLAYBUFFER_RECORD = 2
};
enum {
NUM_REPLAYBUFFERS = 8,
REPLAYBUFFERSIZE = 100000
};
struct tGeneralPacket
{
uint8 type;
bool in_rcvehicle;
CMatrix camera_pos;
CVector player_pos;
};
VALIDATE_SIZE(tGeneralPacket, 88);
struct tClockPacket
{
uint8 type;
uint8 hours;
uint8 minutes;
private:
uint8 __align;
};
VALIDATE_SIZE(tClockPacket, 4);
struct tWeatherPacket
{
uint8 type;
uint8 old_weather;
uint8 new_weather;
float interpolation;
};
VALIDATE_SIZE(tWeatherPacket, 8);
struct tTimerPacket
{
uint8 type;
uint32 timer;
};
VALIDATE_SIZE(tTimerPacket, 8);
struct tPedHeaderPacket
{
uint8 type;
uint8 index;
uint16 mi;
uint8 pedtype;
private:
uint8 __align[3];
};
VALIDATE_SIZE(tPedHeaderPacket, 8);
struct tBulletTracePacket
{
uint8 type;
uint8 frames;
uint8 lifetime;
uint8 index;
CVector inf;
CVector sup;
};
VALIDATE_SIZE(tBulletTracePacket, 28);
struct tEndOfFramePacket
{
uint8 type;
private:
uint8 __align[3];
};
VALIDATE_SIZE(tEndOfFramePacket, 4);
struct tPedUpdatePacket
{
uint8 type;
uint8 index;
int8 heading;
int8 vehicle_index;
CStoredAnimationState anim_state;
CCompressedMatrixNotAligned matrix;
int8 assoc_group_id;
uint8 weapon_model;
};
VALIDATE_SIZE(tPedUpdatePacket, 40);
struct tVehicleUpdatePacket
{
uint8 type;
uint8 index;
uint8 health;
uint8 acceleration;
CCompressedMatrixNotAligned matrix;
int8 door_angles[2];
uint16 mi;
uint32 panels;
int8 velocityX;
int8 velocityY;
int8 velocityZ;
union {
int8 car_gun;
int8 wheel_state;
};
uint8 wheel_susp_dist[4];
uint8 wheel_rotation[4];
uint8 door_status;
uint8 primary_color;
uint8 secondary_color;
};
VALIDATE_SIZE(tVehicleUpdatePacket, 48);
private:
static uint8 Mode;
static CAddressInReplayBuffer Record;
static CAddressInReplayBuffer Playback;
static uint8* pBuf0;
static CAutomobile* pBuf1;
static uint8* pBuf2;
static CPlayerPed* pBuf3;
static uint8* pBuf4;
static CCutsceneHead* pBuf5;
static uint8* pBuf6;
static CPtrNode* pBuf7;
static uint8* pBuf8;
static CEntryInfoNode* pBuf9;
static uint8* pBuf10;
static CDummyPed* pBuf11;
static uint8* pRadarBlips;
static uint8* pStoredCam;
static uint8* pWorld1;
static CReference* pEmptyReferences;
static CStoredDetailedAnimationState* pPedAnims;
static uint8* pPickups;
static uint8* pReferences;
static uint8 BufferStatus[NUM_REPLAYBUFFERS];
static uint8 Buffers[NUM_REPLAYBUFFERS][REPLAYBUFFERSIZE];
static bool bPlayingBackFromFile;
static bool bReplayEnabled;
static uint32 SlowMotion;
static uint32 FramesActiveLookAroundCam;
static bool bDoLoadSceneWhenDone;
static CPtrNode* WorldPtrList;
static CPtrNode* BigBuildingPtrList;
static CWanted PlayerWanted;
static CPlayerInfo PlayerInfo;
static uint32 Time1;
static uint32 Time2;
static uint32 Time3;
static uint32 Time4;
static uint32 Frame;
static uint8 ClockHours;
static uint8 ClockMinutes;
static uint16 OldWeatherType;
static uint16 NewWeatherType;
static float WeatherInterpolationValue;
static float TimeStepNonClipped;
static float TimeStep;
static float TimeScale;
static float CameraFixedX;
static float CameraFixedY;
static float CameraFixedZ;
static int32 OldRadioStation;
static int8 CameraMode;
static bool bAllowLookAroundCam;
static float LoadSceneX;
static float LoadSceneY;
static float LoadSceneZ;
static float CameraFocusX;
static float CameraFocusY;
static float CameraFocusZ;
static bool bPlayerInRCBuggy;
static float fDistanceLookAroundCam;
static float fAlphaAngleLookAroundCam;
static float fBetaAngleLookAroundCam;
#ifdef FIX_BUGS
static uint8* pGarages;
static CFire* FireArray;
static uint32 NumOfFires;
static uint8* paProjectileInfo;
static uint8* paProjectiles;
static int nHandleOfPlayerPed[NUMPLAYERS];
#endif
public:
static void Init(void) REPLAY_STUB;
static void DisableReplays(void) REPLAY_STUB;
static void EnableReplays(void) REPLAY_STUB;
static void Update(void) REPLAY_STUB;
static void FinishPlayback(void) REPLAY_STUB;
static void EmptyReplayBuffer(void) REPLAY_STUB;
static void Display(void) REPLAY_STUB;
static void TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) REPLAY_STUB;
static void StreamAllNecessaryCarsAndPeds(void) REPLAY_STUB;
#ifndef GTA_REPLAY
static bool ShouldStandardCameraBeProcessed(void) { return true; }
static bool IsPlayingBack() { return false; }
static bool IsPlayingBackFromFile() { return false; }
#else
static bool ShouldStandardCameraBeProcessed(void);
static bool IsPlayingBack() { return Mode == MODE_PLAYBACK; }
static bool IsPlayingBackFromFile() { return bPlayingBackFromFile; }
private:
static void RecordThisFrame(void);
static void StorePedUpdate(CPed *ped, int id);
static void StorePedAnimation(CPed *ped, CStoredAnimationState *state);
static void StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state);
static void ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer);
static void RetrievePedAnimation(CPed *ped, CStoredAnimationState *state);
static void RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state);
static void PlaybackThisFrame(void);
static void TriggerPlaybackLastCoupleOfSeconds(uint32, uint8, float, float, float, uint32);
static bool FastForwardToTime(uint32);
static void StoreCarUpdate(CVehicle *vehicle, int id);
static void ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer);
static bool PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer);
static void ProcessReplayCamera(void);
static void StoreStuffInMem(void);
static void RestoreStuffFromMem(void);
static void EmptyPedsAndVehiclePools(void);
static void EmptyAllPools(void);
static void MarkEverythingAsNew(void);
static void SaveReplayToHD(void);
static void PlayReplayFromHD(void); // out of class in III PC and later because of SecuROM
static void FindFirstFocusCoordinate(CVector *coord);
static void ProcessLookAroundCam(void);
static size_t FindSizeOfPacket(uint8);
#endif
};

249
src/control/Restart.cpp Normal file
View file

@ -0,0 +1,249 @@
#include "common.h"
#include "Restart.h"
#include "SaveBuf.h"
#include "Zones.h"
#include "PathFind.h"
uint8 CRestart::OverrideHospitalLevel;
uint8 CRestart::OverridePoliceStationLevel;
bool CRestart::bFadeInAfterNextArrest;
bool CRestart::bFadeInAfterNextDeath;
bool CRestart::bOverrideRestart;
CVector CRestart::OverridePosition;
float CRestart::OverrideHeading;
CVector CRestart::HospitalRestartPoints[NUM_RESTART_POINTS];
float CRestart::HospitalRestartHeadings[NUM_RESTART_POINTS];
uint16 CRestart::NumberOfHospitalRestarts;
CVector CRestart::PoliceRestartPoints[NUM_RESTART_POINTS];
float CRestart::PoliceRestartHeadings[NUM_RESTART_POINTS];
uint16 CRestart::NumberOfPoliceRestarts;
void
CRestart::Initialise()
{
OverridePoliceStationLevel = LEVEL_GENERIC;
OverrideHospitalLevel = LEVEL_GENERIC;
bFadeInAfterNextArrest = true;
bFadeInAfterNextDeath = true;
OverrideHeading = 0.0f;
OverridePosition = CVector(0.0f, 0.0f, 0.0f);
bOverrideRestart = false;
NumberOfPoliceRestarts = 0;
NumberOfHospitalRestarts = 0;
for (int i = 0; i < NUM_RESTART_POINTS; i++) {
HospitalRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f);
HospitalRestartHeadings[i] = 0.0f;
PoliceRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f);
PoliceRestartHeadings[i] = 0.0f;
}
}
void
CRestart::AddHospitalRestartPoint(const CVector &pos, float heading)
{
HospitalRestartPoints[NumberOfHospitalRestarts] = pos;
HospitalRestartHeadings[NumberOfHospitalRestarts++] = heading;
}
void
CRestart::AddPoliceRestartPoint(const CVector &pos, float heading)
{
PoliceRestartPoints[NumberOfPoliceRestarts] = pos;
PoliceRestartHeadings[NumberOfPoliceRestarts++] = heading;
}
void
CRestart::OverrideNextRestart(const CVector &pos, float heading)
{
bOverrideRestart = true;
OverridePosition = pos;
OverrideHeading = heading;
}
void
CRestart::CancelOverrideRestart()
{
bOverrideRestart = false;
}
void
CRestart::FindClosestHospitalRestartPoint(const CVector &pos, CVector *outPos, float *outHeading)
{
if (bOverrideRestart) {
*outPos = OverridePosition;
*outHeading = OverrideHeading;
CancelOverrideRestart();
return;
}
eLevelName curlevel = CTheZones::FindZoneForPoint(pos);
float fMinDist = SQR(4000.0f);
int closestPoint = NUM_RESTART_POINTS;
// find closest point on this level
for (int i = 0; i < NumberOfHospitalRestarts; i++) {
if (CTheZones::FindZoneForPoint(HospitalRestartPoints[i]) == (OverrideHospitalLevel != LEVEL_GENERIC ? OverrideHospitalLevel : curlevel)) {
float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr();
if (fMinDist >= dist) {
fMinDist = dist;
closestPoint = i;
}
}
}
// if we didn't find anything, find closest point on any level
if (closestPoint == NUM_RESTART_POINTS) {
for (int i = 0; i < NumberOfHospitalRestarts; i++) {
float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr();
if (fMinDist >= dist) {
fMinDist = dist;
closestPoint = i;
}
}
}
// if we still didn't find anything, find closest path node
if (closestPoint == NUM_RESTART_POINTS) {
*outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition();
*outHeading = 0.0f;
printf("Couldn't find a hospital restart zone near the player %f %f %f->%f %f %f\n", pos.x, pos.y, pos.z, outPos->x, outPos->y, outPos->z);
} else {
*outPos = HospitalRestartPoints[closestPoint];
*outHeading = HospitalRestartHeadings[closestPoint];
}
}
void
CRestart::FindClosestPoliceRestartPoint(const CVector &pos, CVector *outPos, float *outHeading)
{
if (bOverrideRestart) {
*outPos = OverridePosition;
*outHeading = OverrideHeading;
CancelOverrideRestart();
return;
}
eLevelName curlevel = CTheZones::FindZoneForPoint(pos);
float fMinDist = SQR(4000.0f);
int closestPoint = NUM_RESTART_POINTS;
// find closest point on this level
for (int i = 0; i < NumberOfPoliceRestarts; i++) {
if (CTheZones::FindZoneForPoint(PoliceRestartPoints[i]) == (OverridePoliceStationLevel != LEVEL_GENERIC ? OverridePoliceStationLevel : curlevel)) {
float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr();
if (fMinDist >= dist) {
fMinDist = dist;
closestPoint = i;
}
}
}
// if we didn't find anything, find closest point on any level
if (closestPoint == NUM_RESTART_POINTS) {
for (int i = 0; i < NumberOfPoliceRestarts; i++) {
float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr();
if (fMinDist >= dist) {
fMinDist = dist;
closestPoint = i;
}
}
}
// if we still didn't find anything, find closest path node
if (closestPoint == NUM_RESTART_POINTS) {
printf("Couldn't find a police restart zone near the player\n");
*outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition();
*outHeading = 0.0f;
} else {
*outPos = PoliceRestartPoints[closestPoint];
*outHeading = PoliceRestartHeadings[closestPoint];
}
}
void
CRestart::LoadAllRestartPoints(uint8 *buf, uint32 size)
{
Initialise();
INITSAVEBUF
CheckSaveHeader(buf, 'R','S','T','\0', size - SAVE_HEADER_SIZE);
for (int i = 0; i < NUM_RESTART_POINTS; i++) {
ReadSaveBuf(&HospitalRestartPoints[i], buf);
ReadSaveBuf(&HospitalRestartHeadings[i], buf);
}
for (int i = 0; i < NUM_RESTART_POINTS; i++) {
ReadSaveBuf(&PoliceRestartPoints[i], buf);
ReadSaveBuf(&PoliceRestartHeadings[i], buf);
}
ReadSaveBuf(&NumberOfHospitalRestarts, buf);
ReadSaveBuf(&NumberOfPoliceRestarts, buf);
ReadSaveBuf(&bOverrideRestart, buf);
// skip something unused
SkipSaveBuf(buf, 3);
ReadSaveBuf(&OverridePosition, buf);
ReadSaveBuf(&OverrideHeading, buf);
ReadSaveBuf(&bFadeInAfterNextDeath, buf);
ReadSaveBuf(&bFadeInAfterNextArrest, buf);
ReadSaveBuf(&OverrideHospitalLevel, buf);
ReadSaveBuf(&OverridePoliceStationLevel, buf);
VALIDATESAVEBUF(size);
}
void
CRestart::SaveAllRestartPoints(uint8 *buf, uint32 *size)
{
*size = SAVE_HEADER_SIZE
+ sizeof(HospitalRestartPoints)
+ sizeof(HospitalRestartHeadings)
+ sizeof(PoliceRestartPoints)
+ sizeof(PoliceRestartHeadings)
+ sizeof(NumberOfHospitalRestarts)
+ sizeof(NumberOfPoliceRestarts)
+ sizeof(bOverrideRestart)
+ sizeof(uint8)
+ sizeof(uint16)
+ sizeof(OverridePosition)
+ sizeof(OverrideHeading)
+ sizeof(bFadeInAfterNextDeath)
+ sizeof(bFadeInAfterNextArrest)
+ sizeof(OverrideHospitalLevel)
+ sizeof(OverridePoliceStationLevel); // == 292
INITSAVEBUF
WriteSaveHeader(buf, 'R','S','T','\0', *size - SAVE_HEADER_SIZE);
for (int i = 0; i < NUM_RESTART_POINTS; i++) {
WriteSaveBuf(buf, HospitalRestartPoints[i]);
WriteSaveBuf(buf, HospitalRestartHeadings[i]);
}
for (int i = 0; i < NUM_RESTART_POINTS; i++) {
WriteSaveBuf(buf, PoliceRestartPoints[i]);
WriteSaveBuf(buf, PoliceRestartHeadings[i]);
}
WriteSaveBuf(buf, NumberOfHospitalRestarts);
WriteSaveBuf(buf, NumberOfPoliceRestarts);
WriteSaveBuf(buf, bOverrideRestart);
WriteSaveBuf(buf, (uint8)0);
WriteSaveBuf(buf, (uint16)0);
WriteSaveBuf(buf, OverridePosition);
WriteSaveBuf(buf, OverrideHeading);
WriteSaveBuf(buf, bFadeInAfterNextDeath);
WriteSaveBuf(buf, bFadeInAfterNextArrest);
WriteSaveBuf(buf, OverrideHospitalLevel);
WriteSaveBuf(buf, OverridePoliceStationLevel);
VALIDATESAVEBUF(*size);
}

36
src/control/Restart.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#define NUM_RESTART_POINTS 8
class CRestart
{
public:
static void AddPoliceRestartPoint(const CVector&, float);
static void AddHospitalRestartPoint(const CVector&, float);
static void OverrideNextRestart(const CVector&, float);
static void FindClosestHospitalRestartPoint(const CVector &, CVector *, float *);
static void FindClosestPoliceRestartPoint(const CVector &, CVector *, float *);
static void Initialise();
static void CancelOverrideRestart();
static void LoadAllRestartPoints(uint8 *buf, uint32 size);
static void SaveAllRestartPoints(uint8 *buf, uint32 *size);
static uint8 OverrideHospitalLevel;
static uint8 OverridePoliceStationLevel;
static bool bFadeInAfterNextArrest;
static bool bFadeInAfterNextDeath;
static bool bOverrideRestart;
static CVector OverridePosition;
static float OverrideHeading;
static CVector HospitalRestartPoints[NUM_RESTART_POINTS];
static float HospitalRestartHeadings[NUM_RESTART_POINTS];
static uint16 NumberOfHospitalRestarts;
static CVector PoliceRestartPoints[NUM_RESTART_POINTS];
static float PoliceRestartHeadings[NUM_RESTART_POINTS];
static uint16 NumberOfPoliceRestarts;
};

197
src/control/RoadBlocks.cpp Normal file
View file

@ -0,0 +1,197 @@
#include "common.h"
#include "RoadBlocks.h"
#include "PathFind.h"
#include "ModelIndices.h"
#include "Streaming.h"
#include "World.h"
#include "PedPlacement.h"
#include "Automobile.h"
#include "CopPed.h"
#include "VisibilityPlugins.h"
#include "PlayerPed.h"
#include "Wanted.h"
#include "Camera.h"
#include "CarCtrl.h"
#include "General.h"
#define ROADBLOCKDIST (80.0f)
int16 CRoadBlocks::NumRoadBlocks;
int16 CRoadBlocks::RoadBlockObjects[NUMROADBLOCKS];
bool CRoadBlocks::InOrOut[NUMROADBLOCKS];
void
CRoadBlocks::Init(void)
{
int i;
NumRoadBlocks = 0;
for (i = 0; i < ThePaths.m_numMapObjects; i++) {
if (ThePaths.m_objectFlags[i] & UseInRoadBlock) {
if (NumRoadBlocks < NUMROADBLOCKS) {
InOrOut[NumRoadBlocks] = true;
RoadBlockObjects[NumRoadBlocks] = i;
NumRoadBlocks++;
} else {
#ifndef MASTER
printf("Not enough room for the potential roadblocks\n");
#endif
// FIX: Don't iterate loop after NUMROADBLOCKS
return;
}
}
}
}
void
CRoadBlocks::GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType, int16 roadBlockNode)
{
static const CVector vecRoadBlockOffets[6] = { CVector(-1.5, 1.8f, 0.0f), CVector(-1.5f, -1.8f, 0.0f), CVector(1.5f, 1.8f, 0.0f),
CVector(1.5f, -1.8f, 0.0f), CVector(-1.5f, 0.0f, 0.0f), CVector(1.5, 0.0, 0.0) };
CEntity* pEntityToAttack = (CEntity*)FindPlayerVehicle();
if (!pEntityToAttack)
pEntityToAttack = (CEntity*)FindPlayerPed();
CColModel* pPoliceColModel = CModelInfo::GetColModel(MI_POLICE);
float fRadius = pVehicle->GetBoundRadius() / pPoliceColModel->boundingSphere.radius;
for (int32 i = 0; i < 2; i++) {
const int32 roadBlockIndex = i + 2 * roadBlockType;
CVector posForZ = pVehicle->GetMatrix() * (fRadius * vecRoadBlockOffets[roadBlockIndex]);
int32 modelInfoId = MI_COP;
eCopType copType = COP_STREET;
switch (pVehicle->GetModelIndex())
{
case MI_FBICAR:
modelInfoId = MI_FBI;
copType = COP_FBI;
break;
case MI_ENFORCER:
modelInfoId = MI_SWAT;
copType = COP_SWAT;
break;
case MI_BARRACKS:
modelInfoId = MI_ARMY;
copType = COP_ARMY;
break;
}
if (!CStreaming::HasModelLoaded(modelInfoId))
copType = COP_STREET;
CCopPed* pCopPed = new CCopPed(copType);
if (copType == COP_STREET)
pCopPed->SetCurrentWeapon(WEAPONTYPE_COLT45);
CPedPlacement::FindZCoorForPed(&posForZ);
pCopPed->SetPosition(posForZ);
pCopPed->SetOrientation(0.0f, 0.0f, -HALFPI);
pCopPed->m_bIsDisabledCop = true;
pCopPed->SetIdle();
pCopPed->bKindaStayInSamePlace = true;
pCopPed->bNotAllowedToDuck = false;
pCopPed->m_nRoadblockNode = roadBlockNode;
pCopPed->bCrouchWhenShooting = roadBlockType != 2;
if (pEntityToAttack) {
pCopPed->SetWeaponLockOnTarget(pEntityToAttack);
pCopPed->SetAttack(pEntityToAttack);
}
pCopPed->m_pMyVehicle = pVehicle;
pVehicle->RegisterReference((CEntity**)&pCopPed->m_pMyVehicle);
pCopPed->bCullExtraFarAway = true;
CVisibilityPlugins::SetClumpAlpha(pCopPed->GetClump(), 0);
CWorld::Add(pCopPed);
}
}
void
CRoadBlocks::GenerateRoadBlocks(void)
{
#ifdef SQUEEZE_PERFORMANCE
if (FindPlayerPed()->m_pWanted->m_RoadblockDensity == 0)
return;
#endif
CMatrix offsetMatrix;
uint32 frame = CTimer::GetFrameCounter() & 0xF;
int16 nRoadblockNode = (int16)(NUMROADBLOCKS * frame) / 16;
const int16 maxRoadBlocks = (int16)(NUMROADBLOCKS * (frame + 1)) / 16;
for (; nRoadblockNode < Min(NumRoadBlocks, maxRoadBlocks); nRoadblockNode++) {
CTreadable *mapObject = ThePaths.m_mapObjects[RoadBlockObjects[nRoadblockNode]];
CVector2D vecDistance = FindPlayerCoors() - mapObject->GetPosition();
if (vecDistance.x > -ROADBLOCKDIST && vecDistance.x < ROADBLOCKDIST &&
vecDistance.y > -ROADBLOCKDIST && vecDistance.y < ROADBLOCKDIST &&
vecDistance.Magnitude() < ROADBLOCKDIST) {
if (!InOrOut[nRoadblockNode]) {
InOrOut[nRoadblockNode] = true;
if (FindPlayerVehicle() && (CGeneral::GetRandomNumber() & 0x7F) < FindPlayerPed()->m_pWanted->m_RoadblockDensity) {
CWanted *pPlayerWanted = FindPlayerPed()->m_pWanted;
float fMapObjectRadius = 2.0f * mapObject->GetColModel()->boundingBox.max.x;
int32 vehicleId = MI_POLICE;
if (pPlayerWanted->AreArmyRequired())
vehicleId = MI_BARRACKS;
else if (pPlayerWanted->AreFbiRequired())
vehicleId = MI_FBICAR;
else if (pPlayerWanted->AreSwatRequired())
vehicleId = MI_ENFORCER;
if (!CStreaming::HasModelLoaded(vehicleId))
vehicleId = MI_POLICE;
CColModel *pVehicleColModel = CModelInfo::GetColModel(vehicleId);
float fModelRadius = 2.0f * pVehicleColModel->boundingSphere.radius + 0.25f;
int16 radius = (int16)(fMapObjectRadius / fModelRadius);
if (radius >= 6)
continue;
CVector2D vecDistanceToCamera = TheCamera.GetPosition() - mapObject->GetPosition();
float fDotProduct = DotProduct2D(vecDistanceToCamera, mapObject->GetForward());
float fOffset = 0.5f * fModelRadius * (float)(radius - 1);
for (int16 i = 0; i < radius; i++) {
uint8 nRoadblockType = fDotProduct < 0.0f;
if (CGeneral::GetRandomNumber() & 1) {
offsetMatrix.SetRotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f + HALFPI);
}
else {
nRoadblockType = !nRoadblockType;
offsetMatrix.SetRotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f - HALFPI);
}
if (ThePaths.m_objectFlags[RoadBlockObjects[nRoadblockNode]] & ObjectEastWest)
offsetMatrix.GetPosition() = CVector(0.0f, i * fModelRadius - fOffset, 0.6f);
else
offsetMatrix.GetPosition() = CVector(i * fModelRadius - fOffset, 0.0f, 0.6f);
CMatrix vehicleMatrix = mapObject->GetMatrix() * offsetMatrix;
float fModelRadius = CModelInfo::GetColModel(vehicleId)->boundingSphere.radius - 0.25f;
int16 colliding = 0;
CWorld::FindObjectsKindaColliding(vehicleMatrix.GetPosition(), fModelRadius, 0, &colliding, 2, nil, false, true, true, false, false);
if (!colliding) {
CAutomobile *pVehicle = new CAutomobile(vehicleId, RANDOM_VEHICLE);
pVehicle->SetStatus(STATUS_ABANDONED);
// pVehicle->GetHeightAboveRoad(); // called but return value is ignored?
vehicleMatrix.GetPosition().z += fModelRadius - 0.6f;
pVehicle->SetMatrix(vehicleMatrix);
pVehicle->PlaceOnRoadProperly();
pVehicle->SetIsStatic(false);
pVehicle->GetMatrix().UpdateRW();
pVehicle->m_nDoorLock = CARLOCK_UNLOCKED;
CCarCtrl::JoinCarWithRoadSystem(pVehicle);
pVehicle->bIsLocked = false;
pVehicle->AutoPilot.m_nCarMission = MISSION_NONE;
pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE;
pVehicle->AutoPilot.m_nCurrentLane = 0;
pVehicle->AutoPilot.m_nNextLane = 0;
pVehicle->AutoPilot.m_fMaxTrafficSpeed = 0.0f;
pVehicle->AutoPilot.m_nCruiseSpeed = 0.0f;
pVehicle->bExtendedRange = true;
if (pVehicle->UsesSiren(pVehicle->GetModelIndex()) && CGeneral::GetRandomNumber() & 1)
pVehicle->m_bSirenOrAlarm = true;
if (pVehicle->GetUp().z > 0.94f) {
CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0);
CWorld::Add(pVehicle);
pVehicle->bCreateRoadBlockPeds = true;
pVehicle->m_nRoadblockType = nRoadblockType;
pVehicle->m_nRoadblockNode = nRoadblockNode;
}
else {
delete pVehicle;
}
}
}
}
}
} else {
InOrOut[nRoadblockNode] = false;
}
}
}

16
src/control/RoadBlocks.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "common.h"
class CVehicle;
class CRoadBlocks
{
public:
static int16 NumRoadBlocks;
static int16 RoadBlockObjects[NUMROADBLOCKS];
static bool InOrOut[NUMROADBLOCKS];
static void Init(void);
static void GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType, int16 roadBlockNode);
static void GenerateRoadBlocks(void);
};

1127
src/control/SceneEdit.cpp Normal file

File diff suppressed because it is too large Load diff

96
src/control/SceneEdit.h Normal file
View file

@ -0,0 +1,96 @@
#pragma once
#ifdef GTA_SCENE_EDIT
class CPed;
class CVehicle;
struct CMovieCommand
{
int32 m_nCommandId;
CVector m_vecPosition;
CVector m_vecCamera;
int16 m_nActorId;
int16 m_nActor2Id;
int16 m_nVehicleId;
int16 m_nModelIndex;
};
class CSceneEdit
{
public:
enum {
MOVIE_DO_NOTHING = 0,
MOVIE_NEW_ACTOR,
MOVIE_MOVE_ACTOR,
MOVIE_SELECT_ACTOR,
MOVIE_DELETE_ACTOR,
MOVIE_NEW_VEHICLE,
MOVIE_MOVE_VEHICLE,
MOVIE_SELECT_VEHICLE,
MOVIE_DELETE_VEHICLE,
MOVIE_GIVE_WEAPON,
MOVIE_GOTO,
MOVIE_GOTO_WAIT,
MOVIE_GET_IN_CAR,
MOVIE_GET_OUT_CAR,
MOVIE_KILL,
MOVIE_FLEE,
MOVIE_WAIT,
MOVIE_POSITION_CAMERA,
MOVIE_SET_CAMERA_TARGET,
MOVIE_SELECT_CAMERA_MODE,
MOVIE_SAVE_MOVIE,
MOVIE_LOAD_MOVIE,
MOVIE_PLAY_MOVIE,
MOVIE_END,
MOVIE_TOTAL_COMMANDS
};
enum {
NUM_ACTORS_IN_MOVIE = 5,
NUM_VEHICLES_IN_MOVIE = 5,
NUM_COMMANDS_IN_MOVIE = 20
};
static int32 m_bCameraFollowActor;
static CVector m_vecCurrentPosition;
static CVector m_vecCamHeading;
static CVector m_vecGotoPosition;
static int32 m_nVehicle;
static int32 m_nVehicle2;
static int32 m_nActor;
static int32 m_nActor2;
static int32 m_nVehiclemodelId;
static int32 m_nPedmodelId;
static int16 m_nCurrentMovieCommand;
static int16 m_nCurrentCommand;
static int16 m_nCurrentVehicle;
static int16 m_nCurrentActor;
static bool m_bEditOn;
static bool m_bRecording;
static bool m_bCommandActive;
static bool m_bActorSelected;
static bool m_bActor2Selected;
static bool m_bVehicleSelected;
static int16 m_nNumActors;
static int16 m_nNumVehicles;
static int16 m_nNumMovieCommands;
static int16 m_nWeaponType;
static CPed* pActors[NUM_ACTORS_IN_MOVIE];
static CVehicle* pVehicles[NUM_VEHICLES_IN_MOVIE];
static bool m_bDrawGotoArrow;
static CMovieCommand Movie[NUM_COMMANDS_IN_MOVIE];
static void LoadMovie(void);
static void SaveMovie(void);
static void Initialise(void);
static void InitPlayback(void);
static void ReInitialise(void);
static void Update(void);
static void Draw(void);
static void ProcessCommand(void);
static void PlayBack(void);
static void ClearForNewCommand(void);
static void SelectActor(void);
static void SelectActor2(void);
static void SelectVehicle(void);
static bool SelectWeapon(void);
};
#endif

3014
src/control/Script.cpp Normal file

File diff suppressed because it is too large Load diff

630
src/control/Script.h Normal file
View file

@ -0,0 +1,630 @@
#pragma once
#include "Font.h"
#include "PedType.h"
#include "Text.h"
#include "Sprite2d.h"
class CEntity;
class CBuilding;
class CVehicle;
class CPed;
class CObject;
class CPlayerInfo;
class CRunningScript;
extern int32 ScriptParams[32];
void FlushLog();
#define script_assert(_Expression) FlushLog(); assert(_Expression);
#define PICKUP_PLACEMENT_OFFSET (0.5f)
#define PED_FIND_Z_OFFSET (5.0f)
#define UPSIDEDOWN_UP_THRESHOLD (-0.97f)
#define UPSIDEDOWN_MOVE_SPEED_THRESHOLD (0.01f)
#define UPSIDEDOWN_TURN_SPEED_THRESHOLD (0.02f)
#define UPSIDEDOWN_TIMER_THRESHOLD (1000)
#define SPHERE_MARKER_R (0)
#define SPHERE_MARKER_G (128)
#define SPHERE_MARKER_B (255)
#define SPHERE_MARKER_A (128)
#define SPHERE_MARKER_PULSE_PERIOD (2048)
#define SPHERE_MARKER_PULSE_FRACTION (0.1f)
#ifdef USE_PRECISE_MEASUREMENT_CONVERTION
#define MILES_IN_METER (0.000621371192f)
#define METERS_IN_FOOT (0.3048f)
#define FEET_IN_METER (3.28084f)
#else
#define MILES_IN_METER (1 / 1670.f)
#define METERS_IN_FOOT (0.3f)
#define FEET_IN_METER (3.33f)
#endif
#define KEY_LENGTH_IN_SCRIPT (8)
#if GTA_VERSION <= GTA3_PS2_160
#define GTA_SCRIPT_COLLECTIVE
#endif
struct intro_script_rectangle
{
bool m_bIsUsed;
bool m_bBeforeFade;
int16 m_nTextureId;
CRect m_sRect;
CRGBA m_sColor;
intro_script_rectangle() { }
~intro_script_rectangle() { }
};
VALIDATE_SIZE(intro_script_rectangle, 0x18);
enum {
SCRIPT_TEXT_MAX_LENGTH = 500
};
struct intro_text_line
{
float m_fScaleX;
float m_fScaleY;
CRGBA m_sColor;
bool m_bJustify;
bool m_bCentered;
bool m_bBackground;
bool m_bBackgroundOnly;
float m_fWrapX;
float m_fCenterSize;
CRGBA m_sBackgroundColor;
bool m_bTextProportional;
bool m_bTextBeforeFade;
bool m_bRightJustify;
int32 m_nFont;
float m_fAtX;
float m_fAtY;
wchar m_Text[SCRIPT_TEXT_MAX_LENGTH];
intro_text_line() { }
~intro_text_line() { }
void Reset()
{
m_fScaleX = 0.48f;
m_fScaleY = 1.12f;
m_sColor = CRGBA(225, 225, 225, 255);
m_bJustify = false;
m_bRightJustify = false;
m_bCentered = false;
m_bBackground = false;
m_bBackgroundOnly = false;
m_fWrapX = 182.0f;
m_fCenterSize = DEFAULT_SCREEN_WIDTH;
m_sBackgroundColor = CRGBA(128, 128, 128, 128);
m_bTextProportional = true;
m_bTextBeforeFade = false;
m_nFont = FONT_HEADING;
m_fAtX = 0.0f;
m_fAtY = 0.0f;
memset(&m_Text, 0, sizeof(m_Text));
}
};
VALIDATE_SIZE(intro_text_line, 0x414);
struct script_sphere_struct
{
bool m_bInUse;
uint16 m_Index;
uint32 m_Id;
CVector m_vecCenter;
float m_fRadius;
script_sphere_struct() { }
};
struct CStoredLine
{
CVector vecInf;
CVector vecSup;
uint32 color1;
uint32 color2;
};
enum {
CLEANUP_UNUSED = 0,
CLEANUP_CAR,
CLEANUP_CHAR,
CLEANUP_OBJECT
};
struct cleanup_entity_struct
{
uint8 type;
int32 id;
};
enum {
MAX_CLEANUP = 50,
MAX_UPSIDEDOWN_CAR_CHECKS = 6,
MAX_STUCK_CAR_CHECKS = 6
};
class CMissionCleanup
{
public:
cleanup_entity_struct m_sEntities[MAX_CLEANUP];
uint8 m_nCount;
CMissionCleanup();
void Init();
cleanup_entity_struct* FindFree();
void AddEntityToList(int32, uint8);
void RemoveEntityFromList(int32, uint8);
void Process();
};
struct upsidedown_car_data
{
int32 m_nVehicleIndex;
uint32 m_nUpsideDownTimer;
};
class CUpsideDownCarCheck
{
upsidedown_car_data m_sCars[MAX_UPSIDEDOWN_CAR_CHECKS];
public:
void Init();
bool IsCarUpsideDown(int32);
bool IsCarUpsideDown(CVehicle*);
void UpdateTimers();
bool AreAnyCarsUpsideDown();
void AddCarToCheck(int32);
void RemoveCarFromCheck(int32);
bool HasCarBeenUpsideDownForAWhile(int32);
};
struct stuck_car_data
{
int32 m_nVehicleIndex;
CVector m_vecPos;
int32 m_nLastCheck;
float m_fRadius;
uint32 m_nStuckTime;
bool m_bStuck;
stuck_car_data() { }
void Reset();
};
class CStuckCarCheck
{
stuck_car_data m_sCars[MAX_STUCK_CAR_CHECKS];
public:
void Init();
void Process();
void AddCarToCheck(int32, float, uint32);
void RemoveCarFromCheck(int32);
bool HasCarBeenStuckForAWhile(int32);
};
enum {
ARGUMENT_END = 0,
ARGUMENT_INT32,
ARGUMENT_GLOBALVAR,
ARGUMENT_LOCALVAR,
ARGUMENT_INT8,
ARGUMENT_INT16,
ARGUMENT_FLOAT
};
struct tCollectiveData
{
int32 colIndex;
int32 pedIndex;
};
enum {
USED_OBJECT_NAME_LENGTH = 24
};
struct tUsedObject
{
char name[USED_OBJECT_NAME_LENGTH];
int32 index;
};
struct tBuildingSwap
{
CBuilding* m_pBuilding;
int32 m_nNewModel;
int32 m_nOldModel;
};
enum {
#if GTA_VERSION > GTA3_PS2_160
MAX_STACK_DEPTH = 6,
#else
MAX_STACK_DEPTH = 4,
#endif
NUM_LOCAL_VARS = 16,
NUM_TIMERS = 2
};
class CRunningScript
{
enum {
ANDOR_NONE = 0,
ANDS_1 = 1,
ANDS_2,
ANDS_3,
ANDS_4,
ANDS_5,
ANDS_6,
ANDS_7,
ANDS_8,
ORS_1 = 21,
ORS_2,
ORS_3,
ORS_4,
ORS_5,
ORS_6,
ORS_7,
ORS_8
};
public:
CRunningScript* next;
CRunningScript* prev;
char m_abScriptName[8];
uint32 m_nIp;
uint32 m_anStack[MAX_STACK_DEPTH];
uint16 m_nStackPointer;
int32 m_anLocalVariables[NUM_LOCAL_VARS + NUM_TIMERS];
bool m_bCondResult;
bool m_bIsMissionScript;
bool m_bSkipWakeTime;
uint32 m_nWakeTime;
uint16 m_nAndOrState;
bool m_bNotFlag;
bool m_bDeatharrestEnabled;
bool m_bDeatharrestExecuted;
bool m_bMissionFlag;
public:
void SetIP(uint32 ip) { m_nIp = ip; }
CRunningScript* GetNext() const { return next; }
void Save(uint8*& buf);
void Load(uint8*& buf);
void UpdateTimers(float timeStep) {
m_anLocalVariables[NUM_LOCAL_VARS] += timeStep;
m_anLocalVariables[NUM_LOCAL_VARS + 1] += timeStep;
}
void Init();
void Process();
void RemoveScriptFromList(CRunningScript**);
void AddScriptToList(CRunningScript**);
static const uint32 nSaveStructSize;
void CollectParameters(uint32*, int16);
int32 CollectNextParameterWithoutIncreasingPC(uint32);
int32* GetPointerToScriptVariable(uint32*, int16);
void StoreParameters(uint32*, int16);
int8 ProcessOneCommand();
void DoDeatharrestCheck();
void UpdateCompareFlag(bool);
int16 GetPadState(uint16, uint16);
int8 ProcessCommands0To99(int32);
int8 ProcessCommands100To199(int32);
int8 ProcessCommands200To299(int32);
int8 ProcessCommands300To399(int32);
int8 ProcessCommands400To499(int32);
int8 ProcessCommands500To599(int32);
int8 ProcessCommands600To699(int32);
int8 ProcessCommands700To799(int32);
int8 ProcessCommands800To899(int32);
int8 ProcessCommands900To999(int32);
int8 ProcessCommands1000To1099(int32);
#if GTA_VERSION > GTA3_PS2_160
int8 ProcessCommands1100To1199(int32);
#endif
void LocatePlayerCommand(int32, uint32*);
void LocatePlayerCharCommand(int32, uint32*);
void LocatePlayerCarCommand(int32, uint32*);
void LocateCharCommand(int32, uint32*);
void LocateCharCharCommand(int32, uint32*);
void LocateCharCarCommand(int32, uint32*);
#if GTA_VERSION > GTA3_PS2_160
void LocateCharObjectCommand(int32, uint32*);
#endif
void LocateCarCommand(int32, uint32*);
#if GTA_VERSION > GTA3_PS2_160
void LocateSniperBulletCommand(int32, uint32*);
#endif
void PlayerInAreaCheckCommand(int32, uint32*);
void PlayerInAngledAreaCheckCommand(int32, uint32*);
void CharInAreaCheckCommand(int32, uint32*);
void CarInAreaCheckCommand(int32, uint32*);
#ifdef GTA_SCRIPT_COLLECTIVE
void LocateCollectiveCommand(int32, uint32*);
void LocateCollectiveCharCommand(int32, uint32*);
void LocateCollectiveCarCommand(int32, uint32*);
void LocateCollectivePlayerCommand(int32, uint32*);
void CollectiveInAreaCheckCommand(int32, uint32*);
#endif
#ifdef MISSION_REPLAY
bool CanAllowMissionReplay();
#endif
#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT
int CollectParameterForDebug(char* buf, bool& var);
void GetStoredParameterForDebug(char* buf);
void LogOnStartProcessing();
void LogBeforeProcessingCommand(int32 command);
void LogAfterProcessingCommand(int32 command);
static char commandInfo[];
static uint32 storedIp;
#endif
float LimitAngleOnCircle(float angle) { return angle < 0.0f ? angle + 360.0f : angle; }
bool ThisIsAValidRandomPed(uint32 pedtype) {
switch (pedtype) {
case PEDTYPE_CIVMALE:
case PEDTYPE_CIVFEMALE:
case PEDTYPE_GANG1:
case PEDTYPE_GANG2:
case PEDTYPE_GANG3:
case PEDTYPE_GANG4:
case PEDTYPE_GANG5:
case PEDTYPE_GANG6:
case PEDTYPE_GANG7:
case PEDTYPE_GANG8:
case PEDTYPE_GANG9:
case PEDTYPE_CRIMINAL:
case PEDTYPE_PROSTITUTE:
return true;
default:
return false;
}
}
};
enum {
VAR_LOCAL = 1,
VAR_GLOBAL = 2,
};
enum {
SIZE_MAIN_SCRIPT = 128 * 1024,
SIZE_MISSION_SCRIPT = 32 * 1024,
SIZE_SCRIPT_SPACE = SIZE_MAIN_SCRIPT + SIZE_MISSION_SCRIPT
};
enum {
MAX_NUM_SCRIPTS = 128,
MAX_NUM_CONTACTS = 16,
MAX_NUM_INTRO_TEXT_LINES = 2,
MAX_NUM_INTRO_RECTANGLES = 16,
MAX_NUM_SCRIPT_SRPITES = 16,
MAX_NUM_SCRIPT_SPHERES = 16,
MAX_NUM_COLLECTIVES = 32,
MAX_NUM_USED_OBJECTS = 200,
MAX_NUM_MISSION_SCRIPTS = 120,
MAX_NUM_BUILDING_SWAPS = 25,
MAX_NUM_INVISIBILITY_SETTINGS = 20,
MAX_NUM_STORED_LINES = 1024
};
class CTheScripts
{
public:
static uint8 ScriptSpace[SIZE_SCRIPT_SPACE];
static CRunningScript ScriptsArray[MAX_NUM_SCRIPTS];
static int32 BaseBriefIdForContact[MAX_NUM_CONTACTS];
static int32 OnAMissionForContactFlag[MAX_NUM_CONTACTS];
static intro_text_line IntroTextLines[MAX_NUM_INTRO_TEXT_LINES];
static intro_script_rectangle IntroRectangles[MAX_NUM_INTRO_RECTANGLES];
static CSprite2d ScriptSprites[MAX_NUM_SCRIPT_SRPITES];
static script_sphere_struct ScriptSphereArray[MAX_NUM_SCRIPT_SPHERES];
static tCollectiveData CollectiveArray[MAX_NUM_COLLECTIVES];
static tUsedObject UsedObjectArray[MAX_NUM_USED_OBJECTS];
static int32 MultiScriptArray[MAX_NUM_MISSION_SCRIPTS];
static tBuildingSwap BuildingSwapArray[MAX_NUM_BUILDING_SWAPS];
static CEntity* InvisibilitySettingArray[MAX_NUM_INVISIBILITY_SETTINGS];
static CStoredLine aStoredLines[MAX_NUM_STORED_LINES];
static bool DbgFlag;
static uint32 OnAMissionFlag;
static CMissionCleanup MissionCleanUp;
static CStuckCarCheck StuckCars;
static CUpsideDownCarCheck UpsideDownCars;
static int32 StoreVehicleIndex;
static bool StoreVehicleWasRandom;
static CRunningScript *pIdleScripts;
static CRunningScript *pActiveScripts;
static int32 NextFreeCollectiveIndex;
static int32 LastRandomPedId;
static uint16 NumberOfUsedObjects;
static bool bAlreadyRunningAMissionScript;
static bool bUsingAMultiScriptFile;
static uint16 NumberOfMissionScripts;
static uint32 LargestMissionScriptSize;
static uint32 MainScriptSize;
static uint8 FailCurrentMission;
static uint8 CountdownToMakePlayerUnsafe;
static uint8 DelayMakingPlayerUnsafeThisTime;
static uint16 NumScriptDebugLines;
static uint16 NumberOfIntroRectanglesThisFrame;
static uint16 NumberOfIntroTextLinesThisFrame;
static uint8 UseTextCommands;
static uint16 CommandsExecuted;
static uint16 ScriptsUpdated;
static void Init();
static void Process();
static CRunningScript* StartTestScript();
static bool IsPlayerOnAMission();
static void ClearSpaceForMissionEntity(const CVector&, CEntity*);
static void UndoBuildingSwaps();
static void UndoEntityInvisibilitySettings();
static void ScriptDebugLine3D(float x1, float y1, float z1, float x2, float y2, float z2, uint32 col, uint32 col2);
static void RenderTheScriptDebugLines();
static void SaveAllScripts(uint8*, uint32*);
static void LoadAllScripts(uint8*, uint32);
static bool IsDebugOn() { return DbgFlag; };
static void InvertDebugFlag() { DbgFlag = !DbgFlag; }
static int32* GetPointerToScriptVariable(int32 offset) { assert(offset >= 8 && offset < CTheScripts::GetSizeOfVariableSpace()); return (int32*)&ScriptSpace[offset]; }
static void ResetCountdownToMakePlayerUnsafe() { CountdownToMakePlayerUnsafe = 0; }
static bool IsCountdownToMakePlayerUnsafeOn() { return CountdownToMakePlayerUnsafe != 0; }
static int32 Read4BytesFromScript(uint32* pIp) {
int32 retval = ScriptSpace[*pIp + 3] << 24 | ScriptSpace[*pIp + 2] << 16 | ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp];
*pIp += 4;
return retval;
}
static int16 Read2BytesFromScript(uint32* pIp) {
int16 retval = ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp];
*pIp += 2;
return retval;
}
static int8 Read1ByteFromScript(uint32* pIp) {
int8 retval = ScriptSpace[*pIp];
*pIp += 1;
return retval;
}
static float ReadFloatFromScript(uint32* pIp) {
return Read2BytesFromScript(pIp) / 16.0f;
}
static void ReadTextLabelFromScript(uint32* pIp, char* buf) {
strncpy(buf, (const char*)&CTheScripts::ScriptSpace[*pIp], KEY_LENGTH_IN_SCRIPT);
}
static wchar* GetTextByKeyFromScript(uint32* pIp) {
wchar* text = TheText.Get((const char*)&CTheScripts::ScriptSpace[*pIp]);
*pIp += KEY_LENGTH_IN_SCRIPT;
return text;
}
static int32 GetSizeOfVariableSpace()
{
uint32 tmp = 3;
return Read4BytesFromScript(&tmp);
}
static CRunningScript* StartNewScript(uint32);
static void CleanUpThisVehicle(CVehicle*);
static void CleanUpThisPed(CPed*);
static void CleanUpThisObject(CObject*);
static bool IsPedStopped(CPed*);
static bool IsPlayerStopped(CPlayerInfo*);
static bool IsVehicleStopped(CVehicle*);
static void PrintListSizes();
static void ReadObjectNamesFromScript();
static void UpdateObjectIndices();
static void ReadMultiScriptFileOffsetsFromScript();
static void DrawScriptSpheres();
static void HighlightImportantArea(uint32, float, float, float, float, float);
static void HighlightImportantAngledArea(uint32, float, float, float, float, float, float, float, float, float);
static void DrawDebugSquare(float, float, float, float);
static void DrawDebugAngledSquare(float, float, float, float, float, float, float, float);
static void DrawDebugCube(float, float, float, float, float, float);
static void DrawDebugAngledCube(float, float, float, float, float, float, float, float, float, float);
static void AddToInvisibilitySwapArray(CEntity*, bool);
static void AddToBuildingSwapArray(CBuilding*, int32, int32);
static int32 GetActualScriptSphereIndex(int32 index);
static int32 AddScriptSphere(int32 id, CVector pos, float radius);
static int32 GetNewUniqueScriptSphereIndex(int32 index);
static void RemoveScriptSphere(int32 index);
#ifdef GTA_SCRIPT_COLLECTIVE
static void AdvanceCollectiveIndex()
{
if (NextFreeCollectiveIndex == INT32_MAX)
NextFreeCollectiveIndex = 0;
else
NextFreeCollectiveIndex++;
}
static int AddPedsInVehicleToCollective(int);
static int AddPedsInAreaToCollective(float, float, float, float);
static int FindFreeSlotInCollectiveArray();
static void SetObjectiveForAllPedsInCollective(int, eObjective, int16, int16);
static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector, float);
static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector);
static void SetObjectiveForAllPedsInCollective(int, eObjective, void*);
static void SetObjectiveForAllPedsInCollective(int, eObjective);
#endif
#ifdef MISSION_SWITCHER
public:
static void SwitchToMission(int32 mission);
#endif
#ifdef USE_DEBUG_SCRIPT_LOADER
static int ScriptToLoad;
static int OpenScript();
#endif
#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT
static void LogAfterScriptInitializing();
static void LogBeforeScriptProcessing();
static void LogAfterScriptProcessing();
#endif
};
#ifdef MISSION_REPLAY
extern int AllowMissionReplay;
extern uint32 WaitForMissionActivate;
extern uint32 WaitForSave;
extern uint32 MissionStartTime;
extern int missionRetryScriptIndex;
extern bool doingMissionRetry;
uint32 AddExtraDeathDelay();
void RetryMission(int, int unk = 0);
enum {
MISSION_RETRY_TYPE_SUGGEST_TO_PLAYER = 0,
MISSION_RETRY_TYPE_1,
MISSION_RETRY_TYPE_BEGIN_RESTARTING
};
enum {
MISSION_RETRY_STAGE_NORMAL = 0,
MISSION_RETRY_STAGE_WAIT_FOR_SCRIPT_TO_TERMINATE,
MISSION_RETRY_STAGE_START_PROCESSING,
MISSION_RETRY_STAGE_WAIT_FOR_DELAY,
MISSION_RETRY_STAGE_WAIT_FOR_MENU,
MISSION_RETRY_STAGE_WAIT_FOR_USER,
MISSION_RETRY_STAGE_START_RESTARTING,
MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART,
};
#endif

1555
src/control/Script2.cpp Normal file

File diff suppressed because it is too large Load diff

2362
src/control/Script3.cpp Normal file

File diff suppressed because it is too large Load diff

2178
src/control/Script4.cpp Normal file

File diff suppressed because it is too large Load diff

2606
src/control/Script5.cpp Normal file

File diff suppressed because it is too large Load diff

1356
src/control/Script6.cpp Normal file

File diff suppressed because it is too large Load diff

1194
src/control/ScriptCommands.h Normal file

File diff suppressed because it is too large Load diff

1441
src/control/ScriptDebug.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,330 @@
#include "common.h"
#include "Camera.h"
#include "Clock.h"
#include "Coronas.h"
#include "General.h"
#include "PathFind.h"
#include "PointLights.h"
#include "Shadows.h"
#include "SpecialFX.h"
#include "Timecycle.h"
#include "Timer.h"
#include "TrafficLights.h"
#include "Vehicle.h"
#include "Weather.h"
#include "World.h"
// TODO: figure out the meaning of this
enum { SOME_FLAG = 0x80 };
void
CTrafficLights::DisplayActualLight(CEntity *ent)
{
if(ent->GetUp().z < 0.96f || ent->bRenderDamaged)
return;
int phase;
if(FindTrafficLightType(ent) == 1)
phase = LightForCars1();
else
phase = LightForCars2();
int i;
CBaseModelInfo *mi = CModelInfo::GetModelInfo(ent->GetModelIndex());
float x = mi->Get2dEffect(0)->pos.x;
float yMin = mi->Get2dEffect(0)->pos.y;
float yMax = mi->Get2dEffect(0)->pos.y;
float zMin = mi->Get2dEffect(0)->pos.z;
float zMax = mi->Get2dEffect(0)->pos.z;
for(i = 1; i < 6; i++){
assert(mi->Get2dEffect(i));
yMin = Min(yMin, mi->Get2dEffect(i)->pos.y);
yMax = Max(yMax, mi->Get2dEffect(i)->pos.y);
zMin = Min(zMin, mi->Get2dEffect(i)->pos.z);
zMax = Max(zMax, mi->Get2dEffect(i)->pos.z);
}
CVector pos1, pos2;
uint8 r, g;
int id;
switch(phase){
case CAR_LIGHTS_GREEN:
r = 0;
g = 255;
pos1 = ent->GetMatrix() * CVector(x, yMax, zMin);
pos2 = ent->GetMatrix() * CVector(x, yMin, zMin);
id = 0;
break;
case CAR_LIGHTS_YELLOW:
r = 255;
g = 128;
pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin+zMax)/2.0f);
pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin+zMax)/2.0f);
id = 1;
break;
case CAR_LIGHTS_RED:
default:
r = 255;
g = 0;
pos1 = ent->GetMatrix() * CVector(x, yMax, zMax);
pos2 = ent->GetMatrix() * CVector(x, yMin, zMax);
id = 2;
break;
}
if(CClock::GetHours() > 19 || CClock::GetHours() < 6 || CWeather::Foggyness > 0.05f)
CPointLights::AddLight(CPointLights::LIGHT_POINT,
pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f,
r/255.0f, g/255.0f, 0/255.0f, CPointLights::FOG_NORMAL, true);
CShadows::StoreStaticShadow((uintptr)ent,
SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1,
8.0f, 0.0f, 0.0f, -8.0f, 128,
r*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
g*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
0*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
12.0f, 1.0f, 40.0f, false, 0.0f);
if(DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f)
CCoronas::RegisterCorona((uintptr)ent + id,
r*CTimeCycle::GetSpriteBrightness()*0.7f,
g*CTimeCycle::GetSpriteBrightness()*0.7f,
0*CTimeCycle::GetSpriteBrightness()*0.7f,
255,
pos1, 1.75f*CTimeCycle::GetSpriteSize(), 50.0f,
CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
else
CCoronas::RegisterCorona((uintptr)ent + id + 3,
r*CTimeCycle::GetSpriteBrightness()*0.7f,
g*CTimeCycle::GetSpriteBrightness()*0.7f,
0*CTimeCycle::GetSpriteBrightness()*0.7f,
255,
pos2, 1.75f*CTimeCycle::GetSpriteSize(), 50.0f,
CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN);
CBrightLights::RegisterOne(pos2, ent->GetUp(), -ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN);
static const float top = -0.127f;
static const float bot = -0.539f;
static const float mid = bot + (top-bot)/3.0f;
static const float left = 1.256f;
static const float right = 0.706f;
phase = CTrafficLights::LightForPeds();
if(phase == PED_LIGHTS_DONT_WALK){
CVector p0(2.7f, right, top);
CVector p1(2.7f, left, top);
CVector p2(2.7f, right, mid);
CVector p3(2.7f, left, mid);
CShinyTexts::RegisterOne(ent->GetMatrix()*p0, ent->GetMatrix()*p1, ent->GetMatrix()*p2, ent->GetMatrix()*p3,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
SHINYTEXT_WALK, 255, 0, 0, 60.0f);
}else if(phase == PED_LIGHTS_WALK || CTimer::GetTimeInMilliseconds() & 0x100){
CVector p0(2.7f, right, mid);
CVector p1(2.7f, left, mid);
CVector p2(2.7f, right, bot);
CVector p3(2.7f, left, bot);
CShinyTexts::RegisterOne(ent->GetMatrix()*p0, ent->GetMatrix()*p1, ent->GetMatrix()*p2, ent->GetMatrix()*p3,
1.0f, 0.5f, 0.0f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f,
SHINYTEXT_WALK, 255, 255, 255, 60.0f);
}
}
void
CTrafficLights::ScanForLightsOnMap(void)
{
int x, y;
int i, j, k, l;
CPtrNode *node;
for(x = 0; x < NUMSECTORS_X; x++)
for(y = 0; y < NUMSECTORS_Y; y++){
CPtrList &list = CWorld::GetSector(x, y)->m_lists[ENTITYLIST_DUMMIES];
for(node = list.first; node; node = node->next){
CEntity *light = (CEntity*)node->item;
if(light->GetModelIndex() != MI_TRAFFICLIGHTS)
continue;
// Check cars
for(i = 0; i < ThePaths.m_numCarPathLinks; i++){
CVector2D dist = ThePaths.m_carPathLinks[i].GetPosition() - light->GetPosition();
float dotY = Abs(DotProduct2D(dist, light->GetForward())); // forward is direction of car light
float dotX = DotProduct2D(dist, light->GetRight()); // towards base of light
// it has to be on the correct side of the node and also not very far away
if(dotX < 0.0f && dotX > -15.0f && dotY < 3.0f){
float dz = ThePaths.m_pathNodes[ThePaths.m_carPathLinks[i].pathNodeIndex].GetZ() -
light->GetPosition().z;
if(dz < 15.0f){
ThePaths.m_carPathLinks[i].trafficLightType = FindTrafficLightType(light);
// Find two neighbour nodes of this one
int n1 = -1;
int n2 = -1;
for(j = 0; j < ThePaths.m_numPathNodes; j++)
for(l = 0; l < ThePaths.m_pathNodes[j].numLinks; l++)
if(ThePaths.m_carPathConnections[ThePaths.m_pathNodes[j].firstLink + l] == i){
if(n1 == -1)
n1 = j;
else
n2 = j;
}
// What's going on here?
if(ThePaths.m_pathNodes[n1].numLinks <= ThePaths.m_pathNodes[n2].numLinks)
n1 = n2;
if(ThePaths.m_carPathLinks[i].pathNodeIndex != n1)
ThePaths.m_carPathLinks[i].trafficLightType |= SOME_FLAG;
}
}
}
// Check peds
for(i = ThePaths.m_numCarPathNodes; i < ThePaths.m_numPathNodes; i++){
float dist1, dist2;
dist1 = Abs(ThePaths.m_pathNodes[i].GetX() - light->GetPosition().x) +
Abs(ThePaths.m_pathNodes[i].GetY() - light->GetPosition().y);
if(dist1 < 50.0f){
for(l = 0; l < ThePaths.m_pathNodes[i].numLinks; l++){
j = ThePaths.m_pathNodes[i].firstLink + l;
if(ThePaths.ConnectionCrossesRoad(j)){
k = ThePaths.ConnectedNode(j);
dist2 = Abs(ThePaths.m_pathNodes[k].GetX() - light->GetPosition().x) +
Abs(ThePaths.m_pathNodes[k].GetY() - light->GetPosition().y);
if(dist1 < 15.0f || dist2 < 15.0f)
ThePaths.ConnectionSetTrafficLight(j);
}
}
}
}
}
}
}
bool
CTrafficLights::ShouldCarStopForLight(CVehicle *vehicle, bool alwaysStop)
{
int node, type;
node = vehicle->AutoPilot.m_nNextPathNodeInfo;
type = ThePaths.m_carPathLinks[node].trafficLightType;
if(type){
if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nNextRouteNode) &&
(!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nNextRouteNode))
if(alwaysStop ||
(type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
(type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
ThePaths.m_carPathLinks[node].GetDirection());
if(vehicle->AutoPilot.m_nNextDirection == -1){
if(dist > 0.0f && dist < 8.0f)
return true;
}else{
if(dist < 0.0f && dist > -8.0f)
return true;
}
}
}
node = vehicle->AutoPilot.m_nCurrentPathNodeInfo;
type = ThePaths.m_carPathLinks[node].trafficLightType;
if(type){
if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nCurrentRouteNode) &&
(!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nCurrentRouteNode))
if(alwaysStop ||
(type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
(type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
ThePaths.m_carPathLinks[node].GetDirection());
if(vehicle->AutoPilot.m_nCurrentDirection == -1){
if(dist > 0.0f && dist < 8.0f)
return true;
}else{
if(dist < 0.0f && dist > -8.0f)
return true;
}
}
}
if(vehicle->GetStatus() == STATUS_PHYSICS){
node = vehicle->AutoPilot.m_nPreviousPathNodeInfo;
type = ThePaths.m_carPathLinks[node].trafficLightType;
if(type){
if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nPrevRouteNode) &&
(!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nPrevRouteNode))
if(alwaysStop ||
(type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
(type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
ThePaths.m_carPathLinks[node].GetDirection());
if(vehicle->AutoPilot.m_nPreviousDirection == -1){
if(dist > 0.0f && dist < 6.0f)
return true;
}else{
if(dist < 0.0f && dist > -6.0f)
return true;
}
}
}
}
return false;
}
bool
CTrafficLights::ShouldCarStopForBridge(CVehicle *vehicle)
{
return ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nNextPathNodeInfo].bBridgeLights &&
!ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nCurrentPathNodeInfo].bBridgeLights;
}
int
CTrafficLights::FindTrafficLightType(CEntity *light)
{
float orientation = RADTODEG(CGeneral::GetATanOfXY(light->GetForward().x, light->GetForward().y));
if((orientation > 60.0f && orientation < 60.0f + 90.0f) ||
(orientation > 240.0f && orientation < 240.0f + 90.0f))
return 1;
return 2;
}
uint8
CTrafficLights::LightForPeds(void)
{
uint32 period = CTimer::GetTimeInMilliseconds() % 16384;
if(period < 12000)
return PED_LIGHTS_DONT_WALK;
else if(period < 16384 - 1000)
return PED_LIGHTS_WALK;
else
return PED_LIGHTS_WALK_BLINK;
}
uint8
CTrafficLights::LightForCars1(void)
{
uint32 period = CTimer::GetTimeInMilliseconds() % 16384;
if(period < 5000)
return CAR_LIGHTS_GREEN;
else if(period < 5000 + 1000)
return CAR_LIGHTS_YELLOW;
else
return CAR_LIGHTS_RED;
}
uint8
CTrafficLights::LightForCars2(void)
{
uint32 period = CTimer::GetTimeInMilliseconds() % 16384;
if(period < 6000)
return CAR_LIGHTS_RED;
else if(period < 12000 - 1000)
return CAR_LIGHTS_GREEN;
else if(period < 12000)
return CAR_LIGHTS_YELLOW;
else
return CAR_LIGHTS_RED;
}

View file

@ -0,0 +1,27 @@
#pragma once
class CEntity;
class CVehicle;
enum {
PED_LIGHTS_WALK,
PED_LIGHTS_WALK_BLINK,
PED_LIGHTS_DONT_WALK,
CAR_LIGHTS_GREEN = 0,
CAR_LIGHTS_YELLOW,
CAR_LIGHTS_RED
};
class CTrafficLights
{
public:
static void DisplayActualLight(CEntity *ent);
static void ScanForLightsOnMap(void);
static int FindTrafficLightType(CEntity *light);
static uint8 LightForPeds(void);
static uint8 LightForCars1(void);
static uint8 LightForCars2(void);
static bool ShouldCarStopForLight(CVehicle*, bool);
static bool ShouldCarStopForBridge(CVehicle*);
};