Files
GTASource/game/control/garages.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

3579 lines
108 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : garages.cpp
// PURPOSE : Logic for garages, bomb shops, respray places etc
// AUTHOR : Obbe.
// CREATED : 5/10/00
//
/////////////////////////////////////////////////////////////////////////////////
// Framework Headers
#include "grcore/debugdraw.h"
#include "fwmaths/random.h"
#include "fwmaths/vector.h"
#include "fwmaths/angle.h"
// Game headers
#include "control/garages.h"
#include "camera/CamInterface.h"
#include "camera/viewports/Viewport.h"
#include "control/gamelogic.h"
#include "control/GarageEditor.h"
#include "control/replay/replay.h"
#include "core/game.h"
#include "cutscene/CutSceneManagerNew.h"
#include "frontend/hud_colour.h"
#include "Frontend/MobilePhone.h"
#include "game/clock.h"
#include "game/modelindices.h"
#include "Stats/StatsMgr.h"
#include "modelInfo/vehiclemodelinfo.h"
#include "network/Events/NetworkEventTypes.h"
#include "network/NetworkInterface.h"
#include "network/players/NetworkPlayerMgr.h"
#include "objects/door.h"
#include "objects/DummyObject.h"
#include "objects/object.h"
#include "peds/ped_channel.h"
#include "peds/pedIntelligence.h"
#include "peds/ped.h"
#include "peds/popcycle.h"
#include "scene/world/gameWorld.h"
#include "streaming/streaming.h"
#include "fwsys/timer.h"
#include "system/pad.h"
#include "text/messages.h"
#include "vehicles/automobile.h"
#include "vehicles/bike.h"
#include "vehicles/vehicleFactory.h"
#include "vehicles/heli.h"
#include "vfx/Systems/VfxVehicle.h"
#include "audioengine\controller.h"
#include "frontend\MobilePhone.h"
#include "Stats\StatsInterface.h"
#define DOORSPEEDOPEN (0.011f)
#define DOORSPEEDCLOSE (0.013f)
#define MARGIN_FOR_CAMERA (0.5f) // Distance for the camera to stay out of the actual zone
#define MARGIN_FOR_CAMERA_NARROW (0.5f) // Same for import export garages. Smaller range since player can go all the wat round
AI_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
atArray<CGarage> CGarages::aGarages;
bool CGarages::RespraysAreFree; // resprays can be made free during missions
bool CGarages::NoResprays; // resprays are disabled
bool CGarages::ResprayHappened; // A respray (in any garage) has happened. For the script to test
CGarage* CGarages::pGarageCamShouldBeOutside; // Camera should be outside since stuff is going on
s32 CGarages::LastGaragePlayerWasIn; // the last garage any part of the player's vehicle was in
s32 CGarages::NumSafeHousesRegistered; // How many safehouses have been registered on the map
u32 CGarages::timeForNextCollisionTest; // when to trigger a new collision test for spawning vehicles
#if __BANK
bool CGarages::bDebugDisplayGarages = false; // Render a cube for the garage activation area
bool CGarages::bAlternateOccupantSearch = true; // Loop over vehicles and peds directly when checking garage occupancy
#endif // __BANK
CStoredVehicle CGarages::aCarsInSafeHouse[NUM_SAFE_HOUSES][MAX_NUM_STORED_CARS_IN_SAFEHOUSE];
#define RESPRAYCOST (100)
#define BOMBSCOST (500)
#if !__FINAL
bool bPrintNearestObject = false;
#endif
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ClearAll
// PURPOSE : Removes all cars from safehouses
/////////////////////////////////////////////////////////////////////////////////
void CGarages::ClearAll()
{
for (s32 C = 0; C < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; C++)
{
for (s32 C2 = 0; C2 < NUM_SAFE_HOUSES; C2++)
{
aCarsInSafeHouse[C2][C].Clear();
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : InitLevel
// PURPOSE : Puts the garages back to their original state.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::Init(unsigned initMode)
{
if (initMode == INIT_BEFORE_MAP_LOADED)
{
}
else if (initMode == INIT_SESSION)
{
RespraysAreFree = false;
NoResprays = false;
ResprayHappened = false;
LastGaragePlayerWasIn = -1;
NumSafeHousesRegistered = 0;
timeForNextCollisionTest = 0;
// Clear the cars in the safehouses
ClearAll();
Load();
}
}
//
// name: ShutdownLevel
// description: Shut's down the garages system
void CGarages::Shutdown(unsigned shutdownMode)
{
if( shutdownMode == SHUTDOWN_SESSION )
{
ClearAll();
}
CGarages::aGarages.Reset();
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Load
// PURPOSE : Loads garage data from meta data
/////////////////////////////////////////////////////////////////////////////////
void CGarages::Load()
{
CGarageInitDataList garageData;
PARSER.LoadObject(GARAGE_META_FILE, GARAGE_META_FILE_EXT, garageData);
for (s32 i = 0; i < garageData.garages.GetCount(); ++i)
{
const CGarageInitData& g = garageData.garages[i];
AddOne(g.m_boxes, g.type, g.name, g.owner, g.permittedVehicleType, g.startedWithVehicleSavingEnabled, g.isMPGarage, g.isEnclosed, g.InteriorBoxIDX, g.ExteriorBoxIDX);
}
}
#if __BANK
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Save
// PURPOSE : Saves garage data to meta data
/////////////////////////////////////////////////////////////////////////////////
void CGarages::Save()
{
CGarageInitDataList garageData;
garageData.garages.Reserve(aGarages.GetCount());
for (s32 i = 0; i < aGarages.GetCount(); ++i)
{
CGarageInitData& g = garageData.garages.Append();
CGarage &garage = aGarages[i];
for (int j = 0; j < garage.m_boxes.GetCount(); ++j)
{
g.m_boxes.PushAndGrow(garage.m_boxes[j]);
}
g.name = aGarages[i].name;
g.owner = aGarages[i].owner;
g.type = aGarages[i].type;
g.permittedVehicleType = aGarages[i].m_permittedVehicleType;
g.startedWithVehicleSavingEnabled = aGarages[i].Flags.bSavingVehiclesWasEnabledBeforeSave;
// Error check box IDX's for MP garages
Assertf( !(garage.m_IsMPGarage == true && garage.m_ExteriorBoxIDX == -1), "ERROR: MP flagged garage %s doesn't have and exterior box defined", garage.name.GetCStr() );
Assertf( !(garage.m_IsMPGarage == true && garage.m_InteriorBoxIDX == -1), "ERROR: MP flagged garage %s doesn't have and interior box defined", garage.name.GetCStr() );
g.isEnclosed = garage.m_IsEnclosed;
g.isMPGarage = garage.m_IsMPGarage;
g.ExteriorBoxIDX = garage.m_ExteriorBoxIDX;
g.InteriorBoxIDX = garage.m_InteriorBoxIDX;
}
PARSER.SaveObject(GARAGE_META_FILE, GARAGE_META_FILE_EXT, &garageData);
}
#endif // __BANK
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Update
// PURPOSE : Updates the garages.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::Update(void)
{
#if __BANK
CGarageEditor::UpdateDebug();
#endif // __BANK
s32 Index;
static u32 GarageToBeTidied = 0;
static u32 GarageToBeOccupancyChecked = 0;
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
return;
#endif
if (CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning())
return;
if (!aGarages.GetCount())
return;
pGarageCamShouldBeOutside = NULL;
CPhoneMgr::ClearRemoveFlags(CPhoneMgr::WANTS_PHONE_REMOVED_GARAGES);
#define GARAGES_TO_BE_OCCUPANCY_CHECKED (1)
for(u32 i = 0; i < GARAGES_TO_BE_OCCUPANCY_CHECKED; i++)
{
GarageToBeOccupancyChecked++;
if(GarageToBeOccupancyChecked >= aGarages.GetCount())
GarageToBeOccupancyChecked = 0;
if(aGarages[GarageToBeOccupancyChecked].type != GARAGE_NONE)
{
#if __BANK
aGarages[GarageToBeOccupancyChecked].UpdateOccupiedBoxes(bAlternateOccupantSearch);
#else
aGarages[GarageToBeOccupancyChecked].UpdateOccupiedBoxes(true);
#endif
}
}
for (Index = 0; Index < aGarages.GetCount(); Index++)
{
if (aGarages[Index].type != GARAGE_NONE)
{
aGarages[Index].Update(Index);
}
}
// Once in a while we tidy up a garage. (Remove wreck etc)
if (!aGarages[GarageToBeTidied].Flags.bDisableTidyingUp && (fwTimer::GetSystemFrameCount() & 15) == 12)
{
GarageToBeTidied++;
if (GarageToBeTidied >= aGarages.GetCount()) GarageToBeTidied = 0;
if (aGarages[GarageToBeTidied].type != GARAGE_NONE)
{
// Make sure the camera is not too close
if ( ABS(camInterface::GetPos().x - aGarages[GarageToBeTidied].MinX) > 40.0f ||
ABS(camInterface::GetPos().y - aGarages[GarageToBeTidied].MinY) > 40.0f)
{
aGarages[GarageToBeTidied].TidyUpGarage();
}
else
{
aGarages[GarageToBeTidied].TidyUpGarageClose();
}
}
}
#if __BANK
{
// Debug thing:
// If this flag is set we will go through the objects and print the one closest to the cam.
// Handy for finding doors.
if (bPrintNearestObject)
{
CEntity *pResult[128], *pClosest = NULL;
s32 Num, Loop;
float SmallestDist = 99999.9f, Dist;
CGameWorld::FindObjectsInRange(camInterface::GetPos(), 20.0f, false, &Num, 128, pResult, true, false, false, true, true);
for (Loop = 0; Loop < Num; Loop++)
{
Dist = (camInterface::GetPos() - VEC3V_TO_VECTOR3(pResult[Loop]->GetTransform().GetPosition())).Mag();
if (Dist < SmallestDist)
{
SmallestDist = Dist;
pClosest = pResult[Loop];
}
}
if (pClosest)
{
Displayf("MI:%d %s Coors:%f %f\n", pClosest->GetModelIndex(), pClosest->GetModelName(), pClosest->GetTransform().GetPosition().GetXf(), pClosest->GetTransform().GetPosition().GetYf());
// A little test thing for the algorithm that works out the projection on z of the bb.
Vector3 P1, P2, P3, P4;
pClosest->CalculateBBProjection(&P1, &P2, &P3, &P4);
grcDebugDraw::Line(P1, P2, Color32(0xff, 0x00, 0x00, 0xff), Color32(0xff, 0xff, 0xff, 0xff));
grcDebugDraw::Line(P2, P3, Color32(0x00, 0xff, 0x00, 0xff), Color32(0xff, 0xff, 0xff, 0xff));
grcDebugDraw::Line(P3, P4, Color32(0x00, 0x00, 0xff, 0xff), Color32(0xff, 0xff, 0xff, 0xff));
grcDebugDraw::Line(P4, P1, Color32(0xff, 0xff, 0x00, 0xff), Color32(0xff, 0xff, 0xff, 0xff));
}
}
}
#endif
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AddOne
// PURPOSE : Finds a slot for and generates a new garage.
/////////////////////////////////////////////////////////////////////////////////
s32 CGarages::AddOne(float BaseX, float BaseY, float BaseZ, float Delta1X, float Delta1Y, float Delta2X, float Delta2Y, float CeilingZ, GarageType type, atHashString name, atHashString owner, bool isMpGarage, bool isEnclosed, s8 interiorBoxIDX, s8 exteriorBoxIDX)
{
atArray<CGarage::Box> box;
CGarage::Box &b = box.Grow();
Assertf(box.GetCount() <= (sizeof(BoxFlags)<<3), "Increase size of BoxFlags");
b.BaseX = BaseX;
b.BaseY = BaseY;
b.BaseZ = BaseZ;
b.Delta1X = Delta1X;
b.Delta1Y = Delta1Y;
b.Delta2X = Delta2X;
b.Delta2Y = Delta2Y;
b.CeilingZ = CeilingZ;
b.useLineIntersection = false;
return AddOne(box, type, name, owner, VEHICLE_TYPE_NONE, true, isMpGarage, isEnclosed, interiorBoxIDX, exteriorBoxIDX);
}
s32 CGarages::AddOne(const atArray<CGarage::Box> &boxes, GarageType type, atHashString name, atHashString owner, int permittedVehicleType, bool vehicleSavingEnabled, bool isMpGarage, bool isEnclosed, s8 interiorBoxIDX, s8 exteriorBoxIDX)
{
CGarage &g = aGarages.Grow();
for (int i = 0; i < boxes.GetCount(); ++i)
{
g.m_boxes.Grow();
g.m_boxesOccupiedForPlayers.Grow();
g.UpdateSize(i, boxes[i]);
}
g.TimeOfNextEvent = ~(u32)0; // Really large number so the garages won't skip past stuff.
g.pCarToCollect = NULL;
g.name = name;
g.owner = owner;
g.m_permittedVehicleType = VehicleType(permittedVehicleType);
g.Flags.bSavingVehiclesWasEnabledBeforeSave = vehicleSavingEnabled;
g.Flags.bSavingVehiclesEnabled = vehicleSavingEnabled;
g.m_IsEnclosed = isEnclosed;
g.m_IsMPGarage = isMpGarage;
g.m_InteriorBoxIDX = interiorBoxIDX;
g.m_ExteriorBoxIDX = exteriorBoxIDX;
#if __ASSERT
for (u32 garage_loop = 0; garage_loop < aGarages.GetCount() - 1; garage_loop++)
{
Assertf(g.name.GetHash() != aGarages[garage_loop].name.GetHash(), "CGarages::AddOne - more than one garage called %s exists", name.GetCStr());
}
#endif // __ASSERT
g.m_DoorSlideTimeOutTimer = 0;
g.type = type;
g.originalType = type;
g.state = GSTATE_CLOSED;
// Some are open to begin with. Some are closed.
switch (g.type)
{
// These ones are closed to begin with
case GARAGE_NONE:
case GARAGE_MISSION:
case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE:
case GARAGE_AMBIENT_PARKING:
case GARAGE_SAFEHOUSE_PARKING_ZONE:
break;
case GARAGE_RESPRAY:
// These ones are open to begin with
g.state = GSTATE_OPEN;
break;
default:
Assertf(0, "CGarages::AddOne - unknown garage type");
break;
}
// The hideouts are now assigned by the game. This way the artists don't have to worry about type
// clashes as they set the type for hideout garages.
if (type == GARAGE_SAFEHOUSE_PARKING_ZONE)
{
g.safeHouseIndex = (s8)NumSafeHousesRegistered;
NumSafeHousesRegistered++;
Assert(NumSafeHousesRegistered <= NUM_SAFE_HOUSES);
}
else
{
g.safeHouseIndex = -1;
}
g.Flags.bClosingEmpty = true;
g.Flags.bLeaveCameraAlone = false;
g.Flags.bDoorSlidingPreviousFrame = false;
g.Flags.bDoorSlidingThisFrame = false;
g.Flags.bSafehouseGarageWasOpenBeforeSave = false;
g.Flags.bDisableTidyingUp = false;
g.m_pDoorObject = NULL;
g.m_oldDoorOpenness = 0.0f;
g.m_oldDoorFrameCount = 0;
return aGarages.GetCount();
}
inline bool DoorIsInUseByScript(CDoor *pDoor)
{
if (!Verifyf(pDoor, "pDoor is NULL"))
{
return false;
}
CDoorSystemData *pData = pDoor->GetDoorSystemData();
// If script has locked the door or it has an open ratio thats not 0 its in used by script
if (pData && (pData->GetLocked() || pData->GetTargetRatio() != 0.0f))
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SlideDoorOpen
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::SlideDoorOpen()
{
// If this door is controlled by script its safe to move it
if (DoorIsInUseByScript(m_pDoorObject))
{
return true;
}
// This deals with a timeout. After 5 seconds of trying to open we return true so that the player doesn't get stuck.
bool bForceDone = false;
Flags.bDoorSlidingThisFrame = true;
if (!Flags.bDoorSlidingPreviousFrame)
{
m_DoorSlideTimeOutTimer = fwTimer::GetTimeInMilliseconds();
}
if (fwTimer::GetTimeInMilliseconds() > m_DoorSlideTimeOutTimer + 5000)
{
bForceDone = true;
Flags.bDoorSlidingThisFrame = false;
}
if (m_pDoorObject && m_pDoorObject->GetCurrentPhysicsInst())
{
m_pDoorObject->SetFixedPhysics(false, false);
m_pDoorObject->SetTargetDoorRatio(1.0f, false);
float doorOpenness = FindDoorOpenness();
if (bForceDone || doorOpenness > 0.98f || (doorOpenness > 0.5f && doorOpenness <= m_oldDoorOpenness && m_oldDoorFrameCount == (fwTimer::GetSystemFrameCount()-1) ))
{
m_pDoorObject->SetTargetDoorRatio(1.0f, true);
m_pDoorObject->SetFixedPhysics(true, false);
return true;
}
m_oldDoorOpenness = doorOpenness;
m_oldDoorFrameCount = fwTimer::GetSystemFrameCount();
}
// if (bForceDone)
// {
// if (m_pDoorObject && m_pDoorObject->GetCurrentPhysicsInst()) // Need to try and force door open as player can get stuck inside.
// {
// m_pDoorObject->SetTargetDoorRatio(1.0f, true);
// m_pDoorObject->SetFixedPhysics(true, false);
// }
//
// return true;
// }
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SlideDoorClosed
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::SlideDoorClosed()
{
// If this door is controlled by script its safe to move it
if (DoorIsInUseByScript(m_pDoorObject))
{
return true;
}
// This deals with a timeout. After 5 seconds of trying to close we return true so that the player doesn't get stuck.
bool bForceDone = false;
Flags.bDoorSlidingThisFrame = true;
if (!Flags.bDoorSlidingPreviousFrame)
{
m_DoorSlideTimeOutTimer = fwTimer::GetTimeInMilliseconds();
}
if (fwTimer::GetTimeInMilliseconds() > m_DoorSlideTimeOutTimer + 5000)
{
bForceDone = true;
Flags.bDoorSlidingThisFrame = false;
}
if (m_pDoorObject && m_pDoorObject->GetCurrentPhysicsInst())
{
m_pDoorObject->SetFixedPhysics(false, false);
m_pDoorObject->SetTargetDoorRatio(0.0001f, false);
float doorOpenness = FindDoorOpenness();
// Done if door shut or doesn't progress anymore.
if (bForceDone || doorOpenness < 0.02f || (doorOpenness < 0.5f && doorOpenness >= m_oldDoorOpenness && m_oldDoorFrameCount == (fwTimer::GetSystemFrameCount()-1)))
{
m_pDoorObject->SetFixedPhysics(true, false);
return true;
}
m_oldDoorOpenness = doorOpenness;
m_oldDoorFrameCount = fwTimer::GetSystemFrameCount();
}
if (bForceDone)
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindDoorOpenness
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
float CGarage::FindDoorOpenness()
{
Assert(m_pDoorObject);
Vector3 vecDoorForward(VEC3V_TO_VECTOR3(m_pDoorObject->GetTransform().GetB()));
float fCurrentHeading = rage::Atan2f(vecDoorForward.z, vecDoorForward.XYMag()) / HALF_PI;
return fabs(fCurrentHeading);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindGarageIndex
// PURPOSE : This function finds the garage index for this string.
/////////////////////////////////////////////////////////////////////////////////
s32 CGarages::FindGarageIndexFromNameHash(u32 NameHash)
{
for (u32 C = 0; C < aGarages.GetCount(); C++)
{
if (NameHash == aGarages[C].name.GetHash())
{
return C;
}
}
Assertf(0, "Garage wasn't found");
return -1;
}
s32 CGarages::GetNumGarages()
{
return aGarages.GetCount();
}
s32 CGarages::FindGarageIndex(char *pName)
{
u32 garageHash = atStringHash(pName);
return FindGarageIndexFromNameHash(garageHash);
}
s32 CGarages::FindGarageWithSafeHouseIndex(s32 safeHouseIdx)
{
for (u32 C = 0; C < aGarages.GetCount(); C++)
{
if (aGarages[C].type == GARAGE_SAFEHOUSE_PARKING_ZONE)
{
if (aGarages[C].safeHouseIndex == safeHouseIdx)
{
return C;
}
}
}
Assertf(0, "CGarages::FindGarageWithSafeHouseIndex - Garage with index %d wasn't found", safeHouseIdx);
return -1;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ChangeGarageType
// PURPOSE : Change the type of this garage. Mostly used for bomb shops. They
// can change depending on the mission.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::ChangeGarageType(s32 NumGarage, s32 NewType)
{
CGarage *pGarage;
pGarage = &aGarages[NumGarage];
if (Verifyf(pGarage->type != GARAGE_SAFEHOUSE_PARKING_ZONE, "CGarages::ChangeGarageType - it's not safe to change the type of a garage from GARAGE_SAFEHOUSE_PARKING_ZONE. This will cause problems when saving the game"))
{
if (Verifyf(NewType != GARAGE_SAFEHOUSE_PARKING_ZONE, "CGarages::ChangeGarageType - it's probably not safe to change the type of a garage to GARAGE_SAFEHOUSE_PARKING_ZONE. This could cause problems when saving the game"))
{
pGarage->type = (GarageType)(NewType);
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : PrintMessage
// PURPOSE : Prints a garage related message
/////////////////////////////////////////////////////////////////////////////////
void CGarage::PrintMessage(const char *pTextLabel)
{
char *pText = TheText.Get(pTextLabel);
s32 TextBlock = TheText.GetBlockContainingLastReturnedString();
CMessages::AddMessage(pText, TextBlock,
4000, true, true, PREVIOUS_BRIEF_NO_OVERRIDE, NULL, 0, NULL, 0, false);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Update
// PURPOSE : Updates one garage
/////////////////////////////////////////////////////////////////////////////////
void CGarage::Update(s32 MyIndex)
{
u8 NewCol1, NewCol2, NewCol3, NewCol4, NewCol5, NewCol6;
bool bChargeMoney;
bool bChangedColour = false;
bool bPlayerWasWanted;
float DistanceSqr;
CPed * pPlayerPed = FindPlayerPed();
if (!AssertVerify(pPlayerPed))
return;
CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfo();
Flags.bDoorSlidingThisFrame = false;
#if !__FINAL
if (pCarToCollect)
{
AssertEntityPointerValid(pCarToCollect);
}
#endif
//do garage camera thingy first
switch (state)
{
case GSTATE_CLOSED:
case GSTATE_OPEN:
case GSTATE_CLOSING:
case GSTATE_OPENING:
case GSTATE_OPENEDWAITINGTOBEREACTIVATED:
case GSTATE_MISSIONCOMPLETED:
if (FindPlayerPed())
{
//sometimes the level designers don't want the camera to go out
//so check this first
if (Flags.bLeaveCameraAlone==false)
{
if (type != GARAGE_SAFEHOUSE_PARKING_ZONE) // This shouldn't be needed as the parking zones should have been set up by the artists with bLeaveCameraAlone but it's too late to get this fixed properly.
{
CVehicle* PlayerVehicle=NULL;
PlayerVehicle=CGameWorld::FindLocalPlayerVehicle();
if (PlayerVehicle)
{
// If we've already been in this garage we set the margin a bit bigger. This way we avoid the first person glitch whilst walking into a garage backwards.
CEntity *pEntityToTest = FindPlayerPed();
if (IsEntityEntirelyInside3D(pEntityToTest, 1.0f)==true)
{
CGarages::pGarageCamShouldBeOutside = this;
CPhoneMgr::SetRemoveFlags(CPhoneMgr::WANTS_PHONE_REMOVED_GARAGES); // Make player put phone away
}
}
}
}
}
break;
default:
break;
}
static u32 policeBackoffTime;
switch(type)
{
///////////////////////// GARAGE_RESPRAY ////////////////////////
case GARAGE_RESPRAY:
switch (GetState())
{
case GSTATE_OPEN:
if (!CGarages::NoResprays)
{
if (IsStaticPlayerCarEntirelyInside() && !AnyNetworkPlayersOnFootInGarage(1.0f) &&
!pPlayerPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMPLEX_USE_MOBILE_PHONE, true) &&
!pPlayerInfo->AreControlsDisabled())
{ // Activate the dude
if (CGarages::IsCarSprayable((CAutomobile *)CGameWorld::FindLocalPlayerVehicle())) // Is car the right type ?
{
StatId stat;
if(NetworkInterface::IsInFreeMode())
{
// This is dealt by script in mp
}
else
{
stat = STAT_TOTAL_CASH.GetStatId();
}
if ((StatsInterface::IsKeyValid(stat) && StatsInterface::GetIntStat(stat) >= RESPRAYCOST) || CGarages::RespraysAreFree || NetworkInterface::IsGameInProgress())
{
Vector3 Crs;
CalcCentrePointOfGarage(Crs);
if( pPlayerInfo->GetWanted().m_WantedLevel >= WANTED_LEVEL1 && CWanted::WorkOutPolicePresence(VECTOR3_TO_VEC3V(Crs), 35.0f) > 0)
{
PrintMessage("GA_POL"); // // The cops saw you enter.
SetState(GSTATE_OPENEDWAITINGTOBEREACTIVATED);
policeBackoffTime = fwTimer::GetTimeInMilliseconds() + 8000;
}
else
{
SetState(GSTATE_CLOSING);
// Disable controls
pPlayerInfo->DisableControlsGarages();
}
}
else
{ // We don't have enough money
PrintMessage("GA_3"); // Come back when you've got the cash.
SetState(GSTATE_OPENEDWAITINGTOBEREACTIVATED);
policeBackoffTime = fwTimer::GetTimeInMilliseconds() + 10000;
}
}
else
{
PrintMessage("GA_1"); // They're not going to respray THAT.
SetState(GSTATE_OPENEDWAITINGTOBEREACTIVATED);
policeBackoffTime = fwTimer::GetTimeInMilliseconds() + 10000;
}
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = true;
CGarages::LastGaragePlayerWasIn = MyIndex;
}
else if (!IsPlayerOutsideGarage(FindPlayerPed()))
{
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = true;
CGarages::LastGaragePlayerWasIn = MyIndex;
}
else if (MyIndex == CGarages::LastGaragePlayerWasIn)
{
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = false;
}
}
break;
case GSTATE_CLOSING:
if (CGameWorld::FindLocalPlayerVehicle()) ThrowCarsNearDoorOutOfGarage(CGameWorld::FindLocalPlayerVehicle());
if (SlideDoorClosed())
{
SetState(GSTATE_CLOSED);
if (NetworkInterface::IsGameInProgress())
{
TimeOfNextEvent = fwTimer::GetTimeInMilliseconds() + 400;
camInterface::FadeOut(400);
}
else
{
TimeOfNextEvent = fwTimer::GetTimeInMilliseconds() + 2000;
camInterface::FadeOut(2000);
}
StatId stat = StatsInterface::GetStatsModelHashId("KILLS_SINCE_LAST_CHECKPOINT");
if (StatsInterface::IsKeyValid(stat))
{
StatsInterface::IncrementStat(StatsInterface::GetStatsModelHashId("TOTAL_LEGITIMATE_KILLS"), (float)StatsInterface::GetIntStat(stat));
StatsInterface::SetStatData(stat, 0);
}
}
// postpone explosion
if (CGameWorld::FindLocalPlayerVehicle())
{
CGameWorld::FindLocalPlayerVehicle()->m_nVehicleFlags.bDisableParticles = true;
}
break;
case GSTATE_CLOSED:
if (!CGarages::NoResprays)
{
{
if (fwTimer::GetTimeInMilliseconds() > TimeOfNextEvent && !camInterface::IsFading())
{
if (!NetworkInterface::IsGameInProgress())
{
CClock::PassTime(3 * 60, false);
CHeli::RemovePoliceHelisUponDeathArrest();
CGameWorld::ClearExcitingStuffFromArea(pPlayerInfo->GetPos(), 4000.0f, TRUE, true, true, false, m_pDoorObject, true, true);
}
if (NetworkInterface::IsGameInProgress())
{
camInterface::FadeIn(400);
}
else
{
camInterface::FadeIn(400);
}
SetState(GSTATE_OPENING);
bChargeMoney = false; // Should we charge money ?
// Reset wanted state
bPlayerWasWanted = CGameWorld::FindLocalPlayerWanted()->m_WantedLevel != WANTED_CLEAN;
if (bPlayerWasWanted)
{
bChargeMoney = true;
CPed * pPlayerPed = FindPlayerPed();
pPlayerPed->GetPlayerWanted()->SetWantedLevel(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()), WANTED_CLEAN, 0, WL_REMOVING);
}
CVehicle *pPlayerVehicle = CGameWorld::FindLocalPlayerVehicle();
if(pPlayerVehicle && !pPlayerVehicle->IsNetworkClone())
{
// Fix car
if (pPlayerVehicle && (pPlayerVehicle->InheritsFromAutomobile() || pPlayerVehicle->InheritsFromBike()) &&
pPlayerVehicle->GetStatus() != STATUS_WRECKED) // Don't fix vehicles that are completely wrecked.
{
// If the car has full health and there is no wanted level we will
// just change the colour and not charge any money
if (pPlayerVehicle->GetHealth() < 970)
{
bChargeMoney = true;
}
pPlayerVehicle->SetHealth(rage::Max(CREATED_VEHICLE_HEALTH, pPlayerVehicle->GetHealth()));
// Also make sure the car won't explode
if (pPlayerVehicle->InheritsFromAutomobile())
{
((CAutomobile *)pPlayerVehicle)->Fix();
// AF: this is not needed anymore
//CGameWorld::FindLocalPlayerVehicle()->SetCarDoorLocks(CARLOCK_UNLOCKED); // Without this the player could knock the doors of a car and fix it->unjackable
}
else
{
Assert(pPlayerVehicle->InheritsFromBike());
((CBike *)pPlayerVehicle)->Fix();
// AF: this is not needed anymore
//CGameWorld::FindLocalPlayerVehicle()->SetCarDoorLocks(CARLOCK_UNLOCKED); // Without this the player could knock the doors of a car and fix it->unjackable
}
// If the car is upside down we put it right side up
if (pPlayerVehicle->GetTransform().GetC().GetZf() < 0.0f)
{ // Simply invert the side and up vectors
Matrix34 mat = MAT34V_TO_MATRIX34(pPlayerVehicle->GetMatrix());
mat.c = -mat.c;
//mat.c.x = -mat.c.x; // Up vector
//mat.c.y = -mat.c.y;
//mat.c.z = -mat.c.z;
mat.a = -mat.a;
//mat.a.x = -mat.a.x; // Right vector
//mat.a.y = -mat.a.y;
//mat.a.z = -mat.a.z;
// This should work since the centerpoint is below the middle. (Car shouldn't get stuck in ground)
pPlayerVehicle->SetMatrix(mat, false, false, false);
}
// Change colour of the car too
bChangedColour = false;
if( !pPlayerVehicle->m_nVehicleFlags.bShouldNotChangeColour )
{
CVehicleModelInfo* pVehicleModelInfo = pPlayerVehicle->GetVehicleModelInfo();
// pVehicleModelInfo->ChooseVehicleColourFancy(pPlayerVehicle, NewCol1, NewCol2, NewCol3, NewCol4);
// Instead of the fancy function we always pick the next colour so that the colour always changes. (if more than 1 colour set up)
pVehicleModelInfo->GetNextVehicleColour(pPlayerVehicle, NewCol1, NewCol2, NewCol3, NewCol4, NewCol5, NewCol6);
if( pPlayerVehicle->GetBodyColour1() != NewCol1 ||
pPlayerVehicle->GetBodyColour2() != NewCol2 ||
pPlayerVehicle->GetBodyColour3() != NewCol3 ||
pPlayerVehicle->GetBodyColour4() != NewCol4 ||
pPlayerVehicle->GetBodyColour5() != NewCol5 ||
pPlayerVehicle->GetBodyColour6() != NewCol6 )
bChangedColour = true;
pPlayerVehicle->SetBodyColour1(NewCol1);
pPlayerVehicle->SetBodyColour2(NewCol2);
pPlayerVehicle->SetBodyColour3(NewCol3);
pPlayerVehicle->SetBodyColour4(NewCol4);
pPlayerVehicle->SetBodyColour5(NewCol5);
pPlayerVehicle->SetBodyColour6(NewCol6);
pPlayerVehicle->UpdateBodyColourRemapping(); // let shaders know, that body colours changed
// trigger the particle effect
if (m_pDoorObject)
{
Vector3 fxPos = VEC3V_TO_VECTOR3(m_pDoorObject->GetTransform().GetPosition());
fxPos.z = m_boxes[0].BaseZ; // Ignoring other boxes for now
Vector3 fxDir = VEC3V_TO_VECTOR3(m_pDoorObject->GetTransform().GetB());
fxDir.z = 0.0f;
fxDir.Normalize();
g_vfxVehicle.TriggerPtFxRespray(pPlayerVehicle, RCC_VEC3V(fxPos), RCC_VEC3V(fxDir));
}
}
pPlayerVehicle->m_fBodyDirtLevel = 0.0f; // Give the car a wash.
pPlayerVehicle->m_nVehicleFlags.bDisableParticles = false;
}
// Take money away from player
if (CGarages::RespraysAreFree)
{
PrintMessage("GA_22");
CGarages::RespraysAreFree = false; // Only one freebie
// CMessages::AddBigMessage( TheText.Get("GA_22"), -1, 4000, BIG_MESSAGE_4, true);
}
else
{
if (bChargeMoney)
{
if(NetworkInterface::IsInFreeMode())
{
// this is dealt by script.
}
else
{
StatsInterface::DecrementStat(STAT_TOTAL_CASH.GetStatId(), RESPRAYCOST);
}
bool isTaxi = pPlayerVehicle && CVehicle::IsTaxiModelId(pPlayerVehicle->GetModelId());
if (bPlayerWasWanted)
{
if (pPlayerVehicle && isTaxi)
{
PrintMessage("GA_2T");
}
else
{
PrintMessage("GA_2");
}
// CMessages::AddBigMessage( TheText.Get("GA_2"), -1, 4000, BIG_MESSAGE_4, true);
}
else
{
if (pPlayerVehicle && (isTaxi || pPlayerVehicle->GetModelIndex() == MI_CAR_FIRETRUCK) )
{
PrintMessage("GA_XT"); // As good as new.
}
else
{
{
switch (fwRandom::GetRandomNumberInRange(0, 3))
{
case 0:
{
PrintMessage("GA_X1");
}
// CMessages::AddBigMessage( TheText.Get("GA_X1"), -1, 4000, BIG_MESSAGE_4, true);
break;
case 1:
{
PrintMessage("GA_X2");
}
// CMessages::AddBigMessage( TheText.Get("GA_X2"), -1, 4000, BIG_MESSAGE_4, true);
break;
case 2:
{
PrintMessage("GA_X3");
}
// CMessages::AddBigMessage( TheText.Get("GA_X3"), -1, 4000, BIG_MESSAGE_4, true);
break;
}
}
}
}
}
else
{
if (bChangedColour)
{
{
if (fwRandom::GetRandomNumber() & 1)
{
PrintMessage("GA_15");
// CMessages::AddBigMessage( TheText.Get("GA_15"), -1, 4000, BIG_MESSAGE_4, true);
}
else
{
PrintMessage("GA_16");
// CMessages::AddBigMessage( TheText.Get("GA_16"), -1, 4000, BIG_MESSAGE_4, true);
}
}
}
}
}
CGarages::ResprayHappened = true;
if (CGameWorld::FindLocalPlayerVehicle())
{
CGameWorld::FindLocalPlayerVehicle()->m_nVehicleFlags.bHasBeenResprayed = true;
}
}
}
}
}
break;
case GSTATE_OPENING:
if (SlideDoorOpen())
{
SetState(GSTATE_OPENEDWAITINGTOBEREACTIVATED);
policeBackoffTime = fwTimer::GetTimeInMilliseconds() + 5000;
}
// Enable controls
{
{
pPlayerInfo->EnableControlsGarages();
// Also stop the cops chasing player. Unfair to get arrested now.
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = false;
}
}
break;
case GSTATE_OPENEDWAITINGTOBEREACTIVATED:
if (fwTimer::GetTimeInMilliseconds() > policeBackoffTime)
{
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = false;
}
if (IsPlayerOutsideGarage(FindPlayerPed()))
{
SetState(GSTATE_OPEN);
}
break;
default:
break;
}
break;
// Mission garages only open if the player brings a specific car
case GARAGE_MISSION:
switch (state)
{
case GSTATE_OPEN:
DistanceSqr = ( (CGameWorld::FindLocalPlayerCoors().x - (MinX + MaxX) * 0.5f) * (CGameWorld::FindLocalPlayerCoors().x - (MinX + MaxX) * 0.5f) +
(CGameWorld::FindLocalPlayerCoors().y - (MinY + MaxY) * 0.5f) * (CGameWorld::FindLocalPlayerCoors().y - (MinY + MaxY) * 0.5f) );
if (DistanceSqr > 30.0f * 30.0f)
{
if (!(fwTimer::GetSystemFrameCount() & 31))
{
if ( (!pCarToCollect) || (!IsEntityTouching3D(pCarToCollect))) // Added just before Miami-PC
{
// Close the door now please.
SetState(GSTATE_CLOSING);
Flags.bClosingEmpty = true;
}
}
}
else
{
// Wait till the car is INside and the player is OUTside.
// At this point we close the door.
if (NetworkInterface::IsGameInProgress() || CGameWorld::FindLocalPlayerVehicle() != pCarToCollect)
{
if(pCarToCollect && IsEntityEntirelyInside3D(pCarToCollect))
{ // Car IS inside
if (AllPlayerEntitiesEntirelyOutside(), 2.0f) // THIS IS WORNG AND HAS TO BE FIXED (Not now as gtaIV was submitted yesterday)
{ // Player IS outside
// Excellent. Take car.
// Freeze player to stop him from going back.
if (!NetworkInterface::IsGameInProgress())
{
pPlayerInfo->DisableControlsGarages();
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = true;
}
// Close the door now please.
SetState(GSTATE_CLOSING);
Flags.bClosingEmpty = false;
}
}
}
}
break;
case GSTATE_CLOSING:
if (pCarToCollect) ThrowCarsNearDoorOutOfGarage(pCarToCollect);
if (SlideDoorClosed())
{
if (!Flags.bClosingEmpty)
{
if (pCarToCollect)
{
SetState(GSTATE_MISSIONCOMPLETED);
// Destroy the car. (Not needed anymore)
CVehicleFactory::GetFactory()->Destroy(pCarToCollect);
pCarToCollect = NULL;
}
else
{
SetState(GSTATE_CLOSED);
}
// release player
if (!NetworkInterface::IsGameInProgress())
{
pPlayerInfo->EnableControlsGarages();
CGameWorld::FindLocalPlayerWanted()->m_PoliceBackOffGarage = false;
}
}
else
{
SetState(GSTATE_CLOSED);
}
}
break;
case GSTATE_CLOSED:
// If the player drives the right car and is nearby
// we open the door.
if (CGameWorld::FindLocalPlayerVehicle() == pCarToCollect && pCarToCollect)
{
// Work out the distance to the garage
const Vector3 vPlayerVehiclePos = VEC3V_TO_VECTOR3(CGameWorld::FindLocalPlayerVehicle()->GetTransform().GetPosition());
if (CalcDistToGarageRectangleSquared(vPlayerVehiclePos.x, vPlayerVehiclePos.y) < DOOROPENDISTSQR)
{
SetState(GSTATE_OPENING);
}
}
break;
case GSTATE_OPENING:
if (SlideDoorOpen())
{
SetState(GSTATE_OPEN);
}
break;
default:
break;
}
break;
// This garage can be opened by the script. Once opened it can be closed again
case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE:
switch (state)
{
case GSTATE_OPEN:
break;
case GSTATE_CLOSING:
if (SlideDoorClosed())
{
SetState(GSTATE_CLOSED);
}
break;
case GSTATE_CLOSED:
break;
case GSTATE_OPENING:
if (SlideDoorOpen())
{
SetState(GSTATE_OPEN);
}
break;
default:
break;
}
break;
case GARAGE_SAFEHOUSE_PARKING_ZONE:
if (!NetworkInterface::IsGameInProgress() && Flags.bSavingVehiclesEnabled) // Parking spots outside safehouse only work in single player.
{
switch (state)
{
case GSTATE_OPEN:
// Do we need to think about removing the cars?
if( m_IsEnclosed )
{
// An enclosed garage
if( IsBeyondStoreDistance2(SAFE_HOUSE_ENCLOSED_GARAGES_REMOVE_CARS_DIST*SAFE_HOUSE_ENCLOSED_GARAGES_REMOVE_CARS_DIST) )
{
// Yes, but we must await the door to be closed
if( !m_pDoorObject || SlideDoorClosed() )
{ // Save the cars; remove them from the map and close the garage.
StoreAndRemoveCarsForThisHideOut(&CGarages::aCarsInSafeHouse[safeHouseIndex][0]);
state = GSTATE_CLOSED;
// Tell door it can start moving again
SafeSetDoorFixedPhysics(false, false);
}
}
}
else
{
// An open Garage
if( IsBeyondStoreDistance2(SAFE_HOUSE_GARAGES_REMOVE_CARS_DIST*SAFE_HOUSE_GARAGES_REMOVE_CARS_DIST) )
{ // Save the cars; remove them from the map and close the garage.
StoreAndRemoveCarsForThisHideOut(&CGarages::aCarsInSafeHouse[safeHouseIndex][0]);
state = GSTATE_CLOSED;
}
}
break;
case GSTATE_CLOSED:
// Do we need to think about spawning the cars in this garage?
if( m_IsEnclosed )
{
if( IsWithinRestoreDistance2(SAFE_HOUSE_ENCLOSED_GARAGES_CREATE_CARS_DIST*SAFE_HOUSE_ENCLOSED_GARAGES_CREATE_CARS_DIST) )
{
// Yes
if( !m_pDoorObject || SlideDoorClosed() )
{
if (RestoreCarsForThisHideOut(&CGarages::aCarsInSafeHouse[safeHouseIndex][0]))
{
state = GSTATE_OPEN;
// Tell door it can start moving again
SafeSetDoorFixedPhysics(false, false);
}
}
}
}
else
{
if( IsWithinRestoreDistance2(SAFE_HOUSE_GARAGES_CREATE_CARS_DIST*SAFE_HOUSE_GARAGES_CREATE_CARS_DIST) )
{ // Revive the cars in the garage
if (RestoreCarsForThisHideOut(&CGarages::aCarsInSafeHouse[safeHouseIndex][0]))
{
state = GSTATE_OPEN;
}
}
}
break;
default:
Assert(0);
break;
}
}
break;
case GARAGE_AMBIENT_PARKING:
switch (state)
{
case GSTATE_OPEN:
if (!m_pDoorObject) // Need a proper condition to close here eventually
{
SetState(GSTATE_CLOSED);
}
break;
case GSTATE_CLOSING:
if (SlideDoorClosed())
{
SetState(GSTATE_CLOSED);
}
break;
case GSTATE_CLOSED:
if (m_pDoorObject) // We only have a door object when reasonably close
{
if ( ( (MyIndex + fwTimer::GetSystemFrameCount()) & 15) == 0)
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
while(i--)
{
pVehicle = pool->GetSlot(i);
if (pVehicle)
{
if (IsLessThanAll(MagXY(m_pDoorObject->GetTransform().GetPosition() - pVehicle->GetTransform().GetPosition()), ScalarV(V_FIVE)))
{
SetState(GSTATE_OPENING);
}
}
}
}
}
break;
case GSTATE_OPENING:
if (SlideDoorOpen())
{
SetState(GSTATE_OPEN);
}
break;
default:
break;
}
break;
default:
break;
}
Flags.bDoorSlidingPreviousFrame = Flags.bDoorSlidingThisFrame;
NetworkUpdate(MyIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : UpdateSize
// PURPOSE : Updates garage size values
/////////////////////////////////////////////////////////////////////////////////
void CGarage::UpdateSize(int boxIndex, const Box &aBox)
{
Box &box = m_boxes[boxIndex];
MinX = MIN(MIN(MIN(MIN(aBox.BaseX, aBox.Delta1X + aBox.BaseX), aBox.Delta2X + aBox.BaseX), aBox.Delta1X + aBox.BaseX + aBox.Delta2X), MinX);
MaxX = MAX(MAX(MAX(MAX(aBox.BaseX, aBox.Delta1X + aBox.BaseX), aBox.Delta2X + aBox.BaseX), aBox.Delta1X + aBox.BaseX + aBox.Delta2X), MaxX);
MinY = MIN(MIN(MIN(MIN(aBox.BaseY, aBox.Delta1Y + aBox.BaseY), aBox.Delta2Y + aBox.BaseY), aBox.Delta1Y + aBox.BaseY + aBox.Delta2Y), MinY);
MaxY = MAX(MAX(MAX(MAX(aBox.BaseY, aBox.Delta1Y + aBox.BaseY), aBox.Delta2Y + aBox.BaseY), aBox.Delta1Y + aBox.BaseY + aBox.Delta2Y), MaxY);
MinZ = MIN(MinZ, aBox.BaseZ);
MaxZ = MAX(MaxZ, aBox.CeilingZ);
box.BaseX = aBox.BaseX;
box.BaseY = aBox.BaseY;
box.BaseZ = aBox.BaseZ;
box.Delta1X = aBox.Delta1X;
box.Delta1Y = aBox.Delta1Y;
box.Delta2X = aBox.Delta2X;
box.Delta2Y = aBox.Delta2Y;
box.CeilingZ = aBox.CeilingZ;
box.useLineIntersection = aBox.useLineIntersection;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION :
// PURPOSE :
//
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsBeyondStoreDistance2(float distToCheck2)
{
float distSqr = (CGameWorld::FindLocalPlayerCoors() - Vector3(m_boxes[0].BaseX, m_boxes[0].BaseY, m_boxes[0].BaseZ)).Mag2(); // Ignoring other boxes for now
if( distSqr > distToCheck2 )
{
return true;
}
return false;
}
bool CGarage::IsWithinRestoreDistance2(float distToCheck2)
{
float distSqr = (CGameWorld::FindLocalPlayerCoors() - Vector3(m_boxes[0].BaseX, m_boxes[0].BaseY, m_boxes[0].BaseZ)).Mag2(); // Ignoring other boxes for now
if( distSqr < distToCheck2 )
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION :
// PURPOSE :
//
/////////////////////////////////////////////////////////////////////////////////
void CGarage::SafeSetDoorFixedPhysics(bool bFixed, bool bNetwork)
{
// If this door is not controlled by script its safe to set its physics
if (m_pDoorObject && !DoorIsInUseByScript(m_pDoorObject))
{
m_pDoorObject->SetFixedPhysics(bFixed, bNetwork);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsStaticPlayerCarInside
// PURPOSE : returns true if the player is in his car inside the garage
// and not moving.
/////////////////////////////////////////////////////////////////////////////////
#define CARISSTATICSPEED (0.01f)
bool CGarage::IsStaticPlayerCarEntirelyInside(void)
{
float SpeedSqr, SpeedX, SpeedY, SpeedZ;
CAutomobile *pCar;
if (!CGameWorld::FindLocalPlayerVehicle()) return (false);
if (!CGameWorld::FindLocalPlayerVehicle()->InheritsFromAutomobile() && !CGameWorld::FindLocalPlayerVehicle()->InheritsFromBike()) return (false);
if(FindPlayerPed()->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_EXIT_VEHICLE)) return false;
pCar = (CAutomobile *)CGameWorld::FindLocalPlayerVehicle();
const Vector3 vCarPos = VEC3V_TO_VECTOR3(pCar->GetTransform().GetPosition());
if (vCarPos.x < MinX) return false;
if (vCarPos.x > MaxX) return false;
if (vCarPos.y < MinY) return false;
if (vCarPos.y > MaxY) return false;
if ( (SpeedX = ABS(pCar->GetVelocity().x)) > CARISSTATICSPEED) return false;
if ( (SpeedY = ABS(pCar->GetVelocity().y)) > CARISSTATICSPEED) return false;
if ( (SpeedZ = ABS(pCar->GetVelocity().z)) > CARISSTATICSPEED) return false;
SpeedSqr = SpeedX * SpeedX + SpeedY * SpeedY + SpeedZ * SpeedZ;
if (SpeedSqr > CARISSTATICSPEED*CARISSTATICSPEED) return false;
return IsEntityEntirelyInside3D(pCar, 0.5f);
}
void CGarage::BuildQuadTestPoints(const CEntity *pEntity, Vector3 &p0, Vector3 &p1, Vector3 &p2, Vector3 &p3)
{
if (pEntity->GetIsTypeVehicle())
{
Vector3 BBox0 = pEntity->GetBoundingBoxMin();
Vector3 BBox3 = pEntity->GetBoundingBoxMax();
Vector3 BBox1(BBox0.x, BBox3.y, BBox0.z);
Vector3 BBox2(BBox3.x, BBox0.y, BBox3.z);
BBox0 = pEntity->TransformIntoWorldSpace(BBox0);
BBox1 = pEntity->TransformIntoWorldSpace(BBox1);
BBox2 = pEntity->TransformIntoWorldSpace(BBox2);
BBox3 = pEntity->TransformIntoWorldSpace(BBox3);
float averageZ = (BBox0.z + BBox3.z) * 0.5f;
BBox0.z = averageZ;
BBox1.z = averageZ;
BBox2.z = averageZ;
BBox3.z = averageZ;
p0 = BBox0;
p1 = BBox1;
p2 = BBox2;
p3 = BBox3;
}
else
{
Vector3 BBoxMin = pEntity->TransformIntoWorldSpace(pEntity->GetBoundingBoxMin());
Vector3 BBoxMax = pEntity->TransformIntoWorldSpace(pEntity->GetBoundingBoxMax());
float averageZ = (BBoxMin.z + BBoxMax.z) * 0.5f;
p0 = Vector3(BBoxMin.x, BBoxMin.y, averageZ);
p1 = Vector3(BBoxMin.x, BBoxMax.y, averageZ);
p2 = Vector3(BBoxMax.x, BBoxMin.y, averageZ);
p3 = Vector3(BBoxMax.x, BBoxMax.y, averageZ);
}
}
// 2------------5
// /| /|
// / | / |
// / | / |
// 1 ----------6 |
// | | | |
// | 3--------|--4
// | / | /
// |/ |/
// 0 --------- 5
inline void CGarage::BuildBoxTestPoints(const CEntity *pEntity, Vector3 &_p0, Vector3 &_p1, Vector3 &_p2, Vector3 &_p3,
Vector3 &_p4, Vector3 &_p5, Vector3 &_p6, Vector3 &_p7)
{
if (pEntity->GetIsTypeVehicle())
{
Vector3 p0 = pEntity->GetBoundingBoxMin();
Vector3 p7 = pEntity->GetBoundingBoxMax();
Vector3 p1(p0.x, p0.y, p7.z);
Vector3 p2(p0.x, p7.y, p7.z);
Vector3 p3(p0.x, p7.y, p0.z);
Vector3 p4(p7.x, p7.y, p0.z);
Vector3 p5(p7.x, p0.y, p0.z);
Vector3 p6(p7.x, p0.y, p7.z);
_p0 = pEntity->TransformIntoWorldSpace(p0);
_p1 = pEntity->TransformIntoWorldSpace(p1);
_p2 = pEntity->TransformIntoWorldSpace(p2);
_p3 = pEntity->TransformIntoWorldSpace(p3);
_p4 = pEntity->TransformIntoWorldSpace(p4);
_p5 = pEntity->TransformIntoWorldSpace(p5);
_p6 = pEntity->TransformIntoWorldSpace(p6);
_p7 = pEntity->TransformIntoWorldSpace(p7);
}
else
{
_p0 = pEntity->TransformIntoWorldSpace(pEntity->GetBoundingBoxMin());
_p7 = pEntity->TransformIntoWorldSpace(pEntity->GetBoundingBoxMax());
_p1 = Vector3(_p0.x, _p0.y, _p7.z);
_p2 = Vector3(_p0.x, _p7.y, _p7.z);
_p3 = Vector3(_p0.x, _p7.y, _p0.z);
_p4 = Vector3(_p7.x, _p7.y, _p0.z);
_p5 = Vector3(_p7.x, _p0.y, _p0.z);
_p6 = Vector3(_p7.x, _p0.y, _p7.z);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsEntityEntirelyInside3D
// PURPOSE : returns true if this object and all its spheres are entirely inside
// the garage.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsEntityEntirelyInside3D(const CEntity *pEntity, float Margin, int boxIndex)
{
const Vector3 vEntityPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
if (vEntityPos.x < MinX-Margin) return false;
if (vEntityPos.x > MaxX+Margin) return false;
if (vEntityPos.y < MinY-Margin) return false;
if (vEntityPos.y > MaxY+Margin) return false;
if (vEntityPos.z < MinZ-Margin) return false;
if (vEntityPos.z > MaxZ+Margin) return false;
Vector3 p0, p1, p2, p3;
BuildQuadTestPoints(pEntity, p0, p1, p2, p3);
if (!IsPointInsideGarage(p0, Margin, boxIndex)) return false;
if (!IsPointInsideGarage(p1, Margin, boxIndex)) return false;
if (!IsPointInsideGarage(p2, Margin, boxIndex)) return false;
if (!IsPointInsideGarage(p3, Margin, boxIndex)) return false;
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsEntityEntirelyOutside
// PURPOSE : returns true if this object and all its spheres are entirely outside
// the garage.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsEntityEntirelyOutside(const CEntity *pEntity, float Margin, int boxIndex)
{
const Vector3 vEntityPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
if (vEntityPos.x > (MinX-Margin) &&
vEntityPos.x < (MaxX+Margin) &&
vEntityPos.y > (MinY-Margin) &&
vEntityPos.y < (MaxY+Margin))
{
return false;
}
Vector3 p0, p1, p2, p3, p4, p5, p6, p7;
CGarage::BuildBoxTestPoints(pEntity, p0, p1, p2, p3, p4, p5, p6, p7);
if (IsPointInsideGarage(p0, Margin)) return false;
if (IsPointInsideGarage(p7, Margin)) return false;
if (IsPointInsideGarage(p1, Margin, boxIndex)) return false;
if (IsPointInsideGarage(p2, Margin, boxIndex)) return false;
if (IsPointInsideGarage(p3, Margin, boxIndex)) return false;
if (IsPointInsideGarage(p4, Margin, boxIndex)) return false;
if (IsPointInsideGarage(p5, Margin, boxIndex)) return false;
if (IsPointInsideGarage(p6, Margin, boxIndex)) return false;
// This is not really an accurate solution. It is possible for the car to be partially
// within the garage even though all the corner points are outside.
// I don't think this will be a problem as the cars will never approach a garage from a
// corner but if it is; I'll have to re-write this function.
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AllPlayerEntitiesEntirelyOutside
// PURPOSE : returns true if this object and all its spheres are entirely outside
// the garage.
// This tests for all the players (in a network game)
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::AllPlayerEntitiesEntirelyOutside(float Margin)
{
if (AssertVerify(NetworkInterface::IsGameInProgress()))
{
unsigned numPhysicalPlayers = netInterface::GetNumPhysicalPlayers();
const netPlayer * const *allPhysicalPlayers = netInterface::GetAllPhysicalPlayers();
for(unsigned index = 0; index < numPhysicalPlayers; index++)
{
const CNetGamePlayer *pPlayer = SafeCast(const CNetGamePlayer, allPhysicalPlayers[index]);
if(pPlayer->GetPlayerPed())
{
if (pPlayer->GetPlayerPed()->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPlayer->GetPlayerPed()->GetMyVehicle())
{
if (!IsEntityEntirelyOutside( pPlayer->GetPlayerPed()->GetMyVehicle(), Margin))
{
return false;
}
}
else
{
if (!IsEntityEntirelyOutside( pPlayer->GetPlayerPed(), Margin))
{
return false;
}
}
}
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AnyNetworkPlayersOnFootInGarage
// PURPOSE : returns true if any of the network players are within the garage.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::AnyNetworkPlayersOnFootInGarage(float Margin)
{
if (NetworkInterface::IsGameInProgress())
{
unsigned numPhysicalPlayers = netInterface::GetNumPhysicalPlayers();
const netPlayer * const *allPhysicalPlayers = netInterface::GetAllPhysicalPlayers();
for(unsigned index = 0; index < numPhysicalPlayers; index++)
{
const CNetGamePlayer *pPlayer = SafeCast(const CNetGamePlayer, allPhysicalPlayers[index]);
if(pPlayer->GetPlayerPed())
{
if (!pPlayer->GetPlayerPed()->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
if (IsPointInsideGarage(VEC3V_TO_VECTOR3(pPlayer->GetPlayerPed()->GetTransform().GetPosition()), Margin))
{
return true;
}
}
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsGarageEmpty
// PURPOSE : Tests whether this garage is empty.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsGarageEmpty(int boxIndex, bool alternateSearch)
{
if(alternateSearch)
{
spdAABB boxBound;
boxBound.Set(VECTOR3_TO_VEC3V(Vector3(MinX, MinY, MinZ)), VECTOR3_TO_VEC3V(Vector3(MaxX, MaxY, MaxZ)));
// check vehicles
CVehicle::Pool* vehiclePool = CVehicle::GetPool();
for(s32 i = 0; i < vehiclePool->GetSize(); i++)
{
CVehicle* pVeh = vehiclePool->GetSlot(i);
if(pVeh)
{
spdAABB tempBox;
spdAABB vehBound = pVeh->GetBoundBox(tempBox);
if(boxBound.IntersectsAABB(vehBound))
{
if (IsEntityTouching3D(pVeh, boxIndex))
{
return false;
}
}
}
}
// check peds
CPed::Pool* pedPool = CPed::GetPool();
for(s32 i = 0; i < pedPool->GetSize(); i++)
{
CPed* pPed = pedPool->GetSlot(i);
if(pPed)
{
spdAABB tempBox;
spdAABB pedBound = pPed->GetBoundBox(tempBox);
if(boxBound.IntersectsAABB(pedBound))
{
if (IsEntityTouching3D(pPed, boxIndex))
{
return false;
}
}
}
}
}
else
{
s32 Num = 0;
s32 C;
CEntity *apEnts[16];
CGameWorld::FindObjectsIntersectingCube(Vector3(MinX, MinY, MinZ), Vector3(MaxX, MaxY, MaxZ), &Num, 16, apEnts, false, true, true, false, false);
// Go through the entities found and test whether any of them really are touching the garage
for (C = 0; C < Num; C++)
{
if (IsEntityTouching3D(apEnts[C], boxIndex))
{
return false;
}
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPlayerOutsideGarage
// PURPOSE : Tests whether this garage is empty.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsPlayerOutsideGarage(const CEntity *pEntity, float margin, int boxIndex)
{
Assertf(pEntity->GetBaseModelInfo()->GetModelType() == MI_TYPE_PED, "Player Model Type is not MI_TYPE_PED, %d", pEntity->GetBaseModelInfo()->GetModelType());
const CPed *pPlayer = static_cast<const CPed*>(pEntity);
if (pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
CVehicle *pVehicle = pPlayer->GetMyVehicle();
return IsEntityEntirelyOutside(pVehicle, margin, boxIndex);
}
return IsEntityEntirelyOutside(pPlayer, margin, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPlayerEntirelyInsideGarage
// PURPOSE : Tests whether this garage is empty.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsPlayerEntirelyInsideGarage(const CEntity *pEntity, float margin, int boxIndex)
{
Assertf(pEntity, "CGarage::IsPlayerEntirelyInsideGarage Invalid Entity");
Assertf(pEntity->GetIsTypePed(), "CGarage::IsPlayerEntirelyInsideGarage Passed in Entity %s is not a ped",pEntity->GetModelName());
Assertf(pEntity->GetBaseModelInfo()->GetModelType() == MI_TYPE_PED, "Player Model Type is not MI_TYPE_PED, %d", pEntity->GetBaseModelInfo()->GetModelType());
const CPed *pPlayer = static_cast<const CPed*>(pEntity);
if(m_IsMPGarage && boxIndex == ALL_BOXES_INDEX )
{
// MP Garage stuff (B*782985)
Assertf(m_InteriorBoxIDX!=-1, "ERROR: MP Garage has no interior box IDX" );
Assertf(m_ExteriorBoxIDX!=-1, "ERROR: MP Garage has no exterior box IDX" );
// MP Garage functionality
if (pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
CVehicle *pVehicle = pPlayer->GetMyVehicle();
return IsEntityEntirelyInside3D(pVehicle, margin, m_ExteriorBoxIDX) &&
IsPointInsideGarage(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()), margin, m_InteriorBoxIDX);
}
return IsEntityEntirelyInside3D(pPlayer, margin, m_ExteriorBoxIDX) &&
IsPointInsideGarage(VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition()), margin, m_InteriorBoxIDX);
}
// Normal non MP Garage functionality
if (pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
CVehicle *pVehicle = pPlayer->GetMyVehicle();
if (Verifyf(pVehicle, "IsPlayerEntirelyInsideGarage ... Ped [%s] has no vehicle but has config flag CPED_CONFIG_FLAG_InVehicle", pPlayer->GetModelName()))
{
return IsEntityEntirelyInside3D(pVehicle, margin, boxIndex);
}
}
return IsEntityEntirelyInside3D(pPlayer, margin, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsObjectEntirelyInsideGarage
// PURPOSE : Tests whether this garage is empty.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsObjectEntirelyInsideGarage(const CEntity *pEntity, float margin, int boxIndex)
{
if(m_IsMPGarage && boxIndex == CGarage::ALL_BOXES_INDEX )
{
// MP Garage stuff (B*782985)
Assertf(m_InteriorBoxIDX!=-1, "ERROR: MP Garage has no interior box IDX" );
Assertf(m_ExteriorBoxIDX!=-1, "ERROR: MP Garage has no exterior box IDX" );
// MP Garage functionality
return IsEntityEntirelyInside3D(pEntity, margin, m_ExteriorBoxIDX) &&
IsPointInsideGarage(VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()), margin, m_InteriorBoxIDX);
}
return IsEntityEntirelyInside3D(pEntity, margin, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AreEntitiesEntirelyInsideGarage
// PURPOSE : returns true if all entities of the specified type(s) are fully inside the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::AreEntitiesEntirelyInsideGarage(bool peds, bool vehs, bool objs, float margin, int boxIndex)
{
s32 Num;
CEntity *apEnts[32];
CGameWorld::FindObjectsIntersectingCube(Vector3(MinX, MinY, MinZ), Vector3(MaxX, MaxY, MaxZ), &Num, 16, apEnts, false, vehs, peds, objs, false);
// Go through the entities found and test whether any of them really are touching the garage
for (s32 i = 0; i < Num; ++i)
{
if (!IsObjectEntirelyInsideGarage(apEnts[i], margin, boxIndex))
{
return false;
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsAnyEntitiesEntirelyInsideGarage
// PURPOSE : returns true if any entities of the specified type(s) are fully inside the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsAnyEntityEntirelyInsideGarage(bool peds, bool vehs, bool objs, float margin, int boxIndex)
{
s32 Num;
CEntity *apEnts[32];
CGameWorld::FindObjectsIntersectingCube(Vector3(MinX, MinY, MinZ), Vector3(MaxX, MaxY, MaxZ), &Num, 16, apEnts, false, vehs, peds, objs, false);
// Go through the entities found and test whether any of them really are touching the garage
for (s32 i = 0; i < Num; ++i)
{
if (IsEntityEntirelyInside3D(apEnts[i], margin, boxIndex))
{
return true;
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsEntityTouching3D
// PURPOSE : returns true if at least one of the spheres of this object touch the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsEntityTouching3D(const CEntity *pEntity, int boxIndex)
{
float Radius = pEntity->GetBoundRadius();
const Vector3 vEntityPos = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
if (vEntityPos.x < MinX - Radius) return false;
if (vEntityPos.x > MaxX + Radius) return false;
if (vEntityPos.y < MinY - Radius) return false;
if (vEntityPos.y > MaxY + Radius) return false;
if (vEntityPos.z < MinZ - Radius) return false;
if (vEntityPos.z > MaxZ + Radius) return false;
// We go through the 8 corner points of the bounding box and check if any point is inside
Vector3 p0, p1, p2, p3, p4, p5, p6, p7;
CGarage::BuildBoxTestPoints(pEntity, p0, p1, p2, p3, p4, p5, p6, p7);
if (IsPointInsideGarage(p0, boxIndex)) return true;
if (IsPointInsideGarage(p7, boxIndex)) return true;
if (IsPointInsideGarage(p1, boxIndex)) return true;
if (IsPointInsideGarage(p2, boxIndex)) return true;
if (IsPointInsideGarage(p3, boxIndex)) return true;
if (IsPointInsideGarage(p4, boxIndex)) return true;
if (IsPointInsideGarage(p5, boxIndex)) return true;
if (IsPointInsideGarage(p6, boxIndex)) return true;
int startIndex;
int count;
if (boxIndex == ALL_BOXES_INDEX)
{
startIndex = 0;
count = m_boxes.GetCount();
}
else
{
startIndex = boxIndex;
count = boxIndex + 1;
}
// 2------------7
// /| /|
// / | / |
// / | / |
// 1 ----------6 |
// | | | |
// | 3--------|-- 4
// | / | /
// |/ | /
// 0 --------- 5
Vector3 pA(p0.x, p0.y, (p0.z + p1.z)* 0.5f);
Vector3 pB(p5.x, p5.y, (p5.z + p6.z)* 0.5f);
Vector3 pC(p3.x, p3.y, (p3.z + p2.z)* 0.5f);
Vector3 pD(p4.x, p4.y, (p4.z + p7.z)* 0.5f);
for (int i = startIndex; i < count; ++i)
{
if (!m_boxes[i].useLineIntersection)
{
continue;
}
if (IsBoxIntersectingGarage(pA, pB, pC, pD, i))
{
return true;
}
}
return false;
}
inline bool CalcLineParams(const Vector2& p0, const Vector2& p1, float &m, float &c)
{
float xDiff = p0.x - p1.x;
if ((fabs(xDiff) < 0.01f))
{
m = 0.0f;
c = 0.0f;
return true;
}
m = (p0.y - p1.y)/ xDiff;
c = -p1.x * m + p1.y;
return false;
}
inline bool LinesIntersect(const Vector2& pA0, const Vector2& pA1, const Vector2& pB0, const Vector2& pB1)
{
float mA, cA;
bool verticalA = CalcLineParams(pA0, pA1, mA, cA);
float mB, cB;
bool verticalB = CalcLineParams(pB0, pB1, mB, cB);
if (verticalA && verticalB)
{
return false;
}
Vector2 p;
if (verticalA)
{
p.x = pA0.x;
p.y = mB*p.x + cB;
}
else if (verticalB)
{
p.x = pB0.x;
p.y = mA*p.x + cA;
}
else if (fabs(mA - mB) < 0.005f)
{
return false;
}
else
{
float invGradientDiff = 1.0f / (mA - mB);
p.x = (cB - cA) * invGradientDiff;
p.y = (cB* mA - mB * cA) * invGradientDiff;
}
const float epsilon = 0.001f;
float minXA = rage::Min(pA0.x, pA1.x);
float minXB = rage::Min(pB0.x, pB1.x);
float testMinX = rage::Max(minXA, minXB) - epsilon;
float minYA = rage::Min(pA0.y, pA1.y);
float minYB = rage::Min(pB0.y, pB1.y);
float testMinY = rage::Max(minYA, minYB) - epsilon;
float maxXA = rage::Max(pA0.x, pA1.x);
float maxXB = rage::Max(pB0.x, pB1.x);
float testMaxX = rage::Min(maxXA, maxXB) + epsilon;
float maxYA = rage::Max(pA0.y, pA1.y);
float maxYB = rage::Max(pB0.y, pB1.y);
float testMaxY = rage::Min(maxYA, maxYB) + epsilon;
return (p.x >= testMinX && p.x <= testMaxX && p.y >= testMinY && p.y <= testMaxY);
}
bool CGarage::IsBoxIntersectingGarage(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2, const Vector3 &p3, int boxIndex)
{
// p2 ------------ p3
// | |
// | |
// | |
// | |
// | |
// p0 ------------ p1
if (IsLineIntersectingGarage(p0, p1, boxIndex))
{
return true;
}
if (IsLineIntersectingGarage(p2, p3, boxIndex))
{
return true;
}
if (IsLineIntersectingGarage(p0, p2, boxIndex))
{
return true;
}
if (IsLineIntersectingGarage(p1, p3, boxIndex))
{
return true;
}
return false;
}
bool CGarage::IsLineIntersectingGarage(const Vector3 &start3, const Vector3 &end3, int boxIndex)
{
Vector2 start(start3.x, start3.y);
Vector2 end(end3.x, end3.y);
int startIndex;
int count;
if (boxIndex == ALL_BOXES_INDEX)
{
startIndex = 0;
count = m_boxes.GetCount();
}
else
{
startIndex = boxIndex;
count = boxIndex + 1;
}
float middleZ = (start3.z + end3.z) * 0.5f;
for (int i = startIndex; i < count; ++i)
{
Box &box = m_boxes[i];
if (middleZ >= box.BaseZ && middleZ <= box.CeilingZ)
{
// p2 ------------ p3
// | |
// | |
// | |
// | |
// | |
// p0 ------------ p1
Vector2 p0(box.BaseX, box.BaseY);
Vector2 p1(p0.x + box.Delta1X, p0.y + box.Delta1Y);
Vector2 p2(p0.x + box.Delta2X, p0.y + box.Delta2Y);
Vector2 p3(p1.x + box.Delta2X, p1.y + box.Delta2Y);
// front
if (LinesIntersect(start, end, p0, p1))
{
return true;
}
// back
if (LinesIntersect(start, end, p2, p3))
{
return true;
}
// left
if (LinesIntersect(start, end, p0, p2))
{
return true;
}
// right
if (LinesIntersect(start, end, p1, p3))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : EntityHasASphereWayOutsideGarage
// PURPOSE : returns true if this entity has at least one sphere way outside the
// garage. We are talking about the radius of the sphere + a margin.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::EntityHasASphereWayOutsideGarage(CEntity *pEntity, float Margin)
{
// We go through the 8 corner points of the bounding box and check inf any sphere is
// outside the garage.
Vector3 p0, p1, p2, p3, p4, p5, p6, p7;
CGarage::BuildBoxTestPoints(pEntity, p0, p1, p2, p3, p4, p5, p6, p7);
if (!IsPointInsideGarage(p0, Margin)) return true;
if (!IsPointInsideGarage(p7, Margin)) return true;
if (!IsPointInsideGarage(p1, Margin)) return true;
if (!IsPointInsideGarage(p2, Margin)) return true;
if (!IsPointInsideGarage(p3, Margin)) return true;
if (!IsPointInsideGarage(p4, Margin)) return true;
if (!IsPointInsideGarage(p5, Margin)) return true;
if (!IsPointInsideGarage(p6, Margin)) return true;
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsAnyOtherCarTouchingGarage
// PURPOSE : returns true if a car that is not the player touches the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsAnyOtherCarTouchingGarage(CVehicle *pException)
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
while(i--)
{
pVehicle = pool->GetSlot(i);
if(pVehicle && pVehicle != pException && pVehicle->GetStatus() != STATUS_WRECKED)
{
if (IsEntityTouching3D(pVehicle))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ThrowCarsNearDoorOutOfGarage
// PURPOSE : Any car touching a door of this garage will slowly be pushed out
/////////////////////////////////////////////////////////////////////////////////
void CGarage::ThrowCarsNearDoorOutOfGarage(const CVehicle *pException)
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
while(i--)
{
pVehicle = pool->GetSlot(i);
if(pVehicle && pVehicle != pException)
{
if (IsEntityTouching3D(pVehicle) && EntityHasASphereWayOutsideGarage(pVehicle))
{ // Slowly push this guy away from the center of the garage.
Vector3 Diff;
const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
Diff.x = vVehiclePos.x - (MinX + MaxX) * 0.5f;
Diff.y = vVehiclePos.y - (MinY + MaxY) * 0.5f;
Diff.z = 0.0f;
Diff.Normalize();
pVehicle->SetVelocity(pVehicle->GetVelocity() + Diff * 0.02f * 50.0f*fwTimer::GetTimeStep());
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsAnyOtherPedTouchingGarage
// PURPOSE : returns true if a ped that is not the exception touches the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsAnyOtherPedTouchingGarage(CPed *pException)
{
CPed::Pool *pool = CPed::GetPool();
CPed* pPed;
s32 i=pool->GetSize();
while(i--)
{
pPed = pool->GetSlot(i);
if(pPed && pPed != pException)
{
if (IsEntityTouching3D(pPed))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsAnyCarBlockingDoor
// PURPOSE : returns true if a car has at least 1 sphere entirely inside and
// one sphere entirely outside garage. This should stop certain garages
// from closing.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsAnyCarBlockingDoor()
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
while(i--)
{
pVehicle = pool->GetSlot(i);
if(pVehicle)
{
if (IsEntityTouching3D(pVehicle) && EntityHasASphereWayOutsideGarage(pVehicle))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CountCarsWithCenterPointWithinGarage
// PURPOSE : Returns the number of them
/////////////////////////////////////////////////////////////////////////////////
s32 CGarage::CountCarsWithCenterPointWithinGarage(CEntity *pException)
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
s32 Result = 0;
while(i--)
{
pVehicle = pool->GetSlot(i);
if(pVehicle && pVehicle != pException)
{
if (IsPointInsideGarage(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition())))
{
Result++;
}
}
}
return Result;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : RemoveCarsBlockingDoorNotInside
// PURPOSE : Removes cars touching the door but not inside
/////////////////////////////////////////////////////////////////////////////////
void CGarage::RemoveCarsBlockingDoorNotInside()
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
while(i--)
{
pVehicle = pool->GetSlot(i);
if(pVehicle)
{
if (IsEntityTouching3D(pVehicle))
{
if (!IsPointInsideGarage(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition())))
{ // Remove this guy (if we are allowed)
if ((!pVehicle->m_nVehicleFlags.bCannotRemoveFromWorld) && (pVehicle->CanBeDeleted()))
{
CVehicleFactory::GetFactory()->Destroy(pVehicle);
return;
}
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsCarSprayable
// PURPOSE : Works out whether the spray dude will take this car.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsCarSprayable(CVehicle *pCar)
{
CVehicleModelInfo* vmi = pCar->GetVehicleModelInfo();
Assert(vmi);
if (vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_NO_RESPRAY))
return false;
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetTargetCarForMissionGarage
// PURPOSE : Tells a specific garage to open for a specific car only.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::SetTargetCarForMissionGarage(s32 NumGarage, CAutomobile *pCar)
{
if (!pCar)
{
aGarages[NumGarage].pCarToCollect = NULL;
return;
}
// We found the garage. Make sure it's a mission one
Assert(aGarages[NumGarage].type == GARAGE_MISSION);
// Make sure the car is kosher
if (pCar)
{
AssertVehiclePointerValid(pCar);
}
aGarages[NumGarage].pCarToCollect = pCar;
// If the guy has been used for a previous mission we re-activate him.
if (aGarages[NumGarage].GetState() == GSTATE_MISSIONCOMPLETED) aGarages[NumGarage].SetState(GSTATE_CLOSED);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : HasCarBeenDroppedOffYet
// PURPOSE : Tells the script whether a car has been dropped of by the player yet.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::HasCarBeenDroppedOffYet(s32 NumGarage)
{
return (aGarages[NumGarage].GetState() == GSTATE_MISSIONCOMPLETED);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsGarageOpen
// PURPOSE : Returns true if this garage is open.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsGarageOpen(s32 NumGarage)
{
CGarage *pGarage;
pGarage = &aGarages[NumGarage];
if (pGarage->GetState() == GSTATE_OPEN || pGarage->GetState() == GSTATE_OPENEDWAITINGTOBEREACTIVATED)
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsGarageClosed
// PURPOSE : Returns true if this garage is closed.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsGarageClosed(s32 NumGarage)
{
CGarage *pGarage;
pGarage = &aGarages[NumGarage];
if (pGarage->GetState() == GSTATE_CLOSED)
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : OpenThisGarage
// PURPOSE : If this is the right type of garage it will be opened.
/////////////////////////////////////////////////////////////////////////////////
void CGarage::OpenThisGarage()
{
Assert(type == GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE);
if (state == GSTATE_CLOSED || state == GSTATE_CLOSING || state == GSTATE_MISSIONCOMPLETED)
{
//@@: location CGARAGE_OPENTHISGARAGE
SetState(GSTATE_OPENING);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CloseThisGarage
// PURPOSE : If this is the right type of garage it will be closed.
/////////////////////////////////////////////////////////////////////////////////
void CGarage::CloseThisGarage()
{
Assert(type == GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE);
if (state == GSTATE_OPEN || state == GSTATE_OPENING)
{
SetState(GSTATE_CLOSING);
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CalcDistToGarageRectangleSquared
// PURPOSE : Works out the distance from a point to the area that is defined for
// the garage. This is better and more accurate than simply taking the
// distance to the center point.
/////////////////////////////////////////////////////////////////////////////////
float CGarage::CalcDistToGarageRectangleSquared(float X, float Y)
{
float DistX, DistY;
if (X < MinX)
{
DistX = X - MinX;
}
else if (X > MaxX)
{
DistX = X - MaxX;
}
else
{
DistX = 0.0f;
}
if (Y < MinY)
{
DistY = Y - MinY;
}
else if (Y > MaxY)
{
DistY = Y - MaxY;
}
else
{
DistY = 0.0f;
}
return (DistX*DistX + DistY*DistY);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsThisCarWithinGarageArea
// PURPOSE : Returns true if the car is in the catchment area of the garage.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsThisCarWithinGarageArea(s32 NumGarage, const CEntity *pEntity)
{
return (aGarages[NumGarage].IsEntityEntirelyInside3D(pEntity));
}
bool CGarage::CanThisVehicleBeStoredInThisGarage(const CVehicle *pVehicle)
{
bool storeVehicle = false;
if (pVehicle && pVehicle->CanSaveInGarage())
{
if (!pVehicle->PopTypeIsMission()) // Don't want to remove mission cars. That would fail the mission
{
if (!pVehicle->HasPedsInIt() )
{
if( IsPointInsideGarage(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()))) // Make sure there is no-one in it. Don't want to save players vehicles or random ambient cars
{
bool isAcceptableGarage = true;
// Does this garage have an owner?
if(owner.GetHash() != 0)
{
// Yes
CPed *pPed = pVehicle->GetSeatManager()->GetLastPedInSeat(0);
if(pPed)
{
// If the last person to drive this car (the person who parked it), isn't the owner of this garage, then it is not allowed to be saved.
if( pPed->GetPedModelInfo()->GetModelNameHash() != owner.GetHash() )
{
isAcceptableGarage = false;
}
}
}
if(isAcceptableGarage)
{
if (m_permittedVehicleType == VEHICLE_TYPE_NONE)
{
storeVehicle = (pVehicle->GetVehicleType() == VEHICLE_TYPE_CAR || pVehicle->GetVehicleType() == VEHICLE_TYPE_BIKE || pVehicle->GetVehicleType() == VEHICLE_TYPE_BICYCLE || pVehicle->GetVehicleType() == VEHICLE_TYPE_HELI || pVehicle->GetVehicleType() == VEHICLE_TYPE_QUADBIKE);
}
else
{
storeVehicle = m_permittedVehicleType == pVehicle->GetVehicleType();
}
}
}
}
}
}
return storeVehicle;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : StoreAndRemoveCarsForThisHideOut
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
void CGarage::StoreAndRemoveCarsForThisHideOut(CStoredVehicle *aStoredCars, bool bRemoveCars)
{
s32 C;
Vector3 GarageCentre;
CalcCentrePointOfGarage(GarageCentre);
// Clear the cars first
for (C = 0; C < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; C++)
{
aStoredCars[C].Clear();
}
// Find the first car in the area of the garage.
// Go through the cars in the vehicle list
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVehicle;
s32 i = (s32) pool->GetSize();
C = 0;
CVehicle *pVehToStore[MAX_NUM_STORED_CARS_IN_SAFEHOUSE];
float distVehToStore[MAX_NUM_STORED_CARS_IN_SAFEHOUSE];
for (s32 j = 0; j < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; j++)
{
pVehToStore[j] = NULL;
distVehToStore[j] = 99999.9f;
}
const u32 MaxCarsWeExpectToEverFitInAGarage = 10;
CVehicle *pCarsToRemove[MaxCarsWeExpectToEverFitInAGarage];
u32 numberOfCarsFoundInGarage = 0;
while(i--)
{
pVehicle = pool->GetSlot(i);
if (pVehicle)
{
if (CanThisVehicleBeStoredInThisGarage(pVehicle))
{
float distToCentre = (VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()) - GarageCentre).Mag();
if (distToCentre < distVehToStore[0])
{
pVehToStore[1] = pVehToStore[0];
distVehToStore[1] = distVehToStore[0];
pVehToStore[0] = pVehicle;
distVehToStore[0] = distToCentre;
}
else if (distToCentre < distVehToStore[1])
{
pVehToStore[1] = pVehicle;
distVehToStore[1] = distToCentre;
}
if (numberOfCarsFoundInGarage < MaxCarsWeExpectToEverFitInAGarage)
{
pCarsToRemove[numberOfCarsFoundInGarage++] = pVehicle;
}
}
}
}
for (s32 j = 0; j < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; j++)
{
if (pVehToStore[j])
{
if(Verifyf(!pVehToStore[j]->IsNetworkClone(), "CGarage::StoreAndRemoveCarsForThisHideOut - Graeme - just checking if the game can ever reach here for a network clone"))
{
aStoredCars[C].StoreVehicle(pVehToStore[j]);
C++;
}
}
}
// Fill in the rest with zeros
while (C < MAX_NUM_STORED_CARS_IN_SAFEHOUSE)
{
aStoredCars[C].Clear();
C++;
}
if (bRemoveCars)
{ // Remove the cars we stored (up to 2) and the excess cars in the garage that we didn't store
for (u32 removeLoop = 0; removeLoop < numberOfCarsFoundInGarage; removeLoop++)
{
CVehicleFactory::GetFactory()->Destroy(pCarsToRemove[removeLoop]);
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : RestoreCarsForThisHideOut
// PURPOSE :
// RETURNS : true if all the cars have been generated.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::RestoreCarsForThisHideOut(CStoredVehicle *aStoredCars)
{
s32 C;
CVehicle *pVehicle;
bool physicsLoaded = true;
bool modelsLoaded = true;
for (C = 0; C < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; C++)
{
// We have to request the appropriate cars and wait for them to be loaded first
if (aStoredCars[C].IsUsed())
{
fwModelId vehicleModelId;
u32 vehicleHash = aStoredCars[C].GetModelHashKey();
if(!CModelInfo::GetBaseModelInfoFromHashKey(vehicleHash, &vehicleModelId)) // we need to check this for DLC vehicles
{
Warningf("CGarage::RestoreCarsForThisHideOut vehicle with model hash 0x%X doesn't exist! - > Removing reference to it in savegame", vehicleHash);
aStoredCars[C].Clear();
continue;
}
bool assetsLoaded = CModelInfo::HaveAssetsLoaded(vehicleModelId);
if (!assetsLoaded)
{
CModelInfo::RequestAssets(vehicleModelId, STRFLAG_FORCE_LOAD | STRFLAG_INTERIOR_REQUIRED);
modelsLoaded = false;
aStoredCars[C].m_bInteriorRequired = true;
}
bool testPending = false;
switch (aStoredCars[C].m_collisionTestResult.GetResultsStatus())
{
case WorldProbe::WAITING_ON_RESULTS:
physicsLoaded = false;
testPending = true;
break;
case WorldProbe::RESULTS_READY:
physicsLoaded = aStoredCars[C].m_collisionTestResult.GetNumHits() > 0;
if (!physicsLoaded)
{
aStoredCars[C].m_collisionTestResult.Reset();
}
testPending = true; // Vital, or the next vehicle will never be tested, which will cause the spawn to fail
break;
case WorldProbe::TEST_NOT_SUBMITTED: // fall through, don't do anything and re-launch a test each interval..
case WorldProbe::SUBMISSION_FAILED:
case WorldProbe::TEST_ABORTED:
default:
physicsLoaded = false;
break;
}
if (CGarages::timeForNextCollisionTest < fwTimer::GetTimeInMilliseconds() && !testPending)
{
const Vector3 carPos(aStoredCars[C].m_CoorX, aStoredCars[C].m_CoorY, aStoredCars[C].m_CoorZ);
WorldProbe::CShapeTestProbeDesc probeDesc;
probeDesc.SetResultsStructure(&aStoredCars[C].m_collisionTestResult);
const float extent = 3.0f;
probeDesc.SetStartAndEnd(carPos + Vector3(extent, extent, extent), carPos + Vector3(-extent, -extent, -extent));
probeDesc.SetExcludeEntity(NULL);
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc, WorldProbe::PERFORM_ASYNCHRONOUS_TEST);
CGarages::timeForNextCollisionTest = fwTimer::GetTimeInMilliseconds() + 2000;
}
}
}
if (physicsLoaded && modelsLoaded)
{
for (C = 0; C < MAX_NUM_STORED_CARS_IN_SAFEHOUSE; C++)
{
if (aStoredCars[C].IsUsed())
{
pVehicle = aStoredCars[C].RestoreVehicle();
if(aStoredCars[C].m_bInteriorRequired)
{
fwModelId vehicleModelId;
u32 vehicleHash = aStoredCars[C].GetModelHashKey();
CModelInfo::GetBaseModelInfoFromHashKey(vehicleHash, &vehicleModelId); // ignores return value
CModelInfo::ClearAssetRequiredFlag(vehicleModelId, STRFLAG_INTERIOR_REQUIRED);
aStoredCars[C].m_bInteriorRequired = false;
}
Assert(pVehicle);
if (pVehicle)
{
aStoredCars[C].Clear();
}
}
}
return true; // done reviving this garage
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CameraShouldBeOutside
// PURPOSE : If any garage is doing stuff (opening, closing etc) the camera should
// be outside of the garage.
/////////////////////////////////////////////////////////////////////////////////
CGarage *CGarages::GarageCameraShouldBeOutside()
{
return pGarageCamShouldBeOutside;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : TidyUpGarage
// PURPOSE : Removes stuff that can potentially block the car. (Wrecks etc)
// Should only be called if the player cannot see the garage.
/////////////////////////////////////////////////////////////////////////////////
void CGarage::TidyUpGarage()
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVeh;
s32 i = (s32) pool->GetSize()-1;
while (i)
{
pVeh = pool->GetSlot(i);
if (pVeh && (pVeh->InheritsFromAutomobile() || pVeh->InheritsFromBike()))
{
if (IsPointInsideGarage(VEC3V_TO_VECTOR3(pVeh->GetTransform().GetPosition())))
{
// If this is a wreck we'll take it away
if (pVeh->GetStatus() == STATUS_WRECKED || pVeh->GetTransform().GetC().GetZf() < 0.5f)
{
if (pVeh->CanBeDeleted())
{
if (pVeh->GetNetworkObject()==0 || (pVeh->GetNetworkObject()->CanDelete() && !pVeh->IsNetworkClone()))
{
CGameWorld::Remove(pVeh);
CVehicleFactory::GetFactory()->Destroy(pVeh);
}
}
}
}
}
i--;
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : TidyUpGarageClose
// PURPOSE : Removes stuff that can potentially block the car. (Wrecks etc)
// This version will be called even when player is close.
// Emergency stuff.
/////////////////////////////////////////////////////////////////////////////////
void CGarage::TidyUpGarageClose()
{
CVehicle::Pool *pool = CVehicle::GetPool();
CVehicle* pVeh;
s32 i = (s32) pool->GetSize()-1;
Vector3 Center;
bool bRemove;
static u32 missionVehCount = 0; // Fudgy counter to make sure mission vehicles don't get removed the second they explode.
while (i)
{
pVeh = pool->GetSlot(i);
// Wrecks should be removed asap since the player cannot always move them himself.
if (pVeh && (pVeh->InheritsFromAutomobile() || pVeh->InheritsFromBike()) && (pVeh->GetStatus() == STATUS_WRECKED) )
{
if (IsEntityTouching3D(pVeh))
{
// Make sure we have at least one sphere outside garage
bRemove = false;
if (state == GSTATE_CLOSED)
{
bRemove = true;
}
else if (EntityHasASphereWayOutsideGarage(pVeh))
{
bRemove = true;
}
if (pVeh->GetNetworkObject() && (!pVeh->GetNetworkObject()->CanDelete() || pVeh->IsNetworkClone()))
{
bRemove = false;
}
if (bRemove)
{
if (pVeh->CanBeDeleted())
{
CGameWorld::Remove(pVeh);
CVehicleFactory::GetFactory()->Destroy(pVeh);
}
else
{
missionVehCount++; // Mission vehicles don't get removed straight away.
if (missionVehCount > 100)
{
CGameWorld::Remove(pVeh);
CVehicleFactory::GetFactory()->Destroy(pVeh);
missionVehCount = 0;
}
}
}
}
}
i--;
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : PlayerArrestedOrDied
// PURPOSE : Takes appropriate steps if the player is arrested or has died.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::PlayerArrestedOrDied(void)
{
s32 Index;
for (Index = 0; Index < aGarages.GetCount(); Index++)
{
if (aGarages[Index].type != GARAGE_NONE)
{
aGarages[Index].PlayerArrestedOrDied();
}
}
CloseHideOutGaragesBeforeSave(true);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : PlayerArrestedOrDied
// PURPOSE : Takes appropriate steps if the player is arrested or has died for
// this garage.
/////////////////////////////////////////////////////////////////////////////////
void CGarage::PlayerArrestedOrDied(void)
{
switch(type)
{
// Garages that should be open
case GARAGE_RESPRAY:
switch(state)
{
case GSTATE_OPEN:
break;
case GSTATE_OPENING:
case GSTATE_CLOSING:
case GSTATE_CLOSED:
SetState(GSTATE_OPENING);
break;
default:
break;
}
break;
// Garages that should be closed
case GARAGE_MISSION:
case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE:
case GARAGE_AMBIENT_PARKING:
switch(state)
{
case GSTATE_CLOSED:
break;
case GSTATE_OPENING:
case GSTATE_CLOSING:
case GSTATE_OPEN:
SetState(GSTATE_CLOSING);
break;
default:
break;
}
break;
case GARAGE_SAFEHOUSE_PARKING_ZONE:
// Do nothing
break;
default:
Assert(0);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPointInsideGarage
// PURPOSE : Tests whether this point is inside a garage.
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsPointInsideGarage(const Vector3& Point, int boxIndex)
{
return IsPointInsideGarage(Point, 0.f, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPointInsideGarage
// PURPOSE : Tests whether this point is inside a garage.
// A positive margin makes it easier for the point to fix (garage bigger)
// A negative margin makes it harder (smaller garage or radius)
/////////////////////////////////////////////////////////////////////////////////
bool CGarage::IsPointInsideGarage(const Vector3& Point, float Margin, int boxIndex)
{
int startIndex;
int count;
if (boxIndex == ALL_BOXES_INDEX)
{
startIndex = 0;
count = m_boxes.GetCount();
}
else
{
startIndex = boxIndex;
count = boxIndex + 1;
}
for (int i = startIndex; i < count; ++i)
{
Box &box = m_boxes[i];
if ((Point.z < box.BaseZ - Margin) || (Point.z > box.CeilingZ + Margin))
{
continue;
}
float Delta1Length = rage::Sqrtf(box.Delta1X * box.Delta1X + box.Delta1Y * box.Delta1Y);
Assertf(Delta1Length > 0.1f, "Garage corner points of Box %d are identical", i);
float invDelta1Length = 1.0f / Delta1Length;
float normDelta1X = box.Delta1X * invDelta1Length;
float normDelta1Y = box.Delta1Y * invDelta1Length;
float DotProd = (Point.x - box.BaseX) * normDelta1X + (Point.y - box.BaseY) * normDelta1Y;
if ((DotProd < -Margin) || (DotProd > Delta1Length + Margin))
{
continue;
}
float Delta2Length = rage::Sqrtf(box.Delta2X * box.Delta2X + box.Delta2Y * box.Delta2Y);
Assertf(Delta1Length > 0.1f, "Garage corner points of Box %d are identical", i);
float invDelta2Length = 1.0f / Delta2Length;
float normDelta2X = box.Delta2X * invDelta2Length;
float normDelta2Y = box.Delta2Y * invDelta2Length;
DotProd = (Point.x - box.BaseX) * normDelta2X + (Point.y - box.BaseY) * normDelta2Y;
if ((DotProd < -Margin) || (DotProd > Delta2Length + Margin))
{
continue;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CalcCentrePointOfGarage
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
void CGarage::CalcCentrePointOfGarage(Vector3& result)
{
// For the moment just use box 0 as the Garage and log a warning out if theres more than 1
Box &box0 = m_boxes[0];
result = Vector3(box0.BaseX, box0.BaseY, box0.BaseZ);
result.x += 0.5f * box0.Delta1X + 0.5f * box0.Delta2X;
result.y += 0.5f * box0.Delta1Y + 0.5f * box0.Delta2Y;
result.z = (box0.BaseZ + box0.CeilingZ) * 0.5f;
#if __DEV
if (m_boxes.GetCount() > 1)
{
Warningf("CalcCentrePointOfGarage only uses box 0 to calculate the centre point");
}
#endif
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CloseHideOutGaragesBeforeSave
// PURPOSE : Closes hideout garages that are open so that cars will be saved.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::CloseHideOutGaragesBeforeSave(bool bByGameLogic)
{
if (!NetworkInterface::IsGameInProgress()) // Parking spots outside safehouse only work in single player.
{
// Go through the garages
for (s32 Index = 0; Index < aGarages.GetCount(); Index++)
{
if ( (aGarages[Index].type == GARAGE_SAFEHOUSE_PARKING_ZONE) && (aGarages[Index].Flags.bSavingVehiclesEnabled) )
{ // If this is one of the hideout garages
if (aGarages[Index].GetState() != GSTATE_CLOSED)
{
aGarages[Index].StoreAndRemoveCarsForThisHideOut(&CGarages::aCarsInSafeHouse[aGarages[Index].safeHouseIndex][0], bByGameLogic);
aGarages[Index].state = GSTATE_CLOSED;
aGarages[Index].Flags.bSafehouseGarageWasOpenBeforeSave = true;
}
else
{
aGarages[Index].Flags.bSafehouseGarageWasOpenBeforeSave = false;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : OpenHideOutGaragesAfterSave
// PURPOSE : Open hideout garages that were open before. Cars should still sit on top.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::OpenHideOutGaragesAfterSave()
{
s32 Index;
// Go through the garages
for (Index = 0; Index < aGarages.GetCount(); Index++)
{
if (aGarages[Index].type == GARAGE_SAFEHOUSE_PARKING_ZONE)
{ // If this is one of the hideout garages
if (aGarages[Index].Flags.bSafehouseGarageWasOpenBeforeSave)
{
aGarages[Index].state = GSTATE_OPEN;
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CloseSafeHouseGarages
// PURPOSE : Closes hideout garages that are open so that cars will be saved.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::CloseSafeHouseGarages()
{
// Go through the garages
for (s32 Index = 0; Index < aGarages.GetCount(); Index++)
{
if ( (aGarages[Index].type == GARAGE_SAFEHOUSE_PARKING_ZONE) && (aGarages[Index].Flags.bSavingVehiclesEnabled) )
{ // If this is one of the hideout garages
if (aGarages[Index].GetState() != GSTATE_CLOSED)
{
aGarages[Index].StoreAndRemoveCarsForThisHideOut(&CGarages::aCarsInSafeHouse[aGarages[Index].safeHouseIndex][0], true);
aGarages[Index].state = GSTATE_CLOSED;
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : FindMaxNumStoredCarsForGarage
// PURPOSE : Works out how many cars could possible
/////////////////////////////////////////////////////////////////////////////////
s32 CGarage::FindMaxNumStoredCarsForGarage()
{
return (MAX_NUM_STORED_CARS_IN_SAFEHOUSE);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : StopCarFromBlowingUp
// PURPOSE : Make sure the car is not about to blow up
/////////////////////////////////////////////////////////////////////////////////
void CGarages::StopCarFromBlowingUp(CAutomobile *pCar)
{
pCar->SetHealth(MAX(pCar->GetHealth(), CAR_ON_FIRE_HEALTH+50));
pCar->GetVehicleDamage()->ResetEngineHealth();
pCar->GetVehicleDamage()->ResetPetrolTankHealth();
//pCar->Damage.SetEngineStatus(MAX(DT_ENGINE_ON_FIRE+50, pCar->Damage.GetEngineStatus()));
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Returns true if this vehicle is within a save garage. (Car should not be deleted)
// PURPOSE : Does the same checks as CGarage::Update() and StoreAndRemoveCarsForThisHideOut()
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsVehicleWithinHideOutGarageThatCanSaveIt(const CVehicle *pVehicle)
{
if (NetworkInterface::IsGameInProgress() REPLAY_ONLY(|| CReplayMgr::IsClearingWorldForReplay()))
{ // Parking spots outside safehouse only work in single player.
return false;
}
if (pVehicle)
{
for (s32 Garage = 0; Garage < aGarages.GetCount(); Garage++)
{
CGarage &thisGarage = aGarages[Garage];
if ( (thisGarage.type == GARAGE_SAFEHOUSE_PARKING_ZONE) && (thisGarage.Flags.bSavingVehiclesEnabled) )
{
if (thisGarage.CanThisVehicleBeStoredInThisGarage(pVehicle))
{
return true;
}
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Returns true if this coordinate is within a garage.
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsPointWithinAnyGarage(const Vector3 &Point)
{
s32 Garage;
for (Garage = 0; Garage < aGarages.GetCount(); Garage++)
{
switch(aGarages[Garage].type)
{
default:
if (aGarages[Garage].IsPointInsideGarage(Point))
{
return true;
}
break;
case GARAGE_NONE:
break;
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AllRespraysCloseOrOpen
// PURPOSE : Goes through all resprays and closes or opens them.
/////////////////////////////////////////////////////////////////////////////////
void CGarages::AllRespraysCloseOrOpen(bool bOpen)
{
if (bOpen)
{
FindPlayerPed()->GetPlayerInfo()->EnableControlsGarages();
}
for (u32 Garage = 0; Garage < aGarages.GetCount(); Garage++)
{
if (aGarages[Garage].type == GARAGE_RESPRAY)
{
if (!aGarages[Garage].m_pDoorObject)
{
if (bOpen)
{
aGarages[Garage].state = GSTATE_OPEN;
}
else
{
aGarages[Garage].state = GSTATE_CLOSED;
}
}
else
{
if (aGarages[Garage].state == GSTATE_CLOSED) // We have to do this here or the garage will repair the players car when SET_NO_RESPRAYS FALSE happens
{
aGarages[Garage].state = GSTATE_OPENING;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AttachDoorToGarage
// PURPOSE : Gets called when an door object is given physics.
// If there is a garage that could use this door we attach the object
// to that garage and the garage can control the door.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::AttachDoorToGarage(CDoor* pObj)
{
for (s32 g = 0; g < aGarages.GetCount(); g++)
{
CGarage &theGarage = aGarages[g];
// Only connect doors to enclosed garages, everything else remains as before
if( theGarage.m_IsEnclosed )
{
if (theGarage.IsPointInsideGarage(VEC3V_TO_VECTOR3(pObj->GetTransform().GetPosition()), 3.0f))
{
Assert(!theGarage.m_pDoorObject);
theGarage.m_pDoorObject = pObj;
/* // Tell the door to be open or closed depending on the state of the garage. This will also cancel their
// default door behaviour.
switch (theGarage.GetState())
{
case GSTATE_OPEN:
case GSTATE_OPENING:
case GSTATE_OPENEDWAITINGTOBEREACTIVATED:
theGarage.SlideDoorOpen();
break;
case GSTATE_CLOSED:
case GSTATE_CLOSING:
case GSTATE_MISSIONCOMPLETED:
default:
theGarage.SlideDoorClosed();
break;
}
*/
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPaynSprayActive
// PURPOSE : Returns true if any of the respray shops is opening/closing or repairing the players car.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsPaynSprayActive()
{
if (NoResprays)
{
return false;
}
for (s32 i = 0; i < aGarages.GetCount(); i++)
{
if (aGarages[i].type == GARAGE_RESPRAY)
{
if (aGarages[i].m_pDoorObject)
{
if (aGarages[i].state != GSTATE_OPEN)
{
return true;
}
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : PlayerIsInteractingWithGarage
// PURPOSE : For the script to use to determine whether the player is interacting with a garage.
// If so certain cut scenes should not kick off.
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::PlayerIsInteractingWithGarage()
{
if (IsPaynSprayActive())
{
return true;
}
if (CGarages::GarageCameraShouldBeOutside())
{
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : InitWidgets
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
#if __BANK
void CGarages::InitWidgets()
{
bkBank *bank = CGameLogic::GetGameLogicBank();
bank->PushGroup("Garages");
bank->AddToggle("Render debug", &bDebugDisplayGarages);
bank->AddToggle("Alternate garage occupancy check", &bAlternateOccupantSearch);
CGarageEditor::InitWidgets(*bank);
bank->PopGroup();
}
#endif // __BANK
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsPlayerPartialyInsideGarage
// PURPOSE : For script to check if player is partially inside garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsPlayerPartiallyInsideGarage(s32 garageIndex, const CEntity *pEntity, int boxIndex)
{
Assertf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex);
Assertf(boxIndex >= CGarage::ALL_BOXES_INDEX && boxIndex < aGarages[garageIndex].m_boxes.GetCount(), "Invalid Box Index %d for Garage %d", boxIndex, garageIndex);
CGarage *pGarage = &aGarages[garageIndex];
Assertf(pEntity->GetBaseModelInfo()->GetModelType() == MI_TYPE_PED, "Player Model Type is not MI_TYPE_PED, %d", pEntity->GetBaseModelInfo()->GetModelType());
const CPed *pPlayer = static_cast<const CPed*>(pEntity);
if (pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
{
CVehicle *pVehicle = pPlayer->GetMyVehicle();
if (Verifyf(pVehicle, "IsPlayerPartiallyInsideGarage ... Ped [%s] has no vehicle but has config flag CPED_CONFIG_FLAG_InVehicle", pPlayer->GetModelName()))
{
return pGarage->IsEntityTouching3D(pVehicle, boxIndex) && !pGarage->IsEntityEntirelyInside3D(pVehicle, 0.0f, boxIndex);
}
}
return pGarage->IsEntityTouching3D(pPlayer, boxIndex) && !pGarage->IsEntityEntirelyInside3D(pPlayer, 0.0f, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : AreEntitiesEntirelyInsideGarage
// PURPOSE : returns true if all entities of the specified type(s) are fully inside the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::AreEntitiesEntirelyInsideGarage(s32 garageIndex, bool peds, bool vehs, bool objs, float margin, int boxIndex)
{
if (!Verifyf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex))
return true;
CGarage *pGarage = &aGarages[garageIndex];
return pGarage->AreEntitiesEntirelyInsideGarage(peds, vehs, objs, margin, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsAnyEntitiesEntirelyInsideGarage
// PURPOSE : returns true if any entities of the specified type(s) are fully inside the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsAnyEntityEntirelyInsideGarage(s32 garageIndex, bool peds, bool vehs, bool objs, float margin, int boxIndex)
{
if (!Verifyf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex))
return true;
CGarage *pGarage = &aGarages[garageIndex];
return pGarage->IsAnyEntityEntirelyInsideGarage(peds, vehs, objs, margin, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsLineIntersectGarage
// PURPOSE : returns true the line intersects the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsLineIntersectingGarage(s32 garageIndex, const Vector3& start, const Vector3 &end, int boxIndex )
{
if (!Verifyf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex))
return true;
CGarage *pGarage = &aGarages[garageIndex];
return pGarage->IsLineIntersectingGarage(start, end, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsLineIntersectGarage
// PURPOSE : returns true the line intersects the garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsBoxIntersectingGarage(s32 garageIndex, const Vector3 &p0, const Vector3 &p1, const Vector3 &p2, const Vector3 &p3, int boxIndex )
{
if (!Verifyf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex))
return true;
CGarage *pGarage = &aGarages[garageIndex];
return pGarage->IsBoxIntersectingGarage(p0, p1, p2, p3, boxIndex);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsObjectPartialyInsideGarage
// PURPOSE : For script to check if object is partially inside garage
/////////////////////////////////////////////////////////////////////////////////
bool CGarages::IsObjectPartiallyInsideGarage(s32 garageIndex, const CEntity *pEntity, int boxIndex)
{
Assertf(garageIndex >= 0 && garageIndex < aGarages.GetCount(), "Invalid Garage Index %d", garageIndex);
Assertf(boxIndex >= CGarage::ALL_BOXES_INDEX && boxIndex < aGarages[garageIndex].m_boxes.GetCount(), "Invalid Box Index %d for Garage %d", boxIndex, garageIndex);
CGarage *pGarage = &aGarages[garageIndex];
return pGarage->IsEntityTouching3D(pEntity, boxIndex) && !pGarage->IsEntityEntirelyInside3D(pEntity, 0.0f, boxIndex);
}
void CGarage::FindAllObjectsInGarage(atArray<CEntity*> &objects, bool checkPeds, bool checkVehicles, bool checkObjects)
{
s32 Num, C;
const int maxEntites = 32;
CEntity *apEnts[maxEntites];
CGameWorld::FindObjectsIntersectingCube(Vector3(MinX, MinY, MinZ), Vector3(MaxX, MaxY, MaxZ), &Num, maxEntites, apEnts,
false, checkVehicles, checkPeds, checkObjects, false);
// Go through the entities found and test whether any of them really are touching the garage
for (C = 0; C < Num; C++)
{
if (IsObjectEntirelyInsideGarage(apEnts[C]))
{
objects.PushAndGrow(apEnts[C]);
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// NETWORK FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////
void CGarage::NetworkInit(unsigned UNUSED_PARAM(garageIndex))
{
for (u32 i=0; i<m_boxesOccupiedForPlayers.GetCount(); i++)
{
m_boxesOccupiedForPlayers[i] = 0;
}
m_lastBoxesOccupied = 0;
m_bBoxesOccupiedHasChanged = false;
}
void CGarage::NetworkUpdate(unsigned garageIndex)
{
// broadcast changed occupied state if necessary
if (NetworkInterface::IsGameInProgress())
{
if (m_bBoxesOccupiedHasChanged)
{
CGarageOccupiedStatusEvent::Trigger(garageIndex, m_lastBoxesOccupied);
m_bBoxesOccupiedHasChanged = false;
}
}
}
void CGarage::UpdateOccupiedBoxes(bool alternateSearch)
{
// check for changes in the occupied status of the garage - network only
if (NetworkInterface::IsGameInProgress())
{
BoxFlags currBoxesOccupied = 0;
for (u32 i=0; i<m_boxes.GetCount(); i++)
{
if (!IsGarageEmpty(i, alternateSearch))
{
currBoxesOccupied |= (1<<i);
}
}
if (currBoxesOccupied != m_lastBoxesOccupied)
{
m_bBoxesOccupiedHasChanged = true; // we need to send out this status to players
m_lastBoxesOccupied = currBoxesOccupied;
}
}
}
bool CGarage::IsGarageEmptyOnAllMachines(int boxIndex)
{
if (NetworkInterface::IsGameInProgress())
{
if (boxIndex == ALL_BOXES_INDEX)
{
for (u32 i=0; i<m_boxes.GetCount(); i++)
{
if (m_boxesOccupiedForPlayers[i] != 0)
{
return false;
}
}
}
else if (Verifyf(boxIndex >= 0 && boxIndex < m_boxesOccupiedForPlayers.GetCount(), "Invalid Box Index %d", boxIndex))
{
if (m_boxesOccupiedForPlayers[boxIndex] != 0)
{
return false;
}
}
}
return IsGarageEmpty(boxIndex);
}
void CGarage::ReceiveOccupiedStateFromPlayer(PhysicalPlayerIndex playerIndex, BoxFlags boxesOccupied)
{
for (u32 i=0; i<m_boxes.GetCount(); i++)
{
if (boxesOccupied & (1<<i))
{
m_boxesOccupiedForPlayers[i] |= (1<<playerIndex);
}
else
{
m_boxesOccupiedForPlayers[i] &= ~(1<<playerIndex);
}
}
}
void CGarage::PlayerHasLeft(PhysicalPlayerIndex playerIndex)
{
for (u32 i=0; i<m_boxes.GetCount(); i++)
{
m_boxesOccupiedForPlayers[i] &= ~(1<<playerIndex);
}
}
void CGarage::PlayerHasJoined(PhysicalPlayerIndex playerIndex, unsigned garageIndex)
{
if (m_lastBoxesOccupied)
{
CGarageOccupiedStatusEvent::Trigger(garageIndex, m_lastBoxesOccupied, playerIndex);
}
}
void CGarages::NetworkInit()
{
for (u32 i=0; i<aGarages.GetCount(); i++)
{
aGarages[i].NetworkInit(i);
}
}
void CGarages::PlayerHasLeft(PhysicalPlayerIndex playerIndex)
{
for (u32 i=0; i<aGarages.GetCount(); i++)
{
aGarages[i].PlayerHasLeft(playerIndex);
}
}
void CGarages::PlayerHasJoined(PhysicalPlayerIndex playerIndex)
{
for (u32 i=0; i<aGarages.GetCount(); i++)
{
aGarages[i].PlayerHasJoined(playerIndex, i);
}
}