diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 7a6a1bcd..330cba5b 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -270,6 +270,42 @@ object GlobalDefinitions {
}
}
+ /*
+ Implants
+ */
+ val
+ advanced_regen = ImplantDefinition(0)
+
+ val
+ targeting = ImplantDefinition(1)
+
+ val
+ audio_amplifier = ImplantDefinition(2)
+
+ val
+ darklight_vision = ImplantDefinition(3)
+
+ val
+ melee_booster = ImplantDefinition(4)
+
+ val
+ personal_shield = ImplantDefinition(5)
+
+ val
+ range_magnifier = ImplantDefinition(6)
+
+ val
+ second_wind = ImplantDefinition(7)
+
+ val
+ silent_run = ImplantDefinition(8)
+
+ val
+ surge = ImplantDefinition(9)
+
+ /*
+ Equipment (locker_container, kits, ammunition, weapons)
+ */
import net.psforever.packet.game.objectcreate.ObjectClass
val
locker_container = new EquipmentDefinition(456) {
diff --git a/common/src/main/scala/net/psforever/objects/Implant.scala b/common/src/main/scala/net/psforever/objects/Implant.scala
deleted file mode 100644
index 2d7483f7..00000000
--- a/common/src/main/scala/net/psforever/objects/Implant.scala
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects
-
-import net.psforever.objects.definition.{ImplantDefinition, Stance}
-import net.psforever.types.{ExoSuitType, ImplantType}
-
-/**
- * A type of installable player utility that grants a perk, usually in exchange for stamina (energy).
- *
- * An implant starts with a never-to-initialized timer value of -1 and will not report as `Ready` until the timer is 0.
- * The `Timer`, however, will report to the user a time of 0 since negative time does not make sense.
- * Although the `Timer` can be manually set, using `Reset` is the better way to default the initialization timer to the correct amount.
- * An external script will be necessary to operate the actual initialization countdown.
- * An implant must be `Ready` before it can be `Active`.
- * The `Timer` must be set (or reset) (or countdown) to 0 to be `Ready` and then it must be activated.
- * @param implantDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
- */
-class Implant(implantDef : ImplantDefinition) {
- private var active : Boolean = false
- private var initTimer : Long = -1L
-
- def Name : String = implantDef.Name
-
- def Ready : Boolean = initTimer == 0L
-
- def Active : Boolean = active
-
- def Active_=(isActive : Boolean) : Boolean = {
- active = Ready && isActive
- Active
- }
-
- def Timer : Long = math.max(0, initTimer)
-
- def Timer_=(time : Long) : Long = {
- initTimer = math.max(0, time)
- Timer
- }
-
- def MaxTimer : Long = implantDef.Initialization
-
- def ActivationCharge : Int = Definition.ActivationCharge
-
- /**
- * Calculate the stamina consumption of the implant for any given moment of being active after its activation.
- * As implant energy use can be influenced by both exo-suit worn and general stance held, both are considered.
- * @param suit the exo-suit being worn
- * @param stance the player's stance
- * @return the amount of stamina (energy) that is consumed
- */
- def Charge(suit : ExoSuitType.Value, stance : Stance.Value) : Int = {
- if(active) {
- implantDef.DurationChargeBase + implantDef.DurationChargeByExoSuit(suit) + implantDef.DurationChargeByStance(stance)
- }
- else {
- 0
- }
- }
-
- /**
- * Place an implant back in its initializing state.
- */
- def Reset() : Unit = {
- Active = false
- Timer = MaxTimer
- }
-
- /**
- * Place an implant back in its pre-initialization state.
- * The implant is inactive and can not proceed to a `Ready` condition naturally from this state.
- */
- def Jammed() : Unit = {
- Active = false
- Timer = -1
- }
-
- def Definition : ImplantDefinition = implantDef
-}
-
-object Implant {
- def default : Implant = new Implant(ImplantDefinition(ImplantType.RangeMagnifier))
-
- def apply(implantDef : ImplantDefinition) : Implant = {
- new Implant(implantDef)
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/ImplantSlot.scala b/common/src/main/scala/net/psforever/objects/ImplantSlot.scala
index bf6d0b01..736b03f1 100644
--- a/common/src/main/scala/net/psforever/objects/ImplantSlot.scala
+++ b/common/src/main/scala/net/psforever/objects/ImplantSlot.scala
@@ -1,61 +1,117 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
-import net.psforever.objects.definition.ImplantDefinition
-import net.psforever.types.ImplantType
+import net.psforever.objects.definition.{ImplantDefinition, Stance}
+import net.psforever.types.{ExoSuitType, ImplantType}
/**
- * A slot "on the player" into which an implant is installed.
+ * A slot "on the player" into which an implant is installed.
+ * In total, players have three implant slots.
*
- * In total, players have three implant slots.
- * At battle rank one (BR1), however, all of those slots are locked.
- * The player earns implants at BR16, BR12, and BR18.
- * A locked implant slot can not be used.
- * (The code uses "not yet unlocked" logic.)
- * When unlocked, an implant may be installed into that slot.
- *
- * The default implant that the underlying slot utilizes is the "Range Magnifier."
- * Until the `Installed` condition is some value other than `None`, however, the implant in the slot will not work.
+ * All implants slots start as "locked" and must be "unlocked" through battle rank advancement.
+ * Only after it is "unlocked" may an implant be "installed" into the slot.
+ * Upon installation, it undergoes an initialization period and then, after which, it is ready for user activation.
+ * Being jammed de-activates the implant, put it into a state of "not being ready," and causes the initialization to repeat.
*/
class ImplantSlot {
/** is this slot available for holding an implant */
private var unlocked : Boolean = false
+ /** whether this implant is ready for use */
+ private var initialized : Boolean = false
+ /** is this implant active */
+ private var active : Boolean = false
/** what implant is currently installed in this slot; None if there is no implant currently installed */
- private var installed : Option[ImplantType.Value] = None
- /** the entry for that specific implant used by the a player; always occupied by some type of implant */
- private var implant : Implant = ImplantSlot.default
+ private var implant : Option[ImplantDefinition] = None
def Unlocked : Boolean = unlocked
def Unlocked_=(lock : Boolean) : Boolean = {
- unlocked = lock
+ unlocked = lock || unlocked
Unlocked
}
- def Installed : Option[ImplantType.Value] = installed
+ def Initialized : Boolean = initialized
- def Implant : Option[Implant] = if(Installed.isDefined) { Some(implant) } else { None }
+ def Initialized_=(init : Boolean) : Boolean = {
+ initialized = Installed.isDefined && init
+ Active = Active && initialized //can not be active just yet
+ Initialized
+ }
- def Implant_=(anImplant : Option[Implant]) : Option[Implant] = {
- anImplant match {
- case Some(module) =>
- Implant = module
- case None =>
- installed = None
+ def Active : Boolean = active
+
+ def Active_=(state : Boolean) : Boolean = {
+ active = Initialized && state
+ Active
+ }
+
+ def Implant : ImplantType.Value = if(Installed.isDefined) {
+ implant.get.Type
+ }
+ else {
+ Active = false
+ Initialized = false
+ ImplantType.None
+ }
+
+ def Implant_=(anImplant : ImplantDefinition) : ImplantType.Value = {
+ Implant_=(Some(anImplant))
+ }
+
+ def Implant_=(anImplant : Option[ImplantDefinition]) : ImplantType.Value = {
+ if(Unlocked) {
+ anImplant match {
+ case Some(_) =>
+ implant = anImplant
+ case None =>
+ implant = None
+ }
}
Implant
}
- def Implant_=(anImplant : Implant) : Option[Implant] = {
- implant = anImplant
- installed = Some(anImplant.Definition.Type)
- Implant
+ def Installed : Option[ImplantDefinition] = implant
+
+ def MaxTimer : Long = Implant match {
+ case ImplantType.None =>
+ -1L
+ case _ =>
+ Installed.get.Initialization
+ }
+
+ def ActivationCharge : Int = {
+ if(Active) {
+ Installed.get.ActivationCharge
+ }
+ else {
+ 0
+ }
+ }
+
+ /**
+ * Calculate the stamina consumption of the implant for any given moment of being active after its activation.
+ * As implant energy use can be influenced by both exo-suit worn and general stance held, both are considered.
+ * @param suit the exo-suit being worn
+ * @param stance the player's stance
+ * @return the amount of stamina (energy) that is consumed
+ */
+ def Charge(suit : ExoSuitType.Value, stance : Stance.Value) : Int = {
+ if(Active) {
+ val inst = Installed.get
+ inst.DurationChargeBase + inst.DurationChargeByExoSuit(suit) + inst.DurationChargeByStance(stance)
+ }
+ else {
+ 0
+ }
+ }
+
+ def Jammed() : Unit = {
+ Active = false
+ Initialized = false
}
}
object ImplantSlot {
- private val default = new Implant(ImplantDefinition(ImplantType.None))
-
def apply() : ImplantSlot = {
new ImplantSlot()
}
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 0873c1c9..9d59f733 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
-import net.psforever.objects.definition.AvatarDefinition
+import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.packet.game.PlanetSideGUID
@@ -334,26 +334,22 @@ class Player(private val name : String,
def Implants : Array[ImplantSlot] = implants
- def Implant(slot : Int) : Option[ImplantType.Value] = {
- if(-1 < slot && slot < implants.length) { implants(slot).Installed } else { None }
+ def Implant(slot : Int) : ImplantType.Value = {
+ if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
}
- def Implant(implantType : ImplantType.Value) : Option[Implant] = {
- implants.find(_.Installed.contains(implantType)) match {
- case Some(slot) =>
- slot.Implant
- case None =>
- None
- }
- }
-
- def InstallImplant(implant : Implant) : Boolean = {
- getAvailableImplantSlot(implants.iterator, implant.Definition.Type) match {
- case Some(slot) =>
- slot.Implant = implant
- slot.Implant.get.Reset()
- true
+ def InstallImplant(implant : ImplantDefinition) : Boolean = {
+ implants.find({p => p.Installed.contains(implant)}) match { //try to find the installed implant
case None =>
+ //install in a free slot
+ getAvailableImplantSlot(implants.iterator, implant.Type) match {
+ case Some(slot) =>
+ slot.Implant = implant
+ true
+ case None =>
+ false
+ }
+ case Some(_) =>
false
}
}
@@ -364,7 +360,7 @@ class Player(private val name : String,
}
else {
val slot = iter.next
- if(!slot.Unlocked || slot.Installed.contains(implantType)) {
+ if(!slot.Unlocked || slot.Implant == implantType) {
None
}
else if(slot.Installed.isEmpty) {
@@ -377,7 +373,7 @@ class Player(private val name : String,
}
def UninstallImplant(implantType : ImplantType.Value) : Boolean = {
- implants.find({slot => slot.Installed.contains(implantType)}) match {
+ implants.find({slot => slot.Implant == implantType}) match {
case Some(slot) =>
slot.Implant = None
true
@@ -388,9 +384,9 @@ class Player(private val name : String,
def ResetAllImplants() : Unit = {
implants.foreach(slot => {
- slot.Implant match {
- case Some(implant) =>
- implant.Reset()
+ slot.Installed match {
+ case Some(_) =>
+ slot.Initialized = false
case None => ;
}
})
@@ -590,7 +586,7 @@ object Player {
//hand over implants
(0 until 3).foreach(index => {
if(obj.Implants(index).Unlocked = player.Implants(index).Unlocked) {
- obj.Implants(index).Implant = player.Implants(index).Implant
+ obj.Implants(index).Implant = player.Implants(index).Installed
}
})
//hand over knife
diff --git a/common/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
index 1825b58e..7984b310 100644
--- a/common/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
@@ -9,10 +9,13 @@ import scala.collection.mutable
* An `Enumeration` of a variety of poses or generalized movement.
*/
object Stance extends Enumeration {
- val Crouching,
- Standing,
- Walking, //not used, but should still be defined
- Running = Value
+ val
+ Crouching,
+ CrouchWalking, //not used, but should still be defined
+ Standing,
+ Walking, //not used, but should still be defined
+ Running
+ = Value
}
/**
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
index 963a7acb..b20812fd 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
@@ -1,9 +1,9 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
-import net.psforever.objects.{EquipmentSlot, Player}
+import net.psforever.objects.{EquipmentSlot, GlobalDefinitions, ImplantSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
+import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
import net.psforever.types.{GrenadeState, ImplantType}
import scala.annotation.tailrec
@@ -18,9 +18,9 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
obj.Armor / obj.MaxArmor * 255, //TODO not precise
DressBattleRank(obj),
DressCommandRank(obj),
+ recursiveMakeImplantEffects(obj.Implants.iterator),
None, //TODO cosmetics
- None, //TODO implant effects
- InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)),
+ InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
GetDrawnSlot(obj)
)
)
@@ -132,14 +132,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @see `ImplantEntry` in `DetailedCharacterData`
*/
private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = {
- obj.Implants.map(impl => {
- impl.Installed match {
- case Some(module) =>
- if(impl.Implant.get.Ready) {
- ImplantEntry(module, None)
+ obj.Implants.map(slot => {
+ slot.Installed match {
+ case Some(_) =>
+ if(slot.Initialized) {
+ ImplantEntry(slot.Implant, None)
}
else {
- ImplantEntry(module, Some(impl.Implant.get.Timer.toInt))
+ ImplantEntry(slot.Implant, Some(slot.Installed.get.Initialization.toInt))
}
case None =>
ImplantEntry(ImplantType.None, None)
@@ -147,6 +147,35 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
}).toList
}
+ /**
+ * Find an active implant whose effect will be displayed on this player.
+ * @param iter an `Iterator` of `ImplantSlot` objects
+ * @return the effect of an active implant
+ */
+ @tailrec private def recursiveMakeImplantEffects(iter : Iterator[ImplantSlot]) : Option[ImplantEffects.Value] = {
+ if(!iter.hasNext) {
+ None
+ }
+ else {
+ val slot = iter.next
+ if(slot.Active) {
+ import GlobalDefinitions._
+ slot.Installed match {
+ case Some(`advanced_regen`) =>
+ Some(ImplantEffects.RegenEffects)
+ case Some(`darklight_vision`) =>
+ Some(ImplantEffects.DarklightEffects)
+ case Some(`personal_shield`) =>
+ Some(ImplantEffects.PersonalShieldEffects)
+ case Some(`surge`) =>
+ Some(ImplantEffects.SurgeEffects)
+ case _ => ;
+ }
+ }
+ recursiveMakeImplantEffects(iter)
+ }
+ }
+
/**
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
@@ -211,6 +240,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
}
+ /**
+ * Given some equipment holsters, convert the contents of those holsters into converted-decoded packet data.
+ * @param iter an `Iterator` of `EquipmentSlot` objects that are a part of the player's holsters
+ * @param builder the function used to transform to the decoded packet form
+ * @param list the current `List` of transformed data
+ * @param index which holster is currently being explored
+ * @return the `List` of inventory data created from the holsters
+ */
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], builder : ((Int, Equipment) => InternalSlot), list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
if(!iter.hasNext) {
list
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
index 721f5978..13ccf6ee 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
@@ -14,7 +14,8 @@ import scala.annotation.tailrec
* An entry in the `List` of valid implant slots in `DetailedCharacterData`.
* `activation`, if defined, indicates the time remaining (in seconds?) before an implant becomes usable.
* @param implant the type of implant
- * @param activation the activation timer
+ * @param activation the activation timer;
+ * technically, this is "unconfirmed"
* @see `ImplantType`
*/
final case class ImplantEntry(implant : ImplantType.Value,
diff --git a/common/src/test/scala/objects/ImplantTest.scala b/common/src/test/scala/objects/ImplantTest.scala
index e1f47936..e7bc2390 100644
--- a/common/src/test/scala/objects/ImplantTest.scala
+++ b/common/src/test/scala/objects/ImplantTest.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package objects
-import net.psforever.objects.Implant
+import net.psforever.objects.ImplantSlot
import net.psforever.objects.definition.{ImplantDefinition, Stance}
import net.psforever.types.{ExoSuitType, ImplantType}
import org.specs2.mutable._
@@ -16,61 +16,122 @@ class ImplantTest extends Specification {
sample.DurationChargeByExoSuit += ExoSuitType.Standard -> 1
sample.DurationChargeByStance += Stance.Running -> 1
- "define" in {
- sample.Initialization mustEqual 90000
- sample.ActivationCharge mustEqual 3
- sample.DurationChargeBase mustEqual 1
- sample.DurationChargeByExoSuit(ExoSuitType.Agile) mustEqual 2
- sample.DurationChargeByExoSuit(ExoSuitType.Reinforced) mustEqual 2
- sample.DurationChargeByExoSuit(ExoSuitType.Standard) mustEqual 1
- sample.DurationChargeByExoSuit(ExoSuitType.Infiltration) mustEqual 0 //default value
- sample.DurationChargeByStance(Stance.Running) mustEqual 1
- sample.DurationChargeByStance(Stance.Crouching) mustEqual 0 //default value
- sample.Type mustEqual ImplantType.SilentRun
+ "ImplantDefinition" should {
+ "define" in {
+ sample.Initialization mustEqual 90000
+ sample.ActivationCharge mustEqual 3
+ sample.DurationChargeBase mustEqual 1
+ sample.DurationChargeByExoSuit(ExoSuitType.Agile) mustEqual 2
+ sample.DurationChargeByExoSuit(ExoSuitType.Reinforced) mustEqual 2
+ sample.DurationChargeByExoSuit(ExoSuitType.Standard) mustEqual 1
+ sample.DurationChargeByExoSuit(ExoSuitType.Infiltration) mustEqual 0 //default value
+ sample.DurationChargeByStance(Stance.Running) mustEqual 1
+ sample.DurationChargeByStance(Stance.Crouching) mustEqual 0 //default value
+ sample.Type mustEqual ImplantType.SilentRun
+ }
}
- "construct" in {
- val obj = new Implant(sample)
- obj.Definition.Type mustEqual sample.Type
- obj.Active mustEqual false
- obj.Ready mustEqual false
- obj.Timer mustEqual 0
- }
+ "ImplantSlot" should {
+ "construct" in {
+ val obj = new ImplantSlot
+ obj.Unlocked mustEqual false
+ obj.Initialized mustEqual false
+ obj.Active mustEqual false
+ obj.Implant mustEqual ImplantType.None
+ obj.Installed mustEqual None
+ }
- "reset/init their timer" in {
- val obj = new Implant(sample)
- obj.Timer mustEqual 0
- obj.Reset()
- obj.Timer mustEqual 90000
- }
+ "load an implant when locked" in {
+ val obj = new ImplantSlot
+ obj.Unlocked mustEqual false
+ obj.Implant mustEqual ImplantType.None
- "reset/init their readiness condition" in {
- val obj = new Implant(sample)
- obj.Ready mustEqual false
- obj.Timer = 0
- obj.Ready mustEqual true
- obj.Reset()
- obj.Ready mustEqual false
- }
+ obj.Implant = sample
+ obj.Implant mustEqual ImplantType.None
+ }
- "not activate until they are ready" in {
- val obj = new Implant(sample)
- obj.Active = true
- obj.Active mustEqual false
- obj.Timer = 0
- obj.Active = true
- obj.Active mustEqual true
- }
+ "load an implant when unlocked" in {
+ val obj = new ImplantSlot
+ obj.Unlocked mustEqual false
+ obj.Implant mustEqual ImplantType.None
+ sample.Type mustEqual ImplantType.SilentRun
- "not cost energy while not active" in {
- val obj = new Implant(sample)
- obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 0
- }
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Implant mustEqual ImplantType.SilentRun
+ }
- "cost energy while active" in {
- val obj = new Implant(sample)
- obj.Timer = 0
- obj.Active = true
- obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 4
+ "can not re-lock an unlocked implant slot" in {
+ val obj = new ImplantSlot
+ obj.Unlocked mustEqual false
+
+ obj.Unlocked = false
+ obj.Unlocked mustEqual false
+ obj.Unlocked = true
+ obj.Unlocked mustEqual true
+ obj.Unlocked = false
+ obj.Unlocked mustEqual true
+ }
+
+ "initialize without an implant" in {
+ val obj = new ImplantSlot
+ obj.Initialized mustEqual false
+ obj.Initialized = true
+ obj.Initialized mustEqual false
+ }
+
+ "initialize an implant" in {
+ val obj = new ImplantSlot
+ obj.Initialized mustEqual false
+
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Initialized = true
+ obj.Initialized mustEqual true
+ }
+
+ "activate an uninitialized implant" in {
+ val obj = new ImplantSlot
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Initialized mustEqual false
+ obj.Active mustEqual false
+
+ obj.Active = true
+ obj.Active mustEqual false
+ }
+
+ "activate an initialized implant" in {
+ val obj = new ImplantSlot
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Initialized mustEqual false
+ obj.Active mustEqual false
+
+ obj.Initialized = true
+ obj.Active = true
+ obj.Active mustEqual true
+ }
+
+ "not cost energy while not active" in {
+ val obj = new ImplantSlot
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Initialized = true
+ obj.Active mustEqual false
+ obj.ActivationCharge mustEqual 0
+ obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 0
+ }
+
+ "cost energy while active" in {
+ val obj = new ImplantSlot
+ obj.Unlocked = true
+ obj.Implant = sample
+ obj.Initialized = true
+ obj.Active = true
+ obj.Active mustEqual true
+ obj.ActivationCharge mustEqual 3
+ obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 4
+ }
}
}
diff --git a/common/src/test/scala/objects/PlayerTest.scala b/common/src/test/scala/objects/PlayerTest.scala
index fd2d3fc5..2a95f598 100644
--- a/common/src/test/scala/objects/PlayerTest.scala
+++ b/common/src/test/scala/objects/PlayerTest.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package objects
-import net.psforever.objects.{Implant, Player, SimpleItem}
+import net.psforever.objects.{Player, SimpleItem}
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
@@ -106,31 +106,39 @@ class PlayerTest extends Specification {
obj.LastDrawnSlot mustEqual 1
}
- "install no implants until a slot is unlocked" in {
- val testplant : Implant = Implant(ImplantDefinition(1))
+ "install an implant" in {
+ val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Implants(0).Unlocked mustEqual false
- obj.Implant(0) mustEqual None
- obj.InstallImplant(testplant)
- obj.Implant(0) mustEqual None
- obj.Implant(ImplantType(1)) mustEqual None
-
obj.Implants(0).Unlocked = true
- obj.InstallImplant(testplant)
- obj.Implant(0) mustEqual Some(testplant.Definition.Type)
- obj.Implant(ImplantType(1)) mustEqual Some(testplant)
+ obj.InstallImplant(testplant) mustEqual true
+ obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
+ case Some(slot) =>
+ slot.Installed mustEqual Some(testplant)
+ case _ =>
+ ko
+ }
+ ok
+ }
+
+ "can not install the same type of implant twice" in {
+ val testplant1 : ImplantDefinition = ImplantDefinition(1)
+ val testplant2 : ImplantDefinition = ImplantDefinition(1)
+ val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Implants(0).Unlocked = true
+ obj.Implants(1).Unlocked = true
+ obj.InstallImplant(testplant1) mustEqual true
+ obj.InstallImplant(testplant2) mustEqual false
}
"uninstall implants" in {
- val testplant : Implant = Implant(ImplantDefinition(1))
+ val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
- obj.InstallImplant(testplant)
- obj.Implant(ImplantType(1)) mustEqual Some(testplant)
+ obj.InstallImplant(testplant) mustEqual true
+ obj.Implants(0).Installed mustEqual Some(testplant)
- obj.UninstallImplant(ImplantType(1))
- obj.Implant(0) mustEqual None
- obj.Implant(ImplantType(1)) mustEqual None
+ obj.UninstallImplant(testplant.Type)
+ obj.Implants(0).Installed mustEqual None
}
"administrate" in {