2732 lines
61 KiB
C++
2732 lines
61 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 "Inventory.h"
|
||
|
|
||
|
#include "../decls/DeclInvItem.h"
|
||
|
#include "../Player.h"
|
||
|
#include "../Weapon.h"
|
||
|
#include "../script/Script_Helper.h"
|
||
|
#include "../script/Script_ScriptObject.h"
|
||
|
#include "../proficiency/StatsTracker.h"
|
||
|
|
||
|
#include "../botai/BotThreadData.h"
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdItemPoolEntry
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdItemPoolEntry::sdItemPoolEntry
|
||
|
==============
|
||
|
*/
|
||
|
sdItemPoolEntry::sdItemPoolEntry( void ) {
|
||
|
flags.disabled = false;
|
||
|
flags.hidden = false;
|
||
|
item = NULL;
|
||
|
joint = INVALID_JOINT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdItemPoolEntry::SetItem
|
||
|
==============
|
||
|
*/
|
||
|
void sdItemPoolEntry::SetItem( const sdDeclInvItem* _item ) {
|
||
|
item = _item;
|
||
|
|
||
|
if ( item ) {
|
||
|
for ( int j = 0; j < item->GetClips().Num(); j++ ) {
|
||
|
clips.Alloc() = -1;
|
||
|
}
|
||
|
} else {
|
||
|
clips.SetNum( 0, false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdPlayerClassSetup
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdPlayerClassSetup::sdPlayerClassSetup
|
||
|
==============
|
||
|
*/
|
||
|
sdPlayerClassSetup::sdPlayerClassSetup( void ) : playerClass( NULL ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdPlayerClassSetup::operator=
|
||
|
==============
|
||
|
*/
|
||
|
void sdPlayerClassSetup::operator=( const sdPlayerClassSetup& rhs ) {
|
||
|
playerClass = rhs.playerClass;
|
||
|
playerClassOptions.SetNum( rhs.playerClassOptions.Num(), false );
|
||
|
for ( int i = 0; i < playerClassOptions.Num(); i++ ) {
|
||
|
playerClassOptions[ i ] = rhs.playerClassOptions[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdPlayerClassSetup::SetOptions
|
||
|
==============
|
||
|
*/
|
||
|
void sdPlayerClassSetup::SetOptions( const idList< int >& options ) {
|
||
|
if ( options.Num() != playerClassOptions.Num() ) {
|
||
|
gameLocal.Warning( "sdPlayerClassSetup::SetOptions Number of options did not match" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
playerClassOptions = options;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdPlayerClassSetup::SetClass
|
||
|
==============
|
||
|
*/
|
||
|
bool sdPlayerClassSetup::SetClass( const sdDeclPlayerClass* pc, bool force ) {
|
||
|
if ( pc == playerClass && !force ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
playerClass = pc;
|
||
|
if ( playerClass ) {
|
||
|
playerClassOptions.Fill( playerClass->GetNumOptions(), 0 );
|
||
|
} else {
|
||
|
playerClassOptions.SetNum( 0, false );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdPlayerClassSetup::SetOption
|
||
|
==============
|
||
|
*/
|
||
|
bool sdPlayerClassSetup::SetOption( int index, int itemIndex ) {
|
||
|
if ( index < 0 || index >= playerClassOptions.Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( playerClassOptions[ index ] == itemIndex ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
playerClassOptions[ index ] = itemIndex;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPlayerClassSetup::GetOption
|
||
|
============
|
||
|
*/
|
||
|
int sdPlayerClassSetup::GetOption( int index ) const {
|
||
|
assert( index >= 0 && index < playerClassOptions.Num() );
|
||
|
|
||
|
return playerClassOptions[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdUpgradeItemPool
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::OnHide
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::OnHide( void ) {
|
||
|
for ( int i = 0; i < modelItems.Num(); i++ ) {
|
||
|
modelItems[ i ]->HideModel();
|
||
|
}
|
||
|
modelItems.SetNum( 0, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::OnShow
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::OnShow( void ) {
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( !items[ i ].IsVisible() ) {
|
||
|
continue;
|
||
|
}
|
||
|
ShowItem( items[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::ShowItem
|
||
|
==============
|
||
|
*/
|
||
|
bool sdUpgradeItemPool::ShowItem( sdItemPoolEntry& item ) {
|
||
|
if ( !item.GetModel().hModel ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
modelItems.AddUnique( &item );
|
||
|
item.ShowModel();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::HideItem
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::HideItem( sdItemPoolEntry& item ) {
|
||
|
modelItems.RemoveFast( &item );
|
||
|
item.HideModel();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::AddItem
|
||
|
==============
|
||
|
*/
|
||
|
int sdUpgradeItemPool::AddItem( const sdDeclInvItem* item, bool enabled ) {
|
||
|
sdItemPoolEntry& entry = items.Alloc();
|
||
|
|
||
|
entry.SetItem( item );
|
||
|
entry.SetDisabled( !enabled );
|
||
|
|
||
|
idPlayer* player = parent->GetOwner();
|
||
|
|
||
|
idRenderModel* model = item->GetModel();
|
||
|
if ( model && enabled ) {
|
||
|
renderEntity_t& entity = entry.GetModel();
|
||
|
|
||
|
entity.spawnID = gameLocal.GetSpawnId( player );//->entityNumber;
|
||
|
entity.suppressSurfaceInViewID = player->entityNumber + 1;
|
||
|
entity.noSelfShadowInViewID = player->entityNumber + 1;
|
||
|
entity.axis.Identity();
|
||
|
entity.origin.Zero();
|
||
|
entity.hModel = model;
|
||
|
entity.bounds = entity.hModel->Bounds( &entity );
|
||
|
entity.maxVisDist = item->GetData().GetInt( "maxVisDist", "2048" );
|
||
|
|
||
|
SetupModelShadows( entry );
|
||
|
ShowItem( entry );
|
||
|
}
|
||
|
|
||
|
entry.joint = player->GetAnimator()->GetJointHandle( item->GetJoint() );
|
||
|
|
||
|
return items.Num() - 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::ClearItem
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::ClearItem( sdItemPoolEntry& entry ) {
|
||
|
HideItem( entry );
|
||
|
entry.SetItem( NULL );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::Init
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::Init( sdInventory* _parent ) {
|
||
|
parent = _parent;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::Clear
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::Clear( void ) {
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
ClearItem( items[ i ] );
|
||
|
}
|
||
|
items.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::ApplyPlayerState
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::ApplyPlayerState( const sdInventoryPlayerStateData& newState ) {
|
||
|
NET_GET_NEW( sdInventoryPlayerStateData );
|
||
|
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
sdItemPoolEntry& item = items[ i ];
|
||
|
|
||
|
for ( int j = 0; j < item.clips.Num(); j++ ) {
|
||
|
if ( item.GetItem()->GetClips()[ j ].maxAmmo <= 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
item.clips[ j ] = newData.itemData[ i ].clips[ j ];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::ReadPlayerState
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::ReadPlayerState( const sdInventoryPlayerStateData& baseState, sdInventoryPlayerStateData& newState, const idBitMsg& msg ) const {
|
||
|
NET_GET_STATES( sdInventoryPlayerStateData );
|
||
|
|
||
|
newData.itemData.SetNum( items.Num() );
|
||
|
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
const sdItemPoolEntry& item = items[ i ];
|
||
|
|
||
|
newData.itemData[ i ].clips.SetNum( item.clips.Num() );
|
||
|
|
||
|
for ( int j = 0; j < item.clips.Num(); j++ ) {
|
||
|
if ( i < baseData.itemData.Num() && j < baseData.itemData[ i ].clips.Num() ) {
|
||
|
newData.itemData[ i ].clips[ j ] = msg.ReadDeltaShort( baseData.itemData[ i ].clips[ j ] );
|
||
|
} else {
|
||
|
newData.itemData[ i ].clips[ j ] = msg.ReadShort();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::WritePlayerState
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::WritePlayerState( const sdInventoryPlayerStateData& baseState, sdInventoryPlayerStateData& newState, idBitMsg& msg ) const {
|
||
|
NET_GET_STATES( sdInventoryPlayerStateData );
|
||
|
|
||
|
newData.itemData.SetNum( items.Num() );
|
||
|
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
const sdItemPoolEntry& item = items[ i ];
|
||
|
|
||
|
newData.itemData[ i ].clips.SetNum( item.clips.Num() );
|
||
|
|
||
|
for ( int j = 0; j < item.clips.Num(); j++ ) {
|
||
|
newData.itemData[ i ].clips[ j ] = item.clips[ j ];
|
||
|
if ( i < baseData.itemData.Num() && j < baseData.itemData[ i ].clips.Num() ) {
|
||
|
msg.WriteDeltaShort( baseData.itemData[ i ].clips[ j ], newData.itemData[ i ].clips[ j ] );
|
||
|
} else {
|
||
|
msg.WriteShort( newData.itemData[ i ].clips[ j ] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::CheckPlayerStateChanges
|
||
|
==============
|
||
|
*/
|
||
|
bool sdUpgradeItemPool::CheckPlayerStateChanges( const sdInventoryPlayerStateData& baseState ) const {
|
||
|
NET_GET_BASE( sdInventoryPlayerStateData );
|
||
|
|
||
|
if ( baseData.itemData.Num() != items.Num() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
const sdItemPoolEntry& item = items[ i ];
|
||
|
|
||
|
if ( baseData.itemData[ i ].clips.Num() != item.clips.Num() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
for ( int j = 0; j < item.clips.Num(); j++ ) {
|
||
|
if ( baseData.itemData[ i ].clips[ j ] != item.clips[ j ] ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::UpdateJoints
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::UpdateJoints( void ) {
|
||
|
idPlayer* owner = parent->GetOwner();
|
||
|
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
items[ i ].joint = owner->GetAnimator()->GetJointHandle( items[ i ].item->GetJoint() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::UpdateModels
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::UpdateModels( void ) {
|
||
|
// no need to do this in reprediction
|
||
|
if ( !gameLocal.isNewFrame ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* owner = parent->GetOwner();
|
||
|
|
||
|
for ( int i = 0; i < modelItems.Num(); i++ ) {
|
||
|
sdItemPoolEntry& item = *modelItems[ i ];
|
||
|
|
||
|
renderEntity_t& entity = item.GetModel();
|
||
|
|
||
|
entity.customSkin = owner->GetRenderEntity()->customSkin;
|
||
|
|
||
|
owner->GetWorldOriginAxisNoUpdate( item.joint, entity.origin, entity.axis );
|
||
|
|
||
|
item.UpdateModel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::~sdUpgradeItemPool
|
||
|
==============
|
||
|
*/
|
||
|
sdUpgradeItemPool::~sdUpgradeItemPool( void ) {
|
||
|
Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdUpgradeItemPool::SetupModelShadows
|
||
|
==============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::SetupModelShadows( sdItemPoolEntry& entry ) {
|
||
|
idPlayer* owner = parent->GetOwner();
|
||
|
|
||
|
renderEntity_t& renderEntity = entry.GetModel();
|
||
|
|
||
|
viewState_t state = owner->HasShadow();
|
||
|
switch ( state ) {
|
||
|
case VS_NONE: {
|
||
|
renderEntity.suppressShadowInViewID = 0;
|
||
|
renderEntity.flags.noShadow = true;
|
||
|
break;
|
||
|
}
|
||
|
case VS_REMOTE: {
|
||
|
renderEntity.suppressShadowInViewID = owner->entityNumber + 1;
|
||
|
renderEntity.flags.noShadow = false;
|
||
|
break;
|
||
|
}
|
||
|
case VS_FULL: {
|
||
|
renderEntity.suppressShadowInViewID = 0;
|
||
|
renderEntity.flags.noShadow = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdUpgradeItemPool::UpdateModelShadows
|
||
|
============
|
||
|
*/
|
||
|
void sdUpgradeItemPool::UpdateModelShadows( void ) {
|
||
|
for ( int i = 0; i < modelItems.Num(); i++ ) {
|
||
|
SetupModelShadows( *modelItems[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdInventory
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
idList< int > sdInventory::slotForBank;
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryBroadcastData::MakeDefault
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryBroadcastData::MakeDefault( void ) {
|
||
|
idealWeapon = -1;
|
||
|
disabledMask.Shutdown();
|
||
|
ammoMask.Shutdown();
|
||
|
|
||
|
proficiencyData.MakeDefault();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryBroadcastData::Write
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryBroadcastData::Write( idFile* file ) const {
|
||
|
file->WriteInt( idealWeapon );
|
||
|
|
||
|
file->WriteInt( disabledMask.GetSize() );
|
||
|
for ( int i = 0; i < disabledMask.GetSize(); i++ ) {
|
||
|
file->WriteInt( disabledMask.GetDirect( i ) );
|
||
|
}
|
||
|
|
||
|
file->WriteInt( ammoMask.GetSize() );
|
||
|
for ( int i = 0; i < ammoMask.GetSize(); i++ ) {
|
||
|
file->WriteInt( ammoMask.GetDirect( i ) );
|
||
|
}
|
||
|
|
||
|
proficiencyData.Write( file );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryBroadcastData::Read
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryBroadcastData::Read( idFile* file ) {
|
||
|
file->ReadInt( idealWeapon );
|
||
|
|
||
|
int sizeDummy;
|
||
|
|
||
|
file->ReadInt( sizeDummy );
|
||
|
disabledMask.SetSize( sizeDummy );
|
||
|
for ( int i = 0; i < disabledMask.GetSize(); i++ ) {
|
||
|
file->ReadInt( disabledMask.GetDirect( i ) );
|
||
|
}
|
||
|
|
||
|
file->ReadInt( sizeDummy );
|
||
|
ammoMask.SetSize( sizeDummy );
|
||
|
for ( int i = 0; i < ammoMask.GetSize(); i++ ) {
|
||
|
file->ReadInt( ammoMask.GetDirect( i ) );
|
||
|
}
|
||
|
|
||
|
proficiencyData.Read( file );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryPlayerStateData::MakeDefault
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryPlayerStateData::MakeDefault( void ) {
|
||
|
ammo.SetNum( gameLocal.declAmmoTypeType.Num() );
|
||
|
for ( int i = 0; i < ammo.Num(); i++ ) {
|
||
|
ammo[ i ] = 0;
|
||
|
}
|
||
|
itemData.SetNum( 0, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryPlayerStateData::Write
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryPlayerStateData::Write( idFile* file ) const {
|
||
|
file->WriteInt( ammo.Num() );
|
||
|
for ( int i = 0; i < ammo.Num(); i++ ) {
|
||
|
file->WriteShort( ammo[ i ] );
|
||
|
}
|
||
|
|
||
|
file->WriteInt( itemData.Num() );
|
||
|
for ( int i = 0; i < itemData.Num(); i++ ) {
|
||
|
itemData[ i ].Write( file );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryPlayerStateData::Read
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryPlayerStateData::Read( idFile* file ) {
|
||
|
int count;
|
||
|
file->ReadInt( count );
|
||
|
|
||
|
assert( gameLocal.declAmmoTypeType.Num() == count );
|
||
|
|
||
|
ammo.SetNum( count );
|
||
|
for ( int i = 0; i < count; i++ ) {
|
||
|
file->ReadShort( ammo[ i ] );
|
||
|
}
|
||
|
|
||
|
file->ReadInt( count );
|
||
|
|
||
|
itemData.SetNum( count );
|
||
|
for ( int i = 0; i < count; i++ ) {
|
||
|
itemData[ i ].Read( file );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryItemStateData::MakeDefault
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryItemStateData::MakeDefault( void ) {
|
||
|
clips.SetNum( 0, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryItemStateData::Write
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryItemStateData::Write( idFile* file ) const {
|
||
|
file->WriteInt( clips.Num() );
|
||
|
for ( int i = 0; i < clips.Num(); i++ ) {
|
||
|
file->WriteShort( clips[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventoryItemStateData::Read
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventoryItemStateData::Read( idFile* file ) {
|
||
|
int count;
|
||
|
file->ReadInt( count );
|
||
|
|
||
|
clips.SetNum( count );
|
||
|
for ( int i = 0; i < count; i++ ) {
|
||
|
file->ReadShort( clips[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetCommandMapIcon
|
||
|
==============
|
||
|
*/
|
||
|
const idMaterial* sdInventory::GetCommandMapIcon( const iconType_e iconType ) const {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
|
||
|
if ( !pc ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
switch( iconType ) {
|
||
|
case IT_CLASS:
|
||
|
return pc->GetCommandmapIconClass();
|
||
|
case IT_ICON:
|
||
|
return pc->GetCommandmapIcon();
|
||
|
case IT_UNKNOWN:
|
||
|
return pc->GetCommandmapIconUnknown();
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::UpdateItems
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::UpdateItems( void ) {
|
||
|
if ( classThread ) {
|
||
|
if ( classThread->Execute() ) {
|
||
|
ShutdownClassThread();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::Present
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::Present( void ) {
|
||
|
items.UpdateModels();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::Init
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::Init( idDict& dict, bool full, bool setWeapon ) {
|
||
|
ammo.AssureSize( gameLocal.declAmmoTypeType.Num(), 0 );
|
||
|
ammoLimits.AssureSize( gameLocal.declAmmoTypeType.Num(), 0 );
|
||
|
|
||
|
ClearAmmo();
|
||
|
items.Clear();
|
||
|
|
||
|
currentWeapon = -1;
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
idealWeapon = -1;
|
||
|
}
|
||
|
switchingWeapon = -1;
|
||
|
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
CheckPlayerClass( setWeapon );
|
||
|
}
|
||
|
SetupClassOptions( true, setWeapon );
|
||
|
|
||
|
SetupModel();
|
||
|
|
||
|
LogClassTime();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::WriteDemoBaseData
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::WriteDemoBaseData( idFile* file ) const {
|
||
|
int playerClassIndex = playerClass.GetClass() ? playerClass.GetClass()->Index() : -1;
|
||
|
|
||
|
file->WriteInt( playerClassIndex );
|
||
|
if ( playerClassIndex != -1 ) {
|
||
|
for ( int i = 0; i < playerClass.GetOptions().Num(); i++ ) {
|
||
|
file->WriteInt( playerClass.GetOptions()[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int cachedPlayerClassIndex = cachedPlayerClass.GetClass() ? cachedPlayerClass.GetClass()->Index() : -1;
|
||
|
|
||
|
file->WriteInt( cachedPlayerClassIndex );
|
||
|
if ( cachedPlayerClassIndex != -1 ) {
|
||
|
for ( int i = 0; i < cachedPlayerClass.GetOptions().Num(); i++ ) {
|
||
|
file->WriteInt( cachedPlayerClass.GetOptions()[ i ] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::ReadDemoBaseData
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::ReadDemoBaseData( idFile* file ) {
|
||
|
int playerClassIndex;
|
||
|
|
||
|
file->ReadInt( playerClassIndex );
|
||
|
if ( playerClassIndex != -1 ) {
|
||
|
const sdDeclPlayerClass* cls = gameLocal.declPlayerClassType[ playerClassIndex ];
|
||
|
|
||
|
SetPlayerClass( cls );
|
||
|
|
||
|
for ( int i = 0; i < playerClass.GetOptions().Num(); i++ ) {
|
||
|
int itemIndex;
|
||
|
|
||
|
file->ReadInt( itemIndex );
|
||
|
playerClass.SetOption( i, itemIndex );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int cachedPlayerClassIndex;
|
||
|
|
||
|
file->ReadInt( cachedPlayerClassIndex );
|
||
|
if ( cachedPlayerClassIndex != -1 ) {
|
||
|
const sdDeclPlayerClass* cls = gameLocal.declPlayerClassType[ cachedPlayerClassIndex ];
|
||
|
cachedPlayerClass.SetClass( cls, true );
|
||
|
|
||
|
for ( int i = 0; i < cachedPlayerClass.GetOptions().Num(); i++ ) {
|
||
|
int itemIndex;
|
||
|
|
||
|
file->ReadInt( itemIndex );
|
||
|
cachedPlayerClass.SetOption( i, itemIndex );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetupClassOptions( true, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::sdInventory
|
||
|
==============
|
||
|
*/
|
||
|
sdInventory::sdInventory( void ) {
|
||
|
weaponChanged = false;
|
||
|
classThread = NULL;
|
||
|
timeClassChanged = 0;
|
||
|
currentWeaponIndex = -1;
|
||
|
idealWeapon = -1;
|
||
|
|
||
|
|
||
|
SetOwner( NULL );
|
||
|
|
||
|
items.Init( this );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetOwner
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetOwner( idPlayer* _owner ) {
|
||
|
owner = _owner;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetOwner
|
||
|
==============
|
||
|
*/
|
||
|
idPlayer* sdInventory::GetOwner( void ) {
|
||
|
return owner;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::~sdInventory
|
||
|
==============
|
||
|
*/
|
||
|
sdInventory::~sdInventory( void ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetAmmoFraction
|
||
|
==============
|
||
|
*/
|
||
|
float sdInventory::GetAmmoFraction( void ) {
|
||
|
if ( !playerClass.GetClass() ) {
|
||
|
return 0.f;
|
||
|
}
|
||
|
|
||
|
int max = 0;
|
||
|
int ammoCount = 0;
|
||
|
for ( int i = 0; i < ammo.Num(); i++ ) {
|
||
|
ammoCount += ammo[ i ];
|
||
|
max += ammoLimits[ i ];
|
||
|
}
|
||
|
|
||
|
return ammoCount / ( float )( max );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::BuildSlotBankLookup
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::BuildSlotBankLookup( void ) {
|
||
|
slotForBank.Clear();
|
||
|
|
||
|
const sdDeclStringMap* stringMap = gameLocal.declStringMapType.LocalFind( "inventorySlots" );
|
||
|
const idDict& dict = stringMap->GetDict();
|
||
|
|
||
|
int i = 0;
|
||
|
while( true ) {
|
||
|
//const sdDeclInvSlot* slot = gameLocal.declInvSlotType.LocalFindByIndex( i, true );
|
||
|
const char* value = dict.GetString( va( "slot%i", i ) );
|
||
|
i++;
|
||
|
if( value[ 0 ] == '\0' ) {
|
||
|
break;
|
||
|
}
|
||
|
const sdDeclInvSlot* slot = gameLocal.declInvSlotType.LocalFind( value, true );
|
||
|
|
||
|
int slotBank = slot->GetBank();
|
||
|
if( slotBank == -1 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
slotForBank.AssureSize( slotBank + 1, -1 );
|
||
|
|
||
|
if( slotForBank[ slotBank ] != -1 ) {
|
||
|
gameLocal.Error( "sdInventory::BuildSlotBankLookup Multiple Slots Using Weapon Bank %i", slotBank );
|
||
|
}
|
||
|
|
||
|
slotForBank[ slotBank ] = slot->Index();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetSlotForWeapon
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::GetSlotForWeapon( int weapon ) const {
|
||
|
if ( weapon >= 0 && weapon < items.Num() ) {
|
||
|
if ( CanEquip( weapon, false ) ) {
|
||
|
|
||
|
int count = slotForBank.Num();
|
||
|
for ( int i = count - 1; i >= 0; i-- ) {
|
||
|
int slot = slotForBank[ i ];
|
||
|
if ( slot == -1 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( items[ weapon ].item->UsesSlot( slot ) ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetCurrentSlot
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::GetCurrentSlot( void ) const {
|
||
|
return GetSlotForWeapon( idealWeapon );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetSwitchingSlot
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::GetSwitchingSlot( void ) const {
|
||
|
return GetSlotForWeapon( switchingWeapon );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::FindBestWeapon
|
||
|
============
|
||
|
*/
|
||
|
int sdInventory::FindBestWeapon( bool allowCurrent ) {
|
||
|
int bestIndex = -1;
|
||
|
int bestRank = 9999;
|
||
|
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( !allowCurrent ) {
|
||
|
if( ( i == switchingWeapon || i == currentWeapon ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !CanAutoEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].item;
|
||
|
|
||
|
int rank = item->GetAutoSwitchPriority();
|
||
|
if ( rank < bestRank && rank > 0 && CheckWeaponHasAmmo( item ) ) {
|
||
|
bestIndex = i;
|
||
|
bestRank = rank;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bestIndex;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CycleNextSafeWeapon
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::SelectBestWeapon( bool allowCurrent ) {
|
||
|
int bestIndex = FindBestWeapon( allowCurrent );
|
||
|
if ( bestIndex != -1 ) {
|
||
|
SetIdealWeapon( bestIndex );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CycleNextSafeWeapon
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::CycleNextSafeWeapon( void ) {
|
||
|
int bestIndex = FindBestWeapon( false );
|
||
|
if ( bestIndex != -1 ) {
|
||
|
SetSwitchingWeapon( bestIndex );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CycleWeaponsNext
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::CycleWeaponsNext( int currentSlot ) {
|
||
|
bool force = true;
|
||
|
bool looped;
|
||
|
int weapon;
|
||
|
|
||
|
if( currentSlot == -999 ) {
|
||
|
force = false;
|
||
|
currentSlot = ChooseCurrentSlot();
|
||
|
}
|
||
|
|
||
|
weapon = CycleWeaponByPosition( currentSlot, true, looped, false, false );
|
||
|
|
||
|
if( !looped && weapon != -1 ) {
|
||
|
SetSwitchingWeapon( weapon );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int cnt = 0;
|
||
|
int max = slotForBank.Num();
|
||
|
for( int i = currentSlot + 1; cnt < max; cnt++ ) {
|
||
|
int pos = ( i + cnt ) % max;
|
||
|
|
||
|
weapon = CycleWeaponByPosition( pos, true, looped, false, false );
|
||
|
if( weapon != -1 ) {
|
||
|
SetSwitchingWeapon( weapon );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CycleWeaponsPrev
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::CycleWeaponsPrev( int currentSlot ) {
|
||
|
bool force = true;
|
||
|
bool looped;
|
||
|
int weapon;
|
||
|
|
||
|
if( currentSlot == -999 ) {
|
||
|
force = false;
|
||
|
currentSlot = ChooseCurrentSlot();
|
||
|
}
|
||
|
|
||
|
weapon = CycleWeaponByPosition( currentSlot, false, looped, false, false );
|
||
|
if( !looped && weapon != -1 ) {
|
||
|
SetSwitchingWeapon( weapon );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int cnt = 0;
|
||
|
int max = slotForBank.Num();
|
||
|
for( int i = currentSlot - 1; cnt < max; cnt++ ) {
|
||
|
int pos = ( ( i - cnt ) + max ) % max;
|
||
|
|
||
|
weapon = CycleWeaponByPosition( pos, false, looped, false, false );
|
||
|
if( weapon != -1 ) {
|
||
|
SetSwitchingWeapon( weapon );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CycleWeaponByPosition
|
||
|
|
||
|
if "primaryOnly" == true, it will only select the first weapon in that slot, never looping to any other weapon that
|
||
|
may share that slot.
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::CycleWeaponByPosition( int pos, bool forward, bool& looped, bool force, bool primaryOnly ) {
|
||
|
if( pos < 0 || pos >= slotForBank.Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int slot = slotForBank[ pos ];
|
||
|
if( slot == -1 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int startpos = 0;
|
||
|
|
||
|
if ( !primaryOnly ) {
|
||
|
if( ( ChooseCurrentSlot() == pos ) ) {
|
||
|
if( switchingWeapon >= 0 ) {
|
||
|
startpos = switchingWeapon;
|
||
|
} else if( idealWeapon >= 0 ) {
|
||
|
startpos = idealWeapon;
|
||
|
} else {
|
||
|
startpos = currentWeapon;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CycleWeaponBySlot( slot, forward, looped, force, startpos );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::UpdatePrimaryWeapon
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::UpdatePrimaryWeapon( void ) {
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].GetItem();
|
||
|
|
||
|
if ( item == NULL || !item->UsesSlot( slotForBank[ GUN_SLOT ] ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !CanEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int weaponNum = item->GetData().GetInt( "player_weapon_num", "-1" );
|
||
|
|
||
|
if ( weaponNum != 14 && weaponNum != -1 ) { //mal_FIXME: 14 = hack for the AR/Grenade Launcher combo. Need to fix this!
|
||
|
botThreadData.GetGameWorldState()->clientInfo[ owner->entityNumber ].weapInfo.primaryWeapon = ( playerWeaponTypes_t ) weaponNum;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CheckWeaponSlotHasAmmo
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::CheckWeaponSlotHasAmmo( int slot ) {
|
||
|
bool hasAmmo = false;
|
||
|
int i, ammoInClip, clipSize;
|
||
|
|
||
|
clientInfo_t &client = botThreadData.GetGameWorldState()->clientInfo[ owner->entityNumber ];
|
||
|
|
||
|
for ( i = 0; i < items.Num(); i++ ) {
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].GetItem();
|
||
|
|
||
|
if ( item == NULL || !item->UsesSlot( slotForBank[ slot ] ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !CanEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( CheckWeaponHasAmmo( item ) ) {
|
||
|
hasAmmo = true;
|
||
|
|
||
|
if ( slot == GUN_SLOT ) { //mal: if this is our primary gun we're checking, see if it needs ammo.
|
||
|
const idList< itemClip_t >& itemClips = item->GetClips();
|
||
|
int ammoFraction;
|
||
|
int maxAmmo, curAmmo;
|
||
|
|
||
|
if ( !itemClips.Num() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( client.team == GDF ) {
|
||
|
if ( client.classType == SOLDIER && client.weapInfo.primaryWeapon == SMG ) {
|
||
|
ammoFraction = 4; //mal: GDF soldiers with assault rifle get as much ammo as strogg!
|
||
|
} else {
|
||
|
ammoFraction = 2;
|
||
|
}
|
||
|
} else {
|
||
|
if ( client.weapInfo.primaryWeapon == ROCKET ) {
|
||
|
ammoFraction = 2;
|
||
|
} else {
|
||
|
ammoFraction = 4; //mal: non-rocket launcher strogg have SO much more ammo then GDF.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
maxAmmo = GetMaxAmmo( itemClips[ MAIN_GUN ].ammoType );
|
||
|
curAmmo = GetAmmo( itemClips[ MAIN_GUN ].ammoType );
|
||
|
|
||
|
//mal: while we're here, lets check if our primary weapon needs a reload, so we can use all of the stuff we've already calc'd
|
||
|
if ( client.team == STROGG && client.weapInfo.primaryWeapon != ROCKET ) {
|
||
|
client.weapInfo.primaryWeapNeedsReload = false;
|
||
|
client.weapInfo.primaryWeapClipEmpty = false;
|
||
|
} else {
|
||
|
ammoInClip = GetClip( i, MAIN_GUN );
|
||
|
clipSize = GetClipSize( i, MAIN_GUN );
|
||
|
|
||
|
if ( ammoInClip == 0 || ammoInClip < clipSize ) {
|
||
|
client.weapInfo.primaryWeapNeedsReload = true;
|
||
|
} else {
|
||
|
client.weapInfo.primaryWeapNeedsReload = false;
|
||
|
}
|
||
|
|
||
|
if ( ammoInClip == 0 ) {
|
||
|
client.weapInfo.primaryWeapClipEmpty = true;
|
||
|
} else {
|
||
|
client.weapInfo.primaryWeapClipEmpty = false;
|
||
|
}
|
||
|
|
||
|
if ( curAmmo > clipSize ) {
|
||
|
client.weapInfo.hasAmmoForReload = true;
|
||
|
} else {
|
||
|
client.weapInfo.hasAmmoForReload = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ( maxAmmo / ammoFraction ) > curAmmo ) {
|
||
|
botThreadData.GetGameWorldState()->clientInfo[ owner->entityNumber ].weapInfo.primaryWeapNeedsAmmo = true;
|
||
|
} else {
|
||
|
botThreadData.GetGameWorldState()->clientInfo[ owner->entityNumber ].weapInfo.primaryWeapNeedsAmmo = false;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hasAmmo;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CyleWeaponBySlot
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::CycleWeaponBySlot( int slot, bool forward, bool& looped, bool force, int startingpos ) {
|
||
|
looped = false;
|
||
|
|
||
|
int i = startingpos;// + ( forward ? 1 : -1 );
|
||
|
int j;
|
||
|
int max = items.Num();
|
||
|
|
||
|
for( j = 0; j < max; j++ ) {
|
||
|
if( forward ) {
|
||
|
i++;
|
||
|
if( i >= max ) { looped = true; i -= max;}
|
||
|
} else {
|
||
|
i--;
|
||
|
if( i < 0 ) { looped = true; i += max; }
|
||
|
}
|
||
|
|
||
|
// if ( !force ) {
|
||
|
//
|
||
|
// if( /*!looped && */( i == switchingWeapon || i == currentWeapon )) {
|
||
|
// continue;
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
if ( !CanAutoEquip( i, false ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].item;
|
||
|
if ( !item->UsesSlot( slot ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !item->GetSelectWhenEmpty() && !CheckWeaponHasAmmo( item )) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SelectWeaponByName
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SelectWeaponByName( const char* weaponName, bool ignoreInhibit ) {
|
||
|
if ( !ignoreInhibit ) {
|
||
|
if ( owner->InhibitWeaponSwitch() ) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int weaponIndex = FindWeapon( weaponName );
|
||
|
if ( weaponIndex == -1 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !CanEquip( weaponIndex, true ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetIdealWeapon( weaponIndex );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SelectWeaponByNumber
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SelectWeaponByNumber( const playerWeaponTypes_t weaponNum ) {
|
||
|
if ( owner->InhibitWeaponSwitch() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int weaponIndex = FindWeaponNum( weaponNum );
|
||
|
if ( weaponIndex == -1 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !CanEquip( weaponIndex, true ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetIdealWeapon( weaponIndex );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CanEquip
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::CanEquip( int index, bool checkRequirements ) const {
|
||
|
if ( index < 0 || index >= items.Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ index ].item;
|
||
|
if ( item == NULL ) {
|
||
|
assert( false );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( items[ index ].IsDisabled() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( owner->GetProxyEntity() != NULL ) {
|
||
|
if( !item->GetType()->IsVehicleEquipable() ) {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
if( !item->GetType()->IsEquipable() ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( checkRequirements ) {
|
||
|
if ( !item->GetUsageRequirements().Check( owner ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CanAutoEquip
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::CanAutoEquip( int index, bool checkExplosive ) const {
|
||
|
if ( !CanEquip( index, true ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ index ].item;
|
||
|
if ( item->GetWeaponMenuIgnore() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( checkExplosive ) {
|
||
|
if ( item->GetAutoSwitchIsExplosive() && owner->userInfo.ignoreExplosiveWeapons ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::FindWeapon
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::FindWeapon( const char* weaponName ) {
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( !CanEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].item;
|
||
|
if ( !idStr::Icmp( item->GetName(), weaponName ) ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::FindWeaponNum
|
||
|
|
||
|
A handy function that lets the player pick a specific weapon, without cycling thru the slots.
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::FindWeaponNum( const playerWeaponTypes_t weaponNum ) {
|
||
|
for( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( !CanEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* weapItem = items[ i ].item;
|
||
|
|
||
|
if ( weapItem->GetData().GetInt( "player_weapon_num", "-1" ) == weaponNum ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetIdealWeapon
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetIdealWeapon( int pos, bool force ) {
|
||
|
if ( pos < -1 || pos >= items.Num() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool set = force || pos == -1;
|
||
|
if ( !set ) {
|
||
|
if ( pos == currentWeapon ) {
|
||
|
set = items[ pos ].item->Index() != currentWeaponIndex;
|
||
|
} else if ( pos != idealWeapon ) {
|
||
|
set = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( set ) {
|
||
|
idealWeapon = pos;
|
||
|
weaponChanged = true;
|
||
|
|
||
|
if ( pos != -1 ) {
|
||
|
currentWeaponIndex = items[ pos ].item->Index();
|
||
|
owner->SpawnToolTip( items[ pos ].item->GetToolTip() );
|
||
|
} else {
|
||
|
currentWeaponIndex = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CancelWeaponSwitch();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetClip
|
||
|
==============
|
||
|
*/
|
||
|
int sdInventory::GetClip( int index, int modIndex ) const {
|
||
|
if ( index >= items.Num() || modIndex >= items[ index ].clips.Num() ) {
|
||
|
return 0;
|
||
|
}
|
||
|
return items[ index ].clips[ modIndex ];
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::GetClipSize
|
||
|
============
|
||
|
*/
|
||
|
int sdInventory::GetClipSize( int index, int modIndex ) const {
|
||
|
if ( index >= items.Num() || modIndex >= items[ index ].clips.Num() ) {
|
||
|
return 0;
|
||
|
}
|
||
|
return items[ index ].GetItem()->GetClips()[ modIndex ].maxAmmo;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetClip
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetClip( int index, int modIndex, int count ) {
|
||
|
if ( index >= items.Num() || modIndex >= items[ index ].clips.Num() ) {
|
||
|
return;
|
||
|
}
|
||
|
items[ index ].clips[ modIndex ] = count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::IsWeaponValid
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::IsWeaponValid( int weapon ) const {
|
||
|
if ( weapon < 0 || weapon >= items.Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return CanEquip( weapon, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::IsWeaponBankValid
|
||
|
0-based weapon bank (0 is fists, 1 is pistol, etc)
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::IsWeaponBankValid( int slot ) const {
|
||
|
if( slot < 0 || slot >= slotForBank.Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool allValid = false;
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( !CanEquip( i, true ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdDeclInvItem* item = items[ i ].GetItem();
|
||
|
if ( !item->UsesSlot( slotForBank[ slot ] )) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( CheckWeaponHasAmmo( item ) || item->GetSelectWhenEmpty() ) {
|
||
|
allValid = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return allValid;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::NumValidWeaponBanks
|
||
|
============
|
||
|
*/
|
||
|
int sdInventory::NumValidWeaponBanks() const {
|
||
|
int numValid = 0;
|
||
|
int max = slotForBank.Num();
|
||
|
for( int i = 0; i < max; i++ ) {
|
||
|
if( IsWeaponBankValid( i )) {
|
||
|
numValid++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numValid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetActivePlayer
|
||
|
==============
|
||
|
*/
|
||
|
idPlayer* sdInventory::GetActivePlayer( void ) const {
|
||
|
idPlayer* modelPlayer = NULL;
|
||
|
|
||
|
int disguiseClient = owner->GetDisguiseClient();
|
||
|
if ( disguiseClient != -1 ) {
|
||
|
modelPlayer = gameLocal.EntityForSpawnId( disguiseClient )->Cast< idPlayer >();
|
||
|
}
|
||
|
if ( modelPlayer == NULL ) {
|
||
|
modelPlayer = owner;
|
||
|
}
|
||
|
return modelPlayer;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetActivePlayerClass
|
||
|
==============
|
||
|
*/
|
||
|
const sdDeclPlayerClass* sdInventory::GetActivePlayerClass( void ) const {
|
||
|
if ( owner->IsDisguised() ) {
|
||
|
return owner->GetDisguiseClass();
|
||
|
}
|
||
|
return playerClass.GetClass();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetupModel
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetupModel( void ) {
|
||
|
SetupModel( GetActivePlayerClass() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetPlayerJoint
|
||
|
==============
|
||
|
*/
|
||
|
jointHandle_t sdInventory::GetPlayerJoint( const idDict& dict, const char* name ) {
|
||
|
const char* value = dict.GetString( name );
|
||
|
jointHandle_t handle = owner->GetAnimator()->GetJointHandle( value );
|
||
|
if ( handle == INVALID_JOINT ) {
|
||
|
gameLocal.Error( "sdInventory::GetPlayerJoint '%s' not found for '%s' on '%s'", value, name, owner->name.c_str() );
|
||
|
}
|
||
|
return handle;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SkinForClass
|
||
|
==============
|
||
|
*/
|
||
|
const idDeclSkin* sdInventory::SkinForClass( const sdDeclPlayerClass* cls ) {
|
||
|
if ( gameLocal.mapSkinPool != NULL ) {
|
||
|
const char* skinKey = cls->GetClimateSkinKey();
|
||
|
if ( *skinKey != '\0' ) {
|
||
|
const char* skinName = gameLocal.mapSkinPool->GetDict().GetString( va( "skin_%s", skinKey ) );
|
||
|
if ( *skinName == '\0' ) {
|
||
|
gameLocal.Warning( "sdInventory::SetupModel No Skin Set For '%s'", skinKey );
|
||
|
} else {
|
||
|
const idDeclSkin* skin = gameLocal.declSkinType[ skinName ];
|
||
|
if ( skin == NULL ) {
|
||
|
gameLocal.Warning( "sdScriptEntity::Spawn Skin '%s' Not Found", skinName );
|
||
|
} else {
|
||
|
return skin;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetupModel
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetupModel( const sdDeclPlayerClass* cls ) {
|
||
|
owner->SetSkin( NULL );
|
||
|
|
||
|
if ( cls == NULL ) {
|
||
|
owner->SetModel( "" );
|
||
|
owner->UpdateShadows();
|
||
|
owner->SetHipJoint( INVALID_JOINT );
|
||
|
owner->SetTorsoJoint( INVALID_JOINT );
|
||
|
owner->SetHeadJoint( INVALID_JOINT );
|
||
|
|
||
|
items.UpdateJoints();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( !cls->GetModel() ) {
|
||
|
gameLocal.Error( "sdInventory::SetupModel NULL model for class '%s'", cls->GetName() );
|
||
|
}
|
||
|
|
||
|
const idDict& dict = cls->GetModelData();
|
||
|
|
||
|
owner->SetModel( cls->GetModel()->GetName() );
|
||
|
owner->UpdateShadows();
|
||
|
|
||
|
const idDeclSkin* skin = SkinForClass( cls );
|
||
|
if ( skin != NULL ) {
|
||
|
owner->SetSkin( skin );
|
||
|
}
|
||
|
|
||
|
owner->SetHipJoint( GetPlayerJoint( dict, "bone_hips" ) );
|
||
|
owner->SetHeadJoint( GetPlayerJoint( dict, "bone_head" ) );
|
||
|
owner->SetChestJoint( GetPlayerJoint( dict, "bone_chest" ) );
|
||
|
owner->SetTorsoJoint( GetPlayerJoint( dict, "bone_torso" ) );
|
||
|
owner->SetShoulderJoint( 0, GetPlayerJoint( dict, "bone_left_shoulder" ) );
|
||
|
owner->SetElbowJoint( 0, GetPlayerJoint( dict, "bone_left_elbow" ) );
|
||
|
owner->SetHandJoint( 0, GetPlayerJoint( dict, "bone_left_hand" ) );
|
||
|
owner->SetFootJoint( 0, GetPlayerJoint( dict, "bone_left_foot" ) );
|
||
|
owner->SetShoulderJoint( 1, GetPlayerJoint( dict, "bone_right_shoulder" ) );
|
||
|
owner->SetElbowJoint( 1, GetPlayerJoint( dict, "bone_right_elbow" ) );
|
||
|
owner->SetHandJoint( 1, GetPlayerJoint( dict, "bone_right_hand" ) );
|
||
|
owner->SetFootJoint( 1, GetPlayerJoint( dict, "bone_right_foot" ) );
|
||
|
|
||
|
owner->SetHeadModelJoint( GetPlayerJoint( dict, "bone_head_model" ) );
|
||
|
owner->SetHeadModelOffset( dict.GetVector( "head_offset" ) );
|
||
|
|
||
|
SetupLocationalDamage( dict );
|
||
|
|
||
|
sdScriptHelper h;
|
||
|
owner->CallNonBlockingScriptEvent( owner->GetScriptFunction( "OnNewModel" ), h );
|
||
|
|
||
|
items.UpdateJoints();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::SetupLocationalDamage
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::SetupLocationalDamage( const idDict& dict ) {
|
||
|
owner->RemoveLocationalDamageInfo();
|
||
|
|
||
|
locationalDamageInfo_t info;
|
||
|
int numLocationDamageJoints = dict.GetInt( "loc_damage_joint_num", "6" );
|
||
|
for( int i = 0; i < numLocationDamageJoints; i++ ) {
|
||
|
const char* jointName = dict.GetString( va( "loc_damage_joint_%d", i ), "" );
|
||
|
info.joint = owner->GetAnimator()->GetJointHandle( jointName );
|
||
|
if( info.joint == INVALID_JOINT ) {
|
||
|
gameLocal.Warning( "Invalid locational damage joint %d", i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if( !owner->GetAnimator()->GetJointTransform( info.joint, gameLocal.time, info.pos ) ) {
|
||
|
gameLocal.Warning( "Invalid local transform for locational damage joint %d", i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
info.area = owner->LocationalDamageAreaForString( dict.GetString( va( "loc_damage_area_%d", i ), "" ) );
|
||
|
if( info.area == LDA_INVALID ) {
|
||
|
gameLocal.Warning( "Invalid locational damage area for joint %d", i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// gameLocal.Printf( "Adding jointPos: ( %.0f %.0f %.0f )\n", info.pos.x, info.pos.y, info.pos.z );
|
||
|
owner->AddLocationalDamageInfo( info );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CheckPlayerClass
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::CheckPlayerClass( bool setWeapon ) {
|
||
|
sdTeamInfo* team = owner->GetTeam();
|
||
|
if ( !team ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass* newClass = playerClass.GetClass();
|
||
|
|
||
|
sdPlayerClassSetup oldCachedClassSetup = cachedPlayerClass;
|
||
|
|
||
|
if ( cachedPlayerClass.GetClass() ) {
|
||
|
if ( cachedPlayerClass.GetClass()->GetTeam() == team ) {
|
||
|
newClass = cachedPlayerClass.GetClass();
|
||
|
}
|
||
|
|
||
|
SetCachedClass( NULL );
|
||
|
}
|
||
|
|
||
|
if ( newClass ) {
|
||
|
if ( newClass->GetTeam() != team ) {
|
||
|
newClass = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !newClass ) {
|
||
|
newClass = team->GetDefaultClass();
|
||
|
}
|
||
|
|
||
|
bool sendInfo = false;
|
||
|
|
||
|
if ( newClass != playerClass.GetClass() ) {
|
||
|
sendInfo |= GiveClass( newClass, false );
|
||
|
}
|
||
|
|
||
|
if ( oldCachedClassSetup.GetClass() == playerClass.GetClass() ) {
|
||
|
playerClass.SetOptions( oldCachedClassSetup.GetOptions() );
|
||
|
|
||
|
SetupClassOptions( true, setWeapon );
|
||
|
|
||
|
sendInfo = true;
|
||
|
}
|
||
|
|
||
|
if ( sendInfo ) {
|
||
|
SendClassInfo( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::IsIdealWeaponValid
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::IsIdealWeaponValid( void ) const {
|
||
|
return IsWeaponValid( idealWeapon );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::IsCurrentWeaponValid
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::IsCurrentWeaponValid( void ) const {
|
||
|
return IsWeaponValid( currentWeapon );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetCurrentItem
|
||
|
==============
|
||
|
*/
|
||
|
const sdDeclInvItem* sdInventory::GetCurrentItem( void ) const {
|
||
|
if ( currentWeapon < 0 || currentWeapon >= items.Num() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( !CanEquip( currentWeapon, false ) ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return items[ currentWeapon ].GetItem();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetCurrentWeaponName
|
||
|
==============
|
||
|
*/
|
||
|
const char* sdInventory::GetCurrentWeaponName( void ) {
|
||
|
const sdDeclInvItem* item = GetCurrentItem();
|
||
|
|
||
|
if ( !item ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return item->GetData().GetString( "anim_prefix" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetCurrentWeaponClass
|
||
|
==============
|
||
|
*/
|
||
|
const char* sdInventory::GetCurrentWeaponClass( void ) {
|
||
|
const sdDeclInvItem* item = GetCurrentItem();
|
||
|
|
||
|
if ( !item ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return item->GetData().GetString( "anim_prefix_class" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::ClearAmmo
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::ClearAmmo( void ) {
|
||
|
for ( int i = 0; i < ammo.Num(); i++ ) {
|
||
|
ammo[ i ] = 0;
|
||
|
ammoLimits[ i ] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GiveClass
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::GiveClass( const sdDeclPlayerClass* cls, bool sendInfo ) {
|
||
|
if ( gameLocal.isClient ) {
|
||
|
assert( false );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( cls == NULL ) {
|
||
|
gameLocal.Error( "sdInventory::GiveClass: NULL player class" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( cls->GetPackage() == NULL ) {
|
||
|
gameLocal.Error( "sdInventory::GiveClass: NULL package on player class '%s'", cls->GetName() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const sdTeamInfo* otherTeam = cls->GetTeam();
|
||
|
if ( otherTeam != NULL && otherTeam != owner->GetGameTeam() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( playerClass.GetClass() == cls ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idEntity* proxy = owner->GetProxyEntity();
|
||
|
if ( proxy != NULL ) {
|
||
|
proxy->GetUsableInterface()->OnExit( owner, true );
|
||
|
}
|
||
|
|
||
|
SetPlayerClass( cls );
|
||
|
SelectBestWeapon( true );
|
||
|
|
||
|
if ( sendInfo ) {
|
||
|
SendClassInfo( false );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SendClassInfo
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SendClassInfo( bool cached ) {
|
||
|
if ( !gameLocal.isServer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdPlayerClassSetup& setup = cached ? cachedPlayerClass : playerClass;
|
||
|
|
||
|
const sdDeclPlayerClass* cls = setup.GetClass();
|
||
|
|
||
|
sdEntityBroadcastEvent msg( owner, cached ? idPlayer::EVENT_SETCACHEDCLASS : idPlayer::EVENT_SETCLASS );
|
||
|
msg.WriteBits( cls ? cls->Index() + 1 : 0, gameLocal.GetNumPlayerClassBits() );
|
||
|
if ( cls ) {
|
||
|
for ( int i = 0; i < cls->GetNumOptions(); i++ ) {
|
||
|
const sdDeclPlayerClass::optionList_t& option = cls->GetOption( i );
|
||
|
msg.WriteBits( setup.GetOptions()[ i ], idMath::BitsForInteger( option.Num() ) );
|
||
|
}
|
||
|
}
|
||
|
msg.Send( true, sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetupClassThread
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetupClassThread( void ) {
|
||
|
ShutdownClassThread();
|
||
|
if ( playerClass.GetClass() ) {
|
||
|
const char* classThreadName = playerClass.GetClass()->GetClassThreadName();
|
||
|
if ( *classThreadName ) {
|
||
|
classThread = gameLocal.program->CreateThread();
|
||
|
classThread->SetName( idStr( owner->GetName() ) + "_classThread" );
|
||
|
classThread->CallFunction( owner->scriptObject, owner->scriptObject->GetFunction( classThreadName ) );
|
||
|
classThread->ManualDelete();
|
||
|
classThread->ManualControl();
|
||
|
classThread->DelayedStart( 0 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::ClearClass
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::ClearClass( void ) {
|
||
|
SetPlayerClass( NULL );
|
||
|
SendClassInfo( false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::LogClassTime
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::LogClassTime( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( pc != NULL ) {
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.timePlayed ) {
|
||
|
int t = MS2SEC( gameLocal.time - timeClassChanged );
|
||
|
stats.timePlayed->IncreaseValue( owner->entityNumber, t );
|
||
|
}
|
||
|
}
|
||
|
timeClassChanged = gameLocal.time;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetPlayerClass
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetPlayerClass( const sdDeclPlayerClass* cls ) {
|
||
|
LogClassTime();
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
owner->DropDisguise();
|
||
|
}
|
||
|
|
||
|
SetCachedClass( NULL );
|
||
|
|
||
|
playerClass.SetClass( cls, true );
|
||
|
|
||
|
SetupClassOptions( true, false );
|
||
|
SetupClassThread();
|
||
|
|
||
|
|
||
|
sdScriptHelper h1;
|
||
|
owner->CallFloatNonBlockingScriptEvent( owner->GetScriptObject()->GetFunction( "OnClassChanged" ), h1 );
|
||
|
|
||
|
SetupModel();
|
||
|
|
||
|
owner->UpdateRating();
|
||
|
|
||
|
UpdatePlayerClassInfo( cls ); //mal: update this clients class info!
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetClassOption
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::SetClassOption( int optionIndex, int itemIndex, bool sendInfo ) {
|
||
|
if ( !playerClass.SetOption( optionIndex, itemIndex ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idEntity* proxy = owner->GetProxyEntity();
|
||
|
if ( proxy != NULL ) {
|
||
|
proxy->GetUsableInterface()->OnExit( owner, true );
|
||
|
}
|
||
|
|
||
|
SetupClassOptions( true, true );
|
||
|
|
||
|
if ( sendInfo ) {
|
||
|
SendClassInfo( false );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::SetCachedClass
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::SetCachedClass( const sdDeclPlayerClass* pc, bool sendInfo ) {
|
||
|
bool changed = cachedPlayerClass.SetClass( pc );
|
||
|
|
||
|
if ( changed && sendInfo ) {
|
||
|
SendClassInfo( true );
|
||
|
}
|
||
|
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::SetCachedClassOption
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::SetCachedClassOption( int optionIndex, int itemIndex, bool sendInfo ) {
|
||
|
if ( optionIndex < 0 || optionIndex >= cachedPlayerClass.GetOptions().Num() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool changed = cachedPlayerClass.SetOption( optionIndex, itemIndex );
|
||
|
|
||
|
if ( changed && sendInfo ) {
|
||
|
SendClassInfo( true );
|
||
|
}
|
||
|
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::AddItems
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::AddItems( const sdDeclItemPackage* package, bool enabled ) {
|
||
|
const sdDeclItemPackageNode& node = package->GetItemRoot();
|
||
|
return AddItemNode( node, enabled, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::CheckItems
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::CheckItems( const sdDeclItemPackage* package ) {
|
||
|
const sdDeclItemPackageNode& node = package->GetItemRoot();
|
||
|
return AddItemNode( node, true, true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::AddItemNode
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::AddItemNode( const sdDeclItemPackageNode& node, bool enabled, bool testOnly ) {
|
||
|
bool added = false;
|
||
|
|
||
|
if ( !node.GetRequirements().Check( owner ) ) {
|
||
|
enabled = false;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < node.GetItems().Num(); i++ ) {
|
||
|
const sdDeclInvItem* item = node.GetItems()[ i ];
|
||
|
if ( !testOnly ) {
|
||
|
items.AddItem( item, enabled );
|
||
|
}
|
||
|
added |= enabled;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < node.GetNodes().Num(); i++ ) {
|
||
|
added |= AddItemNode( *node.GetNodes()[ i ], enabled, testOnly );
|
||
|
}
|
||
|
|
||
|
return added;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SetupClassOptions
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SetupClassOptions( bool clearAmmo, bool setWeapon, bool allowCurrentWeapon ) {
|
||
|
bool disguise = owner->IsDisguised();
|
||
|
if ( disguise ) {
|
||
|
items.Clear();
|
||
|
|
||
|
const sdDeclPlayerClass* cls = owner->GetDisguiseClass();
|
||
|
if ( cls == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AddItems( cls->GetDisguisePackage() );
|
||
|
} else {
|
||
|
|
||
|
if ( clearAmmo ) {
|
||
|
ClearAmmo();
|
||
|
}
|
||
|
|
||
|
items.Clear();
|
||
|
|
||
|
const sdDeclPlayerClass* cls = playerClass.GetClass();
|
||
|
if ( cls == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( clearAmmo ) {
|
||
|
for ( int i = 0; i < ammoLimits.Num(); i++ ) {
|
||
|
ammoLimits[ i ] = cls->GetAmmoLimit( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const sdDeclItemPackage* package = cls->GetPackage();
|
||
|
AddItems( package );
|
||
|
if ( clearAmmo && !disguise ) {
|
||
|
GiveConsumables( package );
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < playerClass.GetOptions().Num(); i++ ) {
|
||
|
const sdDeclPlayerClass::optionList_t& list = cls->GetOption( i );
|
||
|
|
||
|
int index = playerClass.GetOptions()[ i ];
|
||
|
if ( index < 0 || index >= list.Num() ) {
|
||
|
index = 0;
|
||
|
}
|
||
|
|
||
|
if ( !CheckItems( list[ index ] ) ) {
|
||
|
index = 0;
|
||
|
}
|
||
|
|
||
|
for ( int j = 0; j < list.Num(); j++ ) {
|
||
|
AddItems( list[ j ], j == index );
|
||
|
}
|
||
|
|
||
|
if ( clearAmmo ) {
|
||
|
GiveConsumables( list[ index ] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SortClips();
|
||
|
if ( !gameLocal.isClient && setWeapon ) {
|
||
|
SelectBestWeapon( allowCurrentWeapon );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::HasAbility
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::HasAbility( qhandle_t handle ) const {
|
||
|
const sdDeclPlayerClass* cls = playerClass.GetClass();
|
||
|
return cls && cls->HasAbility( handle );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::SortClips
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::SortClips( void ) {
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
const sdDeclInvItem* item = GetItem( i );
|
||
|
for( int clip = 0; clip < item->GetClips().Num(); clip++ ) {
|
||
|
const itemClip_t& clipInfo = item->GetClips()[ clip ];
|
||
|
|
||
|
if ( clipInfo.maxAmmo > 0 && clipInfo.ammoPerShot > 0 ) {
|
||
|
int max = Min( clipInfo.maxAmmo, GetAmmo( clipInfo.ammoType ) );
|
||
|
SetClip( i, clip, max );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GiveConsumables
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::GiveConsumablesNode( const sdDeclItemPackageNode& node ) {
|
||
|
if ( !node.GetRequirements().Check( owner ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool given = false;
|
||
|
|
||
|
idList< const sdConsumable* > consumables = node.GetConsumables();
|
||
|
for ( int i = 0; i < consumables.Num(); i++ ) {
|
||
|
if ( !consumables[ i ]->Give( owner ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
given = true;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < node.GetNodes().Num(); i++ ) {
|
||
|
given |= GiveConsumablesNode( *node.GetNodes()[ i ] );
|
||
|
}
|
||
|
|
||
|
return given;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GiveConsumables
|
||
|
==============
|
||
|
*/
|
||
|
bool sdInventory::GiveConsumables( const sdDeclItemPackage* package ) {
|
||
|
return GiveConsumablesNode( package->GetItemRoot() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetWeaponTitle
|
||
|
==============
|
||
|
*/
|
||
|
const sdDeclLocStr* sdInventory::GetWeaponTitle( void ) const {
|
||
|
if( IsSwitchActive() ) {
|
||
|
return items[ switchingWeapon ].item->GetItemName();
|
||
|
}
|
||
|
|
||
|
if ( !IsCurrentWeaponValid() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return items[ currentWeapon ].item->GetItemName();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::GetWeaponName
|
||
|
==============
|
||
|
*/
|
||
|
const char* sdInventory::GetWeaponName( void ) const {
|
||
|
if( IsSwitchActive() ) {
|
||
|
return items[ switchingWeapon ].item->GetName();
|
||
|
}
|
||
|
|
||
|
if ( !IsCurrentWeaponValid() ) {
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
return items[ currentWeapon ].item->GetName();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CheckWeaponHasAmmo
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::CheckWeaponHasAmmo( const sdDeclInvItem* item ) const {
|
||
|
const idList< itemClip_t >& itemClips = item->GetClips();
|
||
|
|
||
|
if ( !itemClips.Num() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < itemClips.Num(); i++ ) {
|
||
|
if ( GetAmmo( itemClips[ i ].ammoType ) >= itemClips[ i ].ammoPerShot ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::GetAmmo
|
||
|
============
|
||
|
*/
|
||
|
int sdInventory::GetAmmo( int index ) const {
|
||
|
return ammo[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
sdInventory::ShutdownClassThread
|
||
|
==============
|
||
|
*/
|
||
|
void sdInventory::ShutdownClassThread( void ) {
|
||
|
if ( classThread != NULL ) {
|
||
|
gameLocal.program->FreeThread( classThread );
|
||
|
classThread = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CancelWeaponSwitch
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::CancelWeaponSwitch( void ) {
|
||
|
switchingWeapon = -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::AcceptWeaponSwitch
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::AcceptWeaponSwitch( void ) {
|
||
|
if ( CanEquip( switchingWeapon, true ) ) {
|
||
|
SetIdealWeapon( switchingWeapon );
|
||
|
switchingWeapon = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::UpdateCurrentWeapon
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::UpdateCurrentWeapon( void ) {
|
||
|
if ( currentWeapon != idealWeapon ) {
|
||
|
if ( currentWeapon >= 0 && currentWeapon < items.Num() ) {
|
||
|
if ( items.ShowItem( items[ currentWeapon ] ) ) {
|
||
|
items[ currentWeapon ].SetVisible( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
currentWeapon = idealWeapon;
|
||
|
|
||
|
if ( currentWeapon >= 0 && currentWeapon < items.Num() ) {
|
||
|
items[ currentWeapon ].SetVisible( false );
|
||
|
items.HideItem( items[ currentWeapon ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
weaponChanged = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::ApplyNetworkState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::ApplyNetworkState( const sdInventoryBroadcastData& newData ) {
|
||
|
if ( newData.idealWeapon >= -1 && newData.idealWeapon < items.Num() ) {
|
||
|
if ( newData.idealWeapon == -1 || newData.idealWeapon != idealWeapon || ( currentWeaponIndex != items[ newData.idealWeapon ].item->Index() ) ) {
|
||
|
SetIdealWeapon( newData.idealWeapon, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assert( sdBitField_Dynamic::SizeForBits( items.Num() ) == newData.disabledMask.GetSize() );
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
items[ i ].SetDisabled( newData.disabledMask.Get( i ) != 0 );
|
||
|
}
|
||
|
|
||
|
if ( !owner->ShouldReadPlayerState() ) {
|
||
|
assert( sdBitField_Dynamic::SizeForBits( gameLocal.declAmmoTypeType.Num() ) == newData.ammoMask.GetSize() );
|
||
|
for ( int i = 0; i < gameLocal.declAmmoTypeType.Num(); i++ ) {
|
||
|
ammo[ i ] = ( newData.ammoMask[ i ] != 0 ) ? 999 : 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
owner->GetProficiencyTable().ApplyNetworkState( newData.proficiencyData );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::ReadNetworkState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::ReadNetworkState( const sdInventoryBroadcastData& baseData, sdInventoryBroadcastData& newData, const idBitMsg& msg ) const {
|
||
|
newData.idealWeapon = msg.ReadDeltaLong( baseData.idealWeapon );
|
||
|
|
||
|
newData.disabledMask.Init( items.Num() );
|
||
|
for ( int i = 0; i < newData.disabledMask.GetSize(); i++ ) {
|
||
|
if ( i < baseData.disabledMask.GetSize() ) {
|
||
|
newData.disabledMask.GetDirect( i ) = msg.ReadDeltaLong( baseData.disabledMask.GetDirect( i ) );
|
||
|
} else {
|
||
|
newData.disabledMask.GetDirect( i ) = msg.ReadLong();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !owner->ShouldReadPlayerState() ) {
|
||
|
newData.ammoMask.Init( gameLocal.declAmmoTypeType.Num() );
|
||
|
for ( int i = 0; i < newData.ammoMask.GetSize(); i++ ) {
|
||
|
if ( i < baseData.ammoMask.GetSize() ) {
|
||
|
newData.ammoMask.GetDirect( i ) = msg.ReadDeltaLong( baseData.ammoMask.GetDirect( i ) );
|
||
|
} else {
|
||
|
newData.ammoMask.GetDirect( i ) = msg.ReadLong();
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
newData.ammoMask.SetSize( baseData.ammoMask.GetSize() );
|
||
|
for ( int i = 0; i < newData.ammoMask.GetSize(); i++ ) {
|
||
|
newData.ammoMask.GetDirect( i ) = baseData.ammoMask.GetDirect( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
owner->GetProficiencyTable().ReadNetworkState( baseData.proficiencyData, newData.proficiencyData, msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::WriteNetworkState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::WriteNetworkState( const sdInventoryBroadcastData& baseData, sdInventoryBroadcastData& newData, idBitMsg& msg ) const {
|
||
|
newData.idealWeapon = idealWeapon;
|
||
|
|
||
|
msg.WriteDeltaLong( baseData.idealWeapon, newData.idealWeapon );
|
||
|
|
||
|
newData.disabledMask.Init( items.Num() );
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( items[ i ].IsDisabled() ) {
|
||
|
newData.disabledMask.Set( i );
|
||
|
} else {
|
||
|
newData.disabledMask.Clear( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < newData.disabledMask.GetSize(); i++ ) {
|
||
|
if ( i < baseData.disabledMask.GetSize() ) {
|
||
|
msg.WriteDeltaLong( baseData.disabledMask.GetDirect( i ), newData.disabledMask.GetDirect( i ) );
|
||
|
} else {
|
||
|
msg.WriteLong( newData.disabledMask.GetDirect( i ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !owner->ShouldWritePlayerState() ) {
|
||
|
int ammoCount = gameLocal.declAmmoTypeType.Num();
|
||
|
newData.ammoMask.Init( ammoCount );
|
||
|
for ( int i = 0; i < ammoCount; i++ ) {
|
||
|
if ( ammo[ i ] != 0 ) {
|
||
|
newData.ammoMask.Set( i );
|
||
|
} else {
|
||
|
newData.ammoMask.Clear( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < newData.ammoMask.GetSize(); i++ ) {
|
||
|
if ( i < baseData.ammoMask.GetSize() ) {
|
||
|
msg.WriteDeltaLong( baseData.ammoMask.GetDirect( i ), newData.ammoMask.GetDirect( i ) );
|
||
|
} else {
|
||
|
msg.WriteLong( newData.ammoMask.GetDirect( i ) );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
newData.ammoMask.SetSize( baseData.ammoMask.GetSize() );
|
||
|
for ( int i = 0; i < newData.ammoMask.GetSize(); i++ ) {
|
||
|
newData.ammoMask.GetDirect( i ) = baseData.ammoMask.GetDirect( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
owner->GetProficiencyTable().WriteNetworkState( baseData.proficiencyData, newData.proficiencyData, msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CheckNetworkStateChanges
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::CheckNetworkStateChanges( const sdInventoryBroadcastData& baseData ) const {
|
||
|
NET_CHECK_FIELD( idealWeapon, idealWeapon );
|
||
|
|
||
|
if ( sdBitField_Dynamic::SizeForBits( items.Num() ) != baseData.disabledMask.GetSize() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < items.Num(); i++ ) {
|
||
|
if ( ( baseData.disabledMask.Get( i ) != 0 ) != items[ i ].IsDisabled() ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !owner->ShouldWritePlayerState() ) {
|
||
|
if ( baseData.ammoMask.GetSize() != sdBitField_Dynamic::SizeForBits( gameLocal.declAmmoTypeType.Num() ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < gameLocal.declAmmoTypeType.Num(); i++ ) {
|
||
|
if ( ( baseData.ammoMask.Get( i ) != 0 ) != ( ammo[ i ] != 0 ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return owner->GetProficiencyTable().CheckNetworkStateChanges( baseData.proficiencyData );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::ApplyPlayerState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::ApplyPlayerState( const sdInventoryPlayerStateData& newState ) {
|
||
|
NET_GET_NEW( sdInventoryPlayerStateData );
|
||
|
|
||
|
for ( int i = 0; i < newData.ammo.Num(); i++ ) {
|
||
|
SetAmmo( i, newData.ammo[ i ] );
|
||
|
}
|
||
|
|
||
|
items.ApplyPlayerState( newData );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::ReadPlayerState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::ReadPlayerState( const sdInventoryPlayerStateData& baseState, sdInventoryPlayerStateData& newState, const idBitMsg& msg ) const {
|
||
|
NET_GET_STATES( sdInventoryPlayerStateData );
|
||
|
|
||
|
newData.ammo.SetNum( gameLocal.declAmmoTypeType.Num() );
|
||
|
for ( int i = 0; i < newData.ammo.Num(); i++ ) {
|
||
|
newData.ammo[ i ] = msg.ReadDeltaShort( baseData.ammo[ i ] );
|
||
|
}
|
||
|
|
||
|
items.ReadPlayerState( baseState, newData, msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::WritePlayerState
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::WritePlayerState( const sdInventoryPlayerStateData& baseState, sdInventoryPlayerStateData& newState, idBitMsg& msg ) const {
|
||
|
NET_GET_STATES( sdInventoryPlayerStateData );
|
||
|
|
||
|
newData.ammo.SetNum( gameLocal.declAmmoTypeType.Num() );
|
||
|
for ( int i = 0; i < newData.ammo.Num(); i++ ) {
|
||
|
newData.ammo[ i ] = GetAmmo( i );
|
||
|
msg.WriteDeltaShort( baseData.ammo[ i ], newData.ammo[ i ] );
|
||
|
}
|
||
|
|
||
|
items.WritePlayerState( baseState, newState, msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::CheckPlayerStateChanges
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::CheckPlayerStateChanges( const sdInventoryPlayerStateData& baseState ) const {
|
||
|
NET_GET_BASE( sdInventoryPlayerStateData );
|
||
|
|
||
|
for ( int i = 0; i < baseData.ammo.Num(); i++ ) {
|
||
|
if ( baseData.ammo[ i ] != GetAmmo( i ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return items.CheckPlayerStateChanges( baseState );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::HideCurrentItem
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::HideCurrentItem( bool hide ) {
|
||
|
if( currentWeapon != -1 ) {
|
||
|
if ( hide ) {
|
||
|
items[ currentWeapon ].SetVisible( false );
|
||
|
items.HideItem( items[ currentWeapon ] );
|
||
|
} else {
|
||
|
if ( items.ShowItem( items[ currentWeapon ] ) ) {
|
||
|
items[ currentWeapon ].SetVisible( true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::SetClass
|
||
|
============
|
||
|
*/
|
||
|
bool sdInventory::SetClass( const idBitMsg& msg, bool cached ) {
|
||
|
sdPlayerClassSetup& setup = cached ? cachedPlayerClass : playerClass;
|
||
|
|
||
|
int playerClassIndex = msg.ReadBits( gameLocal.GetNumPlayerClassBits() ) - 1;
|
||
|
const sdDeclPlayerClass* pc = NULL;
|
||
|
if ( playerClassIndex != -1 ) {
|
||
|
pc = gameLocal.declPlayerClassType[ playerClassIndex ];
|
||
|
}
|
||
|
|
||
|
if ( setup.GetClass() != pc ) {
|
||
|
if ( cached ) {
|
||
|
SetCachedClass( pc );
|
||
|
} else {
|
||
|
SetPlayerClass( pc );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pc ) {
|
||
|
bool changed = false;
|
||
|
for ( int i = 0; i < pc->GetNumOptions(); i++ ) {
|
||
|
int itemIndex = msg.ReadBits( idMath::BitsForInteger( pc->GetOption( i ).Num() ) );
|
||
|
if ( setup.GetOptions()[ i ] != itemIndex ) {
|
||
|
setup.SetOption( i, itemIndex );
|
||
|
changed = true;
|
||
|
}
|
||
|
}
|
||
|
if ( changed && !cached ) {
|
||
|
SetupClassOptions( true, false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::UpdateForDisguise
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::UpdateForDisguise( void ) {
|
||
|
SetupClassOptions( false, true, false );
|
||
|
SetupModel();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::OnHide
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::OnHide( void ) {
|
||
|
items.OnHide();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::OnShow
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::OnShow( void ) {
|
||
|
items.OnShow();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::LogSuicide
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::LogSuicide( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.suicides ) {
|
||
|
stats.suicides->IncreaseValue( owner->entityNumber, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::LogDeath
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::LogDeath( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.deaths != NULL ) {
|
||
|
stats.deaths->IncreaseValue( owner->entityNumber, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::LogRevive
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::LogRevive( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.revived ) {
|
||
|
stats.revived->IncreaseValue( owner->entityNumber, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::LogTapOut
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::LogTapOut( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.tapouts ) {
|
||
|
stats.tapouts->IncreaseValue( owner->entityNumber, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::LogRespawn
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::LogRespawn( void ) {
|
||
|
const sdDeclPlayerClass* pc = playerClass.GetClass();
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclPlayerClass::stats_t& stats = pc->GetStats();
|
||
|
if ( stats.respawns != NULL ) {
|
||
|
stats.respawns->IncreaseValue( owner->entityNumber, 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdInventory::UpdatePlayerClassInfo
|
||
|
|
||
|
update the players class info game side for the bots.
|
||
|
============
|
||
|
*/
|
||
|
void sdInventory::UpdatePlayerClassInfo( const sdDeclPlayerClass* pc ) {
|
||
|
|
||
|
if ( !pc ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
botThreadData.GetGameWorldState()->clientInfo[ owner->entityNumber ].classType = pc->GetPlayerClassNum();
|
||
|
}
|