mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-08 22:50:27 +00:00
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:
parent
5962227ad2
commit
5b4ba246e1
9 changed files with 335 additions and 223 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.<br>
|
||||
* A slot "on the player" into which an implant is installed.
|
||||
* In total, players have three implant slots.<br>
|
||||
* <br>
|
||||
* 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.<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.
|
||||
* 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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue