etqw-sdk/source/game/misc/PlayerBody.cpp

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;
}