Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

663 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: This is the brickbat weapon
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "npcevent.h"
  11. #include "basehlcombatweapon.h"
  12. #include "basecombatcharacter.h"
  13. #include "ai_basenpc.h"
  14. #include "AI_Memory.h"
  15. #include "player.h"
  16. #include "gamerules.h" // For g_pGameRules
  17. #include "weapon_brickbat.h"
  18. #include "grenade_brickbat.h"
  19. #include "ammodef.h"
  20. #include "in_buttons.h"
  21. #include "game.h"
  22. #include "IEffects.h"
  23. #include "vstdlib/random.h"
  24. #include "baseviewmodel.h"
  25. #include "movevars_shared.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. extern ConVar sk_npc_dmg_brickbat;
  29. extern ConVar sk_plr_dmg_brickbat;
  30. struct BrickbatAmmo_s
  31. {
  32. const char *m_sClassName;
  33. int m_nAmmoType;
  34. int m_nMaxCarry;
  35. const char *m_sViewModel;
  36. const char *m_sWorldModel;
  37. };
  38. BrickbatAmmo_s BrickBatAmmoArray[NUM_BRICKBAT_AMMO_TYPES] =
  39. {
  40. { "grenade_rockbb", BRICKBAT_ROCK, 5, "models/weapons/v_bb_bottle.mdl", "models/props_junk/Rock001a.mdl" },
  41. { "grenade_beerbottle", BRICKBAT_BOTTLE, 3, "models/weapons/v_bb_bottle.mdl", "models/weapons/w_bb_bottle.mdl" },
  42. };
  43. IMPLEMENT_SERVERCLASS_ST(CWeaponBrickbat, DT_WeaponBrickbat)
  44. END_SEND_TABLE()
  45. //LINK_ENTITY_TO_CLASS( weapon_brickbat, CWeaponBrickbat );
  46. //PRECACHE_WEAPON_REGISTER(weapon_brickbat);
  47. acttable_t CWeaponBrickbat::m_acttable[] =
  48. {
  49. { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_THROW, true },
  50. };
  51. IMPLEMENT_ACTTABLE(CWeaponBrickbat);
  52. BEGIN_DATADESC( CWeaponBrickbat )
  53. DEFINE_FIELD( m_bNeedDraw, FIELD_BOOLEAN ),
  54. DEFINE_FIELD( m_bNeedThrow, FIELD_BOOLEAN ),
  55. DEFINE_FIELD( m_iThrowBits, FIELD_INTEGER ),
  56. DEFINE_FIELD( m_fNextThrowCheck, FIELD_TIME ),
  57. DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
  58. DEFINE_ARRAY( m_nAmmoCount, FIELD_INTEGER, NUM_BRICKBAT_AMMO_TYPES ),
  59. DEFINE_KEYFIELD( m_iCurrentAmmoType, FIELD_INTEGER, "BrickbatType" ),
  60. // Function Pointers
  61. DEFINE_FUNCTION( BrickbatTouch ),
  62. END_DATADESC()
  63. //------------------------------------------------------------------------------
  64. // Purpose :
  65. // Input :
  66. // Output :
  67. //------------------------------------------------------------------------------
  68. void CWeaponBrickbat::Precache( void )
  69. {
  70. for (int i=0;i<ARRAYSIZE(BrickBatAmmoArray);i++)
  71. {
  72. PrecacheModel(BrickBatAmmoArray[i].m_sWorldModel);
  73. PrecacheModel(BrickBatAmmoArray[i].m_sViewModel);
  74. }
  75. UTIL_PrecacheOther("grenade_molotov");
  76. BaseClass::Precache();
  77. }
  78. void CWeaponBrickbat::Spawn( void )
  79. {
  80. m_bNeedDraw = true;
  81. m_bNeedThrow = false;
  82. for (int i=0;i<NUM_BRICKBAT_AMMO_TYPES;i++)
  83. {
  84. m_nAmmoCount[i] = 0;
  85. }
  86. // Call base class first
  87. BaseClass::Spawn();
  88. // Deactivate the trigger bounds so we can pick it up with the physgun
  89. CollisionProp()->UseTriggerBounds( false );
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. const char *CWeaponBrickbat::GetViewModel( int viewmodelindex /*=0*/ )
  95. {
  96. return BrickBatAmmoArray[m_iCurrentAmmoType].m_sViewModel;
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose:
  100. //-----------------------------------------------------------------------------
  101. const char *CWeaponBrickbat::GetWorldModel( void )
  102. {
  103. return BrickBatAmmoArray[m_iCurrentAmmoType].m_sWorldModel;
  104. }
  105. //------------------------------------------------------------------------------
  106. // Purpose :
  107. // Input :
  108. // Output :
  109. //------------------------------------------------------------------------------
  110. bool CWeaponBrickbat::Deploy( void )
  111. {
  112. SetModel( GetViewModel() );
  113. m_bNeedDraw = false;
  114. m_bNeedThrow = false;
  115. return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
  116. }
  117. //------------------------------------------------------------------------------
  118. // Purpose : Override to use brickbats pickup touch function
  119. // Input :
  120. // Output :
  121. //------------------------------------------------------------------------------
  122. void CWeaponBrickbat::SetPickupTouch( void )
  123. {
  124. SetTouch( BrickbatTouch );
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Override so give correct ammo
  128. // Input : pOther - the entity that touched me
  129. // Output :
  130. //-----------------------------------------------------------------------------
  131. void CWeaponBrickbat::BrickbatTouch( CBaseEntity *pOther )
  132. {
  133. // ---------------------------------------------------
  134. // First give weapon to touching entity if allowed
  135. // Skip ammo given portion by setting clips to zero
  136. // and handle ammo giving here
  137. // ---------------------------------------------------
  138. BaseClass::DefaultTouch(pOther);
  139. //FIXME: This ammo handling code is a bit bogus, need a real solution if brickbats are going to live
  140. /*
  141. // ----------------------------------------------------
  142. // Give brickbat ammo if touching client
  143. // ----------------------------------------------------
  144. if (pOther->GetFlags() & FL_CLIENT)
  145. {
  146. CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pOther );
  147. // Exit if game rules say I can't have any more of this ammo type.
  148. if ( g_pGameRules->CanHaveAmmo( pBCC, m_iPrimaryAmmoType ) == false )
  149. return;
  150. // ------------------------------------------------
  151. // If already owned weapon of this type remove me
  152. // ------------------------------------------------
  153. CWeaponBrickbat* oldWeapon = (CWeaponBrickbat*)pBCC->Weapon_OwnsThisType( GetClassname() );
  154. // Remove physics object if is one
  155. VPhysicsDestroyObject();
  156. if ( ( oldWeapon != NULL ) && ( oldWeapon != this ) )
  157. {
  158. // Only pick up if not at max ammo amount
  159. if (oldWeapon->m_nAmmoCount[m_iCurrentAmmoType] < BrickBatAmmoArray[m_iCurrentAmmoType].m_nMaxCarry)
  160. {
  161. oldWeapon->m_nAmmoCount[m_iCurrentAmmoType]++;
  162. pBCC->GiveAmmo( 1, oldWeapon->m_iPrimaryAmmoType );
  163. UTIL_Remove( this );
  164. }
  165. }
  166. else
  167. {
  168. // Only pick up if not at max ammo amount
  169. if (m_nAmmoCount[m_iCurrentAmmoType] < BrickBatAmmoArray[m_iCurrentAmmoType].m_nMaxCarry)
  170. {
  171. m_nAmmoCount[m_iCurrentAmmoType]++;
  172. pBCC->GiveAmmo( 1, m_iPrimaryAmmoType );
  173. SetThink (NULL);
  174. }
  175. }
  176. // -----------------------------------------------------
  177. // Switch to this weapon if the only weapon I own
  178. // -----------------------------------------------------
  179. if (!pBCC->GetActiveWeapon() && pBCC->GetActiveWeapon() != this)
  180. {
  181. pBCC->Weapon_Switch(oldWeapon);
  182. }
  183. }
  184. */
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Gets event from anim stream and throws the object
  188. // Input :
  189. // Output :
  190. //-----------------------------------------------------------------------------
  191. void CWeaponBrickbat::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  192. {
  193. switch( pEvent->event )
  194. {
  195. case EVENT_WEAPON_THROW:
  196. {
  197. CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
  198. if (!pNPC)
  199. {
  200. return;
  201. }
  202. Vector vec_target = pNPC->GetEnemyLKP();
  203. // -----------------------------------------------------
  204. // Get position of throw
  205. // -----------------------------------------------------
  206. // If owner has a hand, set position to the hand bone position
  207. Vector launchPos;
  208. int iBIndex = pNPC->LookupBone("Bip01 R Hand");
  209. if (iBIndex != -1) {
  210. Vector origin;
  211. QAngle angles;
  212. pNPC->GetBonePosition( iBIndex, launchPos, angles);
  213. }
  214. // Otherwise just set to in front of the owner
  215. else {
  216. Vector vFacingDir = pNPC->BodyDirection2D( );
  217. vFacingDir = vFacingDir * 60.0;
  218. launchPos = pNPC->GetLocalOrigin()+vFacingDir;
  219. }
  220. ThrowBrickbat( launchPos, m_vecTossVelocity, sk_npc_dmg_brickbat.GetFloat());
  221. // Drop the weapon and remove as no more ammo
  222. pNPC->Weapon_Drop( this );
  223. UTIL_Remove( this );
  224. }
  225. break;
  226. default:
  227. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  228. break;
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. // Input :
  234. // Output :
  235. //-----------------------------------------------------------------------------
  236. bool CWeaponBrickbat::ObjectInWay( void )
  237. {
  238. CBaseCombatCharacter *pOwner = GetOwner();
  239. if (!pOwner)
  240. {
  241. return false;
  242. }
  243. Vector vecSrc = pOwner->Weapon_ShootPosition( );
  244. Vector vecAiming = pOwner->BodyDirection2D( );
  245. trace_t tr;
  246. Vector vecEnd = vecSrc + (vecAiming * 32);
  247. UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
  248. if (tr.fraction < 1.0)
  249. {
  250. // Don't block on a living creature
  251. if (tr.m_pEnt)
  252. {
  253. CBaseEntity *pEntity = tr.m_pEnt;
  254. CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
  255. if (pBCC)
  256. {
  257. return false;
  258. }
  259. }
  260. return true;
  261. }
  262. else
  263. {
  264. return false;
  265. }
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose: Override to allow throw w/o LOS
  269. // Input :
  270. // Output :
  271. //-----------------------------------------------------------------------------
  272. bool CWeaponBrickbat::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos,bool bSetConditions)
  273. {
  274. // <<TODO>> should test if can throw from present location here...
  275. return true;
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose: Override to check throw
  279. // Input :
  280. // Output :
  281. //-----------------------------------------------------------------------------
  282. int CWeaponBrickbat::WeaponRangeAttack1Condition( float flDot, float flDist )
  283. {
  284. // If things haven't changed too much since last time
  285. // just return that previously calculated value
  286. if (gpGlobals->curtime < m_fNextThrowCheck )
  287. {
  288. return m_iThrowBits;
  289. }
  290. if ( flDist < m_fMinRange1)
  291. {
  292. m_iThrowBits = COND_TOO_CLOSE_TO_ATTACK;
  293. }
  294. else if (flDist > m_fMaxRange1)
  295. {
  296. m_iThrowBits = COND_TOO_FAR_TO_ATTACK;
  297. }
  298. else if (flDot < 0.5)
  299. {
  300. m_iThrowBits = COND_NOT_FACING_ATTACK;
  301. }
  302. // If moving, can't throw.
  303. else if ( m_flGroundSpeed != 0 )
  304. {
  305. m_iThrowBits = COND_NONE;
  306. }
  307. else
  308. {
  309. // Ok we should check again as some time has passed
  310. // This function is only used by NPC's so we can cast to a Base Monster
  311. CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
  312. CBaseEntity *pEnemy = pNPC->GetEnemy();
  313. if (!pEnemy)
  314. {
  315. return COND_NONE;
  316. }
  317. // Get Enemy Position
  318. Vector vecTarget;
  319. pEnemy->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecTarget );
  320. // Get Toss Vector
  321. Vector throwStart = pNPC->Weapon_ShootPosition();
  322. Vector vecToss;
  323. CBaseEntity* pBlocker = NULL;
  324. float throwDist = (throwStart - vecTarget).Length();
  325. float fGravity = GetCurrentGravity();
  326. float throwLimit = pNPC->ThrowLimit(throwStart, vecTarget, fGravity, 35, WorldAlignMins(), WorldAlignMaxs(), pEnemy, &vecToss, &pBlocker);
  327. // If I can make the throw (or most of the throw)
  328. if (!throwLimit || (throwLimit != throwDist && throwLimit > 0.8*throwDist))
  329. {
  330. m_vecTossVelocity = vecToss;
  331. m_iThrowBits = COND_CAN_RANGE_ATTACK1;
  332. }
  333. else
  334. {
  335. m_iThrowBits = COND_NONE;
  336. }
  337. }
  338. // don't check again for a while.
  339. m_fNextThrowCheck = gpGlobals->curtime + 0.33; // 1/3 second.
  340. return m_iThrowBits;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. //
  345. //
  346. //-----------------------------------------------------------------------------
  347. void CWeaponBrickbat::ThrowBrickbat( Vector vecSrc, Vector vecVelocity, float damage)
  348. {
  349. CGrenade_Brickbat *pBrickbat = (CGrenade_Brickbat*)Create( BrickBatAmmoArray[m_iCurrentAmmoType].m_sClassName, vecSrc, vec3_angle, GetOwner() );
  350. if (!pBrickbat)
  351. {
  352. Msg("Brickbat type (%s) not defined!\n",BrickBatAmmoArray[m_iCurrentAmmoType].m_sClassName);
  353. return;
  354. }
  355. AngularImpulse vecAngVel;
  356. // Tumble through the air
  357. vecAngVel.x = random->RandomFloat ( -100, -500 );
  358. vecAngVel.z = random->RandomFloat ( -100, -500 );
  359. vecAngVel.y = random->RandomFloat ( -100, -500 );
  360. // If physically simulated
  361. IPhysicsObject *pPhysicsObject = pBrickbat->VPhysicsGetObject();
  362. if ( pPhysicsObject )
  363. {
  364. pPhysicsObject->AddVelocity( &vecVelocity, &vecAngVel );
  365. }
  366. // Otherwise
  367. else
  368. {
  369. pBrickbat->SetAbsVelocity( vecVelocity );
  370. QAngle angVel;
  371. AngularImpulseToQAngle( vecAngVel, angVel );
  372. pBrickbat->SetLocalAngularVelocity( angVel );
  373. }
  374. pBrickbat->SetThrower( GetOwner() );
  375. pBrickbat->SetOwnerEntity( ((CBaseEntity*)GetOwner()) );
  376. pBrickbat->SetDamage(damage);
  377. m_nAmmoCount[m_iCurrentAmmoType]--;
  378. m_bNeedThrow = false;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. //
  383. //
  384. //-----------------------------------------------------------------------------
  385. void CWeaponBrickbat::PrimaryAttack( void )
  386. {
  387. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  388. if (!pPlayer)
  389. {
  390. return;
  391. }
  392. SendWeaponAnim(ACT_VM_PULLBACK);
  393. // Don't fire again until fire animation has completed
  394. float flSequenceEndTime = gpGlobals->curtime + SequenceDuration();
  395. pPlayer->m_flNextAttack = m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
  396. m_bNeedThrow = true;
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose:
  400. //
  401. //
  402. //-----------------------------------------------------------------------------
  403. void CWeaponBrickbat::Throw( void )
  404. {
  405. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  406. if (!pPlayer)
  407. {
  408. return;
  409. }
  410. Vector vecSrc = pPlayer->WorldSpaceCenter();
  411. Vector vecFacing = pPlayer->BodyDirection3D( );
  412. vecSrc = vecSrc + vecFacing * 18.0;
  413. vecSrc.z += 24.0f;
  414. // Player may have turned to face a wall during the throw anim in which case
  415. // we don't want to throw the SLAM into the wall
  416. if (ObjectInWay())
  417. {
  418. vecSrc = pPlayer->WorldSpaceCenter() + vecFacing * 5.0;
  419. }
  420. Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
  421. vecAiming.z += 0.20; // Raise up so passes through reticle
  422. ThrowBrickbat(vecSrc, vecAiming*800, sk_plr_dmg_brickbat.GetFloat());
  423. pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
  424. SendWeaponAnim(ACT_VM_THROW);
  425. // Don't fire again until fire animation has completed
  426. float flSequenceEndTime = gpGlobals->curtime + SequenceDuration();
  427. pPlayer->m_flNextAttack = m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
  428. m_bNeedThrow = false;
  429. m_bNeedDraw = true;
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose:
  433. //
  434. //
  435. //-----------------------------------------------------------------------------
  436. void CWeaponBrickbat::SecondaryAttack( void )
  437. {
  438. int counter = 0;
  439. while (counter < NUM_BRICKBAT_AMMO_TYPES)
  440. {
  441. m_iCurrentAmmoType = ((++m_iCurrentAmmoType)%NUM_BRICKBAT_AMMO_TYPES);
  442. // If I've found a category with ammo stop looking
  443. if (m_nAmmoCount[m_iCurrentAmmoType] > 0)
  444. {
  445. DrawAmmo();
  446. return;
  447. }
  448. counter++;
  449. }
  450. // I'm out of all ammo types
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose:
  454. //
  455. //
  456. //-----------------------------------------------------------------------------
  457. void CWeaponBrickbat::DrawAmmo( void )
  458. {
  459. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  460. // -------------------------------------------
  461. // Make sure I have ammo of the current type
  462. // -------------------------------------------
  463. int counter = 0;
  464. while (m_nAmmoCount[m_iCurrentAmmoType] <=0)
  465. {
  466. m_iCurrentAmmoType = ((++m_iCurrentAmmoType)%NUM_BRICKBAT_AMMO_TYPES);
  467. counter++;
  468. // ----------------------------------------------------
  469. // No ammo of any types so drop the weapon and destroy
  470. // ----------------------------------------------------
  471. if (counter >= NUM_BRICKBAT_AMMO_TYPES)
  472. {
  473. pOwner->Weapon_Drop( this, NULL, NULL );
  474. UTIL_Remove(this);
  475. return;
  476. }
  477. }
  478. SetModel( BrickBatAmmoArray[m_iCurrentAmmoType].m_sViewModel);
  479. CBaseViewModel *vm = pOwner->GetViewModel();
  480. if ( vm )
  481. {
  482. vm->SetModel( BrickBatAmmoArray[m_iCurrentAmmoType].m_sViewModel );
  483. }
  484. //Msg("Drawing %s...\n",BrickBatAmmoArray[m_iCurrentAmmoType].m_sClassName);
  485. m_bNeedDraw = false;
  486. SendWeaponAnim(ACT_VM_DRAW);
  487. // Don't fire again until fire animation has completed
  488. float flSequenceEndTime = gpGlobals->curtime + SequenceDuration();
  489. pOwner->m_flNextAttack = m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Override so shotgun can do mulitple reloads in a row
  493. // Input :
  494. // Output :
  495. //-----------------------------------------------------------------------------
  496. void CWeaponBrickbat::ItemPostFrame( void )
  497. {
  498. /* HANDY FOR DEBUG
  499. for (int i=0;i<NUM_BRICKBAT_AMMO_TYPES;i++)
  500. {
  501. Msg("%i %s",m_nAmmoCount[i],BrickBatAmmoArray[i].m_sClassName);
  502. if (i==m_iCurrentAmmoType)
  503. {
  504. Msg("**");
  505. }
  506. Msg("\n");
  507. }
  508. */
  509. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  510. if (!pOwner)
  511. {
  512. return;
  513. }
  514. if (m_bNeedThrow)
  515. {
  516. Throw();
  517. }
  518. else if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
  519. {
  520. SecondaryAttack();
  521. }
  522. else if ((pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
  523. {
  524. // Uses secondary ammo only
  525. if (pOwner->GetAmmoCount(m_iPrimaryAmmoType))
  526. {
  527. PrimaryAttack();
  528. }
  529. }
  530. else if (m_bNeedDraw)
  531. {
  532. DrawAmmo();
  533. }
  534. else
  535. {
  536. SendWeaponAnim( ACT_VM_IDLE );
  537. //pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
  538. }
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Purpose:
  542. // Input :
  543. // Output :
  544. //-----------------------------------------------------------------------------
  545. void CWeaponBrickbat::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  546. {
  547. if ( info.GetDamageType() & DMG_BULLET)
  548. {
  549. if ( BrickBatAmmoArray[m_iCurrentAmmoType].m_nAmmoType == BRICKBAT_ROCK )
  550. {
  551. g_pEffects->Ricochet(ptr->endpos,ptr->plane.normal);
  552. }
  553. }
  554. BaseClass::TraceAttack( info, vecDir, ptr );
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: Constructor
  558. //-----------------------------------------------------------------------------
  559. CWeaponBrickbat::CWeaponBrickbat( void )
  560. {
  561. #ifdef _DEBUG
  562. m_vecTossVelocity.Init();
  563. #endif
  564. m_fMinRange1 = 200;
  565. m_fMaxRange1 = 1000;
  566. }