613 lines
16 KiB
C++
613 lines
16 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 "PlayerBody.h"
|
|
#include "../Player.h"
|
|
#include "../script/Script_Helper.h"
|
|
#include "../script/Script_ScriptObject.h"
|
|
#include "../ContentMask.h"
|
|
|
|
|
|
/*
|
|
================
|
|
sdPlayerBodyNetworkData::~sdPlayerBodyNetworkData
|
|
================
|
|
*/
|
|
sdPlayerBodyNetworkData::~sdPlayerBodyNetworkData( void ) {
|
|
delete physicsData;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBodyNetworkData::MakeDefault
|
|
================
|
|
*/
|
|
void sdPlayerBodyNetworkData::MakeDefault( void ) {
|
|
if ( physicsData ) {
|
|
physicsData->MakeDefault();
|
|
}
|
|
scriptData.MakeDefault();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBodyNetworkData::Write
|
|
================
|
|
*/
|
|
void sdPlayerBodyNetworkData::Write( idFile* file ) const {
|
|
if ( physicsData ) {
|
|
physicsData->Write( file );
|
|
}
|
|
scriptData.Write( file );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBodyNetworkData::Read
|
|
================
|
|
*/
|
|
void sdPlayerBodyNetworkData::Read( idFile* file ) {
|
|
if ( physicsData ) {
|
|
physicsData->Read( file );
|
|
}
|
|
scriptData.Read( file );
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdPlayerBodyInteractiveInterface
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBodyInteractiveInterface::OnActivate
|
|
==============
|
|
*/
|
|
void sdPlayerBodyInteractiveInterface::Init( sdPlayerBody* owner ) {
|
|
_owner = owner;
|
|
_activateFunc = owner->GetScriptObject()->GetFunction( "OnActivate" );
|
|
_activateHeldFunc = owner->GetScriptObject()->GetFunction( "OnActivateHeld" );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBodyInteractiveInterface::OnActivate
|
|
==============
|
|
*/
|
|
bool sdPlayerBodyInteractiveInterface::OnActivate( idPlayer* player, float distance ) {
|
|
if ( !_activateFunc ) {
|
|
return false;
|
|
}
|
|
|
|
sdScriptHelper h1;
|
|
h1.Push( player->GetScriptObject() );
|
|
h1.Push( distance );
|
|
_owner->GetScriptObject()->CallNonBlockingScriptEvent( _activateFunc, h1 );
|
|
|
|
return gameLocal.program->GetReturnedFloat() != 0.f;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBodyInteractiveInterface::OnActivateHeld
|
|
==============
|
|
*/
|
|
bool sdPlayerBodyInteractiveInterface::OnActivateHeld( idPlayer* player, float distance ) {
|
|
if ( !_activateHeldFunc ) {
|
|
return false;
|
|
}
|
|
|
|
sdScriptHelper h1;
|
|
h1.Push( player->GetScriptObject() );
|
|
h1.Push( distance );
|
|
_owner->GetScriptObject()->CallNonBlockingScriptEvent( _activateHeldFunc, h1 );
|
|
|
|
return gameLocal.program->GetReturnedFloat() != 0.f;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdPlayerBody
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
extern const idEventDef EV_GetOwner;
|
|
extern const idEventDef EV_GetRenderViewAngles;
|
|
|
|
CLASS_DECLARATION( idAnimatedEntity, sdPlayerBody )
|
|
EVENT( EV_GetOwner, sdPlayerBody::Event_GetOwner )
|
|
EVENT( EV_GetRenderViewAngles, sdPlayerBody::Event_GetRenderViewAngles )
|
|
END_CLASS
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBody::sdPlayerBody
|
|
==============
|
|
*/
|
|
sdPlayerBody::sdPlayerBody( void ) {
|
|
client = NULL;
|
|
team = NULL;
|
|
updateCrosshairFunc = NULL;
|
|
rank = NULL;
|
|
creationTime = gameLocal.time;
|
|
viewYaw = 0.f;
|
|
viewAxis.Identity();
|
|
|
|
isSpawnHostableFunc = NULL;
|
|
isSpawnHostFunc = NULL;
|
|
hasNoUniformFunc = NULL;
|
|
prePlayerFullyKilledFunc = NULL;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBody::CanCollide
|
|
==============
|
|
*/
|
|
bool sdPlayerBody::CanCollide( const idEntity* other, int traceId ) const {
|
|
if ( traceId == TM_THIRDPERSON_OFFSET ) {
|
|
return false;
|
|
}
|
|
return idEntity::CanCollide( other, traceId );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBody::Spawn
|
|
==============
|
|
*/
|
|
void sdPlayerBody::Spawn( void ) {
|
|
scriptObject = gameLocal.program->AllocScriptObject( this, "dead_body" );
|
|
ConstructScriptObject();
|
|
|
|
interactiveInterface.Init( this );
|
|
|
|
updateCrosshairFunc = scriptObject->GetFunction( "OnUpdateCrosshairInfo" );
|
|
|
|
isSpawnHostableFunc = scriptObject->GetFunction( "IsSpawnHostable" );
|
|
isSpawnHostFunc = scriptObject->GetFunction( "IsSpawnHost" );
|
|
hasNoUniformFunc = scriptObject->GetFunction( "HasNoUniform" );
|
|
prePlayerFullyKilledFunc = scriptObject->GetFunction( "PrePlayerFullyKilled" );
|
|
|
|
animator.RemoveOriginOffset( true );
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
sdInstanceCollector< sdPlayerBody > bodies( false );
|
|
for ( int i = 0; i < bodies.Num(); ) {
|
|
if ( bodies[ i ]->creationTime == 0 ) {
|
|
bodies.GetList().RemoveIndexFast( i );
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if ( bodies.Num() > MAX_PLAYERBODIES ) {
|
|
bodies.GetList().Sort( SortByTime );
|
|
|
|
bodies[ 0 ]->PostEventMS( &EV_Remove, 0 );
|
|
bodies[ 0 ]->creationTime = 0;
|
|
}
|
|
}
|
|
|
|
idBounds bounds;
|
|
idPhysics_Player::CalcNormalBounds( bounds );
|
|
idTraceModel trm( bounds );
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( trm, true ), 1.0f );
|
|
physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
|
|
physicsObj.SetClipMask( MASK_PLAYERSOLID );
|
|
physicsObj.DisableImpact();
|
|
|
|
idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
|
|
gravity *= g_gravity.GetFloat();
|
|
physicsObj.SetGravity( gravity );
|
|
physicsObj.SetMaxStepHeight( 0 );
|
|
physicsObj.SetContents( 0 );
|
|
|
|
SetPhysics( &physicsObj );
|
|
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBody::SetupBody
|
|
==============
|
|
*/
|
|
void sdPlayerBody::SetupBody( void ) {
|
|
if ( playerClass.GetClass() == NULL ) {
|
|
return;
|
|
}
|
|
|
|
const idDeclModelDef* model = playerClass.GetClass()->GetModel();
|
|
const idDict& info = playerClass.GetClass()->GetModelData();
|
|
|
|
SetModel( model->GetName() );
|
|
SetSkin( sdInventory::SkinForClass( playerClass.GetClass() ) );
|
|
renderEntity.maxVisDist = 5144;
|
|
renderEntity.flags.noShadow = !g_showPlayerShadow.GetBool();
|
|
|
|
SetCombatModel();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdPlayerBody::Init
|
|
==============
|
|
*/
|
|
void sdPlayerBody::Init( idPlayer* _client, const sdPlayerClassSetup* _playerClass, sdTeamInfo* _team ) {
|
|
assert( _playerClass );
|
|
assert( _team );
|
|
|
|
client = _client;
|
|
playerClass.SetClass( _playerClass->GetClass() );
|
|
playerClass.SetOptions( _playerClass->GetOptions() );
|
|
team = _team;
|
|
viewYaw = client->viewAxis.ToAngles().yaw;
|
|
idAngles::YawToMat3( viewYaw, viewAxis );
|
|
|
|
rank = client->GetProficiencyTable().GetRank();
|
|
rating = client->GetRating();
|
|
|
|
SetupBody();
|
|
|
|
GetPhysics()->SetOrigin( client->GetPhysics()->GetOrigin() );
|
|
GetPhysics()->SetAxis( client->GetPhysics()->GetAxis() );
|
|
|
|
idAnimBlend* torosBlend = client->GetAnimator()->CurrentAnim( ANIMCHANNEL_TORSO );
|
|
torsoAnimNum = torosBlend->AnimNum();
|
|
torsoAnimStartTime = torosBlend->GetStartTime();
|
|
GetAnimator()->PlayAnim( ANIMCHANNEL_TORSO, torsoAnimNum, torsoAnimStartTime, 0 );
|
|
|
|
idAnimBlend* legsBlend = client->GetAnimator()->CurrentAnim( ANIMCHANNEL_LEGS );
|
|
legsAnimNum = legsBlend->AnimNum();
|
|
legsAnimStartTime = legsBlend->GetStartTime();
|
|
GetAnimator()->PlayAnim( ANIMCHANNEL_LEGS, legsAnimNum, legsAnimStartTime, 0 );
|
|
|
|
animator.CreateFrame( gameLocal.time, true );
|
|
|
|
// ensure that the combat model has the correct bounds
|
|
// fixes the case where people tapping out made spawn hosting & possess/disguise difficult
|
|
animator.GetBounds( gameLocal.time, renderEntity.bounds, true );
|
|
|
|
// ao: onfullykilled must not have been called on player
|
|
if ( client.IsValid() ) {
|
|
sdScriptHelper h1;
|
|
h1.Push( client->GetScriptObject() );
|
|
CallNonBlockingScriptEvent( prePlayerFullyKilledFunc, h1 );
|
|
}
|
|
|
|
UpdateVisuals();
|
|
Present();
|
|
|
|
if ( gameLocal.isServer ) {
|
|
sdEntityBroadcastEvent msg( this, EVENT_INIT );
|
|
msg.WriteLong( client.GetSpawnId() );
|
|
|
|
// write the class info
|
|
msg.WriteLong( playerClass.GetClass()->Index() );
|
|
|
|
sdTeamManager::GetInstance().WriteTeamToStream( team, msg );
|
|
msg.WriteLong( torsoAnimNum );
|
|
msg.WriteLong( torsoAnimStartTime );
|
|
msg.WriteLong( legsAnimNum );
|
|
msg.WriteLong( legsAnimStartTime );
|
|
msg.WriteUShort( ANGLE2SHORT( viewYaw ) );
|
|
msg.Send( true, sdReliableMessageClientInfoAll() );
|
|
}
|
|
|
|
if ( client->GetPhysics()->GetLinearVelocity().LengthSqr() < idMath::FLT_EPSILON && client->GetPhysics()->GetNumContacts() > 0 ) {
|
|
GetPhysics()->PutToRest();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::ReadDemoBaseData
|
|
================
|
|
*/
|
|
void sdPlayerBody::ReadDemoBaseData( idFile* file ) {
|
|
idEntity::ReadDemoBaseData( file );
|
|
|
|
bool temp;
|
|
file->ReadBool( temp );
|
|
if ( temp ) {
|
|
int clientSpawnId;
|
|
file->ReadInt( clientSpawnId );
|
|
|
|
int rankIndex;
|
|
file->ReadInt( rankIndex );
|
|
|
|
int ratingIndex;
|
|
file->ReadInt( ratingIndex );
|
|
|
|
int classIndex;
|
|
file->ReadInt( classIndex );
|
|
|
|
int numOptions;
|
|
idList< int > classOptions;
|
|
file->ReadInt( numOptions );
|
|
classOptions.SetNum( numOptions );
|
|
for ( int i = 0; i < numOptions; i++ ) {
|
|
file->ReadInt( classOptions[ i ] );
|
|
}
|
|
|
|
sdTeamInfo* teamInfo = sdTeamManager::GetInstance().ReadTeamFromStream( file );
|
|
|
|
int tAnimNum;
|
|
file->ReadInt( tAnimNum );
|
|
|
|
int tAnimStartTime;
|
|
file->ReadInt( tAnimStartTime );
|
|
|
|
int lAnimNum;
|
|
file->ReadInt( lAnimNum );
|
|
|
|
int lAnimStartTime;
|
|
file->ReadInt( lAnimStartTime );
|
|
|
|
float newViewYaw;
|
|
file->ReadFloat( newViewYaw );
|
|
|
|
Init( clientSpawnId, rankIndex, ratingIndex, classIndex, classOptions, teamInfo, tAnimNum, tAnimStartTime, lAnimNum, lAnimStartTime, newViewYaw );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::WriteDemoBaseData
|
|
================
|
|
*/
|
|
void sdPlayerBody::WriteDemoBaseData( idFile* file ) const {
|
|
idEntity::WriteDemoBaseData( file );
|
|
|
|
if ( playerClass.GetClass() == NULL ) {
|
|
// not inited yet
|
|
file->WriteBool( false );
|
|
} else {
|
|
file->WriteBool( true );
|
|
|
|
file->WriteInt( client.GetSpawnId() );
|
|
|
|
file->WriteInt( rank == NULL ? -1 : rank->Index() );
|
|
file->WriteInt( rating == NULL ? -1 : rating->Index() );
|
|
|
|
file->WriteInt( playerClass.GetClass()->Index() );
|
|
|
|
const idList< int >& options = playerClass.GetOptions();
|
|
|
|
file->WriteInt( options.Num() );
|
|
for ( int i = 0; i < options.Num(); i++ ) {
|
|
file->WriteInt( options[ i ] );
|
|
}
|
|
|
|
sdTeamManager::GetInstance().WriteTeamToStream( team, file );
|
|
file->WriteInt( torsoAnimNum );
|
|
file->WriteInt( torsoAnimStartTime );
|
|
file->WriteInt( legsAnimNum );
|
|
file->WriteInt( legsAnimStartTime );
|
|
file->WriteFloat( viewYaw );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::GetPhysicsToVisualTransform
|
|
================
|
|
*/
|
|
bool sdPlayerBody::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
|
|
origin = vec3_origin;
|
|
axis = viewAxis;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::UpdateCrosshairInfo
|
|
================
|
|
*/
|
|
bool sdPlayerBody::UpdateCrosshairInfo( idPlayer* player, sdCrosshairInfo& info ) {
|
|
if ( !updateCrosshairFunc ) {
|
|
return false;
|
|
}
|
|
|
|
crosshairInfo = &info;
|
|
|
|
sdScriptHelper h1;
|
|
h1.Push( player == NULL ? NULL : player->GetScriptObject() );
|
|
scriptObject->CallNonBlockingScriptEvent( updateCrosshairFunc, h1 );
|
|
|
|
crosshairInfo = NULL;
|
|
|
|
return gameLocal.program->GetReturnedFloat() != 0.f;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::Init
|
|
================
|
|
*/
|
|
void sdPlayerBody::Init( int clientSpawnId, int rankIndex, int ratingIndex, int classIndex, const idList< int >& classOptions, sdTeamInfo* teamInfo, int tAnimNum, int tAnimStartTime,
|
|
int lAnimNum, int lAnimStartTime, float newViewYaw ) {
|
|
client.ForceSpawnId( clientSpawnId );
|
|
|
|
rank = gameLocal.declRankType.SafeIndex( rankIndex );
|
|
rating = gameLocal.declRatingType.SafeIndex( ratingIndex );
|
|
|
|
playerClass.SetClass( gameLocal.declPlayerClassType[ classIndex ] );
|
|
for ( int i = 0; i < playerClass.GetOptions().Num(); i++ ) {
|
|
playerClass.SetOption( i, classOptions[ i ] );
|
|
}
|
|
|
|
team = teamInfo;
|
|
|
|
SetupBody();
|
|
|
|
torsoAnimNum = tAnimNum;
|
|
torsoAnimStartTime = tAnimStartTime;
|
|
|
|
legsAnimNum = lAnimNum;
|
|
legsAnimStartTime = lAnimStartTime;
|
|
|
|
viewYaw = newViewYaw;
|
|
idAngles::YawToMat3( viewYaw, viewAxis );
|
|
|
|
GetAnimator()->PlayAnim( ANIMCHANNEL_TORSO, torsoAnimNum, torsoAnimStartTime, 0 );
|
|
GetAnimator()->PlayAnim( ANIMCHANNEL_LEGS, legsAnimNum, legsAnimStartTime, 0 );
|
|
|
|
animator.CreateFrame( gameLocal.time, true );
|
|
|
|
// ensure that the combat model has the correct bounds
|
|
// fixes the case where people tapping out made spawn hosting & possess/disguise difficult
|
|
animator.GetBounds( gameLocal.time, renderEntity.bounds, true );
|
|
|
|
UpdateVisuals();
|
|
Present();
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::GetDecalUsage
|
|
================
|
|
*/
|
|
cheapDecalUsage_t sdPlayerBody::GetDecalUsage( void ) {
|
|
return CDU_INHIBIT;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::ClientReceiveEvent
|
|
================
|
|
*/
|
|
bool sdPlayerBody::ClientReceiveEvent( int event, int time, const idBitMsg& msg ) {
|
|
switch ( event ) {
|
|
case EVENT_INIT: {
|
|
int clientSpawnId = msg.ReadLong();
|
|
int classIndex = msg.ReadLong();
|
|
idList< int > classOptions;
|
|
|
|
const sdDeclPlayerClass* cls = gameLocal.declPlayerClassType[ classIndex ];
|
|
if ( cls != NULL ) {
|
|
for ( int i = 0; i < cls->GetNumOptions(); i++ ) {
|
|
classOptions.Alloc() = 0;
|
|
}
|
|
}
|
|
|
|
sdTeamInfo* teamInfo = sdTeamManager::GetInstance().ReadTeamFromStream( msg );
|
|
|
|
int tAnimNum = msg.ReadLong();
|
|
int tAnimStartTime = msg.ReadLong();
|
|
|
|
int lAnimNum = msg.ReadLong();
|
|
int lAnimStartTime = msg.ReadLong();
|
|
|
|
unsigned short newViewYaw = msg.ReadUShort();
|
|
|
|
Init( clientSpawnId, -1, -1, classIndex, classOptions, teamInfo, tAnimNum, tAnimStartTime, lAnimNum, lAnimStartTime, SHORT2ANGLE( newViewYaw ) );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::Event_GetOwner
|
|
================
|
|
*/
|
|
void sdPlayerBody::Event_GetOwner( void ) {
|
|
sdProgram::ReturnEntity( client );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::Event_GetRenderViewAngles
|
|
================
|
|
*/
|
|
void sdPlayerBody::Event_GetRenderViewAngles( void ) {
|
|
idAngles renderAngles = viewAxis.ToAngles();
|
|
sdProgram::ReturnVector( idVec3( renderAngles[ 0 ], renderAngles[ 1 ], renderAngles[ 2 ] ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
sdPlayerBody::SortByTime
|
|
================
|
|
*/
|
|
int sdPlayerBody::SortByTime( sdPlayerBody* bodyA, sdPlayerBody* bodyB ) {
|
|
return bodyA->creationTime - bodyB->creationTime;
|
|
}
|
|
|
|
/*
|
|
============
|
|
sdPlayerBody::ApplyNetworkState
|
|
============
|
|
*/
|
|
void sdPlayerBody::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
|
|
NET_GET_NEW( sdPlayerBodyNetworkData );
|
|
NET_APPLY_STATE_PHYSICS
|
|
NET_APPLY_STATE_SCRIPT
|
|
}
|
|
|
|
/*
|
|
============
|
|
sdPlayerBody::ReadNetworkState
|
|
============
|
|
*/
|
|
void sdPlayerBody::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
|
|
NET_GET_STATES( sdPlayerBodyNetworkData );
|
|
NET_READ_STATE_PHYSICS
|
|
NET_READ_STATE_SCRIPT
|
|
}
|
|
|
|
/*
|
|
============
|
|
sdPlayerBody::WriteNetworkState
|
|
============
|
|
*/
|
|
void sdPlayerBody::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
|
|
NET_GET_STATES( sdPlayerBodyNetworkData );
|
|
NET_WRITE_STATE_PHYSICS
|
|
NET_WRITE_STATE_SCRIPT
|
|
}
|
|
|
|
/*
|
|
============
|
|
sdPlayerBody::CheckNetworkStateChanges
|
|
============
|
|
*/
|
|
bool sdPlayerBody::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
|
|
NET_GET_BASE( sdPlayerBodyNetworkData );
|
|
NET_CHECK_STATE_PHYSICS
|
|
NET_CHECK_STATE_SCRIPT
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
sdPlayerBody::CreateNetworkStructure
|
|
============
|
|
*/
|
|
sdEntityStateNetworkData* sdPlayerBody::CreateNetworkStructure( networkStateMode_t mode ) const {
|
|
sdPlayerBodyNetworkData* newData = new sdPlayerBodyNetworkData();
|
|
newData->physicsData = GetPhysics()->CreateNetworkStructure( mode );
|
|
return newData;
|
|
}
|