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

1432 lines
41 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : stuntjump.h
// PURPOSE : Logic to deal with the stunt jump
// AUTHOR : Greg
// CREATED : 5/03/04
//
/////////////////////////////////////////////////////////////////////////////////
// rage headers
#include "grcore/debugdraw.h"
// game headers
#include "audio/scriptaudioentity.h"
#include "control/gamelogic.h"
#include "control/stuntjump.h"
#include "core/game.h"
#include "frontend/frontendstatsmgr.h"
#include "frontend/MobilePhone.h"
#include "frontend/NewHud.h"
#include "Frontend/ui_channel.h"
#include "scene/world/gameWorld.h"
#include "peds/ped.h"
#include "scene/playerswitch/PlayerSwitchInterface.h"
#include "scene/world/GameWorldHeightMap.h"
#include "script/script.h"
#include "Stats/StatsMgr.h"
#include "Text/Messages.h"
#include "vehicles/boat.h"
#include "vehicles/Automobile.h"
#include "audio/frontendaudioentity.h"
#include "audio/northaudioengine.h"
#include "task/system/AsyncProbeHelper.h"
#include "camera/CamInterface.h"
#include "Stats/StatsInterface.h"
#include "modelinfo/PedModelInfo.h"
VEHICLE_OPTIMISATIONS()
FW_INSTANTIATE_CLASS_POOL_SPILLOVER(CStuntJump, CONFIGURED_FROM_FILE, 0.16f, atHashString("CStuntJump",0xfd0abf92));
#define STUNT_JUMP_MOVIE "STUNT_JUMPS"
// milliseconds
#define TWEEN_OUT_DURATION (660)
#define PRINT_MESSAGE_TIMEOUT (2000)
#if __BANK
static bool bDisplayStuntJumps = false;
static bool bTriggerJumpMessage = false;
static int iDebugJumpsLeft = -1;
static u32 iDebugNumberOfStuntJumps = 0;
static bool bSetFoundStuntJumps = false;
static bool bSetCompletedStuntJumps = false;
static u32 iDebugStuntJumpBitToSet = 0;
static u64 iDebugFoundBitSet = 0;
static char foundBitSetAsString[52];
static bool bSetFoundStuntJumpsMask = false;
static bool bClearFoundStuntJumpsMask = false;
static u64 iDebugCompletedBitSet = 0;
static char completedBitSetAsString[52];
static bool bSetCompletedStuntJumpsMask = false;
static bool bClearCompletedStuntJumpsMask = false;
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CStuntJump::ShouldActivate(const CVehicle* vehicle)
{
const Vec3V vVehiclePosition = vehicle->GetTransform().GetPosition();
bool boxValid = false;
Vec3V vEndBoxCentre;
if( !m_bIsAngled )
{
// aaBB
if(m_startBox.ContainsPoint(vVehiclePosition))
{
vEndBoxCentre = m_endBox.GetCenter();
boxValid = true;
}
}
else
{
if(m_startAngledBox.TestPoint(VEC3V_TO_VECTOR3(vVehiclePosition)))
{
vEndBoxCentre = VECTOR3_TO_VEC3V( m_endAngledBox.m_vPt1 + ((m_endAngledBox.m_vPt2 - m_endAngledBox.m_vPt1) * 0.5f) );
boxValid = true;
}
}
if(boxValid)
{
// Make sure the player is travelling in the direction of the end box.
Vector3 vDirToEndBox = VEC3V_TO_VECTOR3(vEndBoxCentre - vVehiclePosition);
vDirToEndBox.z = 0.0f;
if (vDirToEndBox.Dot(vehicle->GetVelocity()) > 0.0f)
return true;
}
return false;
}
bool CStuntJump::IsSuccessful(const CVehicle* vehicle)
{
if( !m_bIsAngled )
{
return m_endBox.ContainsPoint(vehicle->GetTransform().GetPosition());
}
return m_endAngledBox.TestPoint( VEC3V_TO_VECTOR3(vehicle->GetTransform().GetPosition()) );
}
#if __BANK
void CStuntJump::DebugRender()
{
Vector3 playerPosn = CGameWorld::FindLocalPlayerCoors();
Vector3 Diff;
if( !m_bIsAngled )
{
Diff = playerPosn - VEC3V_TO_VECTOR3(m_startBox.GetMin());
}
else
{
Diff = playerPosn - m_startAngledBox.m_vPt1;
}
if (Diff.Mag() < 500.0f)
{
if( !m_bIsAngled )
{
grcDebugDraw::BoxAxisAligned(m_startBox.GetMin(), m_startBox.GetMax(), Color32(255, 0, 0, 255), false);
grcDebugDraw::BoxAxisAligned(m_endBox.GetMin(), m_endBox.GetMax(), Color32(0, 255, 0, 255), false);
}
else
{
m_startAngledBox.DebugDraw(Color32(255, 0, 0, 255));
m_endAngledBox.DebugDraw(Color32(0, 255, 0, 255));
}
}
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
CStuntJumpManager::CStuntJumpManager():
m_bActive(true),
mp_Active(NULL),
m_bHitReward(false),
m_fTimer(0.0f),
m_jumpState(JUMP_INACTIVE),
m_iTweenOutColour(HUD_COLOUR_WHITE),
m_iNumJumps(0),
m_iNumActiveJumps(0),
m_levelsEnabled(0),
m_clearTimer(0),
m_messageMovie(false),
m_bMessageVisible(true),
m_bShowOptionalStuntCameras(false),
m_lastStuntJumpId(0)
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::Init(unsigned initMode)
{
//@@: range CSTUNTJUMPMANAGER_INIT {
if(initMode == INIT_CORE)
{
//@@: location CSTUNTJUMPMANAGER_INIT_CORE
CStuntJump::InitPool( MEMBUCKET_GAMEPLAY );
SStuntJumpManager::Instantiate();
}
else if(initMode == INIT_SESSION)
{
//@@: location CSTUNTJUMPMANAGER_INIT_SESSION
CStuntJump::GetPool()->DeleteAll();
if(uiVerify(SStuntJumpManager::IsInstantiated()))
{
SStuntJumpManager::GetInstance().Clear();
}
}
//@@: } CSTUNTJUMPMANAGER_INIT
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::Shutdown(unsigned shutdownMode)
{
if(shutdownMode == SHUTDOWN_CORE)
{
CStuntJump::ShutdownPool();
SStuntJumpManager::Destroy();
}
else if(shutdownMode == SHUTDOWN_SESSION)
{
if(uiVerify(SStuntJumpManager::IsInstantiated()))
{
SStuntJumpManager::GetInstance().AbortStuntJumpInProgress();
}
}
}
void CStuntJumpManager::Clear()
{
mp_Active = NULL;
m_bHitReward = false;
m_fTimer = 0.0f;
m_clearTimer = 0;
m_jumpState = JUMP_INACTIVE;
m_iTweenOutColour = HUD_COLOUR_WHITE;
m_iNumJumps = 0;
// enable single player stunt jumps
m_levelsEnabled = (1<<0);
m_bMessageVisible = true;
m_iStuntJumpCounter = 0;
m_messageMovie.RemoveMovie();
m_bShowOptionalStuntCameras = false;
m_lastStuntJumpId = 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::AbortStuntJumpInProgress()
{
if (m_jumpState != JUMP_INACTIVE)
{
m_jumpState = JUMP_SHUTTING_DOWN;
m_clearTimer = 0;
mp_Active = NULL;
}
}
void CStuntJumpManager::PrintBigMessageStats(const char* pTitle, const char* pTextTag, float distance, float height)
{
if(m_messageMovie.BeginMethod("SHOW_SHARD_STUNT_JUMP"))
{
m_messageMovie.AddParamLocString(pTitle, false);
if(pTextTag)
{
const int bufferSize = 128;
char buffer[bufferSize];
CNumberWithinMessage number[2];
number[0].Set(distance, 2);
number[1].Set(height, 2);
CMessages::InsertNumbersAndSubStringsIntoString(TheText.Get(pTextTag), &number[0], 2, NULL, 0, buffer, bufferSize);
m_messageMovie.AddParamString(buffer, false);
m_messageMovie.AddParamString(buffer, false);
}
m_messageMovie.EndMethod();
}
m_bMessageVisible = true;
}
void CStuntJumpManager::PrintBigMessage(const char* pTitle, const char* UNUSED_PARAM(pText), const char* pTextLine2, int duration, s32 NumberToInsert1)
{
if(m_messageMovie.BeginMethod("SHOW_SHARD_STUNT_JUMP"))
{
m_messageMovie.AddParamLocString(pTitle, false);
if(pTextLine2)
{
if(NumberToInsert1 == NO_NUMBER_TO_INSERT_IN_STRING)
{
m_messageMovie.AddParamLocString(pTextLine2, false);
m_messageMovie.AddParamLocString(pTextLine2, false);
}
else
{
const int bufferSize = 100;
char buffer[bufferSize];
CNumberWithinMessage number;
number.Set(NumberToInsert1);
CMessages::InsertNumbersAndSubStringsIntoString(TheText.Get(pTextLine2), &number, 1, NULL, 0, buffer, bufferSize);
m_messageMovie.AddParamString(buffer, false);
m_messageMovie.AddParamString(buffer, false);
}
}
m_messageMovie.EndMethod();
}
m_bMessageVisible = true;
m_clearTimer = duration;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::Update()
{
if(SStuntJumpManager::IsInstantiated())
{
SStuntJumpManager::GetInstance().UpdateHelper();
}
}
bool IsPlayerInControlOfVehicle(CVehicle* pVehicle, CPlayerInfo* pPlayerInfo, CPed* pPlayerPed)
{
if(!pVehicle || !pPlayerPed || !pPlayerInfo)
{
return false;
}
//player is not playing
if(pPlayerInfo->GetPlayerState() != CPlayerInfo::PLAYERSTATE_PLAYING)
{
return false;
}
//player is not in a vehicle
if(!(pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle )))
{
return false;
}
//car is buggered
if( pVehicle->GetStatus() == STATUS_WRECKED ||
(!pVehicle->InheritsFromBoat() && (pVehicle->m_nVehicleFlags.bIsDrowning || pVehicle->GetIsInWater())))
{
return false;
}
return true;
}
bool CStuntJumpManager::CanJump(bool& outCanKeepMessageUp) const
{
outCanKeepMessageUp = false;
//stunt jump manager is not active
if(!m_bActive)
{
return false;
}
// don't stunt jump while showing character select or switching character
if(CNewHud::IsShowingCharacterSwitch() || g_PlayerSwitch.IsActive())
{
return false;
}
CPed* pPlayerPed = (CPed*)CGameWorld::FindLocalPlayer();
if (!pPlayerPed)
{
return false;
}
CPlayerInfo* pPlayerInfo = CGameWorld::GetMainPlayerInfo();
if(!pPlayerInfo)
{
return false;
}
//player is not playing
if(pPlayerInfo->GetPlayerState() != CPlayerInfo::PLAYERSTATE_PLAYING)
{
return false;
}
CVehicle* pVehicle = CGameWorld::FindLocalPlayerVehicle(false);
//player is not in a vehicle
if(!pVehicle)
{
outCanKeepMessageUp = true;
return false;
}
//if player is not the driver of the vehicle
if(pVehicle->GetDriver() != pPlayerPed)
{
outCanKeepMessageUp = true;
return false;
}
//player is in a flying vehicle
if(pVehicle->GetIsAircraft())
{
return false;
}
//player is in a boosting vehicle
if(pVehicle->IsRocketBoosting())
{
return false;
}
if (!pPlayerInfo->GetAllowStuntJumpCamera())
{
if (MI_BIKE_OPPRESSOR2.IsValid() && pVehicle->GetModelIndex() == MI_BIKE_OPPRESSOR2 &&
pVehicle->GetSpecialFlightModeRatio() >= 1.0f - SMALL_FLOAT)
{
return false;
}
if (pVehicle->pHandling->mFlags & MF_IS_RC)
{
return false;
}
}
return true;
}
static CAsyncProbeHelper s_GroundProbeHelper;
void CStuntJumpManager::ResetJumpStats(const CVehicle* pVehicle)
{
m_takeOffPosition = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
m_maxAltitude = 0.0f;
s_GroundProbeHelper.ResetProbe();
}
void CStuntJumpManager::UpdateJumpStats(const CVehicle* pVehicle)
{
static Vector3 s_probeStart;
// If no probe active start a new one
if(!s_GroundProbeHelper.IsProbeActive())
{
s_probeStart = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
Vector3 vEnd = s_probeStart;
vEnd.z = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(vEnd.x, vEnd.y);
//Create and fire the probe
WorldProbe::CShapeTestProbeDesc probeData;
probeData.SetStartAndEnd(s_probeStart, vEnd);
probeData.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
s_GroundProbeHelper.StartTestLineOfSight(probeData);
}
else
{
// Otherwise wait on probe result
ProbeStatus probeStatus;
Vector3 intersectionPos;
if (s_GroundProbeHelper.GetProbeResultsWithIntersection(probeStatus, &intersectionPos))
{
//Probe is done calculating
if (probeStatus == PS_Blocked)
{
float altitude = s_probeStart.z - intersectionPos.z;
if(altitude > m_maxAltitude)
m_maxAltitude = altitude;
}
}
}
}
void CStuntJumpManager::SetMovieVisible(bool isVisible)
{
if(m_messageMovie.IsActive())
{
m_messageMovie.CallMethod("SET_VISIBLE", isVisible);
m_bMessageVisible = isVisible;
}
else
{
m_bMessageVisible = false;
}
}
#if __BANK
void CStuntJumpManager::UpdateBitFieldWidgets(bool &bSetBit, bool &bClearAllBits, u64 &iBitSet, char *pBitSetAsString, bool bFound)
{
bool bUpdateBitMask = false;
if (bSetBit)
{
iBitSet |= (1ull << iDebugStuntJumpBitToSet);
bUpdateBitMask = true;
bSetBit = false;
}
if (bClearAllBits)
{
iBitSet = 0;
bUpdateBitMask = true;
bClearAllBits = false;
}
if (bUpdateBitMask)
{
if (bFound)
{
SetStuntJumpFoundMask(iBitSet);
}
else
{
SetStuntJumpCompletedMask(iBitSet);
}
for (u32 bitLoop = 0; bitLoop < 50; bitLoop++)
{
pBitSetAsString[bitLoop] = (iBitSet & (1ull << (49-bitLoop))) ? '1' : '0';
}
pBitSetAsString[50] = '\0';
}
}
#endif // __BANK
void CStuntJumpManager::UpdateHelper()
{
CVehicle* pVehicle = NULL;
CPlayerInfo* pPlayerInfo = NULL;
CPed* pPlayerPed = (CPed*)CGameWorld::FindLocalPlayer();
if (pPlayerPed)
{
pVehicle = CGameWorld::FindLocalPlayerVehicle(false);
pPlayerInfo = CGameWorld::GetMainPlayerInfo();
}
#if __BANK
if (bDisplayStuntJumps)
{
const s32 poolSize = CStuntJump::GetPool()->GetSize();
for(s32 i=0;i<poolSize;i++)
{
if(!CStuntJump::GetPool()->GetIsFree(i))
{
CStuntJump* p_stuntJump = CStuntJump::GetPool()->GetSlot(i);
if(p_stuntJump && p_stuntJump->IsEnabled(m_levelsEnabled))
{
p_stuntJump->DebugRender();
}
}
}
}
if (bSetFoundStuntJumps)
{
Displayf("Calling SetStuntJumpFoundStat with %u", iDebugNumberOfStuntJumps);
SetStuntJumpFoundStat(iDebugNumberOfStuntJumps);
Displayf("After calling SetStuntJumpFoundStat(), USJS_FOUND is now %d", GetStuntJumpFoundStat());
bSetFoundStuntJumps = false;
}
if (bSetCompletedStuntJumps)
{
Displayf("Calling SetStuntJumpCompletedStat with %u", iDebugNumberOfStuntJumps);
SetStuntJumpCompletedStat(iDebugNumberOfStuntJumps);
Displayf("After calling SetStuntJumpCompletedStat(), USJS_COMPLETED is now %d", GetStuntJumpCompletedStat());
bSetCompletedStuntJumps = false;
}
UpdateBitFieldWidgets(bSetFoundStuntJumpsMask, bClearFoundStuntJumpsMask, iDebugFoundBitSet, foundBitSetAsString, true);
UpdateBitFieldWidgets(bSetCompletedStuntJumpsMask, bClearCompletedStuntJumpsMask, iDebugCompletedBitSet, completedBitSetAsString, false);
#endif
bool canKeepMessageUp = false;
bool canJump = CStuntJumpManager::CanJump(canKeepMessageUp);
if(!canJump && (m_jumpState != JUMP_INACTIVE || m_messageMovie.IsActive()))
{
if(!canKeepMessageUp && m_jumpState != JUMP_SHUTTING_DOWN)
{
AbortStuntJumpInProgress();
}
}
ConductorMessageData message;
switch(m_jumpState)
{
case JUMP_SHUTTING_DOWN:
{
bool bWheelsAreUp = CNewHud::IsWeaponWheelVisible() || CNewHud::IsRadioWheelActive();
if(m_bMessageVisible)
{
if(CVfxHelper::ShouldRenderInGameUI() && !bWheelsAreUp)
{
int clearTimerBefore = m_clearTimer;
m_clearTimer -= (int)fwTimer::GetTimeStepInMilliseconds();
if(m_bPrintStatsMessage &&
m_clearTimer < 4000 && clearTimerBefore >= 4000)
{
if(CFrontendStatsMgr::ShouldUseMetric())
PrintBigMessageStats("USJC", "USJ_STATSM", m_distance, m_maxAltitude);
else
PrintBigMessageStats("USJC", "USJ_STATSI", METERS_TO_FEET(m_distance), METERS_TO_FEET(m_maxAltitude));
}
// Animate the shard out
if(m_clearTimer < TWEEN_OUT_DURATION && clearTimerBefore >= TWEEN_OUT_DURATION)
{
m_messageMovie.CallMethod("SHARD_ANIM_OUT", m_iTweenOutColour);
}
if(m_clearTimer <= 0 REPLAY_ONLY( || CVideoEditorUi::IsActive() ))
{
m_messageMovie.RemoveMovie();
m_jumpState = JUMP_INACTIVE;
}
}
else
{
SetMovieVisible(false);
}
}
else if(CVfxHelper::ShouldRenderInGameUI() REPLAY_ONLY( && !CVideoEditorUi::IsActive() ) && !bWheelsAreUp)
{
SetMovieVisible(true);
}
}
case JUMP_INACTIVE:
{
if(!NetworkInterface::IsGameInProgress())
{
message.conductorName = VehicleConductor;
message.message = StuntJump;
message.bExtraInfo = false;
SUPERCONDUCTOR.SendConductorMessage(message);
}
//currently not in a jump
CPhoneMgr::ClearRemoveFlags(CPhoneMgr::WANTS_PHONE_REMOVED_STUNTJUMP);
if(!canJump)
{
break;
}
//player must be on the ground
if(pVehicle && pVehicle->GetFrameCollisionHistory()->HasCollidedWithAnyOfTypesThisFrame(ENTITY_TYPE_MASK_BUILDING | ENTITY_TYPE_MASK_ANIMATED_BUILDING))
{
break;
}
float fCarSpeed = pVehicle ? pVehicle->GetVelocity().Mag() : 0.0f;
//car must be going faster than 15 m/s
if(pVehicle && pVehicle->InheritsFromBoat())
{
if(fCarSpeed < 3.0f)
{
break;
}
}
else
{
if(fCarSpeed < 12.0f)
{
break;
}
}
// Don't start a stunt jump in the car is parachuting
if(pVehicle->InheritsFromAutomobile() && static_cast<CAutomobile*>(pVehicle)->IsParachuting())
{
break;
}
if( pVehicle->HasGlider() && pVehicle->GetGliderNormaliseDeployedRatio() != 0.0f )
{
break;
}
//check against the start boxes of each of the stunt jumps
//this will be changed to access through a quadtree
const s32 sizeOfStuntJumpPool = CStuntJump::GetPool()->GetSize();
for(s32 i=0;i<sizeOfStuntJumpPool;i++)
{
CStuntJump* p_stuntJump = CStuntJump::GetPool()->GetSlot(i);
// Is this stuntjump currently enabled
if(p_stuntJump && p_stuntJump->IsEnabled(m_levelsEnabled) && p_stuntJump->ShouldActivate(pVehicle))
{
m_jumpState = JUMP_ACTIVATED;
mp_Active = p_stuntJump;
m_fTimer = 0.0f;
m_bHitReward = false;
m_clearTimer = 0; // For fail cases.
if(!m_messageMovie.IsActive())
{
m_messageMovie.CreateMovie(SF_BASE_CLASS_GENERIC, STUNT_JUMP_MOVIE, Vector2(0.0f, 0.0f), Vector2(1.0f, 1.0f), false); // let the system shut down its own movies
}
break;
}
}
}
break;
case JUMP_ACTIVATED:
{
// if player leaves the vehicle then go to landed
if(!IsPlayerInControlOfVehicle(pVehicle,pPlayerInfo,pPlayerPed))
{
m_jumpState = JUMP_SHUTTING_DOWN;
break;
}
if(!NetworkInterface::IsGameInProgress())
{
message.conductorName = VehicleConductor;
message.message = StuntJump;
message.bExtraInfo = true;
SUPERCONDUCTOR.SendConductorMessage(message);
}
if(pVehicle->IsInAir())
{
if(!IsStuntJumpFound(mp_Active))
{
SetStuntJumpFound(mp_Active);
}
CPhoneMgr::SetRemoveFlags(CPhoneMgr::WANTS_PHONE_REMOVED_STUNTJUMP);
ResetJumpStats(pVehicle);
m_jumpState = JUMP_JUMPING;
break;
}
bool bVehicleInTrigger = false;
// if we leave the box and haven't gained air cancel stuntjump
if( !mp_Active->m_bIsAngled )
{
if(mp_Active->m_startBox.ContainsPoint(pVehicle->GetTransform().GetPosition()))
bVehicleInTrigger = true;
}
else
{
if(mp_Active->m_startAngledBox.TestPoint(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition())))
bVehicleInTrigger = true;
}
// if the centre of the vehicle has left the box, also check the position above the rear axle, longer vehicles like buses
// can still be on the ground and the rear wheels in the box but the centre exited.
if( !bVehicleInTrigger && pVehicle->GetWheelFromId(VEH_WHEEL_LR))
{
Vec3V vRearAxle = pVehicle->GetTransform().GetPosition() + (pVehicle->GetVehicleModelInfo()->GetWheelOffset(VEH_WHEEL_LR).GetY() * pVehicle->GetVehicleForwardDirection());
if( !mp_Active->m_bIsAngled )
{
if(mp_Active->m_startBox.ContainsPoint(vRearAxle))
bVehicleInTrigger = true;
}
else
{
if(mp_Active->m_startAngledBox.TestPoint(VEC3V_TO_VECTOR3(vRearAxle)))
bVehicleInTrigger = true;
}
}
if( !bVehicleInTrigger )
{
m_jumpState = JUMP_SHUTTING_DOWN;
}
}
break;
case JUMP_JUMPING:
{
// If stunt jump results is up clear it
SetMovieVisible(false);
// if player leaves the vehicle then go to landed
if(!IsPlayerInControlOfVehicle(pVehicle,pPlayerInfo,pPlayerPed))
{
m_jumpState = JUMP_LANDED;
break;
}
//currently in jump
bool bRestore = false;
// update height calculations
UpdateJumpStats(pVehicle);
// Don't allow any collisions with non map geometry
bool bSeriousCollisionFound = pVehicle->GetFrameCollisionHistory()->HasCollidedWithAnyOfTypes((u16)~ENTITY_TYPE_MASK_BUILDING);
if(!bSeriousCollisionFound)
{
if(const CCollisionRecord* pCollisionRecord = pVehicle->GetFrameCollisionHistory()->GetMostSignificantCollisionRecordOfType(ENTITY_TYPE_BUILDING))
{
// If we do collide with the map, allow collisions on the bottom of the chassis. These collisions should have normals nearly aligned with the vehicle up vector
const ScalarV minCollisionCosine = ScalarVFromF32(0.866f); // ~30 degrees
const ScalarV collisionAngleCosine = Dot(RCC_VEC3V(pCollisionRecord->m_MyCollisionNormal),pVehicle->GetTransform().GetUp());
if(IsLessThanAll(collisionAngleCosine,minCollisionCosine))
{
bSeriousCollisionFound = true;
}
}
}
const bool bDeluxo = MI_CAR_DELUXO.IsValid() && pVehicle->GetModelIndex() == MI_CAR_DELUXO;
const bool bIsDeluxoFlying = (bDeluxo && pVehicle->GetSpecialFlightModeRatio() >= 1.0f-SMALL_FLOAT);
if(!(bSeriousCollisionFound || bIsDeluxoFlying) || m_fTimer < 1000.0f)
{
// The boat has landed in water, not necessarily stably
if (pVehicle->GetVehicleType() == VEHICLE_TYPE_BOAT && ((CBoat *)pVehicle)->m_BoatHandling.IsInWater())
{
bRestore = true;
}
// Only end the jump if the vehicle isn't in midair and is stuck, otherwise the player will fail as soon as one wheel touches the ground
if (!pVehicle->IsInAir() && pVehicle->GetVelocity().Mag2() < 1.0f)
{
bRestore = true;
}
if(pVehicle->HasLandedStably())
{
bRestore = true;
//if we hit the second box then we trigger the reward
if(mp_Active->IsSuccessful(pVehicle))
{
m_bHitReward = true;
m_distance = (VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()) - m_takeOffPosition).Mag();
}
}
}
else
{
//have landed on the ground
bRestore = true;
}
if(bRestore)
{
m_jumpState = JUMP_LANDED;
m_fTimer = 0.0f;
}
if(!fwTimer::IsGamePaused())
{
m_fTimer += fwTimer::GetTimeStepInMillsecondsHighRes();
}
}
break;
case JUMP_LANDED:
{
if(!fwTimer::IsGamePaused())
{
m_fTimer += fwTimer::GetTimeStepInMillsecondsHighRes();
}
if(m_fTimer < 300.0f)
{
// The player must maintain control of the vehicle until we tell them they were successful
if(m_bHitReward && !IsPlayerInControlOfVehicle(pVehicle,pPlayerInfo,pPlayerPed))
{
m_bHitReward = false;
}
break;
}
CPhoneMgr::ClearRemoveFlags(CPhoneMgr::WANTS_PHONE_REMOVED_STUNTJUMP);
CHelpMessage::Clear(HELP_TEXT_SLOT_STANDARD, false);
m_clearTimer = 0;
}
// Intentionally no break here.
case JUMP_LANDED_PRINTING_TEXT:
{
if (!CVfxHelper::ShouldRenderInGameUI())
{
break;
}
m_bPrintStatsMessage = false;
if(m_messageMovie.IsActive())
{
m_clearTimer = 0;
const char* pTitle = "USJ";
const char* pText = NULL;
bool bCompleted = IsStuntJumpCompleted(mp_Active);
if(m_bHitReward)
{
if(!NetworkInterface::IsGameInProgress())
{
// MP stunt jump audio is handled differently
Displayf("CStuntJumpManager::PlaySound Playing sound STUNT_JUMP_COMPLETE status: JUMP_LANDED_PRINTING_TEXT");
g_FrontendAudioEntity.PlaySound("STUNT_JUMP_COMPLETED", "HUD_AWARDS");
}
m_iTweenOutColour = HUD_COLOUR_WHITE;
pTitle = "USJC";
if (bCompleted)
{
// Passed stunt jump but it had been passed before.
pText = "USJCA";
}
else
{
pText = "USJC";
SetStuntJumpCompleted(mp_Active);
// if in network game attempt to save stats
StatsInterface::TryMultiplayerSave( STAT_SAVETYPE_STUNTJUMP );
}
// Only increment the total jumps completed (this also counts the already completed more than once)
if(!NetworkInterface::IsGameInProgress())
{
StatId statTotal("USJS_TOTAL_COMPLETED");
StatsInterface::IncrementStat(statTotal, 1.0f);
}
else
{
StatId statTotal = StatsInterface::GetStatsModelHashId("USJS_TOTAL_COMPLETED");
StatsInterface::IncrementStat(statTotal, 1.0f);
}
m_lastStuntJumpId = mp_Active->m_id;
}
else
{
if(!NetworkInterface::IsGameInProgress())
{
// MP stunt jump audio is handled differently
Displayf("CStuntJumpManager::PlaySound Playing sound STUNT_JUMP_FAILED status: JUMP_LANDED_PRINTING_TEXT");
g_FrontendAudioEntity.PlaySound("STUNT_JUMP_FAILED", "HUD_AWARDS");
}
m_iTweenOutColour = HUD_COLOUR_RED;
pTitle = "USJFAIL";
if (bCompleted)
{
pText = "USJFAILA";
}
else
{
pText = "USJFAIL";
}
}
// bit of a hack to ensure stunt jumps level 2 and above don't display messages
if(mp_Active->m_level <= 1)
{
int timeToDisplay = 4000;
if(m_bHitReward)
{
m_bPrintStatsMessage = true;
timeToDisplay = 6000;
}
s32 jumpsLeft = m_iNumActiveJumps - GetStuntJumpCompletedStat();
#if __BANK
// Override jumps left
if(iDebugJumpsLeft != -1)
{
jumpsLeft = iDebugJumpsLeft;
}
Displayf("[stuntjump] m_iNumActiveJumps = %d, GetStuntJumpCompletedStat() = %d GetStuntJumpCompletedMask() = %lu",m_iNumActiveJumps, GetStuntJumpCompletedStat(), GetStuntJumpCompletedMask());
Assertf(GetStuntJumpCompletedStat() <= m_iNumActiveJumps, "CStuntJumpManager::UpdateHelper - stunt jumps completed (%d) is greater than the total number of stunt jumps (%d). GetStuntJumpCompletedMask() = %lu", GetStuntJumpCompletedStat(), m_iNumActiveJumps, GetStuntJumpCompletedMask());
#endif
if (jumpsLeft==0)
{
PrintBigMessage(pTitle, pText, "USJ_ALL", timeToDisplay);
}
else if (jumpsLeft==1)
{
PrintBigMessage(pTitle, pText, "USJ_1LEFT", timeToDisplay);
}
else if (jumpsLeft > 1)
{
PrintBigMessage(pTitle, pText, "USJ_LEFT", timeToDisplay, jumpsLeft);
}
else
{
PrintBigMessage(pTitle, pText, "USJ_FAILSAFE", timeToDisplay);
}
}
m_jumpState = JUMP_SHUTTING_DOWN;
mp_Active = NULL;
}
else if(!m_messageMovie.IsFree())
{
m_jumpState = JUMP_LANDED_PRINTING_TEXT;
if(0 < m_clearTimer)
{
m_clearTimer -= (int)fwTimer::GetTimeStepInMilliseconds();
if(m_clearTimer <= 0)
{
AbortStuntJumpInProgress();
}
}
else
{
m_clearTimer = PRINT_MESSAGE_TIMEOUT;
}
}
else
{
AbortStuntJumpInProgress();
}
}
break;
}
pPlayerInfo->SetAllowStuntJumpCamera(false);
#if __BANK
if(bTriggerJumpMessage && m_jumpState == JUMP_INACTIVE && mp_Active == NULL)
{
bTriggerJumpMessage = false;
const s32 sizeOfStuntJumpPool = CStuntJump::GetPool()->GetSize();
for(s32 i=0;i<sizeOfStuntJumpPool;i++)
{
CStuntJump* p_stuntJump = CStuntJump::GetPool()->GetSlot(i);
// Is this stuntjump currently enabled
if(p_stuntJump)
{
m_jumpState = JUMP_LANDED;
mp_Active = p_stuntJump;
m_bHitReward = true;
m_fTimer = 0.0f;
m_messageMovie.CreateMovie(SF_BASE_CLASS_GENERIC, STUNT_JUMP_MOVIE, Vector2(0.0f, 0.0f), Vector2(1.0f, 1.0f));
break;
}
}
}
#endif
}
StatId GetStuntJumpStat(const char* pStatName)
{
if(!NetworkInterface::IsGameInProgress())
{
return StatId(pStatName);
}
else
{
return StatsInterface::GetStatsModelHashId(pStatName);
}
}
int CStuntJumpManager::GetStuntJumpFoundStat()
{
StatId stat = GetStuntJumpStat("USJS_FOUND");
return StatsInterface::GetIntStat(stat);
}
void CStuntJumpManager::SetStuntJumpFoundStat(int number_of_found_stunt_jumps)
{
StatId stat = GetStuntJumpStat("USJS_FOUND");
StatsInterface::SetStatData(stat, number_of_found_stunt_jumps); // const u32 flags = STATUPDATEFLAG_DEFAULT
}
u64 CStuntJumpManager::GetStuntJumpFoundMask()
{
StatId stat = GetStuntJumpStat("USJS_FOUND_MASK");
return StatsInterface::GetUInt64Stat(stat);
}
void CStuntJumpManager::SetStuntJumpFoundMask(u64 bitMask)
{
StatId stat = GetStuntJumpStat("USJS_FOUND_MASK");
StatsInterface::SetStatData(stat, bitMask); // const u32 flags = STATUPDATEFLAG_DEFAULT
}
void CStuntJumpManager::IncrementStuntJumpFoundStat()
{
StatId stat = GetStuntJumpStat("USJS_FOUND");
StatsInterface::IncrementStat(stat, 1.0f);
}
int CStuntJumpManager::GetStuntJumpCompletedStat()
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED");
return StatsInterface::GetIntStat(stat);
}
void CStuntJumpManager::SetStuntJumpCompletedStat(int number_of_completed_stunt_jumps)
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED");
StatsInterface::SetStatData(stat, number_of_completed_stunt_jumps); // const u32 flags = STATUPDATEFLAG_DEFAULT
}
int CStuntJumpManager::GetTotalStuntJumpCompletedStat()
{
StatId stat = GetStuntJumpStat("USJS_TOTAL_COMPLETED");
return StatsInterface::GetIntStat(stat);
}
u64 CStuntJumpManager::GetStuntJumpCompletedMask()
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED_MASK");
return StatsInterface::GetUInt64Stat(stat);
}
void CStuntJumpManager::SetStuntJumpCompletedMask(u64 bitMask)
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED_MASK");
StatsInterface::SetStatData(stat, bitMask); // const u32 flags = STATUPDATEFLAG_DEFAULT
}
void CStuntJumpManager::IncrementStuntJumpCompletedStat()
{
if(!NetworkInterface::IsGameInProgress())
{
StatId stat("USJS_COMPLETED");
StatsInterface::IncrementStat(stat, 1.0f);
StatId percentStat("PERCENT_USJS");
int numStuntJumpsFinished = StatsInterface::GetIntStat(stat);
StatsInterface::SetStatData(percentStat, (int)((100.0f * numStuntJumpsFinished) / GetNumActiveJumps()));
}
else
{
StatId stat = StatsInterface::GetStatsModelHashId("USJS_COMPLETED");
StatsInterface::IncrementStat(stat, 1.0f);
}
}
bool CStuntJumpManager::IsStuntJumpFound(const CStuntJump* pStuntJump)
{
StatId stat = GetStuntJumpStat("USJS_FOUND_MASK");
int id = pStuntJump->m_id;//CStuntJump::GetPool()->GetJustIndex(pStuntJump);
if(id == CStuntJump::INVALID_ID)
return false;
u64 stuntJumpsFoundMask = StatsInterface::GetUInt64Stat(stat);
return (stuntJumpsFoundMask & (1ull<<id)) != 0;
}
void CStuntJumpManager::SetStuntJumpFound(const CStuntJump* pStuntJump)
{
StatId stat = GetStuntJumpStat("USJS_FOUND_MASK");
int id = pStuntJump->m_id;//CStuntJump::GetPool()->GetJustIndex(pStuntJump);
if(id == CStuntJump::INVALID_ID)
return;
u64 stuntJumpsFoundMask = StatsInterface::GetUInt64Stat(stat);
stuntJumpsFoundMask |= (1ull<<id);
StatsInterface::SetStatData(stat, stuntJumpsFoundMask);
if (GetStuntJumpFoundStat() == 0)
{
if (!NetworkInterface::IsGameInProgress())
{ // Bug 890637 - Based on shouldInhibitSlowMotion in camCinematicDirector::UpdateCinematicViewModeSlowMotionControl()
// This does mean that you'll never see the message if the first stuntjump you find is during a network game
CHelpMessage::SetMessageTextAndAddToBrief(HELP_TEXT_SLOT_STANDARD, "USJ_FRST");
}
}
IncrementStuntJumpFoundStat();
}
bool CStuntJumpManager::IsStuntJumpCompleted(const CStuntJump* pStuntJump)
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED_MASK");
int id = pStuntJump->m_id;//CStuntJump::GetPool()->GetJustIndex(pStuntJump);
if(id == CStuntJump::INVALID_ID)
return false;
u64 stuntJumpsFoundMask = StatsInterface::GetUInt64Stat(stat);
return (stuntJumpsFoundMask & (1ull<<id)) != 0;
}
void CStuntJumpManager::SetStuntJumpCompleted(const CStuntJump* pStuntJump)
{
StatId stat = GetStuntJumpStat("USJS_COMPLETED_MASK");
int id = pStuntJump->m_id;//CStuntJump::GetPool()->GetJustIndex(pStuntJump);
if(id == CStuntJump::INVALID_ID)
return;
u64 stuntJumpsFoundMask = StatsInterface::GetUInt64Stat(stat);
stuntJumpsFoundMask |= (1ull<<id);
StatsInterface::SetStatData(stat, stuntJumpsFoundMask);
IncrementStuntJumpCompletedStat();
CTheScripts::SetCodeRequestedAutoSave(true);
}
void CStuntJumpManager::ValidateStuntJumpStats(bool bFixIncorrectTotals)
{
#if !__NO_OUTPUT
const int MaxStuntJumps = 50; // Graeme - I think the max number of stunt jumps is always 50 in GTA5
#endif // !__NO_OUTPUT
int num_completed = GetStuntJumpCompletedStat();
Displayf("CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps completed is %d. Expected it to be >= 0 and <= %d", num_completed, MaxStuntJumps);
Assertf(num_completed >= 0 && num_completed <= MaxStuntJumps, "CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps completed is %d. Expected it to be >= 0 and <= %d", num_completed, MaxStuntJumps);
u64 completed_mask = GetStuntJumpCompletedMask();
u32 bit_loop = 0;
s32 number_of_completed_bits_set = 0;
for (bit_loop = 0; bit_loop < 64; bit_loop++)
{
if ((completed_mask & (1ull<<bit_loop)) != 0)
{
number_of_completed_bits_set++;
}
}
Displayf("CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps completed = %d. The number of bits set in the completed mask = %d", num_completed, number_of_completed_bits_set);
if (num_completed != number_of_completed_bits_set)
{
Assertf(0, "CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps completed (%d) doesn't match the number of bits set in the completed mask (%d)", num_completed, number_of_completed_bits_set);
if (bFixIncorrectTotals)
{
Displayf("CStuntJumpManager::ValidateStuntJumpStats - attempting to set USJS_COMPLETED to %d to match the number of bits set in the completed mask", number_of_completed_bits_set);
SetStuntJumpCompletedStat(number_of_completed_bits_set);
Displayf("CStuntJumpManager::ValidateStuntJumpStats - after calling SetStuntJumpCompletedStat(), USJS_COMPLETED is now %d", GetStuntJumpCompletedStat());
}
}
int num_found = GetStuntJumpFoundStat();
Displayf("CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps found is %d. Expected it to be >= 0 and <= %d", num_found, MaxStuntJumps);
Assertf(num_found >= 0 && num_found <= MaxStuntJumps, "CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps found is %d. Expected it to be >= 0 and <= %d", num_found, MaxStuntJumps);
u64 found_mask = GetStuntJumpFoundMask();
s32 number_of_found_bits_set = 0;
for (bit_loop = 0; bit_loop < 64; bit_loop++)
{
if ((found_mask & (1ull<<bit_loop)) != 0)
{
number_of_found_bits_set++;
}
}
Displayf("CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps found = %d. The number of bits set in the found mask = %d", num_found, number_of_found_bits_set);
if (num_found != number_of_found_bits_set)
{
Assertf(0, "CStuntJumpManager::ValidateStuntJumpStats - the stat for the number of stunt jumps found (%d) doesn't match the number of bits set in the found mask (%d)", num_found, number_of_found_bits_set);
if (bFixIncorrectTotals)
{
Displayf("CStuntJumpManager::ValidateStuntJumpStats - attempting to set USJS_FOUND to %d to match the number of bits set in the found mask", number_of_found_bits_set);
SetStuntJumpFoundStat(number_of_found_bits_set);
Displayf("CStuntJumpManager::ValidateStuntJumpStats - after calling SetStuntJumpFoundStat(), USJS_FOUND is now %d", GetStuntJumpFoundStat());
}
}
}
s8 CStuntJumpManager::GetLastSuccessfulStuntJump() const
{
return m_lastStuntJumpId;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::Render()
{
if(SStuntJumpManager::IsInstantiated())
{
SStuntJumpManager::GetInstance().RenderHelper();
}
}
void CStuntJumpManager::RenderHelper()
{
m_messageMovie.Render();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CStuntJumpManager::IsAStuntjumpInProgress()
{
return SStuntJumpManager::IsInstantiated() ? (SStuntJumpManager::GetInstance().m_jumpState > JUMP_ACTIVATED) : false;
}
bool CStuntJumpManager::IsStuntjumpMessageShowing()
{
return SStuntJumpManager::IsInstantiated() ? (SStuntJumpManager::GetInstance().m_jumpState == JUMP_SHUTTING_DOWN) : false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
int CStuntJumpManager::AddOne(Vec3V_In startMin, Vec3V_In startMax, Vec3V_In endMin, Vec3V_In endMax, Vec3V_In vecCamera, int iScore, int level, bool camOptional)
{
uiAssertf(level >= 0 && level < 32, "stuntjump level can only be 0-31");
spdAABB startBox(startMin, startMax);
spdAABB endBox(endMin, endMax);
uiAssertf(startBox.IsValid(), "Stunt jump start box (%f,%f,%f) (%f,%f,%f) is invalid",
startMin.GetXf(), startMin.GetYf(), startMin.GetZf(),
startMax.GetXf(), startMax.GetYf(), startMax.GetZf());
uiAssertf(endBox.IsValid(), "Stunt jump end box (%f,%f,%f) (%f,%f,%f) is invalid",
endMin.GetXf(), endMin.GetYf(), endMin.GetZf(),
endMax.GetXf(), endMax.GetYf(), endMax.GetZf());
int id = CStuntJump::INVALID_ID;
if(level == 0)
id = m_iStuntJumpCounter++;
CStuntJump* p_newStuntJump = rage_new CStuntJump(startBox, endBox, vecCamera, camOptional, iScore, level, id);
if(!uiVerifyf(p_newStuntJump, "Failed to create a new stunt jump"))
{
return 0;
}
RegisterAddedStuntJump(p_newStuntJump);
return CStuntJump::GetPool()->GetIndex(p_newStuntJump);
}
int CStuntJumpManager::AddOneAngled(Vec3V_In startMin, Vec3V_In startMax, float startWidth, Vec3V_In endMin, Vec3V_In endMax, float endWidth, Vec3V_In vecCamera, int iScore, int level, bool camOptional)
{
uiAssertf(level >= 0 && level < 32, "stuntjump level can only be 0-31");
int id = CStuntJump::INVALID_ID;
if(level == 0)
id = m_iStuntJumpCounter++;
CStuntJump* p_newStuntJump = rage_new CStuntJump(startMin, startMax, startWidth, endMin, endMax, endWidth, vecCamera, camOptional, iScore, level, id);
if(!uiVerifyf(p_newStuntJump, "Failed to create a new stunt jump"))
{
return 0;
}
RegisterAddedStuntJump(p_newStuntJump);
return CStuntJump::GetPool()->GetIndex(p_newStuntJump);
}
void CStuntJumpManager::RegisterAddedStuntJump(const CStuntJump* p_newStuntJump)
{
scrThread* pCurrentScript = scrThread::GetActiveThread();
if(pCurrentScript)
Displayf("Script %s created StuntJump %d in level %d, id %d", pCurrentScript->GetScriptName(), CStuntJump::GetPool()->GetIndex(p_newStuntJump), p_newStuntJump->m_level, p_newStuntJump->m_id);
else
Displayf("Code created StuntJump %d in level %d, id %d", CStuntJump::GetPool()->GetIndex(p_newStuntJump), p_newStuntJump->m_level, p_newStuntJump->m_id);
m_iNumJumps++;
if(p_newStuntJump->IsEnabled(m_levelsEnabled))
m_iNumActiveJumps++;
}
void CStuntJumpManager::DeleteOne(int id)
{
CStuntJump* pStuntJump = CStuntJump::GetPool()->GetAt(id);
if(uiVerifyf(pStuntJump, "Trying to delete stunt jump id:%d when it does not exist", id))
{
scrThread* pCurrentScript = scrThread::GetActiveThread();
if(pCurrentScript)
Displayf("Script %s deleted StuntJump %d from level %d, id %d", pCurrentScript->GetScriptName(), id, pStuntJump->m_level, pStuntJump->m_id);
else
Displayf("Code deleted StuntJump %d from level %d, id %d", id, pStuntJump->m_level, pStuntJump->m_id);
Assertf(pStuntJump->m_id == CStuntJump::INVALID_ID, "Not allowed to remove stunt jump %d with an id %d", id, pStuntJump->m_id);
m_iNumJumps--;
if(pStuntJump->IsEnabled(m_levelsEnabled))
m_iNumActiveJumps--;
delete pStuntJump;
}
}
// PURPOSE: Return number of stunt jumps that are currently active
void CStuntJumpManager::RecalculateNumActiveJumps()
{
m_iNumActiveJumps = 0;
int i = CStuntJump::GetPool()->GetSize();
while(i--)
{
CStuntJump* pStuntJump = CStuntJump::GetPool()->GetSlot(i);
if(pStuntJump && pStuntJump->IsEnabled(m_levelsEnabled))
{
m_iNumActiveJumps++;
}
}
}
void CStuntJumpManager::EnableSet(int level)
{
uiAssertf(level >= 0 && level < 32, "stuntjump level can only be 0-31");
m_levelsEnabled |= (1 << level);
RecalculateNumActiveJumps();
}
void CStuntJumpManager::DisableSet(int level)
{
uiAssertf(level >= 0 && level < 32, "stuntjump level can only be 0-31");
m_levelsEnabled &= ~(1 << level);
RecalculateNumActiveJumps();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStuntJumpManager::SetActive(bool bNewState)
{
m_bActive = bNewState;
}
bool CStuntJumpManager::GetCameraPositionForStuntJump(Vector3& CameraPos)
{
if(mp_Active)
{
CameraPos = VEC3V_TO_VECTOR3(mp_Active->m_vecCamera);
return true;
}
return false;
}
bool CStuntJumpManager::IsStuntCamOptional() const
{
return mp_Active && mp_Active->m_bIsCameraOptional;
}
#if __BANK
void CStuntJumpManager::InitWidgets()
{
bDisplayStuntJumps = false;
bkBank *bank = CGameLogic::GetGameLogicBank();
bank->PushGroup("Stunt Jumps", false);
bank->AddToggle("Display Stunt Jumps", &bDisplayStuntJumps);
bank->AddToggle("Trigger Jump Message", &bTriggerJumpMessage);
bank->AddText("Override Stunt Jumps Left", &iDebugJumpsLeft);
bank->AddSlider("Number of Stunt Jumps", &iDebugNumberOfStuntJumps, 0, 50, 1);
bank->AddToggle("Set Found Stunt Jumps", &bSetFoundStuntJumps);
bank->AddToggle("Set Completed Stunt Jumps", &bSetCompletedStuntJumps);
bank->AddSlider("Stunt Jump Bit To Set", &iDebugStuntJumpBitToSet, 0, 49, 1);
bank->AddText("Found Bits", foundBitSetAsString, NELEM(foundBitSetAsString), true);
bank->AddToggle("Set Found Stunt Jump Bit", &bSetFoundStuntJumpsMask);
bank->AddToggle("Clear Found Stunt Jump Bits", &bClearFoundStuntJumpsMask);
bank->AddText("Completed Bits", completedBitSetAsString, NELEM(completedBitSetAsString), true);
bank->AddToggle("Set Completed Stunt Jump Bit", &bSetCompletedStuntJumpsMask);
bank->AddToggle("Clear Completed Stunt Jump Bits", &bClearCompletedStuntJumpsMask);
bank->PopGroup();
}
#endif