weapon.tscript 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  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. %muzzleVector = VectorScale(VectorNormalize(%muzzleVector), %this.projectileSpread *2);
  187. %muzzleVector = VectorAdd(%muzzleVector, %vec);
  188. }
  189. else
  190. {
  191. // Weapon projectile doesn't have a spread factor so we fire it using
  192. // the straight ahead aiming point of the gun
  193. %muzzleVector = %obj.getMuzzleVector(%slot);
  194. }
  195. // Add player's velocity
  196. %muzzleVelocity = VectorAdd(
  197. VectorScale(%muzzleVector, %this.projectile.muzzleVelocity),
  198. VectorScale(%objectVelocity, %this.projectile.velInheritFactor));
  199. // Create the projectile object
  200. %p = new (%this.projectileType)()
  201. {
  202. dataBlock = %this.projectile;
  203. initialVelocity = %muzzleVelocity;
  204. initialPosition = %obj.getMuzzlePoint(%slot);
  205. sourceObject = %obj;
  206. sourceSlot = %slot;
  207. client = %obj.client;
  208. sourceClass = %obj.getClassName();
  209. };
  210. MissionCleanup.add(%p);
  211. }
  212. }
  213. // ----------------------------------------------------------------------------
  214. // A "generic" weaponimage onAltFire handler for most weapons. Can be
  215. // overridden with an appropriate namespace method for any weapon that requires
  216. // a custom firing solution.
  217. // ----------------------------------------------------------------------------
  218. function WeaponImage::onAltFire(%this, %obj, %slot)
  219. {
  220. //echo("\c4WeaponImage::onAltFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
  221. // Decrement inventory ammo. The image's ammo state is updated
  222. // automatically by the ammo inventory hooks.
  223. %obj.decInventory(%this.ammo, 1);
  224. // Get the player's velocity, we'll then add it to that of the projectile
  225. %objectVelocity = %obj.getVelocity();
  226. %numProjectiles = %this.altProjectileNum;
  227. if (%numProjectiles == 0)
  228. %numProjectiles = 1;
  229. for (%i = 0; %i < %numProjectiles; %i++)
  230. {
  231. if (%this.altProjectileSpread)
  232. {
  233. // We'll need to "skew" this projectile a little bit. We start by
  234. // getting the straight ahead aiming point of the gun
  235. %vec = %obj.getMuzzleVector(%slot);
  236. // Then we'll create a spread matrix by randomly generating x, y, and z
  237. // points in a circle
  238. %matrix = "";
  239. for(%i = 0; %i < 3; %i++)
  240. %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.altProjectileSpread @ " ";
  241. %mat = MatrixCreateFromEuler(%matrix);
  242. // Which we'll use to alter the projectile's initial vector with
  243. %muzzleVector = MatrixMulVector(%mat, %vec);
  244. }
  245. else
  246. {
  247. // Weapon projectile doesn't have a spread factor so we fire it using
  248. // the straight ahead aiming point of the gun.
  249. %muzzleVector = %obj.getMuzzleVector(%slot);
  250. }
  251. // Add player's velocity
  252. %muzzleVelocity = VectorAdd(
  253. VectorScale(%muzzleVector, %this.altProjectile.muzzleVelocity),
  254. VectorScale(%objectVelocity, %this.altProjectile.velInheritFactor));
  255. // Create the projectile object
  256. %p = new (%this.projectileType)()
  257. {
  258. dataBlock = %this.altProjectile;
  259. initialVelocity = %muzzleVelocity;
  260. initialPosition = %obj.getMuzzlePoint(%slot);
  261. sourceObject = %obj;
  262. sourceSlot = %slot;
  263. client = %obj.client;
  264. };
  265. MissionCleanup.add(%p);
  266. }
  267. }
  268. // ----------------------------------------------------------------------------
  269. // A "generic" weaponimage onWetFire handler for most weapons. Can be
  270. // overridden with an appropriate namespace method for any weapon that requires
  271. // a custom firing solution.
  272. // ----------------------------------------------------------------------------
  273. function WeaponImage::onWetFire(%this, %obj, %slot)
  274. {
  275. //echo("\c4WeaponImage::onWetFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
  276. // Decrement inventory ammo. The image's ammo state is updated
  277. // automatically by the ammo inventory hooks.
  278. %obj.decInventory(%this.ammo, 1);
  279. // Get the player's velocity, we'll then add it to that of the projectile
  280. %objectVelocity = %obj.getVelocity();
  281. %numProjectiles = %this.projectileNum;
  282. if (%numProjectiles == 0)
  283. %numProjectiles = 1;
  284. for (%i = 0; %i < %numProjectiles; %i++)
  285. {
  286. if (%this.wetProjectileSpread)
  287. {
  288. // We'll need to "skew" this projectile a little bit. We start by
  289. // getting the straight ahead aiming point of the gun
  290. %vec = %obj.getMuzzleVector(%slot);
  291. // Then we'll create a spread matrix by randomly generating x, y, and z
  292. // points in a circle
  293. %matrix = "";
  294. for(%j = 0; %j < 3; %j++)
  295. %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.wetProjectileSpread @ " ";
  296. %mat = MatrixCreateFromEuler(%matrix);
  297. // Which we'll use to alter the projectile's initial vector with
  298. %muzzleVector = MatrixMulVector(%mat, %vec);
  299. }
  300. else
  301. {
  302. // Weapon projectile doesn't have a spread factor so we fire it using
  303. // the straight ahead aiming point of the gun.
  304. %muzzleVector = %obj.getMuzzleVector(%slot);
  305. }
  306. // Add player's velocity
  307. %muzzleVelocity = VectorAdd(
  308. VectorScale(%muzzleVector, %this.wetProjectile.muzzleVelocity),
  309. VectorScale(%objectVelocity, %this.wetProjectile.velInheritFactor));
  310. // Create the projectile object
  311. %p = new (%this.projectileType)()
  312. {
  313. dataBlock = %this.wetProjectile;
  314. initialVelocity = %muzzleVelocity;
  315. initialPosition = %obj.getMuzzlePoint(%slot);
  316. sourceObject = %obj;
  317. sourceSlot = %slot;
  318. client = %obj.client;
  319. };
  320. MissionCleanup.add(%p);
  321. }
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Clip Management
  325. //-----------------------------------------------------------------------------
  326. function WeaponImage::onClipEmpty(%this, %obj, %slot)
  327. {
  328. //echo("WeaponImage::onClipEmpty: " SPC %this SPC %obj SPC %slot);
  329. // Attempt to automatically reload. Schedule this so it occurs
  330. // outside of the current state that called this method
  331. %this.schedule(0, "reloadAmmoClip", %obj, %slot);
  332. }
  333. function WeaponImage::reloadAmmoClip(%this, %obj, %slot)
  334. {
  335. //echo("WeaponImage::reloadAmmoClip: " SPC %this SPC %obj SPC %slot);
  336. // Make sure we're indeed the currect image on the given slot
  337. if (%this != %obj.getMountedImage(%slot))
  338. return;
  339. if ( %this.isField("clip") )
  340. {
  341. if (%obj.getInventory(%this.clip) > 0)
  342. {
  343. %obj.decInventory(%this.clip, 1);
  344. %obj.setInventory(%this.ammo, %this.ammo.maxInventory);
  345. %obj.setImageAmmo(%slot, true);
  346. }
  347. else
  348. {
  349. %amountInPocket = %obj.getFieldValue( "remaining" @ %this.ammo.getName());
  350. if ( %amountInPocket )
  351. {
  352. %obj.setFieldValue( "remaining" @ %this.ammo.getName(), 0);
  353. %obj.setInventory( %this.ammo, %amountInPocket );
  354. %obj.setImageAmmo( %slot, true );
  355. }
  356. }
  357. }
  358. }
  359. function WeaponImage::clearAmmoClip( %this, %obj, %slot )
  360. {
  361. //echo("WeaponImage::clearAmmoClip: " SPC %this SPC %obj SPC %slot);
  362. // if we're not empty put the remaining bullets from the current clip
  363. // in to the player's "pocket".
  364. if ( %this.isField( "clip" ) )
  365. {
  366. // Commenting out this line will use a "hard clip" system, where
  367. // A player will lose any ammo currently in the gun when reloading.
  368. %pocketAmount = %this.stashSpareAmmo( %obj );
  369. if ( %obj.getInventory( %this.clip ) > 0 || %pocketAmount != 0 )
  370. %obj.setImageAmmo(%slot, false);
  371. }
  372. }
  373. function WeaponImage::stashSpareAmmo( %this, %player )
  374. {
  375. // If the amount in our pocket plus what we are about to add from the clip
  376. // Is over a clip, add a clip to inventory and keep the remainder
  377. // on the player
  378. if (%player.getInventory( %this.ammo ) < %this.ammo.maxInventory )
  379. {
  380. %nameOfAmmoField = "remaining" @ %this.ammo.getName();
  381. %amountInPocket = %player.getFieldValue( %nameOfAmmoField );
  382. %amountInGun = %player.getInventory( %this.ammo );
  383. %combinedAmmo = %amountInGun + %amountInPocket;
  384. // Give the player another clip if the amount in our pocket + the
  385. // Amount in our gun is over the size of a clip.
  386. if ( %combinedAmmo >= %this.ammo.maxInventory )
  387. {
  388. %player.setFieldValue( %nameOfAmmoField, %combinedAmmo - %this.ammo.maxInventory );
  389. %player.incInventory( %this.clip, 1 );
  390. }
  391. else if ( %player.getInventory(%this.clip) > 0 )// Only put it back in our pocket if we have clips.
  392. %player.setFieldValue( %nameOfAmmoField, %combinedAmmo );
  393. return %player.getFieldValue( %nameOfAmmoField );
  394. }
  395. return 0;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Clip Class
  399. //-----------------------------------------------------------------------------
  400. function AmmoClip::onPickup(%this, %obj, %shape, %amount)
  401. {
  402. // The parent Item method performs the actual pickup.
  403. if (Parent::onPickup(%this, %obj, %shape, %amount))
  404. serverPlay3D(AmmoPickupSound, %shape.getTransform());
  405. // The clip inventory state has changed, we need to update the
  406. // current mounted image using this clip to reflect the new state.
  407. if ((%image = %shape.getMountedImage($WeaponSlot)) > 0)
  408. {
  409. // Check if this weapon uses the clip we just picked up and if
  410. // there is no ammo.
  411. if (%image.isField("clip") && %image.clip.getId() == %this.getId())
  412. {
  413. %outOfAmmo = !%shape.getImageAmmo($WeaponSlot);
  414. %currentAmmo = %shape.getInventory(%image.ammo);
  415. if ( isObject( %image.clip ) )
  416. %amountInClips = %shape.getInventory(%image.clip);
  417. %amountInClips *= %image.ammo.maxInventory;
  418. %amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName() );
  419. %shape.client.setAmmoAmountHud(%currentAmmo, %amountInClips );
  420. if (%outOfAmmo)
  421. {
  422. %image.onClipEmpty(%shape, $WeaponSlot);
  423. }
  424. }
  425. }
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Ammmo Class
  429. //-----------------------------------------------------------------------------
  430. function Ammo::onPickup(%this, %obj, %shape, %amount)
  431. {
  432. // The parent Item method performs the actual pickup.
  433. if (Parent::onPickup(%this, %obj, %shape, %amount))
  434. serverPlay3D(AmmoPickupSound, %shape.getTransform());
  435. }
  436. function Ammo::onInventory(%this, %obj, %amount)
  437. {
  438. // The ammo inventory state has changed, we need to update any
  439. // mounted images using this ammo to reflect the new state.
  440. for (%i = 0; %i < 8; %i++)
  441. {
  442. if ((%image = %obj.getMountedImage(%i)) > 0)
  443. if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId())
  444. {
  445. %obj.setImageAmmo(%i, %amount != 0);
  446. %currentAmmo = %obj.getInventory(%this);
  447. if (%obj.getClassname() $= "Player")
  448. {
  449. if ( isObject( %this.clip ) )
  450. {
  451. %amountInClips = %obj.getInventory(%this.clip);
  452. %amountInClips *= %this.maxInventory;
  453. %amountInClips += %obj.getFieldValue( "remaining" @ %this.getName() );
  454. }
  455. else //Is a single fire weapon, like the grenade launcher.
  456. {
  457. %amountInClips = %currentAmmo;
  458. %currentAmmo = 1;
  459. }
  460. if (%obj.client !$= "" && !%obj.isAiControlled)
  461. %obj.client.setAmmoAmountHud(%currentAmmo, %amountInClips);
  462. }
  463. }
  464. }
  465. }
  466. // ----------------------------------------------------------------------------
  467. // Weapon cycling
  468. // ----------------------------------------------------------------------------
  469. function ShapeBase::clearWeaponCycle(%this)
  470. {
  471. %this.totalCycledWeapons = 0;
  472. }
  473. function ShapeBase::addToWeaponCycle(%this, %weapon)
  474. {
  475. %this.cycleWeapon[%this.totalCycledWeapons++ - 1] = %weapon;
  476. }
  477. function ShapeBase::cycleWeapon(%this, %direction)
  478. {
  479. // Can't cycle what we don't have
  480. if (%this.totalCycledWeapons == 0)
  481. return;
  482. // Find out the index of the current weapon, if any (not all
  483. // available weapons may be part of the cycle)
  484. %currentIndex = -1;
  485. if (%this.getMountedImage($WeaponSlot) != 0)
  486. {
  487. %curWeapon = %this.getMountedImage($WeaponSlot).item.getName();
  488. for (%i=0; %i<%this.totalCycledWeapons; %i++)
  489. {
  490. if (%this.cycleWeapon[%i] $= %curWeapon)
  491. {
  492. %currentIndex = %i;
  493. break;
  494. }
  495. }
  496. }
  497. // Get the next weapon index
  498. %nextIndex = 0;
  499. %dir = 1;
  500. if (%currentIndex != -1)
  501. {
  502. if (%direction $= "prev")
  503. {
  504. %dir = -1;
  505. %nextIndex = %currentIndex - 1;
  506. if (%nextIndex < 0)
  507. {
  508. // Wrap around to the end
  509. %nextIndex = %this.totalCycledWeapons - 1;
  510. }
  511. }
  512. else
  513. {
  514. %nextIndex = %currentIndex + 1;
  515. if (%nextIndex >= %this.totalCycledWeapons)
  516. {
  517. // Wrap back to the beginning
  518. %nextIndex = 0;
  519. }
  520. }
  521. }
  522. // We now need to check if the next index is a valid weapon. If not,
  523. // then continue to cycle to the next weapon, in the appropriate direction,
  524. // until one is found. If nothing is found, then do nothing.
  525. %found = false;
  526. for (%i=0; %i<%this.totalCycledWeapons; %i++)
  527. {
  528. %weapon = %this.cycleWeapon[%nextIndex];
  529. if (%weapon !$= "" && %this.hasInventory(%weapon) && %this.hasAmmo(%weapon))
  530. {
  531. // We've found out weapon
  532. %found = true;
  533. break;
  534. }
  535. %nextIndex = %nextIndex + %dir;
  536. if (%nextIndex < 0)
  537. {
  538. %nextIndex = %this.totalCycledWeapons - 1;
  539. }
  540. else if (%nextIndex >= %this.totalCycledWeapons)
  541. {
  542. %nextIndex = 0;
  543. }
  544. }
  545. if (%found)
  546. {
  547. %this.use(%this.cycleWeapon[%nextIndex]);
  548. }
  549. }