// 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(); }