// // Copyright (c) 1999, Valve LLC. All rights reserved. // // This product contains software technology licensed from Id // Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. // All Rights Reserved. // // Use, distribution, and modification of this source code and/or resulting // object code is restricted to non-commercial enhancements to products from // Valve LLC. All other use, distribution, or modification is prohibited // without written permission from Valve LLC. // // ===== weapons.cpp ======================================================== // // functions governing the selection/use of weapons for players // // $Workfile: weapons.cpp $ // $Date: 2002/11/22 21:13:52 $ // //------------------------------------------------------------------------------- // $Log: weapons.cpp,v $ // Revision 1.49 2002/11/22 21:13:52 Flayra // - mp_consistency changes // // Revision 1.48 2002/11/12 02:19:43 Flayra // - Fixed problem where friendly bulletfire was doing damage // // Revision 1.47 2002/10/24 21:19:11 Flayra // - Reworked jetpack effects // - Added a few extra sounds // // Revision 1.46 2002/10/16 00:41:15 Flayra // - Removed unneeds sounds and events // - Added distress beacon event // - Added general purpose particle event // // Revision 1.45 2002/09/25 20:40:09 Flayra // - Play different sound for aliens when they get weapons // // Revision 1.44 2002/09/23 22:04:00 Flayra // - Regular update // // Revision 1.43 2002/08/31 18:01:18 Flayra // - Work at VALVe // // Revision 1.42 2002/07/26 23:01:05 Flayra // - Precache numerical feedback event // // Revision 1.41 2002/07/23 16:48:37 Flayra // - Added distress beacon // // Revision 1.40 2002/07/08 16:35:17 Flayra // - Added document header, updates for cheat protection, added constants // //=============================================================================== #include "../util/nowarnings.h" #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "monsters.h" #include "weapons.h" #include "nodes.h" #include "soundent.h" #include "decals.h" #include "gamerules.h" #include "../mod/AvHConstants.h" #include "../mod/AvHMarineEquipmentConstants.h" #include "../mod/AvHAlienWeaponConstants.h" #include "../mod/AvHAlienEquipmentConstants.h" #include "../mod/AvHPlayer.h" #include "../mod/AvHGamerules.h" #include "../mod/AvHNetworkMessages.h" extern CGraph WorldGraph; extern int gEvilImpulse101; int gJetpackEventID; int gStartOverwatchEventID; int gEndOverwatchEventID; int gTeleportEventID; int gBlinkEffectSuccessEventID; int gPhaseInEventID; int gSiegeHitEventID; int gSiegeViewHitEventID; int gCommanderPointsAwardedEventID; int gAlienSightOnEventID; int gAlienSightOffEventID; //int gParalysisStartEventID; int gRegenerationEventID; int gStartCloakEventID; int gEndCloakEventID; int gSporeCloudEventID; int gUmbraCloudEventID; int gStopScreamEventID; int gWelderEventID; int gWelderConstEventID; //int gWallJumpEventID; //int gFlightEventID; int gEmptySoundEventID; int gNumericalInfoEventID; int gInvalidActionEventID; int gParticleEventID; int gDistressBeaconEventID; int gWeaponAnimationEventID; int gLevelUpEventID; int gMetabolizeSuccessEventID; #define NOT_USED 255 DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS]; AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS]; MULTIDAMAGE gMultiDamage; #define TRACER_FREQ 4 // Tracers fire every fourth bullet extern bool gCanMove[]; //========================================================= // MaxAmmoCarry - pass in a name and this function will tell // you the maximum amount of that type of ammunition that a // player can carry. //========================================================= int MaxAmmoCarry( int iszName ) { for ( int i = 0; i < MAX_WEAPONS; i++ ) { if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) ) return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1; if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) ) return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2; } ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); return -1; } /* ============================================================================== MULTI-DAMAGE Collects multiple small damages into a single damage ============================================================================== */ // // ClearMultiDamage - resets the global multi damage accumulator // void ClearMultiDamage(void) { gMultiDamage.pEntity = NULL; gMultiDamage.amount = 0; gMultiDamage.type = 0; } // // ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity // // GLOBALS USED: // gMultiDamage void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker ) { Vector vecSpot1;//where blood comes from Vector vecDir;//direction blood should go TraceResult tr; if ( !gMultiDamage.pEntity ) return; float theDamage = gMultiDamage.amount; gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, theDamage, gMultiDamage.type ); } // GLOBALS USED: // gMultiDamage void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType) { if ( !pEntity ) return; gMultiDamage.type |= bitsDamageType; if ( pEntity != gMultiDamage.pEntity ) { ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker! gMultiDamage.pEntity = pEntity; gMultiDamage.amount = 0; } gMultiDamage.amount += flDamage; } /* ================ SpawnBlood ================ */ void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) { if(flDamage >= 0.0f) { if(bloodColor == DONT_BLEED) { UTIL_Sparks(vecSpot); } else { UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage ); } } } int DamageDecal( CBaseEntity *pEntity, int bitsDamageType ) { if ( !pEntity ) return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4)); return pEntity->DamageDecal( bitsDamageType ); } void DecalGunshot( TraceResult *pTrace, int iBulletType ) { // Is the entity valid if ( !UTIL_IsValidEntity( pTrace->pHit ) ) return; if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP ) { CBaseEntity *pEntity = NULL; // Decal the wall with a gunshot if ( !FNullEnt(pTrace->pHit) ) pEntity = CBaseEntity::Instance(pTrace->pHit); // switch( iBulletType ) // { // case BULLET_PLAYER_9MM: // case BULLET_MONSTER_9MM: // case BULLET_PLAYER_MP5: // case BULLET_MONSTER_MP5: // case BULLET_PLAYER_BUCKSHOT: // case BULLET_PLAYER_357: // default: // smoke and decal UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); // break; // case BULLET_MONSTER_12MM: // // smoke and decal // UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); // break; // case BULLET_PLAYER_CROWBAR: // // wall decal // UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); // break; // } } } // // EjectBrass - tosses a brass shell from passed origin at passed velocity // void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ) { // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); WRITE_BYTE( TE_MODEL); WRITE_COORD( vecOrigin.x); WRITE_COORD( vecOrigin.y); WRITE_COORD( vecOrigin.z); WRITE_COORD( vecVelocity.x); WRITE_COORD( vecVelocity.y); WRITE_COORD( vecVelocity.z); WRITE_ANGLE( rotation ); WRITE_SHORT( model ); WRITE_BYTE ( soundtype); WRITE_BYTE ( 25 );// 2.5 seconds MESSAGE_END(); } #if 0 // UNDONE: This is no longer used? void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); WRITE_BYTE ( TE_EXPLODEMODEL ); WRITE_COORD( vecOrigin.x ); WRITE_COORD( vecOrigin.y ); WRITE_COORD( vecOrigin.z ); WRITE_COORD( speed ); WRITE_SHORT( model ); WRITE_SHORT( count ); WRITE_BYTE ( 15 );// 1.5 seconds MESSAGE_END(); } #endif int giAmmoIndex = 0; // Precaches the ammo and queues the ammo info for sending to clients void AddAmmoNameToAmmoRegistry( const char *szAmmoname ) { // make sure it's not already in the registry for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) { if ( !CBasePlayerItem::AmmoInfoArray[i].pszName) continue; if ( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 ) return; // ammo already in registry, just quite } giAmmoIndex++; ASSERT( giAmmoIndex < MAX_AMMO_SLOTS ); if ( giAmmoIndex >= MAX_AMMO_SLOTS ) giAmmoIndex = 0; CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant } // Precaches the weapon and queues the weapon info for sending to clients void UTIL_PrecacheOtherWeapon( const char *szClassname ) { edict_t *pent; pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) ); if ( FNullEnt( pent ) ) { ALERT ( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" ); return; } CBaseEntity *pEntity = CBaseEntity::Instance (VARS( pent )); if (pEntity) { ItemInfo II; pEntity->Precache( ); memset( &II, 0, sizeof II ); if ( ((CBasePlayerItem*)pEntity)->GetItemInfo( &II ) ) { CBasePlayerItem::ItemInfoArray[II.iId] = II; if ( II.pszAmmo1 && *II.pszAmmo1 ) { AddAmmoNameToAmmoRegistry( II.pszAmmo1 ); } if ( II.pszAmmo2 && *II.pszAmmo2 ) { AddAmmoNameToAmmoRegistry( II.pszAmmo2 ); } memset( &II, 0, sizeof II ); } } REMOVE_ENTITY(pent); } // called by worldspawn void W_Precache(void) { memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) ); memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) ); giAmmoIndex = 0; // Marine weapons //UTIL_PrecacheOtherWeapon("weapon_9mmhandgun"); //UTIL_PrecacheOtherWeapon("weapon_glock"); //UTIL_PrecacheOther("ammo_9mmclip"); // Player assets PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel); PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel); PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel); PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel); PRECACHE_UNMODIFIED_SOUND(kDistressBeaconSound); PRECACHE_UNMODIFIED_SOUND(kLevelUpMarineSound); PRECACHE_UNMODIFIED_SOUND(kLevelUpAlienSound); PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound1); PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound2); PRECACHE_SOUND(kMyHiveEasterEgg); //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesGrantedSound); //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesLostSound); PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel); PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel); PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel); PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel); PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel); PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel); // : 1072 // Added some client side consistency checks. PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); UTIL_PrecacheOtherWeapon(kwsMine); UTIL_PrecacheOtherWeapon(kwsKnife); UTIL_PrecacheOtherWeapon(kwsMachineGun); UTIL_PrecacheOtherWeapon(kwsPistol); UTIL_PrecacheOtherWeapon(kwsShotGun); UTIL_PrecacheOtherWeapon(kwsHeavyMachineGun); UTIL_PrecacheOtherWeapon(kwsGrenadeGun); UTIL_PrecacheOtherWeapon(kwsGrenade); // Alien weapons UTIL_PrecacheOtherWeapon(kwsSpitGun); UTIL_PrecacheOther(kwsSpitProjectile); UTIL_PrecacheOther(kwsWebProjectile); UTIL_PrecacheOtherWeapon(kwsClaws); UTIL_PrecacheOtherWeapon(kwsSwipe); UTIL_PrecacheOtherWeapon(kwsSporeGun); UTIL_PrecacheOther(kwsSporeProjectile); // UTIL_PrecacheOtherWeapon(kwsParalysisGun); UTIL_PrecacheOtherWeapon(kwsSpikeGun); UTIL_PrecacheOtherWeapon(kwsBiteGun); UTIL_PrecacheOtherWeapon(kwsBite2Gun); UTIL_PrecacheOtherWeapon(kwsHealingSpray); UTIL_PrecacheOtherWeapon(kwsWebSpinner); // UTIL_PrecacheOtherWeapon(kwsBabblerGun); UTIL_PrecacheOtherWeapon(kwsPrimalScream); UTIL_PrecacheOtherWeapon(kwsParasiteGun); UTIL_PrecacheOtherWeapon(kwsMetabolize); UTIL_PrecacheOtherWeapon(kwsUmbraGun); UTIL_PrecacheOtherWeapon(kwsBlinkGun); UTIL_PrecacheOtherWeapon(kwsDivineWind); UTIL_PrecacheOtherWeapon(kwsBileBombGun); UTIL_PrecacheOtherWeapon(kwsAcidRocketGun); UTIL_PrecacheOtherWeapon(kwsStomp); UTIL_PrecacheOtherWeapon(kwsDevour); // UTIL_PrecacheOtherWeapon(kwsAmplify); UTIL_PrecacheOther(kwsBileBomb); // Alien abilities UTIL_PrecacheOtherWeapon(kwsLeap); UTIL_PrecacheOtherWeapon(kwsCharge); // Alien buildings UTIL_PrecacheOther(kwsAlienResourceTower); UTIL_PrecacheOther(kwsOffenseChamber); UTIL_PrecacheOther(kwsDefenseChamber); UTIL_PrecacheOther(kwsSensoryChamber); UTIL_PrecacheOther(kwsMovementChamber); UTIL_PrecacheOther(kesTeamWebStrand); // Equipment //UTIL_PrecacheOtherWeapon("weapon_tripmine"); UTIL_PrecacheOther(kwsScan); UTIL_PrecacheOther(kwsPhaseGate); //UTIL_PrecacheOther(kwsNuke); // Marine buildings UTIL_PrecacheOther(kwsTeamCommand); UTIL_PrecacheOther(kwsResourceTower); UTIL_PrecacheOther(kwsInfantryPortal); UTIL_PrecacheOther(kwsTurretFactory); UTIL_PrecacheOther(kwsArmory); UTIL_PrecacheOther(kwsAdvancedArmory); UTIL_PrecacheOther(kwsArmsLab); UTIL_PrecacheOther(kwsPrototypeLab); UTIL_PrecacheOther(kwsObservatory); //UTIL_PrecacheOther(kwsChemlab); //UTIL_PrecacheOther(kwsMedlab); //UTIL_PrecacheOther(kwsNukePlant); UTIL_PrecacheOther(kwsDeployedTurret); UTIL_PrecacheOther(kwsSiegeTurret); // container for dropped deathmatch weapons UTIL_PrecacheOther("weaponbox"); UTIL_PrecacheOtherWeapon(kwsWelder); UTIL_PrecacheOther(kwsDeployedMine); UTIL_PrecacheOther(kwsHealth); UTIL_PrecacheOther(kwsCatalyst); UTIL_PrecacheOther(kwsGenericAmmo); UTIL_PrecacheOther(kwsHeavyArmor); UTIL_PrecacheOther(kwsJetpack); UTIL_PrecacheOther(kwsAmmoPack); UTIL_PrecacheOther(kwsDebugEntity); // Precache other events gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent); //gStartOverwatchEventID = PRECACHE_EVENT(1, kStartOverwatchEvent); //gEndOverwatchEventID = PRECACHE_EVENT(1, kEndOverwatchEvent); // Alien upgrade events gRegenerationEventID = PRECACHE_EVENT(1, kRegenerationEvent); gStartCloakEventID = PRECACHE_EVENT(1, kStartCloakEvent); gEndCloakEventID = PRECACHE_EVENT(1, kEndCloakEvent); // Extra alien weapon events //gEnsnareHitEventID = PRECACHE_EVENT(1, kEnsnareHitEventName); gSporeCloudEventID = PRECACHE_EVENT(1, kSporeCloudEventName); gUmbraCloudEventID = PRECACHE_EVENT(1, kUmbraCloudEventName); gStopScreamEventID = PRECACHE_EVENT(1, kStopPrimalScreamSoundEvent); // Extra marine events gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent); gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName); gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent); gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent); gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent); gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent); gAlienSightOnEventID = PRECACHE_EVENT(1, kAlienSightOnEvent); gAlienSightOffEventID = PRECACHE_EVENT(1, kAlienSightOffEvent); // gParalysisStartEventID = PRECACHE_EVENT(1, kParalysisStartEventName); //gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent); //gFlightEventID = PRECACHE_EVENT(1, kFlightEvent); PRECACHE_UNMODIFIED_SOUND(kConnectSound); //PRECACHE_UNMODIFIED_SOUND(kDisconnectSound); PRECACHE_UNMODIFIED_MODEL(kNullModel); UTIL_PrecacheOther("monster_sentry"); // Allow welder events in mapper build gWelderEventID = PRECACHE_EVENT(1, kWelderEventName); gWelderConstEventID = PRECACHE_EVENT(1, kWelderConstEventName); PRECACHE_EVENT(1, kWelderStartEventName); PRECACHE_EVENT(1, kWelderEndEventName); gEmptySoundEventID = PRECACHE_EVENT(1, kEmptySoundEvent); gNumericalInfoEventID = PRECACHE_EVENT(1, kNumericalInfoEvent); gInvalidActionEventID = PRECACHE_EVENT(1, kInvalidActionEvent); gParticleEventID = PRECACHE_EVENT(1, kParticleEvent); gDistressBeaconEventID = PRECACHE_EVENT(1, kDistressBeaconEvent); gWeaponAnimationEventID= PRECACHE_EVENT(1, kWeaponAnimationEvent); gLevelUpEventID = PRECACHE_EVENT(1, kLevelUpEvent); gMetabolizeSuccessEventID = PRECACHE_EVENT(1, kMetabolizeSuccessEventName); PRECACHE_UNMODIFIED_SOUND(kPhaseInSound); // Precache reload sound that is hardcoded deep in HL PRECACHE_UNMODIFIED_SOUND(kEmptySound); PRECACHE_UNMODIFIED_SOUND(kInvalidSound); // Not sure who's using these, but I keep getting errors that they're not precached. Buttons? PRECACHE_UNMODIFIED_SOUND("buttons/spark1.wav"); PRECACHE_UNMODIFIED_SOUND("buttons/spark2.wav"); PRECACHE_UNMODIFIED_SOUND("buttons/spark3.wav"); PRECACHE_UNMODIFIED_SOUND("buttons/spark4.wav"); PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav"); PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav"); // For grunts. Careful, this uses the same weapon id that the grenade gun uses //UTIL_PrecacheOtherWeapon("weapon_9mmAR"); // common world objects // UTIL_PrecacheOther( "item_suit" ); // UTIL_PrecacheOther( "item_battery" ); // UTIL_PrecacheOther( "item_antidote" ); // UTIL_PrecacheOther( "item_security" ); // UTIL_PrecacheOther( "item_longjump" ); // shotgun // UTIL_PrecacheOtherWeapon( "weapon_shotgun" ); // UTIL_PrecacheOther( "ammo_buckshot" ); // // // crowbar // UTIL_PrecacheOtherWeapon( "weapon_rowbar" ); // // // glock // UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" ); // UTIL_PrecacheOther( "ammo_9mmclip" ); // // // mp5 // UTIL_PrecacheOtherWeapon( "weapon_9mmAR" ); // UTIL_PrecacheOther( "ammo_9mmAR" ); // UTIL_PrecacheOther( "ammo_ARgrenades" ); //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // python // UTIL_PrecacheOtherWeapon( "weapon_357" ); // UTIL_PrecacheOther( "ammo_357" ); //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // gauss // UTIL_PrecacheOtherWeapon( "weapon_gauss" ); // UTIL_PrecacheOther( "ammo_gaussclip" ); //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // rpg // UTIL_PrecacheOtherWeapon( "weapon_rpg" ); // UTIL_PrecacheOther( "ammo_rpgclip" ); //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // crossbow // UTIL_PrecacheOtherWeapon( "weapon_crossbow" ); // UTIL_PrecacheOther( "ammo_crossbow" ); // UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // egon // UTIL_PrecacheOtherWeapon( "weapon_egon" ); //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // satchel charge // UTIL_PrecacheOtherWeapon( "weapon_satchel" ); //#endif // // // hand grenade // UTIL_PrecacheOtherWeapon("weapon_handgrenade"); // // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // squeak grenade // UTIL_PrecacheOtherWeapon( "weapon_snark" ); //#endif // //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // // hornetgun // UTIL_PrecacheOtherWeapon( "weapon_hornetgun" ); //#endif //#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // if ( g_pGameRules->IsDeathmatch() ) // { // UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons // } //#endif g_sModelIndexFireball = PRECACHE_UNMODIFIED_MODEL ("sprites/zerogxplode.spr");// fireball g_sModelIndexWExplosion = PRECACHE_UNMODIFIED_MODEL ("sprites/WXplo1.spr");// underwater fireball g_sModelIndexSmoke = PRECACHE_UNMODIFIED_MODEL ("sprites/steam1.spr");// smoke g_sModelIndexBubbles = PRECACHE_UNMODIFIED_MODEL ("sprites/bubble2.spr");//bubbles g_sModelIndexBloodSpray = PRECACHE_UNMODIFIED_MODEL ("sprites/bloodspray.spr"); // initial blood g_sModelIndexBloodDrop = PRECACHE_UNMODIFIED_MODEL ("sprites/blood.spr"); // splattered blood g_sModelIndexLaser = PRECACHE_UNMODIFIED_MODEL( (char *)g_pModelNameLaser ); g_sModelIndexLaserDot = PRECACHE_UNMODIFIED_MODEL("sprites/laserdot.spr"); // used by explosions PRECACHE_UNMODIFIED_MODEL ("models/grenade.mdl"); PRECACHE_UNMODIFIED_MODEL ("sprites/explode1.spr"); PRECACHE_SOUND ("weapons/debris1.wav");// explosion aftermaths PRECACHE_SOUND ("weapons/debris2.wav");// explosion aftermaths PRECACHE_SOUND ("weapons/debris3.wav");// explosion aftermaths PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound1); PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound2); PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound3); PRECACHE_UNMODIFIED_SOUND (kGRHitSound); PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit1.wav"); // hit by bullet PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit2.wav"); // hit by bullet PRECACHE_UNMODIFIED_SOUND ("items/weapondrop1.wav");// weapon falls to the ground } TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = { DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating ); TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = { DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ), DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ), DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ), DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem ); void CBasePlayerItem :: SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector(-24, -24, 0); pev->absmax = pev->origin + Vector(24, 24, 16); } BOOL CBasePlayerItem::CanDeploy( void ) { return TRUE; } // can this weapon be put away right now? BOOL CBasePlayerItem::CanHolster( void ) { return TRUE; }; // returns is deploy was successful BOOL CBasePlayerItem::Deploy( ) { return TRUE; } BOOL CBasePlayerItem::IsUseable( void ) { return TRUE; } //========================================================= // Sets up movetype, size, solidtype for a new weapon. //========================================================= void CBasePlayerItem :: FallInit( void ) { pev->movetype = MOVETYPE_TOSS; //SOLID_BBOX caused weapons to get stuck on eachother and float in air pev->solid = SOLID_BSP; UTIL_SetOrigin( pev, pev->origin ); UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0) );//pointsize until it lands on the ground. SetTouch(&CBasePlayerItem::DefaultTouch ); SetThink(&CBasePlayerItem::FallThink ); pev->nextthink = gpGlobals->time + 0.1; } //========================================================= // FallThink - Items that have just spawned run this think // to catch them when they hit the ground. Once we're sure // that the object is grounded, we change its solid type // to trigger and set it in a large box that helps the // player get it. //========================================================= void CBasePlayerItem::FallThink ( void ) { pev->nextthink = gpGlobals->time + 0.1; //timer for weapons stuck floating in air pev->fuser4 += 0.1; if ( pev->flags & FL_ONGROUND ) { // clatter if we have an owner (i.e., dropped by someone) // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) if ( !FNullEnt( pev->owner ) ) { int pitch = 95 + RANDOM_LONG(0,29); EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch); } // lie flat pev->angles.x = 0; pev->angles.z = 0; Materialize(); pev->fuser4 = 0; } //weapons in air for too long from collision issues with other entities change to SOLID_TRIGGER to fall to ground if ((pev->fuser4 > 0.7)) { pev->solid = SOLID_TRIGGER; pev->fuser4 = 0; } } //========================================================= // Materialize - make a CBasePlayerItem visible and tangible //========================================================= void CBasePlayerItem::Materialize( void ) { if ( pev->effects & EF_NODRAW ) { // changing from invisible state to visible. EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); pev->effects &= ~EF_NODRAW; pev->effects |= EF_MUZZLEFLASH; } pev->solid = SOLID_TRIGGER; UTIL_SetOrigin( pev, pev->origin );// link into world. SetTouch (&CBasePlayerItem::DefaultTouch); SetThink (NULL); this->VirtualMaterialize(); } //========================================================= // AttemptToMaterialize - the item is trying to rematerialize, // should it do so now or wait longer? //========================================================= void CBasePlayerItem::AttemptToMaterialize( void ) { float time = g_pGameRules->FlWeaponTryRespawn( this ); if ( time == 0 ) { Materialize(); return; } pev->nextthink = gpGlobals->time + time; } //========================================================= // CheckRespawn - a player is taking this weapon, should // it respawn? //========================================================= void CBasePlayerItem :: CheckRespawn ( void ) { switch ( g_pGameRules->WeaponShouldRespawn( this ) ) { case GR_WEAPON_RESPAWN_YES: Respawn(); break; case GR_WEAPON_RESPAWN_NO: return; break; } } //========================================================= // Respawn- this item is already in the world, but it is // invisible and intangible. Make it visible and tangible. //========================================================= CBaseEntity* CBasePlayerItem::Respawn( void ) { // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code // will decide when to make the weapon visible and touchable. CBaseEntity *pNewWeapon = CBaseEntity::Create( (char *)STRING( pev->classname ), g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner ); if ( pNewWeapon ) { pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now pNewWeapon->SetTouch( NULL );// no touch pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize ); DROP_TO_FLOOR ( ENT(pev) ); // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, // but when it should respawn is based on conditions belonging to the weapon that was taken. pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this ); } else { ALERT ( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) ); } return pNewWeapon; } void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther ) { // if it's not a player, ignore if ( !pOther->IsPlayer() ) return; CBasePlayer *pPlayer = (CBasePlayer *)pOther; // can I have this? if ( !g_pGameRules->CanHavePlayerItem( pPlayer, this ) ) { if ( gEvilImpulse101 ) { UTIL_Remove( this ); } return; } if (pOther->AddPlayerItem( this )) { AttachToPlayer( pPlayer ); AvHPlayer* thePlayer = dynamic_cast(pPlayer); if(thePlayer && thePlayer->GetIsAlien()) { EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2-a.wav", 1, ATTN_NORM); } else { EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); } } SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen? // Haven't had the crash but adding this in case due to weapons being able to turn to SOLID_TRIGGER before touching the ground with 2021 weapon collision fix. // https://github.com/ValveSoftware/halflife/pull/1599 // If the item is falling and its Think remains FallItem after the player picks it up, // then after the item touches the ground its Touch will be set back to DefaultTouch, // so the player will pick it up again, this time Kill-ing the item (since we already have it in the inventory), // which will make the pointer bad and crash the game. if (m_pfnThink == &CBasePlayerItem::FallThink) SetThink(NULL); } BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) { if ( !isPredicted ) { return ( attack_time <= curtime ) ? TRUE : FALSE; } else { return ( attack_time <= 0.0 ) ? TRUE : FALSE; } } void CBasePlayerWeapon::ItemPostFrame( void ) { bool theAttackPressed = (m_pPlayer->pev->button & IN_ATTACK) && !(m_pPlayer->pev->button & IN_ATTACK2); bool theWeaponPrimes = (this->GetWeaponPrimeTime() > 0.0f); bool theWeaponIsPriming = this->GetIsWeaponPriming(); bool theWeaponIsPrimed = this->GetIsWeaponPrimed(); if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) ) { // complete the reload. int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); // Add them to the clip m_iClip += j; m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; m_pPlayer->TabulateAmmo(); m_fInReload = FALSE; } /* // +movement: Removed case for +attack2 since it's used for movement abilities if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) ) { if (m_pPlayer->GetCanUseWeapon()) { if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] ) { m_fFireOnEmpty = TRUE; } m_pPlayer->TabulateAmmo(); SecondaryAttack(); m_pPlayer->pev->button &= ~IN_ATTACK2; } } else */ if ( theAttackPressed && m_pPlayer->GetCanUseWeapon()) { if ((m_fInSpecialReload == 1 || m_fInSpecialReload == 2) && m_iClip != 0 && (CVAR_GET_FLOAT("sv_nsversion") > 322.0f)) { m_fInSpecialReload = 3; Reload(); } else if (CanAttack(m_flNextPrimaryAttack, gpGlobals->time, UseDecrement())) { if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) { m_fFireOnEmpty = TRUE; } m_pPlayer->TabulateAmmo(); PrimaryAttack(); } } else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) { if (m_pPlayer->GetCanUseWeapon()) { // reload when reload is pressed, or if no buttons are down and weapon is empty. Reload(); } } // +movement: Removed case for +attack2 else if ( !(m_pPlayer->pev->button & (IN_ATTACK /* |IN_ATTACK2 */) ) ) { if (m_pPlayer->GetCanUseWeapon()) { // no fire buttons down m_fFireOnEmpty = FALSE; if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) { // weapon isn't useable, switch. if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) ) { m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3; return; } } else { // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) { if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) { Reload(); return; } } } WeaponIdle( ); } return; } // catch all if ( ShouldWeaponIdle() ) { WeaponIdle(); } } void CBasePlayerItem::DestroyItem( void ) { //KGP: this is a virtual function call for a reason... // it had been replaced with the contents of // CBasePlayerItem::VirtualDestroyItem, ignoring // AvHBasePlayerWeapon::VirtualDestroyItem in 3.01 this->VirtualDestroyItem(); } void CBasePlayerItem::VirtualDestroyItem( void ) { if ( m_pPlayer ) { // if attached to a player, remove. m_pPlayer->RemovePlayerItem( this ); } Kill( ); } int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer ) { m_pPlayer = pPlayer; pPlayer->m_iHideHUD &= ~HIDEHUD_WEAPONS; return TRUE; } void CBasePlayerItem::Drop( void ) { SetTouch( NULL ); SetThink(&CBasePlayerItem::SUB_Remove); pev->nextthink = gpGlobals->time + .1; } void CBasePlayerItem::Kill( void ) { SetTouch( NULL ); SetThink(&CBasePlayerItem::SUB_Remove); pev->nextthink = gpGlobals->time + .1; } void CBasePlayerItem::Holster( int skiplocal /* = 0 */ ) { m_pPlayer->pev->viewmodel = 0; m_pPlayer->pev->weaponmodel = 0; } void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) { pev->movetype = MOVETYPE_FOLLOW; pev->solid = SOLID_NOT; pev->aiment = pPlayer->edict(); pev->effects = EF_NODRAW; // ?? pev->modelindex = 0;// server won't send down to clients if modelindex == 0 pev->model = iStringNull; pev->owner = pPlayer->edict(); pev->nextthink = gpGlobals->time + .1; SetTouch( NULL ); } void CBasePlayerItem::Spawn() { CBaseAnimating::Spawn(); } // CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) { if ( m_iDefaultAmmo ) { return ExtractAmmo( (CBasePlayerWeapon *)pOriginal ); } else { // a dead player dropped this. return ExtractClipAmmo( (CBasePlayerWeapon *)pOriginal ); } } int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) { int bResult = CBasePlayerItem::AddToPlayer( pPlayer ); pPlayer->pev->weapons |= (1<GetAmmoIndex( pszAmmo1() ); m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() ); } if (bResult) return AddWeapon( ); return FALSE; } int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) { BOOL bSend = FALSE; int state = 0; // KGP: folded m_iEnabled into the state value and converted it to a proper bitfield. if( pPlayer->m_pActiveItem == this ) { state |= WEAPON_IS_CURRENT; } if( pPlayer->m_fOnTarget ) { state |= WEAPON_ON_TARGET; } if( m_iEnabled ) { state |= WEAPON_IS_ENABLED; } // Forcing send of all data! if ( !pPlayer->m_fWeapon ) { bSend = TRUE; } // This is the current or last weapon, so the state will need to be updated if ( this == pPlayer->m_pActiveItem || this == pPlayer->m_pClientActiveItem ) { if ( pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem ) { bSend = TRUE; } } // If the ammo, state, or fov has changed, update the weapon if ( m_iClip != m_iClientClip || state != m_iClientWeaponState || pPlayer->m_iFOV != pPlayer->m_iClientFOV ) { bSend = TRUE; } if (m_iId == 22 || m_iId == 11 || m_iId == 21) gCanMove[pPlayer->entindex() - 1] = m_iEnabled; if ( bSend ) { NetMsg_CurWeapon( pPlayer->pev, state, m_iId, m_iClip ); m_iClientClip = m_iClip; m_iClientWeaponState = state; pPlayer->m_fWeapon = TRUE; } if ( m_pNext ) m_pNext->UpdateClientData( pPlayer ); return 1; } void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) { if(iAnim >= 0) { if ( UseDecrement() ) skiplocal = 1; else skiplocal = 0; m_pPlayer->pev->weaponanim = iAnim; if ( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) ) return; MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev ); WRITE_BYTE( iAnim ); // sequence number WRITE_BYTE( pev->body ); // weaponmodel bodygroup. MESSAGE_END(); } } BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ) { int iIdAmmo; if (iMaxClip < 1) { m_iClip = -1; iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); } else if (m_iClip == 0) { int i; i = min( m_iClip + iCount, iMaxClip ) - m_iClip; m_iClip += i; iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry ); } else { iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); } // m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = iMaxCarry; // hack for testing if (iIdAmmo > 0) { m_iPrimaryAmmoType = iIdAmmo; if (m_pPlayer->HasPlayerItem( this ) ) { // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. // if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us. EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); } } return iIdAmmo > 0 ? TRUE : FALSE; } BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax ) { int iIdAmmo; iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMax ); //m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] = iMax; // hack for testing if (iIdAmmo > 0) { m_iSecondaryAmmoType = iIdAmmo; EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); } return iIdAmmo > 0 ? TRUE : FALSE; } //========================================================= // IsUseable - this function determines whether or not a // weapon is useable by the player in its current state. // (does it have ammo loaded? do I have any ammo for the // weapon?, etc) //========================================================= BOOL CBasePlayerWeapon :: IsUseable( void ) { if ( m_iClip <= 0 ) { // This looks like a nasty bug Valve didn't notice ASSERT(PrimaryAmmoIndex() >= 0); if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 ) { // clip is empty (or nonexistant) and the player has no more ammo of this type. return FALSE; } } return TRUE; } BOOL CBasePlayerWeapon :: CanDeploy( void ) { BOOL bHasAmmo = 0; // All weapons can always deploy. Once fire is hit, it checks if it's out of ammo. If so, the weapon switches. // This is needed so ammo packs function correctly, and it also feels more responsive. // if ( !pszAmmo1() ) // { // // this weapon doesn't use ammo, can always deploy. // return TRUE; // } // // if ( pszAmmo1() ) // { // bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0); // } // if ( pszAmmo2() ) // { // bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0); // } // if (m_iClip > 0) // { // bHasAmmo |= 1; // } // if (!bHasAmmo) // { // return FALSE; // } return TRUE; } BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body) { if (!CanDeploy( )) return FALSE; m_pPlayer->TabulateAmmo(); m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); SendWeaponAnim( iAnim, skiplocal, body ); // Set the player animation as well //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval; return TRUE; } BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body) { if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) return FALSE; int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); if (j == 0) return FALSE; m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; //!!UNDONE -- reload sound goes here !!! //SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); m_fInReload = TRUE; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; return TRUE; } BOOL CBasePlayerWeapon :: PlayEmptySound( void ) { if (m_iPlayEmptySound) { // EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); int flags = FEV_NOTHOST; PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), gEmptySoundEventID, 0.0, (float *)&(m_pPlayer->pev->origin), (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); m_iPlayEmptySound = 0; return 0; } return 0; } void CBasePlayerWeapon :: ResetEmptySound( void ) { m_iPlayEmptySound = 1; } //========================================================= //========================================================= int CBasePlayerWeapon::PrimaryAmmoIndex( void ) { return m_iPrimaryAmmoType; } //========================================================= //========================================================= int CBasePlayerWeapon::SecondaryAmmoIndex( void ) { return -1; } void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) { m_fInReload = FALSE; // cancel any reload in progress. m_pPlayer->pev->viewmodel = 0; m_pPlayer->pev->weaponmodel = 0; } void CBasePlayerAmmo::Spawn( void ) { pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); UTIL_SetOrigin( pev, pev->origin ); SetTouch(&CBasePlayerAmmo::DefaultTouch ); } CBaseEntity* CBasePlayerAmmo::Respawn( void ) { pev->effects |= EF_NODRAW; SetTouch( NULL ); UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn. SetThink(&CBasePlayerAmmo::Materialize ); pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this ); return this; } void CBasePlayerAmmo::Materialize( void ) { if ( pev->effects & EF_NODRAW ) { // changing from invisible state to visible. EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); pev->effects &= ~EF_NODRAW; pev->effects |= EF_MUZZLEFLASH; } SetTouch(&CBasePlayerAmmo::DefaultTouch ); } void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) { if ( !pOther->IsPlayer() ) { return; } if (AddAmmo( pOther )) { if ( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES ) { Respawn(); } else { SetTouch( NULL ); SetThink(&CBasePlayerAmmo::SUB_Remove); pev->nextthink = gpGlobals->time + .1; } } else if (gEvilImpulse101) { // evil impulse 101 hack, kill always SetTouch( NULL ); SetThink(&CBasePlayerAmmo::SUB_Remove); pev->nextthink = gpGlobals->time + .1; } } //========================================================= // called by the new item with the existing item as parameter // // if we call ExtractAmmo(), it's because the player is picking up this type of weapon for // the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it. // if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in // the weapon clip comes along. //========================================================= int CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon ) { int iReturn; if ( pszAmmo1() != NULL ) { // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, // we only get the ammo in the weapon's clip, which is what we want. iReturn = pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() ); m_iDefaultAmmo = 0; } if ( pszAmmo2() != NULL ) { iReturn = pWeapon->AddSecondaryAmmo( 0, (char *)pszAmmo2(), iMaxAmmo2() ); } return iReturn; } //========================================================= // called by the new item's class with the existing item as parameter //========================================================= int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) { int iAmmo; if ( m_iClip == WEAPON_NOCLIP ) { iAmmo = 0;// guns with no clips always come empty if they are second-hand } else { iAmmo = m_iClip; } return pWeapon->m_pPlayer->GiveAmmo( iAmmo, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType } //========================================================= // RetireWeapon - no more ammo for this gun, put it away. //========================================================= void CBasePlayerWeapon::RetireWeapon( void ) { // first, no viewmodel at all. m_pPlayer->pev->viewmodel = iStringNull; m_pPlayer->pev->weaponmodel = iStringNull; //m_pPlayer->pev->viewmodelindex = NULL; g_pGameRules->GetNextBestWeapon( m_pPlayer, this ); } //********************************************************* // weaponbox code: //********************************************************* LINK_ENTITY_TO_CLASS( weaponbox, CWeaponBox ); TYPEDESCRIPTION CWeaponBox::m_SaveData[] = { DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ), DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity ); //========================================================= // //========================================================= void CWeaponBox::Precache( void ) { PRECACHE_UNMODIFIED_MODEL("models/w_weaponbox.mdl"); } //========================================================= //========================================================= void CWeaponBox :: KeyValue( KeyValueData *pkvd ) { if ( m_cAmmoTypes < MAX_AMMO_SLOTS ) { PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) ); m_cAmmoTypes++;// count this new ammo type. pkvd->fHandled = TRUE; } else { ALERT ( at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS ); } } //========================================================= // CWeaponBox - Spawn //========================================================= void CWeaponBox::Spawn( void ) { Precache( ); pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; UTIL_SetSize( pev, g_vecZero, g_vecZero ); SET_MODEL( ENT(pev), "models/w_weaponbox.mdl"); } //========================================================= // CWeaponBox - Kill - the think function that removes the // box from the world. //========================================================= void CWeaponBox::Kill( void ) { CBasePlayerItem *pWeapon; int i; // destroy the weapons for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pWeapon = m_rgpPlayerItems[ i ]; while ( pWeapon ) { pWeapon->SetThink(&CWeaponBox::SUB_Remove); pWeapon->pev->nextthink = gpGlobals->time + 0.1; pWeapon = pWeapon->m_pNext; } } // remove the box UTIL_Remove( this ); } //========================================================= // CWeaponBox - Touch: try to add my contents to the toucher // if the toucher is a player. //========================================================= void CWeaponBox::Touch( CBaseEntity *pOther ) { if ( !(pev->flags & FL_ONGROUND ) ) { return; } if ( !pOther->IsPlayer() ) { // only players may touch a weaponbox. return; } if ( !pOther->IsAlive() ) { // no dead guys. return; } CBasePlayer *pPlayer = (CBasePlayer *)pOther; int i; // dole out ammo for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) { if ( !FStringNull( m_rgiszAmmo[ i ] ) ) { // there's some ammo of this type. pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ), MaxAmmoCarry( m_rgiszAmmo[ i ] ) ); //ALERT ( at_console, "Gave %d rounds of %s\n", m_rgAmmo[i], STRING(m_rgiszAmmo[i]) ); // now empty the ammo from the weaponbox since we just gave it to the player m_rgiszAmmo[ i ] = iStringNull; m_rgAmmo[ i ] = 0; } } // go through my weapons and try to give the usable ones to the player. // it's important the the player be given ammo first, so the weapons code doesn't refuse // to deploy a better weapon that the player may pick up because he has no ammo for it. for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { if ( m_rgpPlayerItems[ i ] ) { CBasePlayerItem *pItem; // have at least one weapon in this slot while ( m_rgpPlayerItems[ i ] ) { //ALERT ( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[ i ]->pev->classname ) ); pItem = m_rgpPlayerItems[ i ]; m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box if ( pPlayer->AddPlayerItem( pItem ) ) { pItem->AttachToPlayer( pPlayer ); } } } } EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM ); SetTouch(NULL); UTIL_Remove(this); } //========================================================= // CWeaponBox - PackWeapon: Add this weapon to the box //========================================================= BOOL CWeaponBox::PackWeapon( CBasePlayerItem *pWeapon ) { // is one of these weapons already packed in this box? if ( HasWeapon( pWeapon ) ) { return FALSE;// box can only hold one of each weapon type } if ( pWeapon->m_pPlayer ) { if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) ) { // failed to unhook the weapon from the player! return FALSE; } } int iWeaponSlot = pWeapon->iItemSlot(); if ( m_rgpPlayerItems[ iWeaponSlot ] ) { // there's already one weapon in this slot, so link this into the slot's column pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ]; m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; } else { // first weapon we have for this slot m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; pWeapon->m_pNext = NULL; } pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn pWeapon->pev->movetype = MOVETYPE_NONE; pWeapon->pev->solid = SOLID_NOT; pWeapon->pev->effects = EF_NODRAW; pWeapon->pev->modelindex = 0; pWeapon->pev->model = iStringNull; pWeapon->pev->owner = edict(); pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc. pWeapon->SetTouch( NULL ); pWeapon->m_pPlayer = NULL; //ALERT ( at_console, "packed %s\n", STRING(pWeapon->pev->classname) ); return TRUE; } //========================================================= // CWeaponBox - PackAmmo //========================================================= BOOL CWeaponBox::PackAmmo( int iszName, int iCount ) { int iMaxCarry; if ( FStringNull( iszName ) ) { // error here ALERT ( at_console, "NULL String in PackAmmo!\n" ); return FALSE; } iMaxCarry = MaxAmmoCarry( iszName ); if ( iMaxCarry != -1 && iCount > 0 ) { //ALERT ( at_console, "Packed %d rounds of %s\n", iCount, STRING(iszName) ); GiveAmmo( iCount, (char *)STRING( iszName ), iMaxCarry ); return TRUE; } return FALSE; } //========================================================= // CWeaponBox - GiveAmmo //========================================================= int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NULL*/ ) { int i; for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++) { if (stricmp( szName, STRING( m_rgiszAmmo[i])) == 0) { if (pIndex) *pIndex = i; int iAdd = min( iCount, iMax - m_rgAmmo[i]); if (iCount == 0 || iAdd > 0) { m_rgAmmo[i] += iAdd; return i; } return -1; } } if (i < MAX_AMMO_SLOTS) { if (pIndex) *pIndex = i; m_rgiszAmmo[i] = MAKE_STRING( szName ); m_rgAmmo[i] = iCount; return i; } ALERT( at_console, "out of named ammo slots\n"); return i; } //========================================================= // CWeaponBox::HasWeapon - is a weapon of this type already // packed in this box? //========================================================= BOOL CWeaponBox::HasWeapon( CBasePlayerItem *pCheckItem ) { CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; while (pItem) { if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) { return TRUE; } pItem = pItem->m_pNext; } return FALSE; } //========================================================= // CWeaponBox::IsEmpty - is there anything in this box? //========================================================= BOOL CWeaponBox::IsEmpty( void ) { int i; for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { if ( m_rgpPlayerItems[ i ] ) { return FALSE; } } for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) { if ( !FStringNull( m_rgiszAmmo[ i ] ) ) { // still have a bit of this type of ammo return FALSE; } } return TRUE; } //========================================================= //========================================================= void CWeaponBox::SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector(-16, -16, 0); pev->absmax = pev->origin + Vector(16, 16, 16); } void CBasePlayerWeapon::PrintState( void ) { ALERT( at_console, "primary: %f\n", m_flNextPrimaryAttack ); ALERT( at_console, "idle : %f\n", m_flTimeWeaponIdle ); // ALERT( at_console, "nextrl : %f\n", m_flNextReload ); // ALERT( at_console, "nextpum: %f\n", m_flPumpTime ); // ALERT( at_console, "m_frt : %f\n", m_fReloadTime ); ALERT( at_console, "m_finre: %i\n", m_fInReload ); // ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload ); ALERT( at_console, "m_iclip: %i\n", m_iClip ); } TYPEDESCRIPTION CRpg::m_SaveData[] = { DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon ); TYPEDESCRIPTION CRpgRocket::m_SaveData[] = { DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ), }; IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); TYPEDESCRIPTION CShotgun::m_SaveData[] = { DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ), DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), // DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ), DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ), }; IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon ); TYPEDESCRIPTION CGauss::m_SaveData[] = { DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ), // DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ), // DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ), // DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ), DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ), }; IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon ); TYPEDESCRIPTION CEgon::m_SaveData[] = { // DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ), // DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ), // DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ), DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ), DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ), DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ), DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ), DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ), }; IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon ); TYPEDESCRIPTION CSatchel::m_SaveData[] = { DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon );