#include "cbase.h" #include "asw_weapon_ammo_bag_shared.h" #include "in_buttons.h" #include "ammodef.h" #ifdef CLIENT_DLL #include "c_asw_player.h" #include "c_asw_weapon.h" #include "c_asw_marine.h" #include "c_asw_game_resource.h" #include "c_asw_marine_resource.h" #include "asw_hud_crosshair.h" #include "asw_input.h" #define CASW_Marine_Resource C_ASW_Marine_Resource #else #include "asw_pickup.h" #include "asw_marine_resource.h" #include "asw_marine.h" #include "asw_player.h" #include "asw_weapon.h" #include "asw_game_resource.h" #include "npcevent.h" #include "asw_sentry_base.h" #include "world.h" #include "te_effect_dispatch.h" #include "asw_util_shared.h" #include "asw_marine_profile.h" #include "asw_equipment_list.h" #include "asw_weapon_parse.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Weapon_Ammo_Bag, DT_ASW_Weapon_Ammo_Bag ) BEGIN_NETWORK_TABLE( CASW_Weapon_Ammo_Bag, DT_ASW_Weapon_Ammo_Bag ) #ifdef CLIENT_DLL // recvprops RecvPropArray3 ( RECVINFO_ARRAY(m_AmmoCount), RecvPropInt( RECVINFO(m_AmmoCount[0])) ), #else // sendprops SendPropArray3 ( SENDINFO_ARRAY3(m_AmmoCount), SendPropInt( SENDINFO_ARRAY(m_AmmoCount), ASW_AMMO_BAG_SLOTS, SPROP_UNSIGNED ) ), #endif END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CASW_Weapon_Ammo_Bag ) END_PREDICTION_DATA() LINK_ENTITY_TO_CLASS( asw_weapon_ammo_bag, CASW_Weapon_Ammo_Bag ); PRECACHE_WEAPON_REGISTER(asw_weapon_ammo_bag); #define THROW_INTERVAL 0.5 #ifndef CLIENT_DLL //--------------------------------------------------------- // Save/Restore //--------------------------------------------------------- BEGIN_DATADESC( CASW_Weapon_Ammo_Bag ) DEFINE_ARRAY( m_AmmoCount, FIELD_INTEGER, ASW_AMMO_BAG_SLOTS ), DEFINE_FIELD( m_fBurnDamage, FIELD_FLOAT ), END_DATADESC() ConVar asw_ammo_bag_damage("asw_ammo_bag_damage", "150", FCVAR_CHEAT, "Damage from ammo bag explosion"); ConVar asw_ammo_bag_damage_radius("asw_ammo_bag_damage_radius", "400", FCVAR_CHEAT, "Radius from ammo bag explosion"); ConVar asw_ammo_bag_burn_limit("asw_ammo_bag_burn_limit", "30", FCVAR_CHEAT, "Amount of burn damage needed to make ammo bag explode"); extern int g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud #endif /* not client */ CASW_Weapon_Ammo_Bag::CASW_Weapon_Ammo_Bag() { m_fMinRange1 = 0; m_fMaxRange1 = 2048; m_fMinRange2 = 256; m_fMaxRange2 = 1024; // set default ammo types m_iAmmoType[ASW_BAG_SLOT_RIFLE] = GetAmmoDef()->Index("ASW_R"); m_iAmmoType[ASW_BAG_SLOT_AUTOGUN] = GetAmmoDef()->Index("ASW_AG"); m_iAmmoType[ASW_BAG_SLOT_SHOTGUN] = GetAmmoDef()->Index("ASW_SG"); m_iAmmoType[ASW_BAG_SLOT_ASSAULT_SHOTGUN] = GetAmmoDef()->Index("ASW_ASG"); m_iAmmoType[ASW_BAG_SLOT_FLAMER] = GetAmmoDef()->Index("ASW_F"); //m_iAmmoType[ASW_BAG_SLOT_RAILGUN] = GetAmmoDef()->Index("ASW_RG"); m_iAmmoType[ASW_BAG_SLOT_PDW] = GetAmmoDef()->Index("ASW_PDW"); m_iAmmoType[ASW_BAG_SLOT_PISTOL] = GetAmmoDef()->Index("ASW_P"); // set ammo defaults m_AmmoCount.Set(ASW_BAG_SLOT_RIFLE, 5); m_AmmoCount.Set(ASW_BAG_SLOT_AUTOGUN, 1); m_AmmoCount.Set(ASW_BAG_SLOT_SHOTGUN, 10); m_AmmoCount.Set(ASW_BAG_SLOT_ASSAULT_SHOTGUN, 5); m_AmmoCount.Set(ASW_BAG_SLOT_FLAMER, 5); //m_AmmoCount.Set(ASW_BAG_SLOT_RAILGUN, 5); m_AmmoCount.Set(ASW_BAG_SLOT_PDW, 5); m_AmmoCount.Set(ASW_BAG_SLOT_PISTOL, 5); for (int i=0;iGetMarine() == pMarine ) { // commander throwing ammo pTargetMarine = dynamic_cast< CASW_Marine* >( pOwner->GetHighlightEntity() ); flMaxDist = CASW_Weapon_Ammo_Bag::GetPlayerMaxAmmoGiveDistance(); } else { // AI throwing ammo pTargetMarine = pMarine->m_hGiveAmmoTarget; // fudge factor in case ammo give target moved in the moment between AI move and ammo give tasks flMaxDist = CASW_Weapon_Ammo_Bag::GetAIMaxAmmoGiveDistance() * 3.0f; } if ( pTargetMarine && pTargetMarine != pMarine ) { int iType = SelectAmmoTypeToGive( pTargetMarine ); if ( iType != -1 ) { // check he's near enough float flDist = ( pMarine->GetAbsOrigin() - pTargetMarine->GetAbsOrigin() ).Length2D(); if ( flDist <= flMaxDist ) { // give the ammo GiveClipTo( pTargetMarine, iType ); pMarine->OnWeaponFired( this, 1 ); } } } #else // Do a cool client effect/animation here! #endif } #ifdef CLIENT_DLL // if the player has his mouse over another marine, highlight it, cos he's the one we can throw ammo to void CASW_Weapon_Ammo_Bag::MouseOverEntity(C_BaseEntity *pEnt, Vector vecWorldCursor) { C_ASW_Marine *pOtherMarine = C_ASW_Marine::AsMarine( pEnt ); CASW_Player *pOwner = GetCommander(); CASW_Marine *pMarine = GetMarine(); if ( !pOwner || !pMarine ) return; if ( !pOtherMarine ) { C_ASW_Game_Resource *pGameResource = ASWGameResource(); if ( pGameResource ) { // find marine closest to world cursor const float fMustBeThisClose = 70; const C_ASW_Game_Resource::CMarineToCrosshairInfo::tuple_t &info = pGameResource->GetMarineCrosshairCache()->GetClosestMarine(); if ( info.m_fDistToCursor <= fMustBeThisClose ) { pOtherMarine = info.m_hMarine.Get(); } } } // if the marine our cursor is over is near enough, highlight him if ( pOtherMarine && pOtherMarine != pMarine ) { float flDist = ( pMarine->GetAbsOrigin() - pOtherMarine->GetAbsOrigin() ).Length2D(); if ( flDist <= CASW_Weapon_Ammo_Bag::GetPlayerMaxAmmoGiveDistance() ) { // we're mousing over a marine, decide which type of ammo we're going to give him and show it on the tooltip CASWHudCrosshair *pCrosshair = GET_HUDELEMENT( CASWHudCrosshair ); bool bCanGiveAmmo = false; if ( pCrosshair ) { int iAmmoType = SelectAmmoTypeToGive(pOtherMarine); if (iAmmoType != -1) { pCrosshair->SetShowGiveAmmo(true, iAmmoType); bCanGiveAmmo = true; } } ASWInput()->SetHighlightEntity( pOtherMarine, bCanGiveAmmo ); } } } #else void CASW_Weapon_Ammo_Bag::GiveClipTo( CASW_Marine *pTargetMarine, int iAmmoType, bool bSuppressSound ) { if ( !pTargetMarine ) return; int iBagIndex = FindBagIndexForAmmoType(iAmmoType); if ( iBagIndex == -1 ) return; int iCount = m_AmmoCount[iBagIndex]; if ( iCount <= 0 ) return; // find how much ammo per clip for this type CASW_WeaponInfo *pWeaponData = ASWEquipmentList()->GetWeaponDataFor(m_szWeaponClass[iBagIndex]); if (!pWeaponData) return; int iAmmoPerClip = pWeaponData->iMaxClip1; //Msg(" ammo per clip %d\n", iAmmoPerClip); if (iAmmoPerClip <= 0) return; // give ammo to the target marine if ( pTargetMarine->GiveAmmo( iAmmoPerClip, iAmmoType, bSuppressSound ) <= 0 ) { if ( pTargetMarine->GiveAmmoToAmmoBag( iAmmoPerClip, iAmmoType, bSuppressSound ) <= 0 ) return; } IGameEvent * event = gameeventmanager->CreateEvent( "player_give_ammo" ); if ( event ) { CASW_Marine *pMarine = GetMarine(); CASW_Player *pPlayer = NULL; if ( pMarine ) { pPlayer = pMarine->GetCommander(); } event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) ); event->SetInt( "entindex", pTargetMarine->entindex() ); gameeventmanager->FireEvent( event ); } m_AmmoCount.Set(iBagIndex, iCount - 1); } // marine was hurt when holding this weapon void CASW_Weapon_Ammo_Bag::OnMarineDamage(const CTakeDamageInfo &info) { BaseClass::OnMarineDamage(info); if (info.GetDamageType() & DMG_BURN) { m_fBurnDamage += info.GetDamage(); // check if the ammo bag has taken too much burn damage and should now explode if (m_fBurnDamage > asw_ammo_bag_burn_limit.GetInt()) { CASW_Marine *pMarine = GetMarine(); if (pMarine) { pMarine->Weapon_Detach(this); // switch marine's weapon away from this, if he's holding it if (pMarine->GetActiveASWWeapon() == this) pMarine->SwitchToNextBestWeapon(NULL); CASW_Marine_Resource *pMR = pMarine->GetMarineResource(); if ( pMR ) { char szName[ 256 ]; pMR->GetDisplayName( szName, sizeof( szName ) ); UTIL_ClientPrintAll( HUD_PRINTTALK, "#asw_ammo_bag_exploded", szName ); } // explosion effect CPASFilter filter( pMarine->GetAbsOrigin() ); te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 2.0, 15, TE_EXPLFLAG_NONE, asw_ammo_bag_damage_radius.GetInt(), asw_ammo_bag_damage.GetInt() ); Vector vecForward = Vector(0, 0, -1); trace_t tr; UTIL_TraceLine ( pMarine->GetAbsOrigin(), pMarine->GetAbsOrigin() + 60*vecForward, MASK_SHOT, pMarine, COLLISION_GROUP_NONE, &tr); if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0)) { // non-world needs smaller decals if( tr.m_pEnt && !tr.m_pEnt->IsNPC() ) { UTIL_DecalTrace( &tr, "SmallScorch" ); } } else { UTIL_DecalTrace( &tr, "Scorch" ); } UTIL_ASW_ScreenShake( pMarine->GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START ); RadiusDamage ( CTakeDamageInfo( this, info.GetAttacker(), asw_ammo_bag_damage.GetInt(), DMG_BLAST ), pMarine->GetAbsOrigin(), asw_ammo_bag_damage_radius.GetInt(), CLASS_NONE, NULL ); } // remove the ammo bag Kill(); } } } int CASW_Weapon_Ammo_Bag::AddAmmo(int iBullets, int iAmmoIndex) { int iBagSlot = FindBagIndexForAmmoType(iAmmoIndex); if (iBagSlot == -1) // ammo type not carried by the ammo bag return 0; if (!HasRoomForAmmo(iAmmoIndex)) return 0; CASW_WeaponInfo *pWeaponData = ASWEquipmentList()->GetWeaponDataFor(m_szWeaponClass[iBagSlot]); if (!pWeaponData) return 0; int iBulletsPerClip = pWeaponData->iMaxClip1; int clips = iBullets / iBulletsPerClip; m_AmmoCount.Set(iBagSlot, AmmoCount(iBagSlot) + clips); return clips * iBulletsPerClip; } bool CASW_Weapon_Ammo_Bag::DropAmmoPickup(int iBagSlot) { CASW_Marine* pMarine = GetMarine(); if (!pMarine) return false; if (iBagSlot < 0 || iBagSlot >= ASW_AMMO_BAG_SLOTS) return false; int iDropped = 1; if (iBagSlot == ASW_BAG_SLOT_SHOTGUN) { iDropped = 2; } if (AmmoCount(iBagSlot) < iDropped) return false; // create a pickup of this ammo's class CASW_Pickup* pPickup = static_cast( Create( m_szAmmoClass[iBagSlot], pMarine->WorldSpaceCenter(), pMarine->GetAbsAngles(), pMarine ) ); if (!pPickup) return false; Vector vecForward = pMarine->BodyDirection2D() * 100; pPickup->SetAbsVelocity(vecForward); if (pPickup->VPhysicsGetObject()) pPickup->VPhysicsGetObject()->SetVelocity( &vecForward, &vec3_origin ); pPickup->SetOwnerEntity(NULL); // for shotgun ammo we try to drop two m_AmmoCount.Set(iBagSlot, AmmoCount(iBagSlot) - iDropped); return true; } #endif int CASW_Weapon_Ammo_Bag::AmmoCount(int index) { if (index < 0 || index >= ASW_AMMO_BAG_SLOTS) return 0; return m_AmmoCount.Get(index); } int CASW_Weapon_Ammo_Bag::MaxAmmoCount(int index) { if (index < 0 || index >= ASW_AMMO_BAG_SLOTS) return 0; return m_MaxAmmoCount[index]; } int CASW_Weapon_Ammo_Bag::FindBagIndexForAmmoType(int iAmmoType) { for (int i=0;iGetActiveASWWeapon(); int iType = -1; if (pCurrent && CanGiveAmmoToWeapon(pCurrent)) iType = pCurrent->GetPrimaryAmmoType(); else { // go through marine's inventory for (int i=0;i<3;i++) { CASW_Weapon *pWeapon = pOtherMarine->GetASWWeapon(i); if (pWeapon != pCurrent && pWeapon && CanGiveAmmoToWeapon(pWeapon)) { iType = pWeapon->GetPrimaryAmmoType(); } } } // no weapons to give ammo to, so let's check for giving into an ammo bag if (iType == -1) { CASW_Weapon_Ammo_Bag* pBag = dynamic_cast(pOtherMarine->GetWeapon(0)); if (pBag) { for (int i=0;im_iAmmoType[i]; if (pBag->HasRoomForAmmo(iAmmoType) && BagHasAmmo(iAmmoType)) return iAmmoType; } } pBag = dynamic_cast(pOtherMarine->GetWeapon(1)); if (pBag) { for (int i=0;im_iAmmoType[i]; if (pBag->HasRoomForAmmo(iAmmoType) && BagHasAmmo(iAmmoType)) return iAmmoType; } } } return iType; } //----------------------------------------------------------------------------- // Purpose: Return true if this weapon has some ammo loaded in the primary clip // // Useful because some weapons (like the ammo bag) may have special conditions // other than how many bullets are in the clip //----------------------------------------------------------------------------- bool CASW_Weapon_Ammo_Bag::PrimaryAmmoLoaded( void ) { // Needed to allow the primary attack to fire, since it doesn't have // the same concept of a clip as other weapons return true; } bool CASW_Weapon_Ammo_Bag::BagHasAmmo(int iAmmoType) { for (int i=0;i 0) { return true; } } return false; } bool CASW_Weapon_Ammo_Bag::CanGiveAmmoToWeapon(CASW_Weapon *pWeapon) { if (!pWeapon) return false; CASW_Marine *pMarine = dynamic_cast(pWeapon->GetOwner()); if (!pMarine) return false; // grab the ammo type of the weapon int iAmmoType = pWeapon->GetPrimaryAmmoType(); // check if we have that ammo here if (!BagHasAmmo(iAmmoType)) return false; int iGuns = pMarine->GetNumberOfWeaponsUsingAmmo(iAmmoType); // check if the gun is full int ammo = pMarine->GetAmmoCount(pWeapon->GetPrimaryAmmoType()); // ammo the marine is carrying outside the gun if (ammo >= GetAmmoDef()->MaxCarry(iAmmoType,pMarine) * iGuns) return false; return true; } int CASW_Weapon_Ammo_Bag::NumClipsForWeapon(CASW_Weapon *pWeapon) { if (!pWeapon) return 0; // grab the ammo type of the weapon int iAmmoType = pWeapon->GetPrimaryAmmoType(); // check if we have that ammo here for (int i=0;i 0) { return m_AmmoCount[i]; } } return 0; } #ifndef CLIENT_DLL void asw_ammo_bag_report_f() { if ( !ASWGameResource() ) return; CASW_Game_Resource *pGameResource = ASWGameResource(); for (int i=0;iGetMaxMarineResources();i++) { CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if (!pMR) continue; CASW_Marine *pMarine = pMR->GetMarineEntity(); if (!pMarine) continue; CASW_Weapon_Ammo_Bag *pBag = dynamic_cast(pMarine->GetASWWeapon(0)); if (pBag) pBag->DebugContents(); pBag = dynamic_cast(pMarine->GetASWWeapon(1)); if (pBag) pBag->DebugContents(); } } ConCommand asw_ammo_bag_report( "asw_ammo_bag_report", asw_ammo_bag_report_f, "List contents of ammo bag", FCVAR_CHEAT ); #else void asw_ammo_bag_report_client_f() { if ( !ASWGameResource() ) return; C_ASW_Game_Resource *pGameResource = ASWGameResource(); for (int i=0;iGetMaxMarineResources();i++) { C_ASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if (!pMR) continue; C_ASW_Marine *pMarine = pMR->GetMarineEntity(); if (!pMarine) continue; C_ASW_Weapon_Ammo_Bag *pBag = dynamic_cast(pMarine->GetASWWeapon(0)); if (pBag) pBag->DebugContents(); pBag = dynamic_cast(pMarine->GetASWWeapon(1)); if (pBag) pBag->DebugContents(); } } ConCommand asw_ammo_bag_report_client( "asw_ammo_bag_report_client", asw_ammo_bag_report_client_f, "List contents of ammo bag in client.dll", FCVAR_CHEAT ); #endif void CASW_Weapon_Ammo_Bag::DebugContents() { Msg("Ammo bag %d on %s\n", entindex(), #ifdef CLIENT_DLL "client" #else "server" #endif ); for (int i=0;i