1238 lines
32 KiB
C++
1238 lines
32 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "../Player.h"
|
|
#include "TransportExtras.h"
|
|
#include "VehicleView.h"
|
|
#include "VehicleWeapon.h"
|
|
#include "Transport.h"
|
|
#include "VehicleIK.h"
|
|
#include "../ContentMask.h"
|
|
#include "../script/Script_Helper.h"
|
|
#include "../proficiency/StatsTracker.h"
|
|
|
|
#include "../botai/Bot.h"
|
|
#include "../botai/BotThreadData.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdVehicleInput
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
void sdVehicleInput::SetPlayer( idPlayer* player ) {
|
|
data.player = player;
|
|
if ( data.player != NULL ) {
|
|
data.usercmd = gameLocal.usercmds[ player->entityNumber ];
|
|
}
|
|
|
|
for ( int i = 0; i < 3; i++ ) {
|
|
data.cmdAngles[ i ] = SHORT2ANGLE( data.usercmd.angles[ i ] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetForward
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetForward( void ) const {
|
|
return data.usercmd.forwardmove / 127.f ;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetRight
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetRight( void ) const {
|
|
return data.usercmd.rightmove / 127.f;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetUp
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetUp( void ) const {
|
|
return data.usercmd.upmove / 127.f;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetYaw
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetCmdYaw( void ) const {
|
|
return idMath::AngleNormalize180( data.cmdAngles.yaw );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetPitch
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetCmdPitch( void ) const {
|
|
return idMath::AngleNormalize180( data.cmdAngles.pitch );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehicleInput::GetCmdRoll
|
|
================
|
|
*/
|
|
float sdVehicleInput::GetCmdRoll( void ) const {
|
|
return idMath::AngleNormalize180( data.cmdAngles.roll );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdVehiclePosition
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::sdVehiclePosition
|
|
================
|
|
*/
|
|
sdVehiclePosition::sdVehiclePosition( void ) {
|
|
player = NULL;
|
|
transport = NULL;
|
|
weaponIndex = -1;
|
|
index = -1;
|
|
blockedTip = NULL;
|
|
maxViewOffset = 0.f;
|
|
viewOffsetRate = 0.f;
|
|
currentViewOffset = 0.f;
|
|
currentViewOffsetAngles = 0.f;
|
|
statTimeSpent = NULL;
|
|
playerHeight = 0.0f;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::~sdVehiclePosition
|
|
================
|
|
*/
|
|
sdVehiclePosition::~sdVehiclePosition( void ) {
|
|
for ( int i = 0; i < views.Num(); i++ ) {
|
|
delete views[ i ];
|
|
}
|
|
for ( int i = 0; i < ikSystems.Num(); i++ ) {
|
|
delete ikSystems[ i ];
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::LoadData
|
|
================
|
|
*/
|
|
void sdVehiclePosition::LoadData( const idDict& dict ) {
|
|
requirements.Load( dict, "require" );
|
|
|
|
const char* tipName = dict.GetString( "tt_blocked" );
|
|
if ( *tipName ) {
|
|
blockedTip = gameLocal.declToolTipType[ tipName ];
|
|
}
|
|
|
|
maxViewOffset = dict.GetFloat( "max_view_offset", "0.f" );
|
|
viewOffsetRate = dict.GetFloat( "view_offset_rate", "0.f" );
|
|
if ( maxViewOffset ) {
|
|
viewOffsetRate /= maxViewOffset;
|
|
}
|
|
|
|
const char* attachJointName = dict.GetString( "joint_attach" );
|
|
if ( !*attachJointName ) {
|
|
attachJointName = "origin";
|
|
}
|
|
|
|
attachAnim = dict.GetString( "player_anim", "VehicleDefault" );
|
|
showPlayer = dict.GetBool( "show_player" );
|
|
minZfrac = dict.GetFloat( "min_z_frac", "-2.f" );
|
|
ejectOnKilled = dict.GetBool( "use_fallback" );
|
|
takesDamage = dict.GetBool( "take_damage" );
|
|
playerHeight = dict.GetFloat( "player_height" );
|
|
allowWeapon = dict.GetBool( "allow_weapon" );
|
|
allowAdjustBodyAngles = dict.GetBool( "adjust_body_angles" );
|
|
resetViewOnEnter = dict.GetBool( "reset_view_on_enter", "1" );
|
|
damageScale = dict.GetFloat( "damage_scale", "1" );
|
|
playerStance = dict.GetBool( "player_stance_crouch" ) ? PS_CROUCH : PS_NORMAL;
|
|
|
|
const char* iconJointName = dict.GetString( "joint_icon" );
|
|
if ( *iconJointName != '\0' ) {
|
|
iconJoint = transport->GetAnimator()->GetJointHandle( iconJointName );
|
|
} else {
|
|
iconJoint = INVALID_JOINT;
|
|
}
|
|
|
|
const idKeyValue* kv = NULL;
|
|
while ( kv = dict.MatchPrefix( "ability", kv ) ) {
|
|
abilities.Add( kv->GetValue() );
|
|
}
|
|
|
|
attachJoint = transport->GetAnimator()->GetJointHandle( attachJointName );
|
|
if ( attachJoint == INVALID_JOINT ) {
|
|
gameLocal.Warning( "sdVehiclePosition::LoadData Joint \"%s\" does not exist in vscript %s", attachJointName, transport->GetVehicleScript()->GetName() );
|
|
}
|
|
|
|
cockpitName = dict.GetString( "cockpit", "" );
|
|
|
|
const char* statName = dict.GetString( "stat_name" );
|
|
if ( *statName ) {
|
|
sdStatsTracker& tracker = sdGlobalStatsTracker::GetInstance();
|
|
|
|
statTimeSpent = tracker.GetStat( tracker.AllocStat( va( "%s_time_spent", statName ), sdNetStatKeyValue::SVT_INT ) );
|
|
} else {
|
|
gameLocal.Warning( "Missing Stat Name on '%s'", name.c_str() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::CheckRequirements
|
|
================
|
|
*/
|
|
bool sdVehiclePosition::CheckRequirements( idPlayer* player ) {
|
|
return requirements.Check( player, transport );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::Think
|
|
================
|
|
*/
|
|
void sdVehiclePosition::Think( void ) {
|
|
UpdateIK();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::UpdateViews
|
|
================
|
|
*/
|
|
void sdVehiclePosition::UpdateViews( sdVehicleWeapon* weapon ) {
|
|
for ( int i = 0; i < views.Num(); i++ ) {
|
|
views[ i ]->Update( weapon );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::WeaponValid
|
|
================
|
|
*/
|
|
bool sdVehiclePosition::WeaponValid( sdVehicleWeapon* weapon ) const {
|
|
return weapon->GetBasePosition() == this;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::GetPlayer
|
|
================
|
|
*/
|
|
idPlayer* sdVehiclePosition::GetPlayer( void ) const {
|
|
return player;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::FindDefaultWeapon
|
|
================
|
|
*/
|
|
int sdVehiclePosition::FindDefaultWeapon( void ) const {
|
|
if ( transport == NULL ) {
|
|
return -1;
|
|
}
|
|
|
|
int i;
|
|
for ( i = 0; i < transport->NumWeapons(); i++ ) {
|
|
if ( WeaponValid( transport->GetWeapon( i ) ) ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::SetWeaponIndex
|
|
================
|
|
*/
|
|
void sdVehiclePosition::SetWeaponIndex( int _weaponIndex ) {
|
|
if ( weaponIndex == _weaponIndex ) {
|
|
if ( weaponIndex == -1 || transport->GetWeapon( weaponIndex )->GetPosition() == this ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( weaponIndex != -1 ) {
|
|
if ( weaponIndex >= 0 && weaponIndex < transport->NumWeapons() ) {
|
|
if ( transport->GetWeapon( weaponIndex )->GetPosition() == this ) {
|
|
transport->GetWeapon( weaponIndex )->SetPosition( NULL );
|
|
}
|
|
}
|
|
}
|
|
weaponIndex = _weaponIndex;
|
|
if ( weaponIndex != -1 ) {
|
|
if ( weaponIndex >= 0 && weaponIndex < transport->NumWeapons() ) {
|
|
transport->GetWeapon( weaponIndex )->SetPosition( this );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::FindLastWeapon
|
|
================
|
|
*/
|
|
int sdVehiclePosition::FindLastWeapon( void ) const {
|
|
int i;
|
|
for ( i = transport->NumWeapons() - 1; i >= 0; i-- ) {
|
|
if ( WeaponValid( transport->GetWeapon( i ) ) ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::SetPlayer
|
|
================
|
|
*/
|
|
void sdVehiclePosition::SetPlayer( idPlayer* _player ) {
|
|
idPlayer* p = player;
|
|
if ( p && statTimeSpent ) {
|
|
statTimeSpent->IncreaseValue( p->entityNumber, ( int )MS2SEC( gameLocal.time - playerEnteredTime ) );
|
|
}
|
|
|
|
player = _player;
|
|
playerEnteredTime = gameLocal.time;
|
|
|
|
if ( weaponIndex >= 0 && weaponIndex < transport->NumWeapons() ) {
|
|
transport->GetWeapon( weaponIndex )->OnPositionPlayerChanged();
|
|
}
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
// force us to the default weapon
|
|
SetWeaponIndex( -1 );
|
|
}
|
|
transport->SortWeapons();
|
|
|
|
if ( player != NULL ) {
|
|
player->vehicleViewCurrentZoom = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::GetViewParms
|
|
================
|
|
*/
|
|
sdVehicleView& sdVehiclePosition::GetViewParms( void ) {
|
|
int viewMode = 0;
|
|
|
|
if ( !player ) {
|
|
assert( false );
|
|
gameLocal.Warning( "sdVehiclePosition::GetViewParms NULL Player" );
|
|
} else {
|
|
viewMode = player->GetProxyViewMode();
|
|
}
|
|
|
|
if ( viewMode < 0 || viewMode >= views.Num() ) {
|
|
if ( views.Num() <= 0 ) {
|
|
gameLocal.Error( "sdVehiclePosition::GetViewParms No Views on Position" );
|
|
}
|
|
return *views[ 0 ];
|
|
}
|
|
|
|
return *views[ viewMode ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::GetViewParms
|
|
================
|
|
*/
|
|
const sdVehicleView& sdVehiclePosition::GetViewParms( void ) const {
|
|
int viewMode = 0;
|
|
|
|
if ( !player ) {
|
|
assert( false );
|
|
gameLocal.Warning( "sdVehiclePosition::GetViewParms NULL Player" );
|
|
} else {
|
|
viewMode = player->GetProxyViewMode();
|
|
}
|
|
|
|
if ( viewMode < 0 || viewMode >= views.Num() ) {
|
|
if ( views.Num() <= 0 ) {
|
|
gameLocal.Error( "sdVehiclePosition::GetViewParms No Views on Position" );
|
|
}
|
|
return *views[ 0 ];
|
|
}
|
|
|
|
return *views[ viewMode ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::AddIKSystem
|
|
================
|
|
*/
|
|
void sdVehiclePosition::AddIKSystem( sdVehicleIKSystem* _ikSystem ) {
|
|
sdVehicleIKSystem** allocedIK = ikSystems.Alloc();
|
|
if ( allocedIK == NULL ) {
|
|
gameLocal.Error( "sdVehiclePosition::AddIKSystem number of ik systems for position exceeds MAX_POSITION_IK" );
|
|
}
|
|
|
|
*allocedIK = _ikSystem;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::AddView
|
|
================
|
|
*/
|
|
void sdVehiclePosition::AddView( const positionViewMode_t& parms ) {
|
|
sdVehicleView* view = sdVehicleView::AllocView( parms.type );
|
|
if ( view == NULL ) {
|
|
gameLocal.Warning( "sdVehiclePosition::AddView Invalid View Type '%s'", parms.type.c_str() );
|
|
return;
|
|
}
|
|
|
|
sdVehicleView** allocedView = views.Alloc();
|
|
if ( allocedView == NULL ) {
|
|
gameLocal.Error( "sdVehiclePosition::AddView number of views for position exceeds MAX_POSITION_VIEWS" );
|
|
}
|
|
|
|
*allocedView = view;
|
|
view->Init( this, parms );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::CycleCamera
|
|
================
|
|
*/
|
|
void sdVehiclePosition::CycleCamera( void ) {
|
|
int viewMode = player->GetProxyViewMode();
|
|
viewMode++;
|
|
viewMode %= views.Num();
|
|
player->SetProxyViewMode( viewMode, false );
|
|
|
|
if ( player->userInfo.rememberCameraMode ) {
|
|
const sdDeclVehicleScript* script = transport->GetVehicleScript();
|
|
script->SetCameraMode( player->entityNumber, GetPositionId(), viewMode );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::UpdatePlayerView
|
|
================
|
|
*/
|
|
void sdVehiclePosition::UpdatePlayerView( void ) {
|
|
idPlayer* currentPlayer = player;
|
|
if ( currentPlayer ) {
|
|
currentPlayer->CalculateView();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::PresentPlayer
|
|
================
|
|
*/
|
|
void sdVehiclePosition::PresentPlayer( void ) {
|
|
idPlayer* currentPlayer = player;
|
|
if ( currentPlayer ) {
|
|
currentPlayer->UpdateVisuals();
|
|
currentPlayer->Present();
|
|
|
|
idWeapon* weapon = currentPlayer->GetWeapon();
|
|
if ( weapon ) {
|
|
weapon->PresentWeapon();
|
|
}
|
|
|
|
// if ( allowWeapon ) {
|
|
// currentPlayer->AdjustBodyAngles();
|
|
// }
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::UpdateIK
|
|
================
|
|
*/
|
|
void sdVehiclePosition::UpdateIK( void ) {
|
|
if ( transport->GetInput().GetSteerAngle() > 0 ) {
|
|
currentViewOffset += viewOffsetRate * MS2SEC( gameLocal.msec );
|
|
if ( currentViewOffset > 1.f ) {
|
|
currentViewOffset = 1.f;
|
|
}
|
|
} else if ( transport->GetInput().GetSteerAngle() < 0 ) {
|
|
currentViewOffset -= viewOffsetRate * MS2SEC( gameLocal.msec );
|
|
if ( currentViewOffset < -1.f ) {
|
|
currentViewOffset = -1.f;
|
|
}
|
|
} else {
|
|
currentViewOffset -= Min( idMath::Fabs( currentViewOffset ), viewOffsetRate * MS2SEC( gameLocal.msec ) ) * idMath::Sign( currentViewOffset );
|
|
}
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
if ( minZfrac != -2.f && transport->GetPhysics()->GetAxis()[ 2 ].z < minZfrac ) {
|
|
transport->GetPositionManager().EjectPlayer( *this, true );
|
|
}
|
|
}
|
|
|
|
currentViewOffsetAngles = ( idMath::Cos( DEG2RAD( ( currentViewOffset * 180 ) + 180 ) ) + 1 ) * 0.5f * idMath::Sign( currentViewOffset ) * maxViewOffset;
|
|
|
|
for ( int i = 0; i < ikSystems.Num(); i++ ) {
|
|
ikSystems[ i ]->Update();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::ClampAngle
|
|
================
|
|
*/
|
|
bool sdVehiclePosition::ClampAngle( idAngles& newAngles, const idAngles& oldAngles, angleClamp_t clamp, int index, float epsilon ) {
|
|
if ( clamp.flags.limitRate ) {
|
|
float rate = clamp.rate[ 0 ] * MS2SEC( gameLocal.msec );
|
|
float diff = idMath::AngleDelta( newAngles[ index ], oldAngles[ index ] );
|
|
|
|
if( diff > rate ) {
|
|
newAngles[ index ] = idMath::AngleNormalize180( oldAngles[ index ] + rate );
|
|
} else if ( diff < -rate ) {
|
|
newAngles[ index ] = idMath::AngleNormalize180( oldAngles[ index ] - rate );
|
|
}
|
|
}
|
|
|
|
if ( clamp.flags.enabled ) {
|
|
float mid = ( clamp.extents[ 0 ] + clamp.extents[ 1 ] ) * 0.5f;
|
|
float range = ( clamp.extents[ 1 ] - clamp.extents[ 0 ] ) * 0.5f;
|
|
|
|
float temp = idMath::AngleNormalize180( newAngles[ index ] - mid );
|
|
if ( temp < -range ) {
|
|
temp = -range;
|
|
} else if ( temp > range ) {
|
|
temp = range;
|
|
}
|
|
newAngles[ index ] = temp + mid;
|
|
}
|
|
|
|
if ( epsilon <= 0.f ) {
|
|
return newAngles[ index ] == oldAngles[ index ];
|
|
}
|
|
return idMath::Fabs( newAngles[ index ] - oldAngles[ index ] ) < epsilon;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::HasAbility
|
|
================
|
|
*/
|
|
bool sdVehiclePosition::HasAbility( qhandle_t handle ) const {
|
|
return abilities.HasAbility( handle );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdTransportPositionManager
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::sdTransportPositionManager
|
|
================
|
|
*/
|
|
sdTransportPositionManager::sdTransportPositionManager( void ) {
|
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
|
bannedPlayers[ i ] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::~sdTransportPositionManager
|
|
================
|
|
*/
|
|
sdTransportPositionManager::~sdTransportPositionManager( void ) {
|
|
ClearPositions();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::EjectAllPlayers
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::EjectAllPlayers( int flags ) {
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
for( int i = 0; i < positions.Num(); i++ ) {
|
|
idPlayer* player = positions[ i ].GetPlayer();
|
|
EjectPlayer( positions[ i ], true );
|
|
if ( player != NULL ) {
|
|
const sdDeclDamage* damageDecl = transport->GetLastDamage();
|
|
if ( flags & EF_KILL_PLAYERS ) {
|
|
if ( ( damageDecl != NULL && damageDecl->GetForcePassengerKill() ) || !positions[ i ].GetEjectOnKilled() ) {
|
|
player->Kill( transport->GetLastAttacker(), true, transport->GetKillPlayerDamage(), damageDecl );
|
|
}
|
|
}
|
|
|
|
i = -1; // in case any players have been moved around due to the removal of this player
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::FreePosition
|
|
================
|
|
*/
|
|
sdVehiclePosition* sdTransportPositionManager::FreePosition( idPlayer* player, const sdDeclToolTip** tip, int startIndex ) {
|
|
sdVehiclePosition* blocked = NULL;
|
|
int blockedCount = 0;
|
|
|
|
if ( tip ) {
|
|
*tip = NULL;
|
|
}
|
|
|
|
for ( int i = startIndex; i < positions.Num(); i++ ) {
|
|
if ( positions[ i ].GetPlayer() ) {
|
|
if ( !positions[ i ].GetPlayer()->IsType( idBot::Type ) || player->IsType( idBot::Type ) ) {
|
|
continue;
|
|
}
|
|
} //mal: bots dont count - they'll get booted out of this seat, and into another. ONLY for humans tho - they won't boot other bots.
|
|
|
|
if ( i == 0 && transport->IsLocked() ) {
|
|
blocked = &positions[ i ];
|
|
blockedCount++;
|
|
continue;
|
|
}
|
|
|
|
if ( !positions[ i ].CheckRequirements( player ) ) {
|
|
blocked = &positions[ i ];
|
|
blockedCount++;
|
|
continue;
|
|
}
|
|
return &positions[ i ];
|
|
}
|
|
|
|
if ( blockedCount == 1 && tip ) {
|
|
*tip = blocked->GetBlockedToolTip();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::SwapPosition
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::SwapPosition( idPlayer* player, bool allowCycle ) {
|
|
bool haveBotInSeat;
|
|
int startpos = player->GetProxyPositionId();
|
|
int i;
|
|
int endpos;
|
|
sdVehiclePosition* botPosition = NULL;
|
|
idPlayer* botPlayer;
|
|
|
|
bool cycle = transport->CycleAllPositions();
|
|
|
|
if ( cycle && allowCycle ) {
|
|
i = ( startpos + 1 ) % positions.Num();
|
|
endpos = startpos;
|
|
} else {
|
|
i = 0;
|
|
endpos = positions.Num();
|
|
}
|
|
|
|
while( i != endpos ) {
|
|
|
|
if ( ( positions[ i ].GetPlayer() == NULL || positions[ i ].GetPlayer()->IsType( idBot::Type ) && !player->IsType( idBot::Type ) )
|
|
&& positions[ i ].CheckRequirements( player )
|
|
&& ( i != 0 || !transport->IsLocked() ) ) {
|
|
|
|
haveBotInSeat = ( positions[ i ].GetPlayer() != NULL ) ? true : false;
|
|
|
|
if ( haveBotInSeat ) { //mal: if have a bot in this seat, boot him out of the vehicle.
|
|
botPlayer = positions[ i ].GetPlayer();
|
|
botPosition = &transport->GetPositionManager().PositionForPlayer( botPlayer );
|
|
transport->GetPositionManager().EjectPlayer( *botPosition, true );
|
|
botThreadData.GetGameWorldState()->clientInfo[ botPlayer->entityNumber ].resetState = MAJOR_RESET_EVENT; //mal: let the bot know to reset his AI state
|
|
}
|
|
|
|
transport->PlacePlayerInPosition( player, positions[ i ], &positions[ startpos ], true );
|
|
|
|
if ( haveBotInSeat ) { //mal: if had a bot, put him back in the vehicle, in the player's old seat.
|
|
transport->PlacePlayerInPosition( botPlayer, positions[ startpos ], NULL, false );
|
|
botThreadData.GetGameWorldState()->clientInfo[ botPlayer->entityNumber ].proxyInfo.clientChangedSeats = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if( cycle ) {
|
|
i = ( i + 1 ) % positions.Num();
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PositionForId
|
|
================
|
|
*/
|
|
sdVehiclePosition* sdTransportPositionManager::PositionForId( const int positionId ) {
|
|
return &positions[ positionId ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PositionForId
|
|
================
|
|
*/
|
|
const sdVehiclePosition* sdTransportPositionManager::PositionForId( const int positionId ) const {
|
|
return &positions[ positionId ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::ClearPositions
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::ClearPositions( void ) {
|
|
positions.Clear();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::RemovePlayer
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::RemovePlayer( sdVehiclePosition& position ) {
|
|
idPlayer* player = position.GetPlayer();
|
|
if ( !player ) {
|
|
return;
|
|
}
|
|
|
|
position.SetPlayer( NULL );
|
|
|
|
gameLocal.localPlayerProperties.ExitingObject( player, transport );
|
|
|
|
if ( transport->UnbindOnEject() ) {
|
|
player->ClearIKJoints();
|
|
player->Unbind();
|
|
}
|
|
|
|
transport->OnPlayerExited( player, position.GetPositionId() );
|
|
|
|
// add the player to the recent players list
|
|
playerExitTime[ player->entityNumber ] = gameLocal.time;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::EjectPlayer
|
|
================
|
|
*/
|
|
|
|
class sdExitJointDistanceInfo {
|
|
public:
|
|
float distanceSqr;
|
|
jointHandle_t joint;
|
|
idVec3 origin;
|
|
idMat3 axis;
|
|
|
|
static int SortByDistance( const sdExitJointDistanceInfo* a, const sdExitJointDistanceInfo* b ) {
|
|
if ( a->distanceSqr > b->distanceSqr ) {
|
|
return 1;
|
|
} else if ( a->distanceSqr < b->distanceSqr ) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
bool sdTransportPositionManager::EjectPlayer( sdVehiclePosition& position, bool force ) {
|
|
idPlayer* player = position.GetPlayer();
|
|
if ( !player ) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Find a position to eject to
|
|
//
|
|
bool foundOrg = false;
|
|
idVec3 selectedOrg = player->GetPhysics()->GetOrigin();
|
|
idMat3 selectedAxes = player->GetPhysics()->GetAxis();
|
|
|
|
if ( transport->UnbindOnEject() ) {
|
|
if( !gameLocal.isClient ) {
|
|
player->DisableClip( false );
|
|
|
|
sdTeleporter* teleportEnt = transport->GetTeleportEntity();
|
|
if ( teleportEnt != NULL ) {
|
|
teleportEnt->GetTeleportEndPoint( transport, selectedOrg, selectedAxes );
|
|
selectedOrg.z += 64.f;
|
|
foundOrg = true;
|
|
} else {
|
|
// prioritize exit joints by the nearest
|
|
idStaticList< sdExitJointDistanceInfo, MAX_EXIT_JOINTS > sortedExitJoints;
|
|
sortedExitJoints.SetNum( exitJoints.Num() );
|
|
idVec3 traceFromPoint;
|
|
transport->GetWorldOrigin( position.GetAttachJoint(), traceFromPoint );
|
|
for( int i = 0; i < exitJoints.Num(); i++ ) {
|
|
sortedExitJoints[ i ].joint = exitJoints[ i ];
|
|
transport->GetWorldOriginAxis( exitJoints[ i ], sortedExitJoints[ i ].origin, sortedExitJoints[ i ].axis );
|
|
sortedExitJoints[ i ].distanceSqr = ( traceFromPoint - sortedExitJoints[ i ].origin ).LengthSqr();
|
|
}
|
|
|
|
sortedExitJoints.Sort( sdExitJointDistanceInfo::SortByDistance );
|
|
|
|
// choose a point to do the cast-to-exit-point from - if we just use the origin it could
|
|
// potentially be in all sorts of wacky positions depending how the vehicle is built
|
|
// this enures the the point casted from is inside the vehicle
|
|
traceFromPoint = transport->GetPhysics()->GetAxis().TransposeMultiply( traceFromPoint - transport->GetPhysics()->GetOrigin() );
|
|
const idBounds& transportBounds = transport->GetPhysics()->GetBounds();
|
|
traceFromPoint.z = ( transportBounds[ 0 ].z + transportBounds[ 1 ].z ) * 0.5f;
|
|
traceFromPoint = traceFromPoint * transport->GetPhysics()->GetAxis() + transport->GetPhysics()->GetOrigin();
|
|
|
|
// default position to get out is inside the vehicle
|
|
selectedOrg = traceFromPoint;
|
|
selectedAxes;
|
|
|
|
const idClipModel* playerClip = player->GetPlayerPhysics().GetNormalClipModel();
|
|
|
|
for ( int i = 0; i < sortedExitJoints.Num(); i++ ) {
|
|
idVec3 org = sortedExitJoints[ i ].origin;
|
|
idMat3 axes = sortedExitJoints[ i ].axis;
|
|
|
|
if ( gameRenderWorld->PointInArea( org ) == -1 ) {
|
|
// outside the map, so no go
|
|
continue;
|
|
}
|
|
|
|
// check that the point is clear
|
|
int contents = gameLocal.clip.Contents( CLIP_DEBUG_PARMS org, playerClip, mat3_identity, MASK_PLAYERSOLID, NULL );
|
|
if( !contents ) {
|
|
// check that theres nothing in between the vehicle and the exit point
|
|
trace_t trace;
|
|
if( !gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, traceFromPoint, org, MASK_PLAYERSOLID, transport ) ) {
|
|
selectedOrg = org;
|
|
selectedAxes = axes;
|
|
foundOrg = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !foundOrg ) {
|
|
// Search all 8 positions around every exit joint, should find at least one.
|
|
for ( int i = 0; i < sortedExitJoints.Num(); i++ ) {
|
|
idVec3 orgBase = sortedExitJoints[ i ].origin;
|
|
idMat3 axes = sortedExitJoints[ i ].axis;
|
|
const int size = playerClip->GetBounds().GetSize().x;
|
|
const int spacing = 8;
|
|
|
|
for ( int j = -1; j < 2 && !foundOrg; j++ ) {
|
|
for ( int k = -1; k < 2 && !foundOrg; k++ ) {
|
|
if ( j == 0 && k == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
idVec3 org = orgBase + idVec3( j * size + j * spacing, k * size + k * spacing, 0.0f );
|
|
|
|
if ( gameRenderWorld->PointInArea( org ) == -1 ) {
|
|
// outside the map, so no go
|
|
continue;
|
|
}
|
|
|
|
// check that the point is clear
|
|
int contents = gameLocal.clip.Contents( CLIP_DEBUG_PARMS org, playerClip, mat3_identity, MASK_PLAYERSOLID, NULL );
|
|
if( !contents ) {
|
|
// check that theres nothing in between the vehicle and the exit point
|
|
trace_t trace;
|
|
if( !gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, traceFromPoint, org, MASK_PLAYERSOLID, transport ) ) {
|
|
selectedOrg = org;
|
|
selectedAxes = axes;
|
|
foundOrg = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
if ( !foundOrg ) {
|
|
if ( !force ) {
|
|
return false;
|
|
} else {
|
|
gameLocal.Warning( "sdTransportPositionManager::EjectPlayer No Valid Eject Position Found" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Actually eject
|
|
//
|
|
|
|
player->SetSuppressPredictionReset( true );
|
|
RemovePlayer( position );
|
|
|
|
if ( transport->UnbindOnEject() ) {
|
|
// copy the velocity over
|
|
idVec3 v = transport->GetPhysics()->GetLinearVelocity();
|
|
for ( int i = 0; i < 3; i++ ) {
|
|
if ( FLOAT_IS_NAN( v[ i ] ) ) {
|
|
v[ i ] = 0.f;
|
|
}
|
|
}
|
|
v.FixDenormals();
|
|
|
|
player->GetPhysics()->SetLinearVelocity( v );
|
|
|
|
// set the position
|
|
if ( foundOrg ) {
|
|
idAngles temp;
|
|
temp = selectedAxes.ToAngles();
|
|
temp.roll = 0.0f;
|
|
if ( temp.pitch < -10.0f ) {
|
|
temp.pitch = -10.0f;
|
|
}
|
|
player->SetViewAngles( temp );
|
|
player->SetOrigin( selectedOrg );
|
|
}
|
|
player->EnableClip();
|
|
}
|
|
|
|
|
|
player->SetProxyEntity( NULL, 0 );
|
|
// this forces the reset message to be re-sent
|
|
player->SetSuppressPredictionReset( false );
|
|
player->ResetPredictionErrorDecay();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::FindDriver
|
|
================
|
|
*/
|
|
idPlayer* sdTransportPositionManager::FindDriver( void ) {
|
|
if ( positions.Num() ) {
|
|
return positions[ 0 ].GetPlayer();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::Think
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::Think( void ) {
|
|
for ( int i = 0; i < positions.Num(); i++ ) {
|
|
positions[ i ].Think();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::UpdatePlayerViews
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::UpdatePlayerViews( void ) {
|
|
for ( int i = 0; i < positions.Num(); i++ ) {
|
|
positions[ i ].UpdatePlayerView();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PresentPlayers
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::PresentPlayers( void ) {
|
|
for ( int i = 0; i < positions.Num(); i++ ) {
|
|
positions[ i ].PresentPlayer();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::BanPlayer
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::BanPlayer( int clientNum, int length ) {
|
|
int newBanTime = gameLocal.time + length;
|
|
if ( newBanTime > bannedPlayers[ clientNum ] ) {
|
|
bannedPlayers[ clientNum ] = newBanTime;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::ResetBan
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::ResetBan( int clientNum ) {
|
|
bannedPlayers[ clientNum ] = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::Init
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::Init( const sdDeclVehicleScript* vehicleScript, sdTransport* other ) {
|
|
transport = other;
|
|
|
|
idAnimator& animator = *other->GetAnimator();
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
EjectAllPlayers();
|
|
}
|
|
ClearPositions();
|
|
|
|
playerExitTime.SetNum( MAX_CLIENTS );
|
|
for ( int i = 0; i < playerExitTime.Num(); i++ ) {
|
|
playerExitTime[ i ] = 0;
|
|
}
|
|
|
|
for( int i = 0; i < vehicleScript->positions.Num(); i++ ) {
|
|
positionInfo_t& filePosition = vehicleScript->positions[ i ]->positionInfo;
|
|
|
|
// FIXME: Gordon: Uh, the position should do this itself innit.
|
|
|
|
sdVehiclePosition* positionSlot = positions.Alloc();
|
|
if ( positionSlot == NULL ) {
|
|
gameLocal.Error( "sdTransportPositionManager::Init - Number of positions exceeds MAX_POSITIONS" );
|
|
}
|
|
sdVehiclePosition& position = *positionSlot;
|
|
|
|
|
|
position.SetPositionId( i );
|
|
position.SetHudName( filePosition.hudname );
|
|
position.SetName( filePosition.name );
|
|
position.SetTransport( other );
|
|
position.LoadData( filePosition.data );
|
|
|
|
int j;
|
|
|
|
for ( j = 0; j < filePosition.views.Num(); j++ ) {
|
|
position.AddView( filePosition.views[ j ] );
|
|
}
|
|
|
|
for( j = 0; j < filePosition.weapons.Num(); j++ ) {
|
|
sdVehicleWeapon* weapon = sdVehicleWeaponFactory::GetWeapon( filePosition.weapons[ j ].weaponType );
|
|
weapon->SetPosition( NULL );
|
|
weapon->SetBasePosition( &position );
|
|
weapon->Setup( other, *filePosition.weapons[ j ].weaponDef, filePosition.weapons[ j ].clampYaw, filePosition.weapons[ j ].clampPitch );
|
|
other->AddWeapon( weapon );
|
|
}
|
|
|
|
for( j = 0; j < filePosition.ikSystems.Num(); j++ ) {
|
|
const char* ikSystemTypeName = filePosition.ikSystems[ j ].ikType.c_str();
|
|
idTypeInfo* ikType = idClass::GetClass( ikSystemTypeName );
|
|
if ( !ikType ) {
|
|
gameLocal.Error( "sdTransportPositionManager: Invalid ikType '%s'", ikSystemTypeName );
|
|
}
|
|
if ( !ikType->IsType( sdVehicleIKSystem::Type ) ) {
|
|
gameLocal.Error( "sdTransportPositionManager: ikType '%s' Is Not Of Type sdVehicleIKSystem", ikSystemTypeName );
|
|
}
|
|
|
|
sdVehicleIKSystem* ikSystem = reinterpret_cast< sdVehicleIKSystem* >( ikType->CreateInstance() );
|
|
ikSystem->SetPosition( &position );
|
|
ikSystem->Setup( other, filePosition.ikSystems[ j ].clampYaw, filePosition.ikSystems[ j ].clampPitch, filePosition.ikSystems[ j ].ikParms );
|
|
|
|
position.AddIKSystem( ikSystem );
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < vehicleScript->exits.Num(); i++ ) {
|
|
jointHandle_t joint = animator.GetJointHandle( vehicleScript->exits[ i ]->exitInfo.joint );
|
|
if( joint == INVALID_JOINT ) {
|
|
gameLocal.Warning( "sdTransportPositionManager::Init Invalid Joint Name '%s' For Vehicle '%s'", vehicleScript->exits[ i ]->exitInfo.joint.c_str(), other->name.c_str() );
|
|
continue;
|
|
}
|
|
|
|
jointHandle_t* jointSlot = exitJoints.Alloc();
|
|
if ( jointSlot == NULL ) {
|
|
gameLocal.Error( "sdTransportPositionManager::Init - Number of exit joints exceeds MAX_EXIT_JOINTS" );
|
|
}
|
|
|
|
*jointSlot = joint;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::Init
|
|
================
|
|
*/
|
|
sdVehiclePosition* sdTransportPositionManager::FindDriverPosition( void ) {
|
|
if ( positions.Num() ) {
|
|
return &positions[ 0 ];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PositionForPlayer
|
|
================
|
|
*/
|
|
sdVehiclePosition& sdTransportPositionManager::PositionForPlayer( const idPlayer* player ) {
|
|
return positions[ player->GetProxyPositionId() ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PositionForPlayer
|
|
================
|
|
*/
|
|
const sdVehiclePosition& sdTransportPositionManager::PositionForPlayer( const idPlayer* player ) const {
|
|
return positions[ player->GetProxyPositionId() ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::PositionNameForPlayer
|
|
================
|
|
*/
|
|
const wchar_t* sdTransportPositionManager::PositionNameForPlayer( idPlayer* player ) {
|
|
sdVehiclePosition& position = PositionForPlayer( player );
|
|
return position.GetHudName()->GetText();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::IsEmpty
|
|
================
|
|
*/
|
|
bool sdTransportPositionManager::IsEmpty( void ) {
|
|
for( int i = 0; i < positions.Num(); i++ ) {
|
|
if( positions[ i ].GetPlayer() ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::HasFreePosition
|
|
|
|
Just a general check if this vehicle has any open seats at all.
|
|
================
|
|
*/
|
|
bool sdTransportPositionManager::HasFreePosition( void ) {
|
|
|
|
bool hasSeat = false;
|
|
int i;
|
|
|
|
for ( i = 0; i < positions.Num(); i++ ) {
|
|
if ( positions[ i ].GetPlayer() ) {
|
|
continue;
|
|
}
|
|
|
|
hasSeat = true;
|
|
break;
|
|
}
|
|
|
|
return hasSeat;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdTransportPositionManager::OnVehicleScriptReloaded
|
|
================
|
|
*/
|
|
void sdTransportPositionManager::OnVehicleScriptReloaded() {
|
|
for ( int i = 0; i < positions.Num(); i++ ) {
|
|
positions[ i ].OnVehicleScriptReloaded();
|
|
}
|
|
positions.Clear();
|
|
exitJoints.Clear();
|
|
|
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
|
bannedPlayers[ i ] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdVehiclePosition::OnVehicleScriptReloaded
|
|
================
|
|
*/
|
|
void sdVehiclePosition::OnVehicleScriptReloaded() {
|
|
if ( player ) {
|
|
player->SetProxyEntity( NULL, 0 );
|
|
}
|
|
|
|
player = NULL;
|
|
transport = NULL;
|
|
weaponIndex = -1;
|
|
index = -1;
|
|
blockedTip = NULL;
|
|
maxViewOffset = 0.f;
|
|
viewOffsetRate = 0.f;
|
|
currentViewOffset = 0.f;
|
|
currentViewOffsetAngles = 0.f;
|
|
statTimeSpent = NULL;
|
|
playerEnteredTime = 0;
|
|
hudname = NULL;
|
|
name = "";
|
|
attachJoint = INVALID_JOINT;
|
|
attachAnim = "";
|
|
cockpitName = "";
|
|
abilities.Clear();
|
|
requirements.Clear();
|
|
|
|
for ( int i = 0; i < views.Num(); i++ ) {
|
|
delete views[ i ];
|
|
}
|
|
views.Clear();
|
|
for ( int i = 0; i < ikSystems.Num(); i++ ) {
|
|
delete ikSystems[ i ];
|
|
}
|
|
ikSystems.Clear();
|
|
}
|