weapon.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. // ----------------------------------------------------------------------------
  23. // This file contains Weapon and Ammo Class/"namespace" helper methods as well
  24. // as hooks into the inventory system. These functions are not attached to a
  25. // specific C++ class or datablock, but define a set of methods which are part
  26. // of dynamic namespaces "class". The Items include these namespaces into their
  27. // scope using the ItemData and ItemImageData "className" variable.
  28. // ----------------------------------------------------------------------------
  29. // All ShapeBase images are mounted into one of 8 slots on a shape. This weapon
  30. // system assumes all primary weapons are mounted into this specified slot:
  31. $WeaponSlot = 0;
  32. //-----------------------------------------------------------------------------
  33. // Weapon Class
  34. //-----------------------------------------------------------------------------
  35. function Weapon::onUse(%data, %obj)
  36. {
  37. // Default behavior for all weapons is to mount it into the object's weapon
  38. // slot, which is currently assumed to be slot 0
  39. if (%obj.getMountedImage($WeaponSlot) != %data.image.getId())
  40. {
  41. serverPlay3D(WeaponUseSound, %obj.getTransform());
  42. %obj.mountImage(%data.image, $WeaponSlot);
  43. if (%obj.client)
  44. {
  45. if (%data.description !$= "")
  46. messageClient(%obj.client, 'MsgWeaponUsed', '\c0%1 selected.', %data.description);
  47. else
  48. messageClient(%obj.client, 'MsgWeaponUsed', '\c0Weapon selected');
  49. }
  50. // If this is a Player class object then allow the weapon to modify allowed poses
  51. if (%obj.isInNamespaceHierarchy("Player"))
  52. {
  53. // Start by allowing everything
  54. %obj.allowAllPoses();
  55. // Now see what isn't allowed by the weapon
  56. %image = %data.image;
  57. if (%image.jumpingDisallowed)
  58. %obj.allowJumping(false);
  59. if (%image.jetJumpingDisallowed)
  60. %obj.allowJetJumping(false);
  61. if (%image.sprintDisallowed)
  62. %obj.allowSprinting(false);
  63. if (%image.crouchDisallowed)
  64. %obj.allowCrouching(false);
  65. if (%image.proneDisallowed)
  66. %obj.allowProne(false);
  67. if (%image.swimmingDisallowed)
  68. %obj.allowSwimming(false);
  69. }
  70. }
  71. }
  72. function Weapon::onPickup(%this, %obj, %shape, %amount)
  73. {
  74. // The parent Item method performs the actual pickup.
  75. // For player's we automatically use the weapon if the
  76. // player does not already have one in hand.
  77. if (Parent::onPickup(%this, %obj, %shape, %amount))
  78. {
  79. serverPlay3D(WeaponPickupSound, %shape.getTransform());
  80. if (%shape.getClassName() $= "Player" && %shape.getMountedImage($WeaponSlot) == 0)
  81. %shape.use(%this);
  82. }
  83. }
  84. function Weapon::onInventory(%this, %obj, %amount)
  85. {
  86. // Weapon inventory has changed, make sure there are no weapons
  87. // of this type mounted if there are none left in inventory.
  88. if (!%amount && (%slot = %obj.getMountSlot(%this.image)) != -1)
  89. %obj.unmountImage(%slot);
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Weapon Image Class
  93. //-----------------------------------------------------------------------------
  94. function WeaponImage::onMount(%this, %obj, %slot)
  95. {
  96. // Images assume a false ammo state on load. We need to
  97. // set the state according to the current inventory.
  98. if(%this.isField("clip"))
  99. {
  100. // Use the clip system for this weapon. Check if the player already has
  101. // some ammo in a clip.
  102. if (%obj.getInventory(%this.ammo))
  103. {
  104. %obj.setImageAmmo(%slot, true);
  105. %currentAmmo = %obj.getInventory(%this.ammo);
  106. }
  107. else if(%obj.getInventory(%this.clip) > 0)
  108. {
  109. // Fill the weapon up from the first clip
  110. %obj.setInventory(%this.ammo, %this.ammo.maxInventory);
  111. %obj.setImageAmmo(%slot, true);
  112. // Add any spare ammo that may be "in the player's pocket"
  113. %currentAmmo = %this.ammo.maxInventory;
  114. %amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName());
  115. }
  116. else
  117. {
  118. %currentAmmo = 0 + %obj.getFieldValue( "remaining" @ %this.ammo.getName());
  119. }
  120. %amountInClips = %obj.getInventory(%this.clip);
  121. %amountInClips *= %this.ammo.maxInventory;
  122. if (%obj.client !$= "" && !%obj.isAiControlled)
  123. %obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %amountInClips);
  124. }
  125. else if(%this.ammo !$= "")
  126. {
  127. // Use the ammo pool system for this weapon
  128. if (%obj.getInventory(%this.ammo))
  129. {
  130. %obj.setImageAmmo(%slot, true);
  131. %currentAmmo = %obj.getInventory(%this.ammo);
  132. }
  133. else
  134. %currentAmmo = 0;
  135. if (%obj.client !$= "" && !%obj.isAiControlled)
  136. %obj.client.RefreshWeaponHud( 1, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %currentAmmo );
  137. }
  138. }
  139. function WeaponImage::onUnmount(%this, %obj, %slot)
  140. {
  141. if (%obj.client !$= "" && !%obj.isAiControlled)
  142. %obj.client.RefreshWeaponHud(0, "", "");
  143. }
  144. // ----------------------------------------------------------------------------
  145. // A "generic" weaponimage onFire handler for most weapons. Can be overridden
  146. // with an appropriate namespace method for any weapon that requires a custom
  147. // firing solution.
  148. // projectileSpread is a dynamic property declared in the weaponImage datablock
  149. // for those weapons in which bullet skew is desired. Must be greater than 0,
  150. // otherwise the projectile goes straight ahead as normal. lower values give
  151. // greater accuracy, higher values increase the spread pattern.
  152. // ----------------------------------------------------------------------------
  153. function WeaponImage::onFire(%this, %obj, %slot)
  154. {
  155. //echo("\c4WeaponImage::onFire( "@%this.getName()@", "@%obj.client.nameBase@", "@%slot@" )");
  156. // Make sure we have valid data
  157. if (!isObject(%this.projectile))
  158. {
  159. error("WeaponImage::onFire() - Invalid projectile datablock");
  160. return;
  161. }
  162. // Decrement inventory ammo. The image's ammo state is updated
  163. // automatically by the ammo inventory hooks.
  164. if ( !%this.infiniteAmmo )
  165. %obj.decInventory(%this.ammo, 1);
  166. // Get the player's velocity, we'll then add it to that of the projectile
  167. %objectVelocity = %obj.getVelocity();
  168. %numProjectiles = %this.projectileNum;
  169. if (%numProjectiles == 0)
  170. %numProjectiles = 1;
  171. for (%i = 0; %i < %numProjectiles; %i++)
  172. {
  173. if (%this.projectileSpread)
  174. {
  175. // We'll need to "skew" this projectile a little bit. We start by
  176. // getting the straight ahead aiming point of the gun
  177. %vec = %obj.getMuzzleVector(%slot);
  178. // Then we'll create a spread matrix by randomly generating x, y, and z
  179. // points in a circle
  180. %matrix = "";
  181. for(%j = 0; %j < 3; %j++)
  182. %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.projectileSpread @ " ";
  183. %mat = MatrixCreateFromEuler(%matrix);
  184. // Which we'll use to alter the projectile's initial vector with
  185. %muzzleVector = MatrixMulVector(%mat, %vec);
  186. }
  187. else
  188. {
  189. // Weapon projectile doesn't have a spread factor so we fire it using
  190. // the straight ahead aiming point of the gun
  191. %muzzleVector = %obj.getMuzzleVector(%slot);
  192. }
  193. // Add player's velocity
  194. %muzzleVelocity = VectorAdd(
  195. VectorScale(%muzzleVector, %this.projectile.muzzleVelocity),
  196. VectorScale(%objectVelocity, %this.projectile.velInheritFactor));
  197. // Create the projectile object
  198. %p = new (%this.projectileType)()
  199. {
  200. dataBlock = %this.projectile;
  201. initialVelocity = %muzzleVelocity;
  202. initialPosition = %obj.getMuzzlePoint(%slot);
  203. sourceObject = %obj;
  204. sourceSlot = %slot;
  205. client = %obj.client;
  206. sourceClass = %obj.getClassName();
  207. };
  208. MissionCleanup.add(%p);
  209. }
  210. }
  211. // ----------------------------------------------------------------------------
  212. // A "generic" weaponimage onAltFire handler for most weapons. Can be
  213. // overridden with an appropriate namespace method for any weapon that requires
  214. // a custom firing solution.
  215. // ----------------------------------------------------------------------------
  216. function WeaponImage::onAltFire(%this, %obj, %slot)
  217. {
  218. //echo("\c4WeaponImage::onAltFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
  219. // Decrement inventory ammo. The image's ammo state is updated
  220. // automatically by the ammo inventory hooks.
  221. %obj.decInventory(%this.ammo, 1);
  222. // Get the player's velocity, we'll then add it to that of the projectile
  223. %objectVelocity = %obj.getVelocity();
  224. %numProjectiles = %this.altProjectileNum;
  225. if (%numProjectiles == 0)
  226. %numProjectiles = 1;
  227. for (%i = 0; %i < %numProjectiles; %i++)
  228. {
  229. if (%this.altProjectileSpread)
  230. {
  231. // We'll need to "skew" this projectile a little bit. We start by
  232. // getting the straight ahead aiming point of the gun
  233. %vec = %obj.getMuzzleVector(%slot);
  234. // Then we'll create a spread matrix by randomly generating x, y, and z
  235. // points in a circle
  236. %matrix = "";
  237. for(%i = 0; %i < 3; %i++)
  238. %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.altProjectileSpread @ " ";
  239. %mat = MatrixCreateFromEuler(%matrix);
  240. // Which we'll use to alter the projectile's initial vector with
  241. %muzzleVector = MatrixMulVector(%mat, %vec);
  242. }
  243. else
  244. {
  245. // Weapon projectile doesn't have a spread factor so we fire it using
  246. // the straight ahead aiming point of the gun.
  247. %muzzleVector = %obj.getMuzzleVector(%slot);
  248. }
  249. // Add player's velocity
  250. %muzzleVelocity = VectorAdd(
  251. VectorScale(%muzzleVector, %this.altProjectile.muzzleVelocity),
  252. VectorScale(%objectVelocity, %this.altProjectile.velInheritFactor));
  253. // Create the projectile object
  254. %p = new (%this.projectileType)()
  255. {
  256. dataBlock = %this.altProjectile;
  257. initialVelocity = %muzzleVelocity;
  258. initialPosition = %obj.getMuzzlePoint(%slot);
  259. sourceObject = %obj;
  260. sourceSlot = %slot;
  261. client = %obj.client;
  262. };
  263. MissionCleanup.add(%p);
  264. }
  265. }
  266. // ----------------------------------------------------------------------------
  267. // A "generic" weaponimage onWetFire handler for most weapons. Can be
  268. // overridden with an appropriate namespace method for any weapon that requires
  269. // a custom firing solution.
  270. // ----------------------------------------------------------------------------
  271. function WeaponImage::onWetFire(%this, %obj, %slot)
  272. {
  273. //echo("\c4WeaponImage::onWetFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
  274. // Decrement inventory ammo. The image's ammo state is updated
  275. // automatically by the ammo inventory hooks.
  276. %obj.decInventory(%this.ammo, 1);
  277. // Get the player's velocity, we'll then add it to that of the projectile
  278. %objectVelocity = %obj.getVelocity();
  279. %numProjectiles = %this.projectileNum;
  280. if (%numProjectiles == 0)
  281. %numProjectiles = 1;
  282. for (%i = 0; %i < %numProjectiles; %i++)
  283. {
  284. if (%this.wetProjectileSpread)
  285. {
  286. // We'll need to "skew" this projectile a little bit. We start by
  287. // getting the straight ahead aiming point of the gun
  288. %vec = %obj.getMuzzleVector(%slot);
  289. // Then we'll create a spread matrix by randomly generating x, y, and z
  290. // points in a circle
  291. %matrix = "";
  292. for(%j = 0; %j < 3; %j++)
  293. %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.wetProjectileSpread @ " ";
  294. %mat = MatrixCreateFromEuler(%matrix);
  295. // Which we'll use to alter the projectile's initial vector with
  296. %muzzleVector = MatrixMulVector(%mat, %vec);
  297. }
  298. else
  299. {
  300. // Weapon projectile doesn't have a spread factor so we fire it using
  301. // the straight ahead aiming point of the gun.
  302. %muzzleVector = %obj.getMuzzleVector(%slot);
  303. }
  304. // Add player's velocity
  305. %muzzleVelocity = VectorAdd(
  306. VectorScale(%muzzleVector, %this.wetProjectile.muzzleVelocity),
  307. VectorScale(%objectVelocity, %this.wetProjectile.velInheritFactor));
  308. // Create the projectile object
  309. %p = new (%this.projectileType)()
  310. {
  311. dataBlock = %this.wetProjectile;
  312. initialVelocity = %muzzleVelocity;
  313. initialPosition = %obj.getMuzzlePoint(%slot);
  314. sourceObject = %obj;
  315. sourceSlot = %slot;
  316. client = %obj.client;
  317. };
  318. MissionCleanup.add(%p);
  319. }
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Clip Management
  323. //-----------------------------------------------------------------------------
  324. function WeaponImage::onClipEmpty(%this, %obj, %slot)
  325. {
  326. //echo("WeaponImage::onClipEmpty: " SPC %this SPC %obj SPC %slot);
  327. // Attempt to automatically reload. Schedule this so it occurs
  328. // outside of the current state that called this method
  329. %this.schedule(0, "reloadAmmoClip", %obj, %slot);
  330. }
  331. function WeaponImage::reloadAmmoClip(%this, %obj, %slot)
  332. {
  333. //echo("WeaponImage::reloadAmmoClip: " SPC %this SPC %obj SPC %slot);
  334. // Make sure we're indeed the currect image on the given slot
  335. if (%this != %obj.getMountedImage(%slot))
  336. return;
  337. if ( %this.isField("clip") )
  338. {
  339. if (%obj.getInventory(%this.clip) > 0)
  340. {
  341. %obj.decInventory(%this.clip, 1);
  342. %obj.setInventory(%this.ammo, %this.ammo.maxInventory);
  343. %obj.setImageAmmo(%slot, true);
  344. }
  345. else
  346. {
  347. %amountInPocket = %obj.getFieldValue( "remaining" @ %this.ammo.getName());
  348. if ( %amountInPocket )
  349. {
  350. %obj.setFieldValue( "remaining" @ %this.ammo.getName(), 0);
  351. %obj.setInventory( %this.ammo, %amountInPocket );
  352. %obj.setImageAmmo( %slot, true );
  353. }
  354. }
  355. }
  356. }
  357. function WeaponImage::clearAmmoClip( %this, %obj, %slot )
  358. {
  359. //echo("WeaponImage::clearAmmoClip: " SPC %this SPC %obj SPC %slot);
  360. // if we're not empty put the remaining bullets from the current clip
  361. // in to the player's "pocket".
  362. if ( %this.isField( "clip" ) )
  363. {
  364. // Commenting out this line will use a "hard clip" system, where
  365. // A player will lose any ammo currently in the gun when reloading.
  366. %pocketAmount = %this.stashSpareAmmo( %obj );
  367. if ( %obj.getInventory( %this.clip ) > 0 || %pocketAmount != 0 )
  368. %obj.setImageAmmo(%slot, false);
  369. }
  370. }
  371. function WeaponImage::stashSpareAmmo( %this, %player )
  372. {
  373. // If the amount in our pocket plus what we are about to add from the clip
  374. // Is over a clip, add a clip to inventory and keep the remainder
  375. // on the player
  376. if (%player.getInventory( %this.ammo ) < %this.ammo.maxInventory )
  377. {
  378. %nameOfAmmoField = "remaining" @ %this.ammo.getName();
  379. %amountInPocket = %player.getFieldValue( %nameOfAmmoField );
  380. %amountInGun = %player.getInventory( %this.ammo );
  381. %combinedAmmo = %amountInGun + %amountInPocket;
  382. // Give the player another clip if the amount in our pocket + the
  383. // Amount in our gun is over the size of a clip.
  384. if ( %combinedAmmo >= %this.ammo.maxInventory )
  385. {
  386. %player.setFieldValue( %nameOfAmmoField, %combinedAmmo - %this.ammo.maxInventory );
  387. %player.incInventory( %this.clip, 1 );
  388. }
  389. else if ( %player.getInventory(%this.clip) > 0 )// Only put it back in our pocket if we have clips.
  390. %player.setFieldValue( %nameOfAmmoField, %combinedAmmo );
  391. return %player.getFieldValue( %nameOfAmmoField );
  392. }
  393. return 0;
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Clip Class
  397. //-----------------------------------------------------------------------------
  398. function AmmoClip::onPickup(%this, %obj, %shape, %amount)
  399. {
  400. // The parent Item method performs the actual pickup.
  401. if (Parent::onPickup(%this, %obj, %shape, %amount))
  402. serverPlay3D(AmmoPickupSound, %shape.getTransform());
  403. // The clip inventory state has changed, we need to update the
  404. // current mounted image using this clip to reflect the new state.
  405. if ((%image = %shape.getMountedImage($WeaponSlot)) > 0)
  406. {
  407. // Check if this weapon uses the clip we just picked up and if
  408. // there is no ammo.
  409. if (%image.isField("clip") && %image.clip.getId() == %this.getId())
  410. {
  411. %outOfAmmo = !%shape.getImageAmmo($WeaponSlot);
  412. %currentAmmo = %shape.getInventory(%image.ammo);
  413. if ( isObject( %image.clip ) )
  414. %amountInClips = %shape.getInventory(%image.clip);
  415. %amountInClips *= %image.ammo.maxInventory;
  416. %amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName() );
  417. %shape.client.setAmmoAmountHud(%currentAmmo, %amountInClips );
  418. if (%outOfAmmo)
  419. {
  420. %image.onClipEmpty(%shape, $WeaponSlot);
  421. }
  422. }
  423. }
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Ammmo Class
  427. //-----------------------------------------------------------------------------
  428. function Ammo::onPickup(%this, %obj, %shape, %amount)
  429. {
  430. // The parent Item method performs the actual pickup.
  431. if (Parent::onPickup(%this, %obj, %shape, %amount))
  432. serverPlay3D(AmmoPickupSound, %shape.getTransform());
  433. }
  434. function Ammo::onInventory(%this, %obj, %amount)
  435. {
  436. // The ammo inventory state has changed, we need to update any
  437. // mounted images using this ammo to reflect the new state.
  438. for (%i = 0; %i < 8; %i++)
  439. {
  440. if ((%image = %obj.getMountedImage(%i)) > 0)
  441. if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId())
  442. {
  443. %obj.setImageAmmo(%i, %amount != 0);
  444. %currentAmmo = %obj.getInventory(%this);
  445. if (%obj.getClassname() $= "Player")
  446. {
  447. if ( isObject( %this.clip ) )
  448. {
  449. %amountInClips = %obj.getInventory(%this.clip);
  450. %amountInClips *= %this.maxInventory;
  451. %amountInClips += %obj.getFieldValue( "remaining" @ %this.getName() );
  452. }
  453. else //Is a single fire weapon, like the grenade launcher.
  454. {
  455. %amountInClips = %currentAmmo;
  456. %currentAmmo = 1;
  457. }
  458. if (%obj.client !$= "" && !%obj.isAiControlled)
  459. %obj.client.setAmmoAmountHud(%currentAmmo, %amountInClips);
  460. }
  461. }
  462. }
  463. }
  464. // ----------------------------------------------------------------------------
  465. // Weapon cycling
  466. // ----------------------------------------------------------------------------
  467. function ShapeBase::clearWeaponCycle(%this)
  468. {
  469. %this.totalCycledWeapons = 0;
  470. }
  471. function ShapeBase::addToWeaponCycle(%this, %weapon)
  472. {
  473. %this.cycleWeapon[%this.totalCycledWeapons++ - 1] = %weapon;
  474. }
  475. function ShapeBase::cycleWeapon(%this, %direction)
  476. {
  477. // Can't cycle what we don't have
  478. if (%this.totalCycledWeapons == 0)
  479. return;
  480. // Find out the index of the current weapon, if any (not all
  481. // available weapons may be part of the cycle)
  482. %currentIndex = -1;
  483. if (%this.getMountedImage($WeaponSlot) != 0)
  484. {
  485. %curWeapon = %this.getMountedImage($WeaponSlot).item.getName();
  486. for (%i=0; %i<%this.totalCycledWeapons; %i++)
  487. {
  488. if (%this.cycleWeapon[%i] $= %curWeapon)
  489. {
  490. %currentIndex = %i;
  491. break;
  492. }
  493. }
  494. }
  495. // Get the next weapon index
  496. %nextIndex = 0;
  497. %dir = 1;
  498. if (%currentIndex != -1)
  499. {
  500. if (%direction $= "prev")
  501. {
  502. %dir = -1;
  503. %nextIndex = %currentIndex - 1;
  504. if (%nextIndex < 0)
  505. {
  506. // Wrap around to the end
  507. %nextIndex = %this.totalCycledWeapons - 1;
  508. }
  509. }
  510. else
  511. {
  512. %nextIndex = %currentIndex + 1;
  513. if (%nextIndex >= %this.totalCycledWeapons)
  514. {
  515. // Wrap back to the beginning
  516. %nextIndex = 0;
  517. }
  518. }
  519. }
  520. // We now need to check if the next index is a valid weapon. If not,
  521. // then continue to cycle to the next weapon, in the appropriate direction,
  522. // until one is found. If nothing is found, then do nothing.
  523. %found = false;
  524. for (%i=0; %i<%this.totalCycledWeapons; %i++)
  525. {
  526. %weapon = %this.cycleWeapon[%nextIndex];
  527. if (%weapon !$= "" && %this.hasInventory(%weapon) && %this.hasAmmo(%weapon))
  528. {
  529. // We've found out weapon
  530. %found = true;
  531. break;
  532. }
  533. %nextIndex = %nextIndex + %dir;
  534. if (%nextIndex < 0)
  535. {
  536. %nextIndex = %this.totalCycledWeapons - 1;
  537. }
  538. else if (%nextIndex >= %this.totalCycledWeapons)
  539. {
  540. %nextIndex = 0;
  541. }
  542. }
  543. if (%found)
  544. {
  545. %this.use(%this.cycleWeapon[%nextIndex]);
  546. }
  547. }