simplified the implant management process by removing an unnecessary middleman object; corrected tests that included implants; worked implants into player PacketConverter

This commit is contained in:
FateJH 2017-09-07 23:31:25 -04:00
parent 5962227ad2
commit 5b4ba246e1
9 changed files with 335 additions and 223 deletions

View file

@ -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 import net.psforever.packet.game.objectcreate.ObjectClass
val val
locker_container = new EquipmentDefinition(456) { locker_container = new EquipmentDefinition(456) {

View file

@ -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).<br>
* <br>
* 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)
}
}

View file

@ -1,61 +1,117 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects package net.psforever.objects
import net.psforever.objects.definition.ImplantDefinition import net.psforever.objects.definition.{ImplantDefinition, Stance}
import net.psforever.types.ImplantType import net.psforever.types.{ExoSuitType, ImplantType}
/** /**
* A slot "on the player" into which an implant is installed.<br> * A slot "on the player" into which an implant is installed.
* In total, players have three implant slots.<br>
* <br> * <br>
* In total, players have three implant slots. * All implants slots start as "locked" and must be "unlocked" through battle rank advancement.
* At battle rank one (BR1), however, all of those slots are locked. * Only after it is "unlocked" may an implant be "installed" into the slot.
* The player earns implants at BR16, BR12, and BR18. * Upon installation, it undergoes an initialization period and then, after which, it is ready for user activation.
* A locked implant slot can not be used. * Being jammed de-activates the implant, put it into a state of "not being ready," and causes the initialization to repeat.
* (The code uses "not yet unlocked" logic.)
* When unlocked, an implant may be installed into that slot.<br>
* <br>
* 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.
*/ */
class ImplantSlot { class ImplantSlot {
/** is this slot available for holding an implant */ /** is this slot available for holding an implant */
private var unlocked : Boolean = false 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 */ /** what implant is currently installed in this slot; None if there is no implant currently installed */
private var installed : Option[ImplantType.Value] = None private var implant : Option[ImplantDefinition] = 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
def Unlocked : Boolean = unlocked def Unlocked : Boolean = unlocked
def Unlocked_=(lock : Boolean) : Boolean = { def Unlocked_=(lock : Boolean) : Boolean = {
unlocked = lock unlocked = lock || unlocked
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] = { def Active : Boolean = active
anImplant match {
case Some(module) => def Active_=(state : Boolean) : Boolean = {
Implant = module active = Initialized && state
case None => Active
installed = None }
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 Implant
} }
def Implant_=(anImplant : Implant) : Option[Implant] = { def Installed : Option[ImplantDefinition] = implant
implant = anImplant
installed = Some(anImplant.Definition.Type) def MaxTimer : Long = Implant match {
Implant 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 { object ImplantSlot {
private val default = new Implant(ImplantDefinition(ImplantType.None))
def apply() : ImplantSlot = { def apply() : ImplantSlot = {
new ImplantSlot() new ImplantSlot()
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects 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.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.{GridInventory, InventoryItem} import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
@ -334,26 +334,22 @@ class Player(private val name : String,
def Implants : Array[ImplantSlot] = implants def Implants : Array[ImplantSlot] = implants
def Implant(slot : Int) : Option[ImplantType.Value] = { def Implant(slot : Int) : ImplantType.Value = {
if(-1 < slot && slot < implants.length) { implants(slot).Installed } else { None } if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
} }
def Implant(implantType : ImplantType.Value) : Option[Implant] = { def InstallImplant(implant : ImplantDefinition) : Boolean = {
implants.find(_.Installed.contains(implantType)) match { implants.find({p => p.Installed.contains(implant)}) match { //try to find the installed implant
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
case None => 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 false
} }
} }
@ -364,7 +360,7 @@ class Player(private val name : String,
} }
else { else {
val slot = iter.next val slot = iter.next
if(!slot.Unlocked || slot.Installed.contains(implantType)) { if(!slot.Unlocked || slot.Implant == implantType) {
None None
} }
else if(slot.Installed.isEmpty) { else if(slot.Installed.isEmpty) {
@ -377,7 +373,7 @@ class Player(private val name : String,
} }
def UninstallImplant(implantType : ImplantType.Value) : Boolean = { def UninstallImplant(implantType : ImplantType.Value) : Boolean = {
implants.find({slot => slot.Installed.contains(implantType)}) match { implants.find({slot => slot.Implant == implantType}) match {
case Some(slot) => case Some(slot) =>
slot.Implant = None slot.Implant = None
true true
@ -388,9 +384,9 @@ class Player(private val name : String,
def ResetAllImplants() : Unit = { def ResetAllImplants() : Unit = {
implants.foreach(slot => { implants.foreach(slot => {
slot.Implant match { slot.Installed match {
case Some(implant) => case Some(_) =>
implant.Reset() slot.Initialized = false
case None => ; case None => ;
} }
}) })
@ -590,7 +586,7 @@ object Player {
//hand over implants //hand over implants
(0 until 3).foreach(index => { (0 until 3).foreach(index => {
if(obj.Implants(index).Unlocked = player.Implants(index).Unlocked) { 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 //hand over knife

View file

@ -9,10 +9,13 @@ import scala.collection.mutable
* An `Enumeration` of a variety of poses or generalized movement. * An `Enumeration` of a variety of poses or generalized movement.
*/ */
object Stance extends Enumeration { object Stance extends Enumeration {
val Crouching, val
Standing, Crouching,
Walking, //not used, but should still be defined CrouchWalking, //not used, but should still be defined
Running = Value Standing,
Walking, //not used, but should still be defined
Running
= Value
} }
/** /**

View file

@ -1,9 +1,9 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter 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.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 net.psforever.types.{GrenadeState, ImplantType}
import scala.annotation.tailrec import scala.annotation.tailrec
@ -18,9 +18,9 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
obj.Armor / obj.MaxArmor * 255, //TODO not precise obj.Armor / obj.MaxArmor * 255, //TODO not precise
DressBattleRank(obj), DressBattleRank(obj),
DressCommandRank(obj), DressCommandRank(obj),
recursiveMakeImplantEffects(obj.Implants.iterator),
None, //TODO cosmetics None, //TODO cosmetics
None, //TODO implant effects InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)),
GetDrawnSlot(obj) GetDrawnSlot(obj)
) )
) )
@ -132,14 +132,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @see `ImplantEntry` in `DetailedCharacterData` * @see `ImplantEntry` in `DetailedCharacterData`
*/ */
private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = { private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = {
obj.Implants.map(impl => { obj.Implants.map(slot => {
impl.Installed match { slot.Installed match {
case Some(module) => case Some(_) =>
if(impl.Implant.get.Ready) { if(slot.Initialized) {
ImplantEntry(module, None) ImplantEntry(slot.Implant, None)
} }
else { else {
ImplantEntry(module, Some(impl.Implant.get.Timer.toInt)) ImplantEntry(slot.Implant, Some(slot.Installed.get.Initialization.toInt))
} }
case None => case None =>
ImplantEntry(ImplantType.None, None) ImplantEntry(ImplantType.None, None)
@ -147,6 +147,35 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
}).toList }).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. * 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. * 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) 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] = { @tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], builder : ((Int, Equipment) => InternalSlot), list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
if(!iter.hasNext) { if(!iter.hasNext) {
list list

View file

@ -14,7 +14,8 @@ import scala.annotation.tailrec
* An entry in the `List` of valid implant slots in `DetailedCharacterData`. * 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. * `activation`, if defined, indicates the time remaining (in seconds?) before an implant becomes usable.
* @param implant the type of implant * @param implant the type of implant
* @param activation the activation timer * @param activation the activation timer;
* technically, this is "unconfirmed"
* @see `ImplantType` * @see `ImplantType`
*/ */
final case class ImplantEntry(implant : ImplantType.Value, final case class ImplantEntry(implant : ImplantType.Value,

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import net.psforever.objects.Implant import net.psforever.objects.ImplantSlot
import net.psforever.objects.definition.{ImplantDefinition, Stance} import net.psforever.objects.definition.{ImplantDefinition, Stance}
import net.psforever.types.{ExoSuitType, ImplantType} import net.psforever.types.{ExoSuitType, ImplantType}
import org.specs2.mutable._ import org.specs2.mutable._
@ -16,61 +16,122 @@ class ImplantTest extends Specification {
sample.DurationChargeByExoSuit += ExoSuitType.Standard -> 1 sample.DurationChargeByExoSuit += ExoSuitType.Standard -> 1
sample.DurationChargeByStance += Stance.Running -> 1 sample.DurationChargeByStance += Stance.Running -> 1
"define" in { "ImplantDefinition" should {
sample.Initialization mustEqual 90000 "define" in {
sample.ActivationCharge mustEqual 3 sample.Initialization mustEqual 90000
sample.DurationChargeBase mustEqual 1 sample.ActivationCharge mustEqual 3
sample.DurationChargeByExoSuit(ExoSuitType.Agile) mustEqual 2 sample.DurationChargeBase mustEqual 1
sample.DurationChargeByExoSuit(ExoSuitType.Reinforced) mustEqual 2 sample.DurationChargeByExoSuit(ExoSuitType.Agile) mustEqual 2
sample.DurationChargeByExoSuit(ExoSuitType.Standard) mustEqual 1 sample.DurationChargeByExoSuit(ExoSuitType.Reinforced) mustEqual 2
sample.DurationChargeByExoSuit(ExoSuitType.Infiltration) mustEqual 0 //default value sample.DurationChargeByExoSuit(ExoSuitType.Standard) mustEqual 1
sample.DurationChargeByStance(Stance.Running) mustEqual 1 sample.DurationChargeByExoSuit(ExoSuitType.Infiltration) mustEqual 0 //default value
sample.DurationChargeByStance(Stance.Crouching) mustEqual 0 //default value sample.DurationChargeByStance(Stance.Running) mustEqual 1
sample.Type mustEqual ImplantType.SilentRun sample.DurationChargeByStance(Stance.Crouching) mustEqual 0 //default value
sample.Type mustEqual ImplantType.SilentRun
}
} }
"construct" in { "ImplantSlot" should {
val obj = new Implant(sample) "construct" in {
obj.Definition.Type mustEqual sample.Type val obj = new ImplantSlot
obj.Active mustEqual false obj.Unlocked mustEqual false
obj.Ready mustEqual false obj.Initialized mustEqual false
obj.Timer mustEqual 0 obj.Active mustEqual false
} obj.Implant mustEqual ImplantType.None
obj.Installed mustEqual None
}
"reset/init their timer" in { "load an implant when locked" in {
val obj = new Implant(sample) val obj = new ImplantSlot
obj.Timer mustEqual 0 obj.Unlocked mustEqual false
obj.Reset() obj.Implant mustEqual ImplantType.None
obj.Timer mustEqual 90000
}
"reset/init their readiness condition" in { obj.Implant = sample
val obj = new Implant(sample) obj.Implant mustEqual ImplantType.None
obj.Ready mustEqual false }
obj.Timer = 0
obj.Ready mustEqual true
obj.Reset()
obj.Ready mustEqual false
}
"not activate until they are ready" in { "load an implant when unlocked" in {
val obj = new Implant(sample) val obj = new ImplantSlot
obj.Active = true obj.Unlocked mustEqual false
obj.Active mustEqual false obj.Implant mustEqual ImplantType.None
obj.Timer = 0 sample.Type mustEqual ImplantType.SilentRun
obj.Active = true
obj.Active mustEqual true
}
"not cost energy while not active" in { obj.Unlocked = true
val obj = new Implant(sample) obj.Implant = sample
obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 0 obj.Implant mustEqual ImplantType.SilentRun
} }
"cost energy while active" in { "can not re-lock an unlocked implant slot" in {
val obj = new Implant(sample) val obj = new ImplantSlot
obj.Timer = 0 obj.Unlocked mustEqual false
obj.Active = true
obj.Charge(ExoSuitType.Reinforced, Stance.Running) mustEqual 4 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
}
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects 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.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize import net.psforever.objects.equipment.EquipmentSize
import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire} import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
@ -106,31 +106,39 @@ class PlayerTest extends Specification {
obj.LastDrawnSlot mustEqual 1 obj.LastDrawnSlot mustEqual 1
} }
"install no implants until a slot is unlocked" in { "install an implant" in {
val testplant : Implant = Implant(ImplantDefinition(1)) val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) 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.Implants(0).Unlocked = true
obj.InstallImplant(testplant) obj.InstallImplant(testplant) mustEqual true
obj.Implant(0) mustEqual Some(testplant.Definition.Type) obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
obj.Implant(ImplantType(1)) mustEqual Some(testplant) 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 { "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) val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) obj.InstallImplant(testplant) mustEqual true
obj.Implant(ImplantType(1)) mustEqual Some(testplant) obj.Implants(0).Installed mustEqual Some(testplant)
obj.UninstallImplant(ImplantType(1)) obj.UninstallImplant(testplant.Type)
obj.Implant(0) mustEqual None obj.Implants(0).Installed mustEqual None
obj.Implant(ImplantType(1)) mustEqual None
} }
"administrate" in { "administrate" in {