diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 03f64231..15c4af9c 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -1028,55 +1028,47 @@ object GlobalDefinitions {
private def init_tools() : Unit = {
chainblade.Size = EquipmentSize.Melee
chainblade.AmmoTypes += melee_ammo
- chainblade.FireModes += new FireModeDefinition
+ chainblade.FireModes += new InfiniteFireModeDefinition
chainblade.FireModes.head.AmmoTypeIndices += 0
chainblade.FireModes.head.AmmoSlotIndex = 0
chainblade.FireModes.head.Magazine = 1
- chainblade.FireModes.head.Chamber = 0
- chainblade.FireModes += new FireModeDefinition
+ chainblade.FireModes += new InfiniteFireModeDefinition
chainblade.FireModes(1).AmmoTypeIndices += 0
chainblade.FireModes(1).AmmoSlotIndex = 0
chainblade.FireModes(1).Magazine = 1
- chainblade.FireModes.head.Chamber = 0
magcutter.Size = EquipmentSize.Melee
magcutter.AmmoTypes += melee_ammo
- magcutter.FireModes += new FireModeDefinition
+ magcutter.FireModes += new InfiniteFireModeDefinition
magcutter.FireModes.head.AmmoTypeIndices += 0
magcutter.FireModes.head.AmmoSlotIndex = 0
magcutter.FireModes.head.Magazine = 1
- magcutter.FireModes.head.Chamber = 0
- magcutter.FireModes += new FireModeDefinition
+ magcutter.FireModes += new InfiniteFireModeDefinition
magcutter.FireModes(1).AmmoTypeIndices += 0
magcutter.FireModes(1).AmmoSlotIndex = 0
magcutter.FireModes(1).Magazine = 1
- magcutter.FireModes.head.Chamber = 0
forceblade.Size = EquipmentSize.Melee
forceblade.AmmoTypes += melee_ammo
- forceblade.FireModes += new FireModeDefinition
+ forceblade.FireModes += new InfiniteFireModeDefinition
forceblade.FireModes.head.AmmoTypeIndices += 0
forceblade.FireModes.head.AmmoSlotIndex = 0
forceblade.FireModes.head.Magazine = 1
- forceblade.FireModes.head.Chamber = 0
- forceblade.FireModes += new FireModeDefinition
+ forceblade.FireModes += new InfiniteFireModeDefinition
forceblade.FireModes(1).AmmoTypeIndices += 0
forceblade.FireModes(1).AmmoSlotIndex = 0
forceblade.FireModes(1).Magazine = 1
- forceblade.FireModes(1).Chamber = 0
katana.Size = EquipmentSize.Melee
katana.AmmoTypes += melee_ammo
- katana.FireModes += new FireModeDefinition
+ katana.FireModes += new InfiniteFireModeDefinition
katana.FireModes.head.AmmoTypeIndices += 0
katana.FireModes.head.AmmoSlotIndex = 0
katana.FireModes.head.Magazine = 1
- katana.FireModes.head.Chamber = 0
- katana.FireModes += new FireModeDefinition
+ katana.FireModes += new InfiniteFireModeDefinition
katana.FireModes(1).AmmoTypeIndices += 0
katana.FireModes(1).AmmoSlotIndex = 0
katana.FireModes(1).Magazine = 1
- katana.FireModes(1).Chamber = 0
frag_grenade.Size = EquipmentSize.Pistol
frag_grenade.AmmoTypes += frag_grenade_ammo
@@ -1127,11 +1119,12 @@ object GlobalDefinitions {
isp.Size = EquipmentSize.Pistol
isp.AmmoTypes += shotgun_shell
isp.AmmoTypes += shotgun_shell_AP
- isp.FireModes += new FireModeDefinition
+ isp.FireModes += new PelletFireModeDefinition
isp.FireModes.head.AmmoTypeIndices += 0
isp.FireModes.head.AmmoTypeIndices += 1
isp.FireModes.head.AmmoSlotIndex = 0
isp.FireModes.head.Magazine = 8
+ isp.FireModes.head.Chamber = 6 //8 shells x 6 pellets = 48
isp.Tile = InventoryTile.Tile33
beamer.Size = EquipmentSize.Pistol
@@ -1190,11 +1183,12 @@ object GlobalDefinitions {
flechette.Size = EquipmentSize.Rifle
flechette.AmmoTypes += shotgun_shell
flechette.AmmoTypes += shotgun_shell_AP
- flechette.FireModes += new FireModeDefinition
+ flechette.FireModes += new PelletFireModeDefinition
flechette.FireModes.head.AmmoTypeIndices += 0
flechette.FireModes.head.AmmoTypeIndices += 1
flechette.FireModes.head.AmmoSlotIndex = 0
- flechette.FireModes.head.Magazine = 12 //12 shells * 8 pellets = 96
+ flechette.FireModes.head.Magazine = 12
+ flechette.FireModes.head.Chamber = 8 //12 shells * 8 pellets = 96
flechette.Tile = InventoryTile.Tile63
cycler.Size = EquipmentSize.Rifle
@@ -1239,7 +1233,6 @@ object GlobalDefinitions {
anniversary_guna.FireModes(1).AmmoTypeIndices += 0
anniversary_guna.FireModes(1).AmmoSlotIndex = 0
anniversary_guna.FireModes(1).Magazine = 6
- anniversary_guna.FireModes(1).Chamber = 6
anniversary_guna.Tile = InventoryTile.Tile33
anniversary_gun.Size = EquipmentSize.Pistol
@@ -1252,7 +1245,6 @@ object GlobalDefinitions {
anniversary_gun.FireModes(1).AmmoTypeIndices += 0
anniversary_gun.FireModes(1).AmmoSlotIndex = 0
anniversary_gun.FireModes(1).Magazine = 6
- anniversary_gun.FireModes(1).Chamber = 6
anniversary_gun.Tile = InventoryTile.Tile33
anniversary_gunb.Size = EquipmentSize.Pistol
@@ -1265,7 +1257,6 @@ object GlobalDefinitions {
anniversary_gunb.FireModes(1).AmmoTypeIndices += 0
anniversary_gunb.FireModes(1).AmmoSlotIndex = 0
anniversary_gunb.FireModes(1).Magazine = 6
- anniversary_gunb.FireModes(1).Chamber = 6
anniversary_gunb.Tile = InventoryTile.Tile33
spiker.Size = EquipmentSize.Pistol
@@ -1275,6 +1266,7 @@ object GlobalDefinitions {
spiker.FireModes.head.AmmoSlotIndex = 0
spiker.FireModes.head.Magazine = 25
spiker.Tile = InventoryTile.Tile33
+ //TODO the spiker is weird
mini_chaingun.Size = EquipmentSize.Rifle
mini_chaingun.AmmoTypes += bullet_9mm
@@ -1289,17 +1281,18 @@ object GlobalDefinitions {
r_shotgun.Size = EquipmentSize.Rifle
r_shotgun.AmmoTypes += shotgun_shell
r_shotgun.AmmoTypes += shotgun_shell_AP
- r_shotgun.FireModes += new FireModeDefinition
+ r_shotgun.FireModes += new PelletFireModeDefinition
r_shotgun.FireModes.head.AmmoTypeIndices += 0
r_shotgun.FireModes.head.AmmoTypeIndices += 1
r_shotgun.FireModes.head.AmmoSlotIndex = 0
- r_shotgun.FireModes.head.Magazine = 16 //16 shells * 8 pellets = 128
- r_shotgun.FireModes += new FireModeDefinition
+ r_shotgun.FireModes.head.Magazine = 16
+ r_shotgun.FireModes.head.Chamber = 8 //16 shells * 8 pellets = 128
+ r_shotgun.FireModes += new PelletFireModeDefinition
r_shotgun.FireModes(1).AmmoTypeIndices += 0
r_shotgun.FireModes(1).AmmoTypeIndices += 1
r_shotgun.FireModes(1).AmmoSlotIndex = 0
- r_shotgun.FireModes(1).Magazine = 16 //16 shells * 8 pellets = 128
- r_shotgun.FireModes(1).Chamber = 3
+ r_shotgun.FireModes(1).Magazine = 16
+ r_shotgun.FireModes(1).Chamber = 8 //16 shells * 8 pellets = 128
r_shotgun.Tile = InventoryTile.Tile93
lasher.Size = EquipmentSize.Rifle
@@ -1329,6 +1322,7 @@ object GlobalDefinitions {
maelstrom.FireModes(2).AmmoSlotIndex = 0
maelstrom.FireModes(2).Magazine = 150
maelstrom.Tile = InventoryTile.Tile93
+ //TODO the maelstrom is weird
phoenix.Size = EquipmentSize.Rifle
phoenix.AmmoTypes += phoenix_missile
@@ -1387,7 +1381,6 @@ object GlobalDefinitions {
rocklet.FireModes(1).AmmoTypeIndices += 1
rocklet.FireModes(1).AmmoSlotIndex = 0
rocklet.FireModes(1).Magazine = 6
- rocklet.FireModes(1).Chamber = 6
rocklet.Tile = InventoryTile.Tile63
thumper.Size = EquipmentSize.Rifle
@@ -1454,12 +1447,11 @@ object GlobalDefinitions {
flamethrower.FireModes.head.AmmoTypeIndices += 0
flamethrower.FireModes.head.AmmoSlotIndex = 0
flamethrower.FireModes.head.Magazine = 100
- flamethrower.FireModes.head.Chamber = 5
flamethrower.FireModes += new FireModeDefinition
flamethrower.FireModes(1).AmmoTypeIndices += 0
flamethrower.FireModes(1).AmmoSlotIndex = 0
flamethrower.FireModes(1).Magazine = 100
- flamethrower.FireModes(1).Chamber = 50
+ flamethrower.FireModes(1).Rounds = 50
flamethrower.Tile = InventoryTile.Tile63
trhev_dualcycler.Size = EquipmentSize.Max
@@ -1489,18 +1481,21 @@ object GlobalDefinitions {
nchev_scattercannon.Size = EquipmentSize.Max
nchev_scattercannon.AmmoTypes += scattercannon_ammo
- nchev_scattercannon.FireModes += new FireModeDefinition
+ nchev_scattercannon.FireModes += new PelletFireModeDefinition
nchev_scattercannon.FireModes.head.AmmoTypeIndices += 0
nchev_scattercannon.FireModes.head.AmmoSlotIndex = 0
nchev_scattercannon.FireModes.head.Magazine = 40
- nchev_scattercannon.FireModes += new FireModeDefinition
+ nchev_scattercannon.FireModes.head.Chamber = 10
+ nchev_scattercannon.FireModes += new PelletFireModeDefinition
nchev_scattercannon.FireModes(1).AmmoTypeIndices += 0
nchev_scattercannon.FireModes(1).AmmoSlotIndex = 0
nchev_scattercannon.FireModes(1).Magazine = 40
- nchev_scattercannon.FireModes += new FireModeDefinition
+ nchev_scattercannon.FireModes(1).Chamber = 10
+ nchev_scattercannon.FireModes += new PelletFireModeDefinition
nchev_scattercannon.FireModes(2).AmmoTypeIndices += 0
nchev_scattercannon.FireModes(2).AmmoSlotIndex = 0
nchev_scattercannon.FireModes(2).Magazine = 40
+ nchev_scattercannon.FireModes(2).Chamber = 10
nchev_falcon.Size = EquipmentSize.Max
nchev_falcon.AmmoTypes += falcon_ammo
@@ -1584,11 +1579,10 @@ object GlobalDefinitions {
trek.FireModes.head.AmmoTypeIndices += 0
trek.FireModes.head.AmmoSlotIndex = 0
trek.FireModes.head.Magazine = 4
- trek.FireModes += new FireModeDefinition
+ trek.FireModes += new InfiniteFireModeDefinition
trek.FireModes(1).AmmoTypeIndices += 0
trek.FireModes(1).AmmoSlotIndex = 0
trek.FireModes(1).Magazine = 1
- trek.FireModes(1).Chamber = 0
trek.Tile = InventoryTile.Tile33
flail_targeting_laser.Packet = new CommandDetonaterConverter
diff --git a/common/src/main/scala/net/psforever/objects/Tool.scala b/common/src/main/scala/net/psforever/objects/Tool.scala
index df6c3df1..2b6d122a 100644
--- a/common/src/main/scala/net/psforever/objects/Tool.scala
+++ b/common/src/main/scala/net/psforever/objects/Tool.scala
@@ -33,6 +33,7 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
def NextFireMode : FireModeDefinition = {
FireModeIndex = FireModeIndex + 1
+ AmmoSlot.Chamber = FireMode.Chamber
FireMode
}
@@ -59,7 +60,9 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
def MaxMagazine : Int = FireMode.Magazine
- def NextDischarge : Int = math.min(Magazine, FireMode.Chamber)
+ def Discharge : Int = {
+ Magazine = FireMode.Discharge(this)
+ }
def AmmoSlot : Tool.FireModeSlot = ammoSlots(FireMode.AmmoSlotIndex)
@@ -127,6 +130,7 @@ object Tool {
private var ammoTypeIndex : Int = 0
/** a reference to the actual `AmmoBox` of this slot */
private var box : AmmoBox = AmmoBox(AmmoDefinition, fdef.Magazine)
+ private var chamber = fdef.Chamber
def AmmoTypeIndex : Int = ammoTypeIndex
@@ -158,6 +162,13 @@ object Tool {
Magazine
}
+ def Chamber : Int = chamber
+
+ def Chamber_=(chmbr : Int) : Int = {
+ chamber = math.min(math.max(0, chmbr), fdef.Chamber)
+ Chamber
+ }
+
def Box : AmmoBox = box
def Box_=(toBox : AmmoBox) : Option[AmmoBox] = {
diff --git a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
index 423154bc..631e5e40 100644
--- a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
@@ -1,16 +1,25 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.equipment
+import net.psforever.objects.Tool
+
import scala.collection.mutable
class FireModeDefinition {
- private val ammoTypeIndices : mutable.ListBuffer[Int] = mutable.ListBuffer[Int]() //indices pointing to all ammo types used
- private var ammoSlotIndex : Int = 0 //ammunition slot number this fire mode utilizes
- private var chamber : Int = 1 //how many rounds are queued to be fired at once, e.g., 3 for the Jackhammer's triple burst
- private var magazine : Int = 1 //how many rounds are queued for each reload cycle
-// private var target : Any = _ //target designation (self? other?)
+ /** indices pointing to all ammo types used */
+ private val ammoTypeIndices : mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
+ /** ammunition slot number this fire mode utilizes */
+ private var ammoSlotIndex : Int = 0
+ /** how many rounds are replenished each reload cycle */
+ private var magazine : Int = 1
+ /** how much is subtracted from the magazine each fire cycle;
+ * most weapons will only fire 1 round per fire cycle; the flamethrower, fire mode 1, fires 50 */
+ private var rounds : Int = 1
+ /** how many sub-rounds are queued per round fired;
+ * the flechette fires 8 pellets per shell and generates 8 fire reports before the ammo count goes down */
+ private var chamber : Int = 1
- //damage modifiers will follow here ...
+ //damage modifiers will follow here ... ?
def AmmoSlotIndex : Int = ammoSlotIndex
@@ -25,13 +34,6 @@ class FireModeDefinition {
ammoTypeIndices += index
}
- def Chamber : Int = chamber
-
- def Chamber_=(inChamber : Int) : Int = {
- chamber = inChamber
- Chamber
- }
-
def Magazine : Int = magazine
def Magazine_=(inMagazine : Int) : Int = {
@@ -39,16 +41,67 @@ class FireModeDefinition {
Magazine
}
-// def Target : Any = target
-//
-// def Target_+(setAsTarget : Any) : Any = {
-// target = setAsTarget
-// Target
-// }
-}
+ def Rounds : Int = rounds
-object FireModeDefinition {
- def apply() : FireModeDefinition = {
- new FireModeDefinition()
+ def Rounds_=(round : Int) : Int = {
+ rounds = round
+ Rounds
+ }
+
+ def Chamber : Int = chamber
+
+ def Chamber_=(inChamber : Int) : Int = {
+ chamber = inChamber
+ Chamber
+ }
+
+ /**
+ * Shoot a weapon, remove an anticipated amount of ammunition.
+ * @param weapon the weapon
+ * @return the size of the weapon's magazine after discharge
+ */
+ def Discharge(weapon : Tool) : Int = {
+ weapon.Magazine - Rounds
}
}
+
+class PelletFireModeDefinition extends FireModeDefinition {
+ /**
+ * Shoot a weapon, remove an anticipated amount of ammunition.
+ *
+ * For a weapon that has a number of sub-rounds chambered, each will generate unique weapon fire per fire cycle.
+ * Once all of the sub-rounds have been accounted for, the number of rounds for a single fire cycle will subtract.
+ * Since all fire cycles will abide by this chambered number of sub-rounds, the count is reset.
+ * @param weapon the weapon
+ * @return the size of the weapon's magazine after discharge
+ */
+ override def Discharge(weapon : Tool) : Int = {
+ val ammoSlot = weapon.AmmoSlot
+ val magazine = weapon.Magazine
+ val chamber : Int = ammoSlot.Chamber = ammoSlot.Chamber - 1
+ if(chamber <= 0) {
+ ammoSlot.Chamber = Chamber
+ magazine - Rounds
+ }
+ else {
+ magazine
+ }
+ }
+}
+
+class InfiniteFireModeDefinition extends FireModeDefinition {
+ /**
+ * Shoot a weapon, remove an anticipated amount of ammunition.
+ *
+ * No rounds will be subtracted ever.
+ * The weapon can keep firing as much as the user wants.
+ * Since the PlanetSide client also has an internal understanding of ammo values in weapons,
+ * it may interfere with the functionality of this fire mode
+ * if the size of the magazine is not implicitly set per fire cycle.
+ * Works well with melee weapons.
+ * @param weapon the weapon
+ * @return the size of the weapon's magazine after discharge;
+ * will always return 1
+ */
+ override def Discharge(weapon : Tool) : Int = 1
+}
diff --git a/common/src/test/scala/objects/EquipmentTest.scala b/common/src/test/scala/objects/EquipmentTest.scala
index ff44aab7..fe260a3e 100644
--- a/common/src/test/scala/objects/EquipmentTest.scala
+++ b/common/src/test/scala/objects/EquipmentTest.scala
@@ -2,11 +2,11 @@
package objects
import net.psforever.objects._
-import net.psforever.objects.definition._
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.GlobalDefinitions._
+import net.psforever.objects.definition._
import org.specs2.mutable._
class EquipmentTest extends Specification {
@@ -109,12 +109,12 @@ class EquipmentTest extends Specification {
obj.Size = EquipmentSize.Rifle
obj.AmmoTypes += GlobalDefinitions.shotgun_shell
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
- obj.FireModes += FireModeDefinition()
+ obj.FireModes += new FireModeDefinition
obj.FireModes.head.AmmoTypeIndices += 0
obj.FireModes.head.AmmoTypeIndices += 1
obj.FireModes.head.AmmoSlotIndex = 0
obj.FireModes.head.Magazine = 18
- obj.FireModes += FireModeDefinition()
+ obj.FireModes += new FireModeDefinition
obj.FireModes(1).AmmoTypeIndices += 0
obj.FireModes(1).AmmoTypeIndices += 1
obj.FireModes(1).AmmoSlotIndex = 1
@@ -162,47 +162,27 @@ class EquipmentTest extends Specification {
}
"multiple fire modes" in {
- //explanation: sample_weapon has two fire modes; adjusting the FireMode changes between them
- val tdef = ToolDefinition(1076)
- tdef.Size = EquipmentSize.Rifle
- tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
- tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
- tdef.FireModes += new FireModeDefinition
- tdef.FireModes.head.AmmoTypeIndices += 0
- tdef.FireModes.head.AmmoSlotIndex = 0
- tdef.FireModes.head.Magazine = 9
- tdef.FireModes += new FireModeDefinition
- tdef.FireModes(1).AmmoTypeIndices += 1
- tdef.FireModes(1).AmmoSlotIndex = 1
- tdef.FireModes(1).Magazine = 18
- val obj : Tool = Tool(tdef)
+ //explanation: sample_weapon has two fire modes; each fire mode has a different ammunition type
+ val obj : Tool = Tool(punisher)
//fmode = 0
obj.FireModeIndex mustEqual 0
- obj.FireMode.Magazine mustEqual 9
- obj.AmmoType mustEqual Ammo.shotgun_shell
+ obj.FireMode.Magazine mustEqual 30
+ obj.AmmoType mustEqual Ammo.bullet_9mm
//fmode -> 1
obj.NextFireMode
obj.FireModeIndex mustEqual 1
- obj.FireMode.Magazine mustEqual 18
- obj.AmmoType mustEqual Ammo.shotgun_shell_AP
+ obj.FireMode.Magazine mustEqual 1
+ obj.AmmoType mustEqual Ammo.rocket
//fmode -> 0
obj.NextFireMode
obj.FireModeIndex mustEqual 0
- obj.FireMode.Magazine mustEqual 9
- obj.AmmoType mustEqual Ammo.shotgun_shell
+ obj.FireMode.Magazine mustEqual 30
+ obj.AmmoType mustEqual Ammo.bullet_9mm
}
"multiple types of ammunition" in {
//explanation: obj has one fire mode and two ammunitions; adjusting the AmmoType changes between them
- val tdef = ToolDefinition(1076)
- tdef.Size = EquipmentSize.Rifle
- tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
- tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
- tdef.FireModes += new FireModeDefinition
- tdef.FireModes.head.AmmoTypeIndices += 0
- tdef.FireModes.head.AmmoTypeIndices += 1
- tdef.FireModes.head.AmmoSlotIndex = 0
- val obj : Tool = Tool(tdef)
+ val obj : Tool = Tool(flechette)
//ammo = 0
obj.AmmoTypeIndex mustEqual 0
obj.AmmoType mustEqual Ammo.shotgun_shell
@@ -255,6 +235,39 @@ class EquipmentTest extends Specification {
obj.AmmoTypeIndex mustEqual 2
obj.AmmoType mustEqual Ammo.rocket
}
+
+ "discharge (1)" in {
+ val obj = Tool(GlobalDefinitions.punisher)
+ obj.Magazine mustEqual 30
+ obj.Discharge
+ obj.Magazine mustEqual 29
+ obj.Discharge
+ obj.Discharge
+ obj.Magazine mustEqual 27
+ }
+
+ "chamber" in {
+ val obj = Tool(GlobalDefinitions.flechette)
+ obj.Magazine mustEqual 12
+ obj.AmmoSlot.Chamber mustEqual 8
+
+ obj.Discharge
+ obj.Magazine mustEqual 12
+ obj.AmmoSlot.Chamber mustEqual 7
+ obj.Discharge
+ obj.Discharge
+ obj.Magazine mustEqual 12
+ obj.AmmoSlot.Chamber mustEqual 5
+ obj.Discharge
+ obj.Discharge
+ obj.Discharge
+ obj.Discharge
+ obj.Magazine mustEqual 12
+ obj.AmmoSlot.Chamber mustEqual 1
+ obj.Discharge
+ obj.Magazine mustEqual 11
+ obj.AmmoSlot.Chamber mustEqual 8
+ }
}
"Kit" should {
diff --git a/common/src/test/scala/objects/FireModeTest.scala b/common/src/test/scala/objects/FireModeTest.scala
new file mode 100644
index 00000000..07342393
--- /dev/null
+++ b/common/src/test/scala/objects/FireModeTest.scala
@@ -0,0 +1,127 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects.definition.ToolDefinition
+import net.psforever.objects.{GlobalDefinitions, Tool}
+import net.psforever.objects.equipment.{EquipmentSize, FireModeDefinition, InfiniteFireModeDefinition, PelletFireModeDefinition}
+import org.specs2.mutable._
+
+class FireModeTest extends Specification {
+ "FireModeDefinition" should {
+ "construct" in {
+ val obj = new FireModeDefinition
+ obj.AmmoTypeIndices mustEqual Nil
+ obj.AmmoSlotIndex mustEqual 0
+ obj.Magazine mustEqual 1
+ obj.Rounds mustEqual 1
+ obj.Chamber mustEqual 1
+ }
+
+ "test configurations" in {
+ val tdef = ToolDefinition(1076) //fake object id
+ tdef.Size = EquipmentSize.Rifle
+ tdef.AmmoTypes += GlobalDefinitions.bullet_9mm
+ tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
+ tdef.FireModes += new FireModeDefinition
+ tdef.FireModes.head.AmmoTypeIndices += 0
+ tdef.FireModes.head.AmmoSlotIndex = 0
+ tdef.FireModes.head.Magazine = 18
+ tdef.FireModes.head.Rounds = 18
+ tdef.FireModes.head.Chamber = 2
+ tdef.FireModes += new FireModeDefinition
+ tdef.FireModes(1).AmmoTypeIndices += 1
+ tdef.FireModes(1).AmmoTypeIndices += 2
+ tdef.FireModes(1).AmmoSlotIndex = 1
+ tdef.FireModes(1).Magazine = 9
+ tdef.FireModes(1).Rounds = 2
+ tdef.FireModes(1).Chamber = 8
+
+ tdef.AmmoTypes.toList mustEqual List(GlobalDefinitions.bullet_9mm, GlobalDefinitions.shotgun_shell)
+ tdef.FireModes.size mustEqual 2
+ tdef.FireModes.head.AmmoTypeIndices.toList mustEqual List(0)
+ tdef.FireModes.head.AmmoSlotIndex mustEqual 0
+ tdef.FireModes.head.Magazine mustEqual 18
+ tdef.FireModes.head.Rounds mustEqual 18
+ tdef.FireModes.head.Chamber mustEqual 2
+ tdef.FireModes(1).AmmoTypeIndices.toList mustEqual List(1, 2)
+ tdef.FireModes(1).AmmoSlotIndex mustEqual 1
+ tdef.FireModes(1).Magazine mustEqual 9
+ tdef.FireModes(1).Rounds mustEqual 2
+ tdef.FireModes(1).Chamber mustEqual 8
+ }
+
+ "discharge" in {
+ val obj = Tool(GlobalDefinitions.beamer) //see EquipmentTest
+ obj.FireMode.isInstanceOf[FireModeDefinition] mustEqual true
+ obj.Magazine mustEqual 16
+ obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.Chamber mustEqual 1
+
+ obj.Magazine mustEqual 16
+ obj.Discharge
+ obj.Magazine mustEqual 15
+ obj.Discharge
+ obj.Discharge
+ obj.Magazine mustEqual 13
+ }
+ }
+
+ "PelletFireModeDefinition" should {
+ "construct" in {
+ val obj = new PelletFireModeDefinition
+ obj.AmmoTypeIndices mustEqual Nil
+ obj.AmmoSlotIndex mustEqual 0
+ obj.Magazine mustEqual 1
+ obj.Rounds mustEqual 1
+ obj.Chamber mustEqual 1
+ }
+
+ "discharge" in {
+ val obj = Tool(GlobalDefinitions.flechette) //see EquipmentTest
+ obj.FireMode.isInstanceOf[PelletFireModeDefinition] mustEqual true
+ obj.Magazine mustEqual 12
+ obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.Chamber mustEqual 8
+
+ obj.Magazine mustEqual 12
+ obj.Discharge //1
+ obj.Magazine mustEqual 12
+ obj.Discharge //2
+ obj.Discharge //3
+ obj.Magazine mustEqual 12
+ obj.Discharge //4
+ obj.Discharge //5
+ obj.Discharge //6
+ obj.Discharge //7
+ obj.Magazine mustEqual 12
+ obj.Discharge //8
+ obj.Magazine mustEqual 11
+ }
+ }
+
+ "InfiniteFireModeDefinition" should {
+ "construct" in {
+ val obj = new InfiniteFireModeDefinition
+ obj.AmmoTypeIndices mustEqual Nil
+ obj.AmmoSlotIndex mustEqual 0
+ obj.Magazine mustEqual 1
+ obj.Rounds mustEqual 1
+ obj.Chamber mustEqual 1
+ }
+
+ "discharge" in {
+ val obj = Tool(GlobalDefinitions.magcutter) //see EquipmentTest
+ obj.FireMode.isInstanceOf[InfiniteFireModeDefinition] mustEqual true
+ obj.Magazine mustEqual 1
+ obj.FireMode.Rounds mustEqual 1
+ obj.FireMode.Chamber mustEqual 1
+
+ obj.Magazine mustEqual 1
+ obj.Discharge
+ obj.Magazine mustEqual 1
+ obj.Discharge
+ obj.Discharge
+ obj.Magazine mustEqual 1
+ }
+ }
+}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 17bebda3..8ab2cfcc 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -1116,6 +1116,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Certifications += CertificationType.ATV
player.Certifications += CertificationType.Harasser
//
+ player.Certifications += CertificationType.InfiltrationSuit
+ player.Certifications += CertificationType.Sniping
+ player.Certifications += CertificationType.AntiVehicular
+ player.Certifications += CertificationType.HeavyAssault
+ player.Certifications += CertificationType.SpecialAssault
+ player.Certifications += CertificationType.EliteAssault
player.Certifications += CertificationType.GroundSupport
player.Certifications += CertificationType.GroundTransport
player.Certifications += CertificationType.Flail
@@ -1130,7 +1136,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Certifications += CertificationType.GalaxyGunship
player.Certifications += CertificationType.Phantasm
player.Certifications += CertificationType.UniMAX
- player.Certifications += CertificationType.InfiltrationSuit
AwardBattleExperiencePoints(player, 1000000L)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(player.Faction))
@@ -1517,9 +1522,31 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ChangeFireStateMessage_Stop(item_guid) =>
log.info("ChangeFireState_Stop: " + msg)
- if(shooting.contains(item_guid)) {
+ val weapon : Option[Equipment] = if(shooting.contains(item_guid)) {
shooting = None
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
+ FindWeapon
+ }
+ else {
+ //some weapons, e.g., the decimator, do not send a ChangeFireState_Start on the last shot
+ FindWeapon match {
+ case Some(tool : Tool) =>
+ if(tool.Definition == GlobalDefinitions.phoenix) {
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
+ }
+ Some(tool)
+ case _ =>
+ log.warn(s"ChangeFireState_Stop: received an unexpected message about $item_guid")
+ None
+ }
+ }
+ weapon match {
+ case Some(tool : Tool) =>
+ if(tool.Magazine == 0) {
+ FireCycleCleanup(tool)
+ }
+ case _ => ;
}
progressBarUpdate.cancel //TODO independent action?
@@ -1918,7 +1945,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid))
}
else { //shooting
- tool.Magazine = tool.Magazine - 1
+ tool.Discharge
//TODO other stuff?
}
case _ => ;
@@ -2253,7 +2280,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
)
)
- if(0 <= localIndex && localIndex < 5) {
+ if(localTarget.VisibleSlots.contains(localIndex)) {
localService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentInHand(localTarget.GUID, localIndex, localObject))
}
}
@@ -2398,7 +2425,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
override def onSuccess() : Unit = {
localAnnounce ! ResponseToSelf(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(localObjectGUID, 0)))
- if(0 <= localIndex && localIndex < 5) {
+ if(localTarget.VisibleSlots.contains(localIndex)) {
localService ! AvatarServiceMessage(localContinent, AvatarAction.ObjectDelete(localTarget.GUID, localObjectGUID))
}
}
@@ -2827,6 +2854,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
+ /**
+ * After a weapon has finished shooting, determine if it needs to be sorted in a special way.
+ * @param tool a weapon
+ */
+ def FireCycleCleanup(tool : Tool) : Unit = {
+ //TODO this is temporary and will be replaced by more appropriate functionality in the future.
+ val tdef = tool.Definition
+ if(GlobalDefinitions.isGrenade(tdef)) {
+ taskResolver ! RemoveEquipmentFromSlot(player, tool, player.Find(tool).get)
+ }
+ else if(tdef == GlobalDefinitions.phoenix) {
+ taskResolver ! RemoveEquipmentFromSlot(player, tool, player.Find(tool).get)
+ }
+ }
+
/**
* A predicate used to determine if an `InventoryItem` object contains `Equipment` that should be dropped.
* Used to filter through lists of object data before it is placed into a player's inventory.