diff --git a/common/src/main/scala/net/psforever/objects/AmmoBox.scala b/common/src/main/scala/net/psforever/objects/AmmoBox.scala
index 8b8edaa62..0b5e81818 100644
--- a/common/src/main/scala/net/psforever/objects/AmmoBox.scala
+++ b/common/src/main/scala/net/psforever/objects/AmmoBox.scala
@@ -40,6 +40,8 @@ object AmmoBox {
* Accepting an `AmmoBox` object that has an uncertain amount of ammunition in it,
* create multiple `AmmoBox` objects where none contain more than the maximum capacity for that ammunition type,
* and the sum of all objects' capacities is the original object's capacity.
+ * The first element in the returned value is always the same object as the input object.
+ * Even if the original ammo object is not split, a list comprised of that same original object is returned.
* @param box an `AmmoBox` object of unspecified capacity
* @return a `List` of `AmmoBox` objects with correct capacities
*/
@@ -48,7 +50,8 @@ object AmmoBox {
val boxCap : Int = box.Capacity
val maxCap : Int = ammoDef.Capacity
val splitCap : Int = boxCap / maxCap
- val list : List[AmmoBox] = List.fill(splitCap)(new AmmoBox(ammoDef))
+ box.Capacity = math.min(box.Capacity, maxCap)
+ val list : List[AmmoBox] = if(splitCap == 0) { Nil } else { box +: List.fill(splitCap - 1)(new AmmoBox(ammoDef)) }
val leftover = boxCap - maxCap * splitCap
if(leftover > 0) {
list :+ AmmoBox(ammoDef, leftover)
diff --git a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
index 4eea81599..fa36256df 100644
--- a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
@@ -11,11 +11,11 @@ import net.psforever.types.ExoSuitType
* @param suitType the `Enumeration` corresponding to this exo-suit
*/
class ExoSuitDefinition(private val suitType : ExoSuitType.Value) {
- private var permission : Int = 0 //TODO certification type?
- private var maxArmor : Int = 0
- private val holsters : Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
- private var inventoryScale : InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
- private var inventoryOffset : Int = 0
+ protected var permission : Int = 0 //TODO certification type?
+ protected var maxArmor : Int = 0
+ protected val holsters : Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
+ protected var inventoryScale : InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
+ protected var inventoryOffset : Int = 0
def SuitType : ExoSuitType.Value = suitType
@@ -36,7 +36,7 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) {
def InventoryOffset : Int = inventoryOffset
def InventoryOffset_=(offset : Int) : Int = {
- inventoryOffset = offset
+ inventoryOffset = math.min(math.max(0, offset), 65535)
InventoryOffset
}
@@ -60,6 +60,45 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) {
EquipmentSize.Blocked
}
}
+
+ def Use : ExoSuitDefinition = this
+}
+
+class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends ExoSuitDefinition(suitType) {
+ private var activatedSpecial : SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal
+
+ def UsingSpecial : SpecialExoSuitDefinition.Mode.Value = activatedSpecial
+
+ def UsingSpecial_=(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = {
+ activatedSpecial = state
+ UsingSpecial
+ }
+
+ override def Use : ExoSuitDefinition = {
+ val obj = new SpecialExoSuitDefinition(SuitType)
+ obj.MaxArmor = MaxArmor
+ obj.InventoryScale = InventoryScale
+ obj.InventoryOffset = InventoryOffset
+ (0 until 5).foreach(index => { obj.Holster(index, Holster(index)) })
+ obj
+ }
+}
+
+object SpecialExoSuitDefinition {
+ def apply(suitType : ExoSuitType.Value) : SpecialExoSuitDefinition = {
+ new SpecialExoSuitDefinition(suitType)
+ }
+
+ object Mode extends Enumeration {
+ type Type = Value
+
+ val
+ Normal,
+ Anchored,
+ Overdrive,
+ Shielded
+ = Value
+ }
}
object ExoSuitDefinition {
@@ -91,7 +130,7 @@ object ExoSuitDefinition {
Reinforced.Holster(3, EquipmentSize.Rifle)
Reinforced.Holster(4, EquipmentSize.Melee)
- final val Infiltration = ExoSuitDefinition(ExoSuitType.Standard)
+ final val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
Infiltration.permission = 1
Infiltration.MaxArmor = 0
Infiltration.InventoryScale = InventoryTile.Tile66
@@ -99,7 +138,7 @@ object ExoSuitDefinition {
Infiltration.Holster(0, EquipmentSize.Pistol)
Infiltration.Holster(4, EquipmentSize.Melee)
- final val MAX = ExoSuitDefinition(ExoSuitType.MAX)
+ final val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
MAX.permission = 1
MAX.MaxArmor = 650
MAX.InventoryScale = InventoryTile.Tile1612
@@ -118,11 +157,11 @@ object ExoSuitDefinition {
*/
def Select(suit : ExoSuitType.Value) : ExoSuitDefinition = {
suit match {
- case ExoSuitType.Agile => ExoSuitDefinition.Agile
- case ExoSuitType.Infiltration => ExoSuitDefinition.Infiltration
- case ExoSuitType.MAX => ExoSuitDefinition.MAX
- case ExoSuitType.Reinforced => ExoSuitDefinition.Reinforced
- case _ => ExoSuitDefinition.Standard
+ case ExoSuitType.Agile => ExoSuitDefinition.Agile.Use
+ case ExoSuitType.Infiltration => ExoSuitDefinition.Infiltration.Use
+ case ExoSuitType.MAX => ExoSuitDefinition.MAX.Use
+ case ExoSuitType.Reinforced => ExoSuitDefinition.Reinforced.Use
+ case _ => ExoSuitDefinition.Standard.Use
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 91f6c77e1..d3b9c05f9 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -278,11 +278,38 @@ object GlobalDefinitions {
val flamethrower = ToolDefinition(ObjectClass.flamethrower)
- val trhev_dualcycler = ToolDefinition(ObjectClass.trhev_dualcycler)
+ val trhev_dualcycler = new ToolDefinition(ObjectClass.trhev_dualcycler) {
+ override def NextFireModeIndex(index : Int) : Int = index
+ }
- val trhev_pounder = ToolDefinition(ObjectClass.trhev_pounder)
+ val trhev_pounder = new ToolDefinition(ObjectClass.trhev_pounder) {
+ override def NextFireModeIndex(index : Int) : Int = {
+ //TODO other modes
+ if(index == 0 || index == 3) {
+ if(index == 0) {
+ 3 //3-second fuse
+ }
+ else {
+ 0 //explode on contact
+ }
+ }
+ else if(index == 1 || index == 4) {
+ if(index == 1) {
+ 4 //3-second fuse, anchored
+ }
+ else {
+ 1 //explode on contact, anchored
+ }
+ }
+ else {
+ index
+ }
+ }
+ }
- val trhev_burster = ToolDefinition(ObjectClass.trhev_burster)
+ val trhev_burster = new ToolDefinition(ObjectClass.trhev_burster) {
+ override def NextFireModeIndex(index : Int) : Int = index
+ }
val nchev_scattercannon = ToolDefinition(ObjectClass.nchev_scattercannon)
@@ -1672,18 +1699,42 @@ object GlobalDefinitions {
trhev_dualcycler.FireModes.head.AmmoTypeIndices += 0
trhev_dualcycler.FireModes.head.AmmoSlotIndex = 0
trhev_dualcycler.FireModes.head.Magazine = 200
+ trhev_dualcycler.FireModes += new FireModeDefinition //anchored
+ trhev_dualcycler.FireModes(1).AmmoTypeIndices += 0
+ trhev_dualcycler.FireModes(1).AmmoSlotIndex = 0
+ trhev_dualcycler.FireModes(1).Magazine = 200
+ trhev_dualcycler.FireModes += new FireModeDefinition //overdrive?
+ trhev_dualcycler.FireModes(2).AmmoTypeIndices += 0
+ trhev_dualcycler.FireModes(2).AmmoSlotIndex = 0
+ trhev_dualcycler.FireModes(2).Magazine = 200
trhev_pounder.Name = "trhev_pounder"
trhev_pounder.Size = EquipmentSize.Max
trhev_pounder.AmmoTypes += pounder_ammo
trhev_pounder.FireModes += new FireModeDefinition
- trhev_pounder.FireModes.head.AmmoTypeIndices += 0
+ trhev_pounder.FireModes.head.AmmoTypeIndices += 0 //explode on contact
trhev_pounder.FireModes.head.AmmoSlotIndex = 0
trhev_pounder.FireModes.head.Magazine = 30
- trhev_pounder.FireModes += new FireModeDefinition
+ trhev_pounder.FireModes += new FireModeDefinition //explode on contact, anchored
trhev_pounder.FireModes(1).AmmoTypeIndices += 0
trhev_pounder.FireModes(1).AmmoSlotIndex = 0
trhev_pounder.FireModes(1).Magazine = 30
+ trhev_pounder.FireModes += new FireModeDefinition //explode on contact, overdrive?
+ trhev_pounder.FireModes(2).AmmoTypeIndices += 0
+ trhev_pounder.FireModes(2).AmmoSlotIndex = 0
+ trhev_pounder.FireModes(2).Magazine = 30
+ trhev_pounder.FireModes += new FireModeDefinition //3-second fuse
+ trhev_pounder.FireModes(3).AmmoTypeIndices += 0
+ trhev_pounder.FireModes(3).AmmoSlotIndex = 0
+ trhev_pounder.FireModes(3).Magazine = 30
+ trhev_pounder.FireModes += new FireModeDefinition //3-second fuse, anchored
+ trhev_pounder.FireModes(4).AmmoTypeIndices += 0
+ trhev_pounder.FireModes(4).AmmoSlotIndex = 0
+ trhev_pounder.FireModes(4).Magazine = 30
+ trhev_pounder.FireModes += new FireModeDefinition //3-second fuse, overdrive?
+ trhev_pounder.FireModes(5).AmmoTypeIndices += 0
+ trhev_pounder.FireModes(5).AmmoSlotIndex = 0
+ trhev_pounder.FireModes(5).Magazine = 30
trhev_burster.Name = "trhev_burster"
trhev_burster.Size = EquipmentSize.Max
@@ -2080,7 +2131,7 @@ object GlobalDefinitions {
lightning_weapon_system.Name = "lightning_weapon_system"
lightning_weapon_system.Size = EquipmentSize.VehicleWeapon
lightning_weapon_system.AmmoTypes += bullet_75mm
- lightning_weapon_system.AmmoTypes += bullet_25mm
+ lightning_weapon_system.AmmoTypes += bullet_12mm
lightning_weapon_system.FireModes += new FireModeDefinition
lightning_weapon_system.FireModes.head.AmmoTypeIndices += 0
lightning_weapon_system.FireModes.head.AmmoSlotIndex = 0
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 72246fb4c..19cf484be 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -21,7 +21,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
private var maxHealth : Int = 100 //TODO affected by empire benefits, territory benefits, and bops
private var maxStamina : Int = 100 //does anything affect this?
- private var exosuit : ExoSuitType.Value = ExoSuitType.Standard
+ private var exosuit : ExoSuitDefinition = ExoSuitDefinition.Standard
private val freeHand : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
private val holsters : Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
private val inventory : GridInventory = GridInventory()
@@ -48,7 +48,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
/** From PlanetsideAttributeMessage */
var PlanetsideAttribute : Array[Long] = Array.ofDim(120)
- Player.SuitSetup(this, ExoSuit)
+ Player.SuitSetup(this, exosuit)
def Name : String = core.name
@@ -127,9 +127,9 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
Armor
}
- def MaxArmor : Int = ExoSuitDefinition.Select(exosuit).MaxArmor
+ def MaxArmor : Int = exosuit.MaxArmor
- def VisibleSlots : Set[Int] = if(exosuit == ExoSuitType.MAX) { Set(0) } else { Set(0,1,2,3,4) }
+ def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) { Set(0) } else { Set(0,1,2,3,4) }
override def Slot(slot : Int) : EquipmentSlot = {
if(inventory.Offset <= slot && slot <= inventory.LastIndex) {
@@ -262,10 +262,13 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
def LastDrawnSlot : Int = lastDrawnSlot
- def ExoSuit : ExoSuitType.Value = exosuit
+ def ExoSuit : ExoSuitType.Value = exosuit.SuitType
def ExoSuit_=(suit : ExoSuitType.Value) : Unit = {
- exosuit = suit
+ val eSuit = ExoSuitDefinition.Select(suit)
+ exosuit = eSuit
+ Player.SuitSetup(this, eSuit)
+ ChangeSpecialAbility()
}
def LoadLoadout(line : Int) : Option[Loadout] = core.LoadLoadout(line)
@@ -322,6 +325,93 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
Cloaked
}
+ private var usingSpecial : (SpecialExoSuitDefinition.Mode.Value)=>SpecialExoSuitDefinition.Mode.Value = DefaultUsingSpecial
+
+ private var gettingSpecial : ()=>SpecialExoSuitDefinition.Mode.Value = DefaultGettingSpecial
+
+ private def ChangeSpecialAbility() : Unit = {
+ if(ExoSuit == ExoSuitType.MAX) {
+ gettingSpecial = MAXGettingSpecial
+ usingSpecial = Faction match {
+ case PlanetSideEmpire.TR => UsingAnchorsOrOverdrive
+ case PlanetSideEmpire.NC => UsingShield
+ case _ => DefaultUsingSpecial
+ }
+ }
+ else {
+ usingSpecial = DefaultUsingSpecial
+ gettingSpecial = DefaultGettingSpecial
+ }
+ }
+
+ def UsingSpecial : SpecialExoSuitDefinition.Mode.Value = { gettingSpecial() }
+
+ def UsingSpecial_=(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = usingSpecial(state)
+
+ private def DefaultUsingSpecial(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal
+
+ private def UsingAnchorsOrOverdrive(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = {
+ import SpecialExoSuitDefinition.Mode._
+ val curr = UsingSpecial
+ val next = if(curr == Normal) {
+ if(state == Anchored || state == Overdrive) {
+ state
+ }
+ else {
+ Normal
+ }
+ }
+ else if(state == Normal) {
+ Normal
+ }
+ else {
+ curr
+ }
+ MAXUsingSpecial(next)
+ }
+
+ private def UsingShield(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = {
+ import SpecialExoSuitDefinition.Mode._
+ val curr = UsingSpecial
+ val next = if(curr == Normal) {
+ if(state == Shielded) {
+ state
+ }
+ else {
+ Normal
+ }
+ }
+ else if(state == Normal) {
+ Normal
+ }
+ else {
+ curr
+ }
+ MAXUsingSpecial(next)
+ }
+
+ private def DefaultGettingSpecial() : SpecialExoSuitDefinition.Mode.Value = SpecialExoSuitDefinition.Mode.Normal
+
+ private def MAXUsingSpecial(state : SpecialExoSuitDefinition.Mode.Value) : SpecialExoSuitDefinition.Mode.Value = exosuit match {
+ case obj : SpecialExoSuitDefinition =>
+ obj.UsingSpecial = state
+ case _ =>
+ SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ private def MAXGettingSpecial() : SpecialExoSuitDefinition.Mode.Value = exosuit match {
+ case obj : SpecialExoSuitDefinition =>
+ obj.UsingSpecial
+ case _ =>
+ SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ def isAnchored : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored
+
+ def isOverdrived : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive
+
+ def isShielded : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded
+
def AccessingBackpack : Option[PlanetSideGUID] = backpackAccess
def AccessingBackpack_=(guid : PlanetSideGUID) : Option[PlanetSideGUID] = {
@@ -408,16 +498,13 @@ object Player {
new Player(core)
}
- def SuitSetup(player : Player, eSuit : ExoSuitType.Value) : Unit = {
- val esuitDef : ExoSuitDefinition = ExoSuitDefinition.Select(eSuit)
- //exosuit
- player.ExoSuit = eSuit
+ private def SuitSetup(player : Player, eSuit : ExoSuitDefinition) : Unit = {
//inventory
player.Inventory.Clear()
- player.Inventory.Resize(esuitDef.InventoryScale.Width, esuitDef.InventoryScale.Height)
- player.Inventory.Offset = esuitDef.InventoryOffset
+ player.Inventory.Resize(eSuit.InventoryScale.Width, eSuit.InventoryScale.Height)
+ player.Inventory.Offset = eSuit.InventoryOffset
//holsters
- (0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) })
+ (0 until 5).foreach(index => { player.Slot(index).Size = eSuit.Holster(index) })
}
def Respawn(player : Player) : Player = {
diff --git a/common/src/main/scala/net/psforever/objects/Tool.scala b/common/src/main/scala/net/psforever/objects/Tool.scala
index 2b6d122aa..355c5a23b 100644
--- a/common/src/main/scala/net/psforever/objects/Tool.scala
+++ b/common/src/main/scala/net/psforever/objects/Tool.scala
@@ -32,7 +32,15 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
def FireMode : FireModeDefinition = toolDef.FireModes(fireModeIndex)
def NextFireMode : FireModeDefinition = {
- FireModeIndex = FireModeIndex + 1
+ FireModeIndex = toolDef.NextFireModeIndex(FireModeIndex)
+ AmmoSlot.Chamber = FireMode.Chamber
+ FireMode
+ }
+
+ def ToFireMode : Int = toolDef.NextFireModeIndex(FireModeIndex)
+
+ def ToFireMode_=(index : Int) : FireModeDefinition = {
+ FireModeIndex = index
AmmoSlot.Chamber = FireMode.Chamber
FireMode
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
index 54cda7166..c6a066761 100644
--- a/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
@@ -15,6 +15,8 @@ class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
def AmmoTypes : mutable.ListBuffer[AmmoBoxDefinition] = ammoTypes
def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes
+
+ def NextFireModeIndex(index : Int) : Int = index + 1
}
object ToolDefinition {
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
index 8785ae7a0..553d5cc10 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala
@@ -326,9 +326,9 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition
),
"lightning" -> VehicleLoadout("default_lightning", List(),
List(
- SimplifiedEntry(ammo_25mm, 30),
- SimplifiedEntry(ammo_25mm, 34),
- SimplifiedEntry(ammo_25mm, 38),
+ SimplifiedEntry(ammo_12mm, 30),
+ SimplifiedEntry(ammo_12mm, 34),
+ SimplifiedEntry(ammo_12mm, 38),
SimplifiedEntry(ammo_75mm, 90),
SimplifiedEntry(ammo_75mm, 94),
SimplifiedEntry(ammo_75mm, 98)
diff --git a/common/src/main/scala/net/psforever/packet/game/ActionResultMessage.scala b/common/src/main/scala/net/psforever/packet/game/ActionResultMessage.scala
index e90d52100..fe7f0b44a 100644
--- a/common/src/main/scala/net/psforever/packet/game/ActionResultMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ActionResultMessage.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game
-import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import scodec.Codec
import scodec.codecs._
@@ -10,7 +10,8 @@ import scodec.codecs._
* Is sent by the server when the client has performed an action from a menu item
* (i.e create character, delete character, etc...)
*/
-final case class ActionResultMessage(successfull : Boolean, errorCode : Option[Long])
+final case class ActionResultMessage(successful : Boolean,
+ errorCode : Option[Long])
extends PlanetSideGamePacket {
type Packet = ActionResultMessage
def opcode = GamePacketOpcode.ActionResultMessage
@@ -18,6 +19,14 @@ final case class ActionResultMessage(successfull : Boolean, errorCode : Option[L
}
object ActionResultMessage extends Marshallable[ActionResultMessage] {
+ def apply() : ActionResultMessage = {
+ ActionResultMessage(true, None)
+ }
+
+ def apply(error : Long) : ActionResultMessage = {
+ ActionResultMessage(false, Some(error))
+ }
+
implicit val codec : Codec[ActionResultMessage] = (
("successful" | bool) >>:~ { res =>
// if not successful, look for an error code
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
index a424d02bb..acfaa4079 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
@@ -223,7 +223,7 @@ object Prefab {
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
Some(InventoryData(
InventoryItemData(ObjectClass.lightning_weapon_system, weapon_guid, 1,
- WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_25mm, ammo2_guid, 1, AmmoBoxData(0x0))
+ WeaponData(0x4, 0x8, 0, ObjectClass.bullet_75mm, ammo1_guid, 0, AmmoBoxData(0x0), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(0x0))
) :: Nil)
)
)(VehicleFormat.Normal)
diff --git a/common/src/test/scala/game/ActionResultMessageTest.scala b/common/src/test/scala/game/ActionResultMessageTest.scala
index c8ee8eb2c..a6bce43c7 100644
--- a/common/src/test/scala/game/ActionResultMessageTest.scala
+++ b/common/src/test/scala/game/ActionResultMessageTest.scala
@@ -7,27 +7,54 @@ import net.psforever.packet.game._
import scodec.bits._
class ActionResultMessageTest extends Specification {
- "decode" in {
- PacketCoding.DecodePacket(hex"1f 80").require match {
- case ActionResultMessage(okay, code) =>
- okay === true
- code === None
- case _ =>
- ko
- }
+ val string_pass = hex"1f 80"
+ val string_fail = hex"1f 0080000000"
- PacketCoding.DecodePacket((hex"1f".bits ++ bin"0" ++ hex"01000000".bits).toByteVector).require match {
+ "decode (pass)" in {
+ PacketCoding.DecodePacket(string_pass).require match {
case ActionResultMessage(okay, code) =>
- okay === false
- code === Some(1)
+ okay mustEqual true
+ code mustEqual None
case _ =>
ko
}
}
- "encode" in {
- PacketCoding.EncodePacket(ActionResultMessage(true, None)).require.toByteVector === hex"1f 80"
- PacketCoding.EncodePacket(ActionResultMessage(false, Some(1))).require.toByteVector ===
- (hex"1f".bits ++ bin"0" ++ hex"01000000".bits).toByteVector
+ "decode (fail)" in {
+ PacketCoding.DecodePacket(string_fail).require match {
+ case ActionResultMessage(okay, code) =>
+ okay mustEqual false
+ code mustEqual Some(1)
+ case _ =>
+ ko
+ }
+ }
+
+ "encode (pass, full)" in {
+ val msg = ActionResultMessage(true, None)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_pass
+ }
+
+ "encode (pass, minimal)" in {
+ val msg = ActionResultMessage()
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_pass
+ }
+
+ "encode (fail, full)" in {
+ val msg = ActionResultMessage(false, Some(1))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_fail
+ }
+
+ "encode (fail, minimal)" in {
+ val msg = ActionResultMessage(1)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_fail
}
}
diff --git a/common/src/test/scala/objects/ExoSuitTest.scala b/common/src/test/scala/objects/ExoSuitTest.scala
new file mode 100644
index 000000000..484c748c1
--- /dev/null
+++ b/common/src/test/scala/objects/ExoSuitTest.scala
@@ -0,0 +1,145 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects._
+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 net.psforever.types.ExoSuitType
+import org.specs2.mutable._
+
+class ExoSuitTest extends Specification {
+ "ExoSuitDefinition" should {
+ "construct" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.MaxArmor mustEqual 0
+ obj.InventoryScale mustEqual InventoryTile.Tile11
+ obj.InventoryOffset mustEqual 0
+ obj.SuitType mustEqual ExoSuitType.Standard
+ obj.Holsters.length mustEqual 5
+ obj.Holsters.foreach(slot => { if(slot != EquipmentSize.Blocked) { ko } })
+ ok
+ }
+
+ "produce the type of exo-suit that was provided as a clarified type" in {
+ ExoSuitDefinition(ExoSuitType.Standard).SuitType mustEqual ExoSuitType.Standard
+ ExoSuitDefinition(ExoSuitType.Agile).SuitType mustEqual ExoSuitType.Agile
+ }
+
+ "change the maximum armor value" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.MaxArmor mustEqual 0
+ obj.MaxArmor = 1
+ obj.MaxArmor mustEqual 1
+ }
+
+ "not change the maximum armor to an invalid value" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.MaxArmor mustEqual 0
+ obj.MaxArmor = -1
+ obj.MaxArmor mustEqual 0
+ obj.MaxArmor = 65536
+ obj.MaxArmor mustEqual 65535
+ }
+
+ "change the size of the inventory" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.InventoryScale mustEqual InventoryTile.Tile11
+ obj.InventoryScale = InventoryTile.Tile42
+ obj.InventoryScale mustEqual InventoryTile.Tile42
+ }
+
+ "change the start index of the inventory" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.InventoryOffset mustEqual 0
+ obj.InventoryOffset = 1
+ obj.InventoryOffset mustEqual 1
+ }
+
+ "not change the start index of the inventory to an invalid value" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.InventoryOffset mustEqual 0
+ obj.InventoryOffset = -1
+ obj.InventoryOffset mustEqual 0
+ obj.InventoryOffset = 65536
+ obj.InventoryOffset mustEqual 65535
+ }
+
+ "change specific holsters to specific values" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.Holster(0) mustEqual EquipmentSize.Blocked
+ obj.Holster(0, EquipmentSize.Pistol)
+ obj.Holster(0) mustEqual EquipmentSize.Pistol
+ obj.Holster(4) mustEqual EquipmentSize.Blocked
+ obj.Holster(4, EquipmentSize.Rifle)
+ obj.Holster(4) mustEqual EquipmentSize.Rifle
+ (0 to 4).foreach {
+ case 0 => obj.Holsters(0) mustEqual EquipmentSize.Pistol
+ case 4 => obj.Holsters(4) mustEqual EquipmentSize.Rifle
+ case x => obj.Holsters(x) mustEqual EquipmentSize.Blocked
+ }
+ ok
+ }
+
+ "can not change any slot that does not exist" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ obj.Holster(9) mustEqual EquipmentSize.Blocked
+ obj.Holster(9, EquipmentSize.Pistol)
+ obj.Holster(9) mustEqual EquipmentSize.Blocked
+ }
+
+ "produce a copy of the definition" in {
+ val obj = ExoSuitDefinition(ExoSuitType.Standard)
+ val obj2 = obj.Use
+ obj eq obj2
+ }
+ }
+
+ "SpecialExoSuitDefinition" should {
+ "construct" in {
+ val obj = SpecialExoSuitDefinition(ExoSuitType.Standard)
+ obj.MaxArmor mustEqual 0
+ obj.InventoryScale mustEqual InventoryTile.Tile11
+ obj.InventoryOffset mustEqual 0
+ obj.SuitType mustEqual ExoSuitType.Standard
+ obj.Holsters.length mustEqual 5
+ obj.Holsters.foreach(slot => { if(slot != EquipmentSize.Blocked) { ko } })
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ "configure UsingSpecial to various values" in {
+ val obj = SpecialExoSuitDefinition(ExoSuitType.Standard)
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Overdrive
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Shielded
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ "produce a separate copy of the definition" in {
+ val obj = SpecialExoSuitDefinition(ExoSuitType.Standard)
+ val obj2 = obj.Use
+ obj ne obj2
+ }
+ }
+
+ "ExoSuitDefinition.Select" should {
+ "produce common, shared instances of exo suits" in {
+ ExoSuitDefinition.Select(ExoSuitType.Standard) eq ExoSuitDefinition.Select(ExoSuitType.Standard)
+ ExoSuitDefinition.Select(ExoSuitType.Agile) eq ExoSuitDefinition.Select(ExoSuitType.Agile)
+ ExoSuitDefinition.Select(ExoSuitType.Reinforced) eq ExoSuitDefinition.Select(ExoSuitType.Reinforced)
+ ExoSuitDefinition.Select(ExoSuitType.Infiltration) eq ExoSuitDefinition.Select(ExoSuitType.Infiltration)
+ }
+
+ "produces unique instances of the mechanized assault exo suit" in {
+ val obj = ExoSuitDefinition.Select(ExoSuitType.MAX)
+ obj ne ExoSuitDefinition.Select(ExoSuitType.MAX)
+ obj.isInstanceOf[SpecialExoSuitDefinition] mustEqual true
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala
index 67d0f3100..5ddb448e2 100644
--- a/common/src/test/scala/objects/LoadoutTest.scala
+++ b/common/src/test/scala/objects/LoadoutTest.scala
@@ -94,7 +94,7 @@ class LoadoutTest extends Specification {
val player = CreatePlayer()
val slot = player.Slot(0)
slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max)
- Player.SuitSetup(player, ExoSuitType.MAX)
+ player.ExoSuit = ExoSuitType.MAX
val ldout1 = Loadout.Create(player, "weaponless").asInstanceOf[InfantryLoadout]
slot.Equipment = None
@@ -127,7 +127,7 @@ class LoadoutTest extends Specification {
player.ExoSuit = ExoSuitType.Infiltration
val ldout7 = Loadout.Create(player, "inf").asInstanceOf[InfantryLoadout]
- Player.SuitSetup(player, ExoSuitType.MAX)
+ player.ExoSuit = ExoSuitType.MAX
val ldout3 = Loadout.Create(player, "weaponless").asInstanceOf[InfantryLoadout]
slot.Equipment = None
slot.Equipment = Tool(trhev_dualcycler)
diff --git a/common/src/test/scala/objects/PlayerTest.scala b/common/src/test/scala/objects/PlayerTest.scala
index ff9501be5..560b1c524 100644
--- a/common/src/test/scala/objects/PlayerTest.scala
+++ b/common/src/test/scala/objects/PlayerTest.scala
@@ -16,448 +16,512 @@ class PlayerTest extends Specification {
new Player(Avatar(name, faction, sex, head, voice))
}
- "construct" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.isAlive mustEqual false
- obj.FacingYawUpper mustEqual 0
- obj.Jumping mustEqual false
- obj.Crouching mustEqual false
- obj.Cloaked mustEqual false
+ "Player" should {
+ "construct" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.isAlive mustEqual false
+ obj.FacingYawUpper mustEqual 0
+ obj.Jumping mustEqual false
+ obj.Crouching mustEqual false
+ obj.Cloaked mustEqual false
- obj.FacingYawUpper = 1.3f
- obj.Jumping = true
- obj.Crouching = true
- obj.Cloaked = true
- obj.FacingYawUpper mustEqual 1.3f
- obj.Jumping mustEqual true
- obj.Crouching mustEqual true
- obj.Cloaked mustEqual true
- }
-
- "different players" in {
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
-
- (TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
-
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
-
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
-
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
-
- (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
- TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
- }
-
- "(re)spawn" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.isAlive mustEqual false
- obj.Health mustEqual 0
- obj.Stamina mustEqual 0
- obj.Armor mustEqual 0
- obj.MaxHealth mustEqual 100
- obj.MaxStamina mustEqual 100
- obj.MaxArmor mustEqual 50
- obj.Spawn
- obj.isAlive mustEqual true
- obj.Health mustEqual 100
- obj.Stamina mustEqual 100
- obj.Armor mustEqual 50
- }
-
- "will not (re)spawn if not dead" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Spawn
- obj.Health mustEqual 100
- obj.Armor mustEqual 50
- obj.isAlive mustEqual true
-
- obj.Health = 10
- obj.Armor = 10
- obj.Health mustEqual 10
- obj.Armor mustEqual 10
- obj.Spawn
- obj.Health mustEqual 10
- obj.Armor mustEqual 10
- }
-
- "can die" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Spawn
- obj.Armor = 35 //50 -> 35
- obj.isAlive mustEqual true
- obj.Health mustEqual obj.MaxHealth
- obj.Stamina mustEqual obj.MaxStamina
- obj.Armor mustEqual 35
- obj.Die
- obj.isAlive mustEqual false
- obj.Health mustEqual 0
- obj.Stamina mustEqual 0
- obj.Armor mustEqual 35
- }
-
- "can not become a backpack if alive" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Spawn
- obj.isAlive mustEqual true
- obj.isBackpack mustEqual false
- obj.Release
- obj.isAlive mustEqual true
- obj.isBackpack mustEqual false
- }
-
- "can become a backpack" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.isAlive mustEqual false
- obj.isBackpack mustEqual false
- obj.Release
- obj.isAlive mustEqual false
- obj.isBackpack mustEqual true
- }
-
- "set new maximum values (health, stamina)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.MaxHealth mustEqual 100
- obj.MaxStamina mustEqual 100
- obj.MaxHealth = 123
- obj.MaxStamina = 456
- obj.Spawn
- obj.Health mustEqual 123
- obj.Stamina mustEqual 456
- }
-
- "set new values (health, armor, stamina) but only when alive" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Health = 23
- obj.Armor = 34
- obj.Stamina = 45
- obj.Health mustEqual 0
- obj.Armor mustEqual 0
- obj.Stamina mustEqual 0
-
- obj.Spawn
- obj.Health mustEqual obj.MaxHealth
- obj.Armor mustEqual obj.MaxArmor
- obj.Stamina mustEqual obj.MaxStamina
- obj.Health = 23
- obj.Armor = 34
- obj.Stamina = 45
- obj.Health mustEqual 23
- obj.Armor mustEqual 34
- obj.Stamina mustEqual 45
- }
-
- "has visible slots" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.VisibleSlots mustEqual Set(0,1,2,3,4)
- obj.ExoSuit = ExoSuitType.MAX
- obj.VisibleSlots mustEqual Set(0)
- }
-
- "init (Standard Exo-Suit)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.ExoSuit mustEqual ExoSuitType.Standard
- obj.Slot(0).Size mustEqual EquipmentSize.Pistol
- obj.Slot(1).Size mustEqual EquipmentSize.Blocked
- obj.Slot(2).Size mustEqual EquipmentSize.Rifle
- obj.Slot(3).Size mustEqual EquipmentSize.Blocked
- obj.Slot(4).Size mustEqual EquipmentSize.Melee
- obj.Inventory.Width mustEqual 9
- obj.Inventory.Height mustEqual 6
- obj.Inventory.Offset mustEqual 6
- }
-
- "draw equipped holsters only" in {
- val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(1).Size = EquipmentSize.Pistol
- obj.Slot(1).Equipment = wep
- obj.DrawnSlot mustEqual Player.HandsDownSlot
- obj.DrawnSlot = 0
- obj.DrawnSlot mustEqual Player.HandsDownSlot
- obj.DrawnSlot = 1
- obj.DrawnSlot mustEqual 1
- }
-
- "remember the last drawn holster" in {
- val wep1 = SimpleItem(SimpleItemDefinition(149))
- val wep2 = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(0).Size = EquipmentSize.Pistol
- obj.Slot(0).Equipment = wep1
- obj.Slot(1).Size = EquipmentSize.Pistol
- obj.Slot(1).Equipment = wep2
- obj.DrawnSlot mustEqual Player.HandsDownSlot //default value
- obj.LastDrawnSlot mustEqual Player.HandsDownSlot //default value
-
- obj.DrawnSlot = 1
- obj.DrawnSlot mustEqual 1
- obj.LastDrawnSlot mustEqual 1
-
- obj.DrawnSlot = 0
- obj.DrawnSlot mustEqual 0
- obj.LastDrawnSlot mustEqual 0
-
- obj.DrawnSlot = Player.HandsDownSlot
- obj.DrawnSlot mustEqual Player.HandsDownSlot
- obj.LastDrawnSlot mustEqual 0
-
- obj.DrawnSlot = 1
- obj.DrawnSlot mustEqual 1
- obj.LastDrawnSlot mustEqual 1
-
- obj.DrawnSlot = 0
- obj.DrawnSlot mustEqual 0
- obj.LastDrawnSlot mustEqual 0
-
- obj.DrawnSlot = 1
- obj.DrawnSlot mustEqual 1
- obj.LastDrawnSlot mustEqual 1
-
- obj.DrawnSlot = Player.HandsDownSlot
- obj.DrawnSlot mustEqual Player.HandsDownSlot
- obj.LastDrawnSlot mustEqual 1
- }
-
- "hold something in their free hand" in {
- val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(Player.FreeHandSlot).Equipment = wep
-
- obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
- }
-
- "provide an invalid hand that can not hold anything" in {
- val wep = SimpleItem(SimpleItemDefinition(149))
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(-1).Equipment = wep
-
- obj.Slot(-1).Equipment mustEqual None
- }
-
- "search for the smallest available slot in which to store equipment" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Inventory.Resize(3,3) //fits one item
-
- obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
-
- obj.Fit(Tool(GlobalDefinitions.suppressor)) mustEqual Some(2)
-
- val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
- val ammo2 = AmmoBox(GlobalDefinitions.bullet_9mm)
- val ammo3 = AmmoBox(GlobalDefinitions.bullet_9mm)
- obj.Fit(ammo) mustEqual Some(6)
- obj.Slot(6).Equipment = ammo
- obj.Fit(ammo2) mustEqual Some(Player.FreeHandSlot)
- obj.Slot(Player.FreeHandSlot).Equipment = ammo2
- obj.Fit(ammo3) mustEqual None
- }
-
- "can use their free hand to hold things" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
- obj.FreeHand.Equipment mustEqual None
-
- obj.FreeHand.Equipment = ammo
- obj.FreeHand.Equipment mustEqual Some(ammo)
- }
-
- "can access the player's locker-space" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
- }
-
- "can find equipment" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Slot(0).Equipment = {
- val item = Tool(beamer)
- item.GUID = PlanetSideGUID(1)
- item
- }
- obj.Slot(4).Equipment = {
- val item = Tool(forceblade)
- item.GUID = PlanetSideGUID(2)
- item
- }
- obj.Slot(6).Equipment = {
- val item = ConstructionItem(ace)
- item.GUID = PlanetSideGUID(3)
- item
- }
- obj.Locker.Slot(6).Equipment = {
- val item = Kit(medkit)
- item.GUID = PlanetSideGUID(4)
- item
- }
- obj.FreeHand.Equipment = {
- val item = SimpleItem(remote_electronics_kit)
- item.GUID = PlanetSideGUID(5)
- item
+ obj.FacingYawUpper = 1.3f
+ obj.Jumping = true
+ obj.Crouching = true
+ obj.Cloaked = true
+ obj.FacingYawUpper mustEqual 1.3f
+ obj.Jumping mustEqual true
+ obj.Crouching mustEqual true
+ obj.Cloaked mustEqual true
}
- obj.Find(PlanetSideGUID(1)) mustEqual Some(0) //holsters
- obj.Find(PlanetSideGUID(2)) mustEqual Some(4) //holsters, melee
- obj.Find(PlanetSideGUID(3)) mustEqual Some(6) //inventory
- obj.Find(PlanetSideGUID(4)) mustEqual None //can not find in locker-space
- obj.Find(PlanetSideGUID(5)) mustEqual Some(Player.FreeHandSlot) //free hand
- obj.Find(PlanetSideGUID(6)) mustEqual None //not here
- }
+ "different players" in {
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
- "does equipment collision checking (are we already holding something there?)" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val item1 = Tool(beamer)
- val item2 = Kit(medkit)
- val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
- obj.Slot(0).Equipment = item1
- obj.Slot(6).Equipment = item2
- obj.FreeHand.Equipment = item3
+ (TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
- obj.Collisions(0, 1, 1) match {
- case Success(List(item)) =>
- item.obj mustEqual item1
- item.start mustEqual 0
- case _ =>
- ko
- } //holsters
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
- obj.Collisions(1, 1, 1) match {
- case Success(List()) => ;
- case _ =>
- ko
- } //holsters, nothing
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
- obj.Collisions(6, 1, 1)match {
- case Success(List(item)) =>
- item.obj mustEqual item2
- item.start mustEqual 6
- case _ =>
- ko
- } //inventory
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
- obj.Collisions(Player.FreeHandSlot, 1, 1)match {
- case Success(List(item)) =>
- item.obj mustEqual item3
- item.start mustEqual Player.FreeHandSlot
- case _ =>
- ko
- } //free hand
- }
+ (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
+ TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
+ }
- "battle experience point values of the avatar" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val player = Player(avatar)
+ "(re)spawn" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.isAlive mustEqual false
+ obj.Health mustEqual 0
+ obj.Stamina mustEqual 0
+ obj.Armor mustEqual 0
+ obj.MaxHealth mustEqual 100
+ obj.MaxStamina mustEqual 100
+ obj.MaxArmor mustEqual 50
+ obj.Spawn
+ obj.isAlive mustEqual true
+ obj.Health mustEqual 100
+ obj.Stamina mustEqual 100
+ obj.Armor mustEqual 50
+ }
- player.BEP mustEqual avatar.BEP
- avatar.BEP = 1002
- player.BEP mustEqual avatar.BEP
- }
+ "will not (re)spawn if not dead" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Spawn
+ obj.Health mustEqual 100
+ obj.Armor mustEqual 50
+ obj.isAlive mustEqual true
- "command experience point values of the avatar" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val player = Player(avatar)
+ obj.Health = 10
+ obj.Armor = 10
+ obj.Health mustEqual 10
+ obj.Armor mustEqual 10
+ obj.Spawn
+ obj.Health mustEqual 10
+ obj.Armor mustEqual 10
+ }
- player.CEP mustEqual avatar.CEP
- avatar.CEP = 1002
- player.CEP mustEqual avatar.CEP
- }
+ "can die" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Spawn
+ obj.Armor = 35 //50 -> 35
+ obj.isAlive mustEqual true
+ obj.Health mustEqual obj.MaxHealth
+ obj.Stamina mustEqual obj.MaxStamina
+ obj.Armor mustEqual 35
+ obj.Die
+ obj.isAlive mustEqual false
+ obj.Health mustEqual 0
+ obj.Stamina mustEqual 0
+ obj.Armor mustEqual 35
+ }
- "can get a quick summary of implant slots (default)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val player = Player(avatar)
+ "can not become a backpack if alive" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Spawn
+ obj.isAlive mustEqual true
+ obj.isBackpack mustEqual false
+ obj.Release
+ obj.isAlive mustEqual true
+ obj.isBackpack mustEqual false
+ }
- player.Implants mustEqual Array.empty
- }
+ "can become a backpack" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.isAlive mustEqual false
+ obj.isBackpack mustEqual false
+ obj.Release
+ obj.isAlive mustEqual false
+ obj.isBackpack mustEqual true
+ }
- "can get a quick summary of implant slots (two unlocked, one installed)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val player = Player(avatar)
- val temp = new ImplantDefinition(1)
- avatar.Implants(0).Unlocked = true
- avatar.InstallImplant(new ImplantDefinition(1))
- avatar.Implants(1).Unlocked = true
- avatar.InstallImplant(new ImplantDefinition(2))
- avatar.UninstallImplant(temp.Type)
+ "set new maximum values (health, stamina)" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.MaxHealth mustEqual 100
+ obj.MaxStamina mustEqual 100
+ obj.MaxHealth = 123
+ obj.MaxStamina = 456
+ obj.Spawn
+ obj.Health mustEqual 123
+ obj.Stamina mustEqual 456
+ }
- val list = player.Implants
- //slot 0
- val (implant1, init1, active1) = list(0)
- implant1 mustEqual ImplantType.None
- init1 mustEqual -1
- active1 mustEqual false
- //slot 1
- val (implant2, init2, active2) = list(1)
- implant2 mustEqual ImplantType(2)
- init2 mustEqual 0
- active2 mustEqual false
- }
+ "set new values (health, armor, stamina) but only when alive" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Health = 23
+ obj.Armor = 34
+ obj.Stamina = 45
+ obj.Health mustEqual 0
+ obj.Armor mustEqual 0
+ obj.Stamina mustEqual 0
- "can get a quick summary of implant slots (all unlocked, first two installed)" in {
- val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- val player = Player(avatar)
- avatar.Implants(0).Unlocked = true
- avatar.InstallImplant(new ImplantDefinition(1))
- avatar.Implants(0).Initialized = true
- avatar.Implants(0).Active = true
- avatar.Implants(1).Unlocked = true
- avatar.InstallImplant(new ImplantDefinition(2))
- avatar.Implants(1).Initialized = true
- avatar.Implants(1).Active = false
- avatar.Implants(2).Unlocked = true
+ obj.Spawn
+ obj.Health mustEqual obj.MaxHealth
+ obj.Armor mustEqual obj.MaxArmor
+ obj.Stamina mustEqual obj.MaxStamina
+ obj.Health = 23
+ obj.Armor = 34
+ obj.Stamina = 45
+ obj.Health mustEqual 23
+ obj.Armor mustEqual 34
+ obj.Stamina mustEqual 45
+ }
- val list = player.Implants
- //slot 0
- val (implant1, init1, active1) = list(0)
- implant1 mustEqual ImplantType(1)
- init1 mustEqual 0
- active1 mustEqual true
- //slot 1
- val (implant2, init2, active2) = list(1)
- implant2 mustEqual ImplantType(2)
- init2 mustEqual 0
- active2 mustEqual false
- //slot 2
- val (implant3, init3, active3) = list(2)
- implant3 mustEqual ImplantType.None
- init3 mustEqual -1
- active3 mustEqual false
- }
+ "has visible slots" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.VisibleSlots mustEqual Set(0,1,2,3,4)
+ obj.ExoSuit = ExoSuitType.MAX
+ obj.VisibleSlots mustEqual Set(0)
+ }
- "seat in a vehicle" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.VehicleSeated mustEqual None
- obj.VehicleSeated = PlanetSideGUID(65)
- obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
- obj.VehicleSeated = None
- obj.VehicleSeated mustEqual None
- }
+ "init (Standard Exo-Suit)" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.ExoSuit mustEqual ExoSuitType.Standard
+ obj.Slot(0).Size mustEqual EquipmentSize.Pistol
+ obj.Slot(1).Size mustEqual EquipmentSize.Blocked
+ obj.Slot(2).Size mustEqual EquipmentSize.Rifle
+ obj.Slot(3).Size mustEqual EquipmentSize.Blocked
+ obj.Slot(4).Size mustEqual EquipmentSize.Melee
+ obj.Inventory.Width mustEqual 9
+ obj.Inventory.Height mustEqual 6
+ obj.Inventory.Offset mustEqual 6
+ }
- "own in a vehicle" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.VehicleOwned mustEqual None
- obj.VehicleOwned = PlanetSideGUID(65)
- obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
- obj.VehicleOwned = None
- obj.VehicleOwned mustEqual None
- }
+ "draw equipped holsters only" in {
+ val wep = SimpleItem(SimpleItemDefinition(149))
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(1).Size = EquipmentSize.Pistol
+ obj.Slot(1).Equipment = wep
+ obj.DrawnSlot mustEqual Player.HandsDownSlot
+ obj.DrawnSlot = 0
+ obj.DrawnSlot mustEqual Player.HandsDownSlot
+ obj.DrawnSlot = 1
+ obj.DrawnSlot mustEqual 1
+ }
- "remember what zone he is in" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.Continent mustEqual "home2"
- obj.Continent = "ugd01"
- obj.Continent mustEqual "ugd01"
- }
+ "remember the last drawn holster" in {
+ val wep1 = SimpleItem(SimpleItemDefinition(149))
+ val wep2 = SimpleItem(SimpleItemDefinition(149))
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(0).Size = EquipmentSize.Pistol
+ obj.Slot(0).Equipment = wep1
+ obj.Slot(1).Size = EquipmentSize.Pistol
+ obj.Slot(1).Equipment = wep2
+ obj.DrawnSlot mustEqual Player.HandsDownSlot //default value
+ obj.LastDrawnSlot mustEqual Player.HandsDownSlot //default value
- "toString" in {
- val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
- obj.toString mustEqual "TR Chord 0/100 0/50"
+ obj.DrawnSlot = 1
+ obj.DrawnSlot mustEqual 1
+ obj.LastDrawnSlot mustEqual 1
- obj.GUID = PlanetSideGUID(455)
- obj.Continent = "z3"
- obj.toString mustEqual "TR Chord z3-455 0/100 0/50"
+ obj.DrawnSlot = 0
+ obj.DrawnSlot mustEqual 0
+ obj.LastDrawnSlot mustEqual 0
+
+ obj.DrawnSlot = Player.HandsDownSlot
+ obj.DrawnSlot mustEqual Player.HandsDownSlot
+ obj.LastDrawnSlot mustEqual 0
+
+ obj.DrawnSlot = 1
+ obj.DrawnSlot mustEqual 1
+ obj.LastDrawnSlot mustEqual 1
+
+ obj.DrawnSlot = 0
+ obj.DrawnSlot mustEqual 0
+ obj.LastDrawnSlot mustEqual 0
+
+ obj.DrawnSlot = 1
+ obj.DrawnSlot mustEqual 1
+ obj.LastDrawnSlot mustEqual 1
+
+ obj.DrawnSlot = Player.HandsDownSlot
+ obj.DrawnSlot mustEqual Player.HandsDownSlot
+ obj.LastDrawnSlot mustEqual 1
+ }
+
+ "hold something in their free hand" in {
+ val wep = SimpleItem(SimpleItemDefinition(149))
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(Player.FreeHandSlot).Equipment = wep
+
+ obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
+ }
+
+ "provide an invalid hand that can not hold anything" in {
+ val wep = SimpleItem(SimpleItemDefinition(149))
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(-1).Equipment = wep
+
+ obj.Slot(-1).Equipment mustEqual None
+ }
+
+ "search for the smallest available slot in which to store equipment" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Inventory.Resize(3,3) //fits one item
+
+ obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
+
+ obj.Fit(Tool(GlobalDefinitions.suppressor)) mustEqual Some(2)
+
+ val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
+ val ammo2 = AmmoBox(GlobalDefinitions.bullet_9mm)
+ val ammo3 = AmmoBox(GlobalDefinitions.bullet_9mm)
+ obj.Fit(ammo) mustEqual Some(6)
+ obj.Slot(6).Equipment = ammo
+ obj.Fit(ammo2) mustEqual Some(Player.FreeHandSlot)
+ obj.Slot(Player.FreeHandSlot).Equipment = ammo2
+ obj.Fit(ammo3) mustEqual None
+ }
+
+ "can use their free hand to hold things" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
+ obj.FreeHand.Equipment mustEqual None
+
+ obj.FreeHand.Equipment = ammo
+ obj.FreeHand.Equipment mustEqual Some(ammo)
+ }
+
+ "can access the player's locker-space" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
+ }
+
+ "can find equipment" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Slot(0).Equipment = {
+ val item = Tool(beamer)
+ item.GUID = PlanetSideGUID(1)
+ item
+ }
+ obj.Slot(4).Equipment = {
+ val item = Tool(forceblade)
+ item.GUID = PlanetSideGUID(2)
+ item
+ }
+ obj.Slot(6).Equipment = {
+ val item = ConstructionItem(ace)
+ item.GUID = PlanetSideGUID(3)
+ item
+ }
+ obj.Locker.Slot(6).Equipment = {
+ val item = Kit(medkit)
+ item.GUID = PlanetSideGUID(4)
+ item
+ }
+ obj.FreeHand.Equipment = {
+ val item = SimpleItem(remote_electronics_kit)
+ item.GUID = PlanetSideGUID(5)
+ item
+ }
+
+ obj.Find(PlanetSideGUID(1)) mustEqual Some(0) //holsters
+ obj.Find(PlanetSideGUID(2)) mustEqual Some(4) //holsters, melee
+ obj.Find(PlanetSideGUID(3)) mustEqual Some(6) //inventory
+ obj.Find(PlanetSideGUID(4)) mustEqual None //can not find in locker-space
+ obj.Find(PlanetSideGUID(5)) mustEqual Some(Player.FreeHandSlot) //free hand
+ obj.Find(PlanetSideGUID(6)) mustEqual None //not here
+ }
+
+ "does equipment collision checking (are we already holding something there?)" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val item1 = Tool(beamer)
+ val item2 = Kit(medkit)
+ val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
+ obj.Slot(0).Equipment = item1
+ obj.Slot(6).Equipment = item2
+ obj.FreeHand.Equipment = item3
+
+ obj.Collisions(0, 1, 1) match {
+ case Success(List(item)) =>
+ item.obj mustEqual item1
+ item.start mustEqual 0
+ case _ =>
+ ko
+ } //holsters
+
+ obj.Collisions(1, 1, 1) match {
+ case Success(List()) => ;
+ case _ =>
+ ko
+ } //holsters, nothing
+
+ obj.Collisions(6, 1, 1)match {
+ case Success(List(item)) =>
+ item.obj mustEqual item2
+ item.start mustEqual 6
+ case _ =>
+ ko
+ } //inventory
+
+ obj.Collisions(Player.FreeHandSlot, 1, 1)match {
+ case Success(List(item)) =>
+ item.obj mustEqual item3
+ item.start mustEqual Player.FreeHandSlot
+ case _ =>
+ ko
+ } //free hand
+ }
+
+ "battle experience point values of the avatar" in {
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val player = Player(avatar)
+
+ player.BEP mustEqual avatar.BEP
+ avatar.BEP = 1002
+ player.BEP mustEqual avatar.BEP
+ }
+
+ "command experience point values of the avatar" in {
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val player = Player(avatar)
+
+ player.CEP mustEqual avatar.CEP
+ avatar.CEP = 1002
+ player.CEP mustEqual avatar.CEP
+ }
+
+ "can get a quick summary of implant slots (default)" in {
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val player = Player(avatar)
+
+ player.Implants mustEqual Array.empty
+ }
+
+ "can get a quick summary of implant slots (two unlocked, one installed)" in {
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val player = Player(avatar)
+ val temp = new ImplantDefinition(1)
+ avatar.Implants(0).Unlocked = true
+ avatar.InstallImplant(new ImplantDefinition(1))
+ avatar.Implants(1).Unlocked = true
+ avatar.InstallImplant(new ImplantDefinition(2))
+ avatar.UninstallImplant(temp.Type)
+
+ val list = player.Implants
+ //slot 0
+ val (implant1, init1, active1) = list(0)
+ implant1 mustEqual ImplantType.None
+ init1 mustEqual -1
+ active1 mustEqual false
+ //slot 1
+ val (implant2, init2, active2) = list(1)
+ implant2 mustEqual ImplantType(2)
+ init2 mustEqual 0
+ active2 mustEqual false
+ }
+
+ "can get a quick summary of implant slots (all unlocked, first two installed)" in {
+ val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ val player = Player(avatar)
+ avatar.Implants(0).Unlocked = true
+ avatar.InstallImplant(new ImplantDefinition(1))
+ avatar.Implants(0).Initialized = true
+ avatar.Implants(0).Active = true
+ avatar.Implants(1).Unlocked = true
+ avatar.InstallImplant(new ImplantDefinition(2))
+ avatar.Implants(1).Initialized = true
+ avatar.Implants(1).Active = false
+ avatar.Implants(2).Unlocked = true
+
+ val list = player.Implants
+ //slot 0
+ val (implant1, init1, active1) = list(0)
+ implant1 mustEqual ImplantType(1)
+ init1 mustEqual 0
+ active1 mustEqual true
+ //slot 1
+ val (implant2, init2, active2) = list(1)
+ implant2 mustEqual ImplantType(2)
+ init2 mustEqual 0
+ active2 mustEqual false
+ //slot 2
+ val (implant3, init3, active3) = list(2)
+ implant3 mustEqual ImplantType.None
+ init3 mustEqual -1
+ active3 mustEqual false
+ }
+
+ "seat in a vehicle" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.VehicleSeated mustEqual None
+ obj.VehicleSeated = PlanetSideGUID(65)
+ obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
+ obj.VehicleSeated = None
+ obj.VehicleSeated mustEqual None
+ }
+
+ "own in a vehicle" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.VehicleOwned mustEqual None
+ obj.VehicleOwned = PlanetSideGUID(65)
+ obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
+ obj.VehicleOwned = None
+ obj.VehicleOwned mustEqual None
+ }
+
+ "remember what zone he is in" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.Continent mustEqual "home2"
+ obj.Continent = "ugd01"
+ obj.Continent mustEqual "ugd01"
+ }
+
+ "special is typically normal and can not be changed from normal" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ "a TR MAX can change its special to Overdrive or Anchored" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.ExoSuit = ExoSuitType.MAX
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Overdrive
+ //note
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Overdrive
+ }
+
+ "an NC MAX can change its special to Shielded" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
+ obj.ExoSuit = ExoSuitType.MAX
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Shielded
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ "one faction can not use the other's specials" in {
+ val objtr = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ objtr.ExoSuit = ExoSuitType.MAX
+ objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ objtr.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
+ objtr.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+
+ val objnc = TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)
+ objnc.ExoSuit = ExoSuitType.MAX
+ objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ objnc.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
+ objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ objnc.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ objnc.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ }
+
+ "changing exo-suit type resets the special to Normal (and changing back does not revert it again)" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.ExoSuit = ExoSuitType.MAX
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Anchored
+
+ val test = obj.UsingSpecial
+ obj.ExoSuit = ExoSuitType.Standard
+ obj.UsingSpecial mustEqual SpecialExoSuitDefinition.Mode.Normal
+ obj.ExoSuit = ExoSuitType.MAX
+ obj.UsingSpecial != test mustEqual true
+ }
+
+ "toString" in {
+ val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
+ obj.toString mustEqual "TR Chord 0/100 0/50"
+
+ obj.GUID = PlanetSideGUID(455)
+ obj.Continent = "z3"
+ obj.toString mustEqual "TR Chord z3-455 0/100 0/50"
+ }
}
}
diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala
index 4c750b620..64f8e22f8 100644
--- a/pslogin/src/main/scala/Maps.scala
+++ b/pslogin/src/main/scala/Maps.scala
@@ -460,8 +460,6 @@ object Maps {
def Building2() : Unit = {
//HART building C
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
- LocalObject(12, ProximityTerminal.Constructor(repair_silo)) //repair terminal A
- LocalObject(13, Terminal.Constructor(repair_silo)) //rearm terminal A //ItemTransaction: ItemTransactionMessage(PlanetSideGUID(2050),Buy,3,25mmbullet,0,PlanetSideGUID(0))
LocalObject(186, Terminal.Constructor(cert_terminal))
LocalObject(187, Terminal.Constructor(cert_terminal))
LocalObject(188, Terminal.Constructor(cert_terminal))
@@ -509,8 +507,6 @@ object Maps {
LocalObject(1087, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1088, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1089, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
- ObjectToBuilding(12, 2)
- ObjectToBuilding(13, 2)
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
@@ -652,11 +648,15 @@ object Maps {
def Building51() : Unit = {
//air terminal west of HART C
+ LocalObject(12, ProximityTerminal.Constructor(repair_silo)) //repair terminal
+ LocalObject(13, Terminal.Constructor(repair_silo)) //rearm terminal
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform)))
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(292,
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 90.0f))
)
+ ObjectToBuilding(12, 51)
+ ObjectToBuilding(13, 51)
ObjectToBuilding(304, 51)
ObjectToBuilding(292, 51)
TerminalToSpawnPad(304, 292)
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index bc4d076ff..05075da16 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -73,10 +73,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
var delayedProximityTerminalResets : Map[PlanetSideGUID, Cancellable] = Map.empty
var controlled : Option[Int] = None //keep track of avatar's ServerVehicleOverride state
var traveler : Traveler = null
+ var deadState : DeadState.Value = DeadState.Dead
+ var whenUsedLastKit : Long = 0
var clientKeepAlive : Cancellable = DefaultCancellable.obj
var progressBarUpdate : Cancellable = DefaultCancellable.obj
var reviveTimer : Cancellable = DefaultCancellable.obj
+ var respawnTimer : Cancellable = DefaultCancellable.obj
/**
* Convert a boolean value into an integer value.
@@ -89,6 +92,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
override def postStop() = {
clientKeepAlive.cancel
reviveTimer.cancel
+ respawnTimer.cancel
PlayerActionsToCancel()
localService ! Service.Leave()
vehicleService ! Service.Leave()
@@ -111,12 +115,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(player.isAlive) {
//actually being alive or manually deconstructing
- player.VehicleSeated match {
- case Some(vehicle_guid) =>
- DismountVehicleOnLogOut(vehicle_guid, player_guid)
- case None => ;
- }
-
+ DismountVehicleOnLogOut()
continent.Population ! Zone.Population.Release(avatar)
player.Position = Vector3.Zero //save character before doing this
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectDelete(player_guid, player_guid))
@@ -148,7 +147,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Position = Vector3.Zero //save character before doing this
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
- DismountVehicleOnLogOut(vehicle_guid, player_guid)
+ DismountVehicleOnLogOut()
}
}
@@ -170,26 +169,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* Vehicle cleanup that is specific to log out behavior.
- * @param vehicle_guid the vehicle being occupied
- * @param player_guid the player
*/
- def DismountVehicleOnLogOut(vehicle_guid : PlanetSideGUID, player_guid : PlanetSideGUID) : Unit = {
- // Use mountable type instead of Vehicle type to ensure mountables such as implant terminals are correctly dismounted on logout
- val mountable = continent.GUID(vehicle_guid).get.asInstanceOf[Mountable]
- mountable.Seat(mountable.PassengerInSeat(player).get).get.Occupant = None
-
- // If this is a player constructed vehicle then start deconstruction timer
- // todo: Will base guns implement Vehicle type? Don't want those to deconstruct
- continent.GUID(vehicle_guid) match {
+ def DismountVehicleOnLogOut() : Unit = {
+ //TODO Will base guns implement Vehicle type? Don't want those to deconstruct
+ (player.VehicleSeated match {
+ case Some(vehicle_guid) =>
+ continent.GUID(vehicle_guid)
+ case None =>
+ None
+ }) match {
case Some(vehicle : Vehicle) =>
- if(vehicle.Seats.values.count(_.isOccupied) == 0) {
- vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 600L) //start vehicle decay (10m)
- }
+ vehicle.Seat(vehicle.PassengerInSeat(player).get).get.Occupant = None
+ if(vehicle.Seats.values.count(_.isOccupied) == 0) {
+ vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 600L) //start vehicle decay (10m)
+ }
+ vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
+
+ case Some(mobj : Mountable) =>
+ mobj.Seat(mobj.PassengerInSeat(player).get).get.Occupant = None
+
case _ => ;
}
-
- vehicleService ! Service.Leave(Some(s"${mountable.Actor}"))
- vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, 0, true, vehicle_guid))
}
def receive = Initializing
@@ -244,7 +244,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(KeepAliveMessage())
case AvatarServiceResponse(_, guid, reply) =>
- val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) }
+ val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(-1) }
reply match {
case AvatarResponse.ArmorChanged(suit, subtype) =>
if(tplayer_guid != guid) {
@@ -298,8 +298,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
- case AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data) =>
+ case msg @ AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data) =>
if(tplayer_guid != guid) {
+ log.info(s"now dropping a $msg")
sendResponse(
ObjectCreateMessage(
item_id,
@@ -699,13 +700,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
order match {
case Terminal.BuyExosuit(exosuit, subtype) => //refresh armor points
if(tplayer.ExoSuit == exosuit) {
- if(Loadout.DetermineSubtype(tplayer) != subtype) {
- //special case: MAX suit switching to a different MAX suit; we need to change the main weapon
- sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, subtype))
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype))
- val arms = tplayer.Slot(0).Equipment.get
- val putTask = PutEquipmentInSlot(tplayer, Tool(GlobalDefinitions.MAXArms(subtype, tplayer.Faction)), 0)
- taskResolver ! DelayedObjectHeld(tplayer, 0, List(TaskResolver.GiveTask(putTask.task, putTask.subs :+ RemoveEquipmentFromSlot(tplayer, arms, 0))))
+ if(exosuit == ExoSuitType.MAX) {
+ //special MAX case - clear any special state
+ player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
+ player.ExoSuit = exosuit
+ if(Loadout.DetermineSubtype(tplayer) != subtype) {
+ //special MAX case - suit switching to a different MAX suit; we need to change the main weapon
+ sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, subtype))
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype))
+ val arms = tplayer.Slot(0).Equipment.get
+ val putTask = PutEquipmentInSlot(tplayer, Tool(GlobalDefinitions.MAXArms(subtype, tplayer.Faction)), 0)
+ taskResolver ! DelayedObjectHeld(tplayer, 0, List(TaskResolver.GiveTask(putTask.task, putTask.subs :+ RemoveEquipmentFromSlot(tplayer, arms, 0))))
+ }
}
//outside of the MAX condition above, we should seldom reach this point through conventional methods
tplayer.Armor = tplayer.MaxArmor
@@ -713,14 +719,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
}
- else { //load a complete new exo-suit and shuffle the inventory around
+ else {
+ //load a complete new exo-suit and shuffle the inventory around
val originalSuit = tplayer.ExoSuit
//save inventory before it gets cleared (empty holsters)
val dropPred = DropPredicate(tplayer)
val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred)
val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred)
//change suit (clear inventory and change holster sizes; note: holsters must be empty before this point)
- Player.SuitSetup(tplayer, exosuit)
+ tplayer.ExoSuit = exosuit
tplayer.Armor = tplayer.MaxArmor
//delete everything not dropped
(beforeHolsters ++ beforeInventory).foreach({ elem =>
@@ -748,6 +755,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
normalWeapons
}
else {
+ tplayer.DrawnSlot = Player.HandsDownSlot
+ sendResponse(ObjectHeldMessage(tplayer.GUID, Player.HandsDownSlot, true))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectHeld(tplayer.GUID, Player.HandsDownSlot))
beforeHolsters
}
@@ -838,6 +847,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment
log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true))
+ //ensure arm is down
+ tplayer.DrawnSlot = Player.HandsDownSlot
+ sendResponse(ObjectHeldMessage(tplayer.GUID, Player.HandsDownSlot, true))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectHeld(tplayer.GUID, Player.HandsDownSlot))
+ //load
val dropPred = DropPredicate(tplayer)
val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred)
val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred)
@@ -845,7 +859,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val (_, afterInventory) = inventory.partition(dropPred) //dropped items are lost
val beforeFreeHand = tplayer.FreeHand.Equipment
//change suit (clear inventory and change holster sizes; note: holsters must be empty before this point)
- Player.SuitSetup(tplayer, exosuit)
+ tplayer.ExoSuit = exosuit
tplayer.Armor = tplayer.MaxArmor
//delete everything (not dropped)
beforeHolsters.foreach({ elem =>
@@ -1235,11 +1249,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Zone.Lattice.SpawnPoint(zone_id, building, spawn_tube) =>
log.info(s"Zone.Lattice.SpawnPoint: spawn point on $zone_id in ${building.Id} @ ${spawn_tube.GUID.guid} selected")
+ respawnTimer.cancel
reviveTimer.cancel
val sameZone = zone_id == continent.Id
val backpack = player.isBackpack
val respawnTime : Long = if(sameZone) { 10 } else { 0 } //s
val respawnTimeMillis = respawnTime * 1000 //ms
+ deadState = DeadState.RespawnTime
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, respawnTimeMillis, respawnTimeMillis, Vector3.Zero, player.Faction, true))
val tplayer = if(backpack) {
RespawnClone(player) //new player
@@ -1279,7 +1295,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
- context.system.scheduler.scheduleOnce(respawnTime seconds, target, msg)
+ respawnTimer = context.system.scheduler.scheduleOnce(respawnTime seconds, target, msg)
case Zone.Lattice.NoValidSpawnPoint(zone_number, None) =>
log.warn(s"Zone.Lattice.SpawnPoint: zone $zone_number could not be accessed as requested")
@@ -1296,6 +1312,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
LivePlayerList.Add(sessionId, avatar)
traveler = new Traveler(self, continent.Id)
//PropertyOverrideMessage
+ sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil))
@@ -1304,6 +1321,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case InterstellarCluster.GiveWorld(zoneId, zone) =>
log.info(s"Zone $zoneId will now load")
+ avatarService ! Service.Leave(Some(continent.Id))
+ localService ! Service.Leave(Some(continent.Id))
+ vehicleService ! Service.Leave(Some(continent.Id))
player.Continent = zoneId
continent = zone
continent.Population ! Zone.Population.Join(avatar)
@@ -1362,6 +1382,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ChangeShortcutBankMessage(guid, 0))
//FavoritesMessage
sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
+ deadState = DeadState.Alive
sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0,0, tplayer.Position, player.Faction, true))
sendResponse(PlanetsideAttributeMessage(guid, 53, 1))
sendResponse(AvatarSearchCriteriaMessage(guid, List(0,0,0,0,0,0)))
@@ -1378,7 +1399,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TacticsMessage
StopBundlingPackets()
- sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
case Zone.ItemFromGround(tplayer, item) =>
val obj_guid = item.GUID
@@ -1396,6 +1416,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
definition.Packet.DetailedConstructorData(item).get
)
)
+ sendResponse(ActionResultMessage())
if(tplayer.VisibleSlots.contains(slot)) {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, player_guid, slot, item))
}
@@ -1503,7 +1524,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//player.Orientation = Vector3(0f, 0f, 132.1875f)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
player.Slot(0).Equipment = SimpleItem(remote_electronics_kit) //Tool(GlobalDefinitions.StandardPistol(player.Faction))
- player.Slot(2).Equipment = Tool(punisher) //suppressor
+ player.Slot(2).Equipment = Tool(mini_chaingun) //punisher //suppressor
player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction))
player.Slot(6).Equipment = AmmoBox(bullet_9mm, 20) //bullet_9mm
player.Slot(9).Equipment = AmmoBox(rocket, 11) //bullet_9mm
@@ -1545,6 +1566,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("Reticulating splines ...")
traveler.zone = continent.Id
StartBundlingPackets()
+ avatarService ! Service.Join(continent.Id)
+ localService ! Service.Join(continent.Id)
+ vehicleService ! Service.Join(continent.Id)
configZone(continent)
sendResponse(TimeOfDayMessage(1191182336))
@@ -1568,6 +1592,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
//load active players in zone
continent.LivePlayers.filterNot(_.GUID == player.GUID).foreach(char => {
sendResponse(ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get))
+ if(char.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
+ sendResponse(PlanetsideAttributeMessage(char.GUID, 19, 1))
+ }
})
//load corpses in zone
continent.Corpses.foreach { TurnPlayerIntoCorpse }
@@ -1619,9 +1646,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
})
StopBundlingPackets()
- avatarService ! Service.Join(player.Continent)
- localService ! Service.Join(player.Continent)
- vehicleService ! Service.Join(player.Continent)
self ! SetCurrentAvatar(player)
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
@@ -1703,6 +1727,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
reviveTimer.cancel
player.Release
+ deadState = DeadState.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, true))
continent.Population ! Zone.Population.Release(avatar)
player.VehicleSeated match {
@@ -1808,7 +1833,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
if(messagetype == ChatMessageType.CMT_SUICIDE) {
- KillPlayer(player)
+ if(player.isAlive && deadState != DeadState.Release) {
+ KillPlayer(player)
+ }
}
if(messagetype == ChatMessageType.CMT_DESTROY) {
@@ -1939,21 +1966,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
if(previousBox.Capacity > 0) {
- //TODO split previousBox into AmmoBox objects of appropriate max capacity, e.g., 100 9mm -> 2 x 50 9mm
- obj.Inventory.Fit(previousBox.Definition.Tile) match {
- case Some(index) => //put retained magazine in inventory
+ //split previousBox into AmmoBox objects of appropriate max capacity, e.g., 100 9mm -> 2 x 50 9mm
+ obj.Inventory.Fit(previousBox) match {
+ case Some(index) =>
stowFunc(index, previousBox)
- case None => //drop
- log.info(s"ChangeAmmo: dropping ammo box $previousBox")
- val pos = player.Position
- val orient = player.Orientation
- sendResponse(
- ObjectDetachMessage(Service.defaultPlayerGUID, previous_box_guid, pos, 0f, 0f, orient.z)
- )
- val orient2 = Vector3(0f, 0f, orient.z)
- continent.Ground ! Zone.DropItemOnGround(previousBox, pos, orient2)
- val objDef = previousBox.Definition
- avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentOnGround(player.GUID, pos, orient2, objDef.ObjectId, previousBox.GUID, objDef.Packet.ConstructorData(previousBox).get))
+ case None =>
+ NormalItemDrop(player, continent, avatarService)(previousBox)
+ }
+ val dropFunc : (Equipment)=>Unit = NewItemDrop(player, continent, avatarService)
+ AmmoBox.Split(previousBox) match {
+ case Nil | _ :: Nil => ; //done (the former case is technically not possible)
+ case _ :: xs =>
+ modifyFunc(previousBox, 0) //update to changed capacity value
+ xs.foreach(box => {
+ obj.Inventory.Fit(box) match {
+ case Some(index) =>
+ obj.Inventory += index -> box //block early, for purposes of Fit
+ taskResolver ! stowFuncTask(index, box)
+ case None =>
+ dropFunc(box)
+ }
+ })
}
}
else {
@@ -2115,16 +2148,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case msg @ ObjectHeldMessage(avatar_guid, held_holsters, unk1) =>
- log.info("ObjectHeld: " + msg)
+ log.info(s"ObjectHeld: $msg")
val before = player.DrawnSlot
- //TODO remove this kludge; explore how to stop BuyExoSuit(Max) sending a tardy ObjectHeldMessage(me, 255)
- if(player.ExoSuit != ExoSuitType.MAX && (player.DrawnSlot = held_holsters) != before) {
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
- if(player.VisibleSlots.contains(held_holsters)) {
- usingMedicalTerminal match {
- case Some(term_guid) =>
- StopUsingProximityUnit(continent.GUID(term_guid).get.asInstanceOf[ProximityTerminal])
- case None => ;
+ if(before != held_holsters) {
+ if(player.ExoSuit == ExoSuitType.MAX && held_holsters != 0) {
+ log.info(s"ObjectHeld: $player is denied changing hands to $held_holsters as a MAX")
+ player.DrawnSlot = 0
+ sendResponse(ObjectHeldMessage(avatar_guid, 0, true))
+ }
+ else if((player.DrawnSlot = held_holsters) != before) {
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
+ if(player.VisibleSlots.contains(held_holsters)) {
+ usingMedicalTerminal match {
+ case Some(term_guid) =>
+ StopUsingProximityUnit(continent.GUID(term_guid).get.asInstanceOf[ProximityTerminal])
+ case None => ;
+ }
}
}
}
@@ -2328,6 +2367,48 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
accessedContainer = Some(obj)
}
+ else if(!unk3) { //potential kit use
+ continent.GUID(unk1) match {
+ case Some(kit : Kit) =>
+ player.Find(kit) match {
+ case Some(index) =>
+ if(kit.Definition == GlobalDefinitions.medkit) {
+ if(player.Health == player.MaxHealth) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", "@HealComplete", None))
+ }
+ else if(System.currentTimeMillis - whenUsedLastKit < 5000) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^${5 - (System.currentTimeMillis - whenUsedLastKit) / 1000}~", None))
+ }
+ else {
+ player.Find(kit) match {
+ case Some(index) =>
+ whenUsedLastKit = System.currentTimeMillis
+ player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
+ sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ sendResponse(ObjectDeleteMessage(kit.GUID, 0))
+ taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
+ //TODO better health/damage control workflow
+ player.Health = player.Health + 25
+ sendResponse(PlanetsideAttributeMessage(avatar_guid, 0, player.Health))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(avatar_guid, 0, player.Health))
+ case None =>
+ log.error(s"UseItem: anticipated a $kit, but can't find it")
+ }
+ }
+ }
+ else {
+ log.warn(s"UseItem: $kit behavior not supported")
+ }
+
+ case None =>
+ log.error(s"UseItem: anticipated a $kit, but can't find it")
+ }
+ case Some(item) =>
+ log.warn(s"UseItem: looking for Kit to use, but found $item instead")
+ case None =>
+ log.warn(s"UseItem: anticipated a Kit $unk1, but can't find it")
+ }
+ }
case Some(obj : Locker) =>
if(player.Faction == obj.Faction) {
@@ -2404,18 +2485,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
PlayerActionsToCancel()
CancelAllProximityUnits()
player.Release
+ deadState = DeadState.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, true))
continent.Population ! Zone.Population.Release(avatar)
- case Some(obj : PlanetSideGameObject) =>
- if(itemType != 121) {
- sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
- }
- else if(itemType == 121 && !unk3) { // TODO : medkit use ?!
- sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
- sendResponse(PlanetsideAttributeMessage(avatar_guid, 0, 100)) // avatar with 100 hp
- sendResponse(ObjectDeleteMessage(PlanetSideGUID(unk1), 2))
- }
+ case Some(obj) =>
+ log.warn(s"UseItem: don't know how to handle $obj; taking a shot in the dark")
+ sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
case None =>
log.error(s"UseItem: can not find object $object_guid")
@@ -2459,6 +2535,51 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ GenericObjectStateMsg(object_guid, unk1) =>
log.info("GenericObjectState: " + msg)
+ case msg @ GenericActionMessage(action) =>
+ log.info(s"GenericAction: $msg")
+ val (toolOpt, definition) = player.Slot(0).Equipment match {
+ case Some(tool : Tool) =>
+ (Some(tool), tool.Definition)
+ case _ =>
+ (None, GlobalDefinitions.bullet_9mm)
+ }
+ if(action == 15) { //max deployment
+ log.info(s"GenericObject: $player is anchored")
+ player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 19, 1))
+ definition match {
+ case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster =>
+ val tool = toolOpt.get
+ tool.ToFireMode = 1
+ sendResponse(ChangeFireModeMessage(tool.GUID, 1))
+ case GlobalDefinitions.trhev_pounder =>
+ val tool = toolOpt.get
+ val convertFireModeIndex = if(tool.FireModeIndex == 0) { 1 } else { 4 }
+ tool.ToFireMode = convertFireModeIndex
+ sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex))
+ case _ =>
+ log.info(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
+ }
+ }
+ else if(action == 16) {
+ log.info(s"GenericObject: $player has released the anchors")
+ player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 19, 0))
+ definition match {
+ case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster =>
+ val tool = toolOpt.get
+ tool.ToFireMode = 0
+ sendResponse(ChangeFireModeMessage(tool.GUID, 0))
+ case GlobalDefinitions.trhev_pounder =>
+ val tool = toolOpt.get
+ val convertFireModeIndex = if(tool.FireModeIndex == 1) { 0 } else { 3 }
+ tool.ToFireMode = convertFireModeIndex
+ sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex))
+ case _ =>
+ log.info(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
+ }
+ }
+
case msg @ ItemTransactionMessage(terminal_guid, _, _, _, _, _) =>
log.info("ItemTransaction: " + msg)
continent.GUID(terminal_guid) match {
@@ -3740,6 +3861,58 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ /**
+ * Drop an `Equipment` item onto the ground.
+ * Specifically, instruct the item where it will appear,
+ * add it to the list of items that are visible to multiple users,
+ * and then inform others that the item has been dropped.
+ * @param obj a `Container` object that represents where the item will be dropped;
+ * curried for callback
+ * @param zone the continent in which the item is being dropped;
+ * curried for callback
+ * @param service a reference to the event system that announces that the item has been dropped on the ground;
+ * "AvatarService";
+ * curried for callback
+ * @param item the item
+ */
+ def NormalItemDrop(obj : PlanetSideGameObject with Container, zone : Zone, service : ActorRef)(item : Equipment) : Unit = {
+ val itemGUID = item.GUID
+ val ang = obj.Orientation.z
+ val pos = obj.Position
+ val orient = Vector3(0f, 0f, ang)
+ item.Position = pos
+ item.Orientation = orient
+ zone.Ground ! Zone.DropItemOnGround(item, pos, orient)
+ val itemDef = item.Definition
+ service ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentOnGround(Service.defaultPlayerGUID, pos, orient, itemDef.ObjectId, itemGUID, itemDef.Packet.ConstructorData(item).get))
+ }
+
+ /**
+ * Register an `Equipment` item and then drop it on the ground.
+ * @see `NormalItemDrop`
+ * @param obj a `Container` object that represents where the item will be dropped;
+ * curried for callback
+ * @param zone the continent in which the item is being dropped;
+ * curried for callback
+ * @param service a reference to the event system that announces that the item has been dropped on the ground;
+ * "AvatarService";
+ * curried for callback
+ * @param item the item
+ */
+ def NewItemDrop(obj : PlanetSideGameObject with Container, zone : Zone, service : ActorRef)(item : Equipment) : Unit = {
+ taskResolver ! TaskResolver.GiveTask(
+ new Task() {
+ private val localItem = item
+ private val localFunc : (Equipment)=>Unit = NormalItemDrop(obj, zone, service)
+
+ def Execute(resolver : ActorRef) : Unit = {
+ localFunc(localItem)
+ resolver ! scala.util.Success(this)
+ }
+ }, List(GUIDTask.RegisterEquipment(item)(zone.GUID))
+ )
+ }
+
/**
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
* @param tool a weapon
@@ -3972,14 +4145,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
val pos = tplayer.Position
val respawnTimer = 300000 //milliseconds
tplayer.Die
+ deadState = DeadState.Dead
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, player.Faction, true))
if(tplayer.VehicleSeated.nonEmpty) {
+ continent.GUID(tplayer.VehicleSeated.get) match {
+ case Some(obj : Vehicle) =>
+ TotalDriverVehicleControl(obj)
+ case _ => ;
+ }
//make player invisible (if not, the cadaver sticks out the side in a seated position)
- TotalDriverVehicleControl(continent.GUID(tplayer.VehicleSeated.get).get.asInstanceOf[Vehicle])
sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
}
@@ -3997,7 +4175,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Other players in the same zone must be made aware that the player has stopped as well.
*
* Things whose configuration should not be changed:
- * - if the player is seated
+ * - if the player is seated
+ * - if anchored
*/
def PlayerActionsToCancel() : Unit = {
progressBarUpdate.cancel
diff --git a/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala b/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala
index 446907e92..1486fb5be 100644
--- a/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala
+++ b/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala
@@ -51,7 +51,8 @@ class CorpseRemovalActor extends Actor {
def Processing : Receive = {
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
- if(corpse.isBackpack && !buriedCorpses.exists(_.corpse == corpse)) {
+ import CorpseRemovalActor.SimilarCorpses
+ if(corpse.isBackpack && !buriedCorpses.exists(entry => SimilarCorpses(entry.corpse, corpse) )) {
if(corpses.isEmpty) {
//we were the only entry so the event must be started from scratch
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
@@ -60,7 +61,7 @@ class CorpseRemovalActor extends Actor {
else {
//unknown number of entries; append, sort, then re-time tasking
val oldHead = corpses.head
- if(!corpses.exists(_.corpse == corpse)) {
+ if(!corpses.exists(entry => SimilarCorpses(entry.corpse, corpse))) {
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
if(oldHead != corpses.head) {
RetimeFirstTask()
@@ -80,45 +81,44 @@ class CorpseRemovalActor extends Actor {
if(targets.size == 1) {
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
//simple selection
- CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
+ CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
case None => ;
case Some(index) =>
decomposition.cancel
BurialTask(corpses(index))
buriedCorpses = buriedCorpses :+ corpses(index)
- corpses = corpses.take(index) ++ corpses.drop(index+1)
+ corpses = (corpses.take(index) ++ corpses.drop(index + 1)).sortBy(_.timeAlive)
import scala.concurrent.ExecutionContext.Implicits.global
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
}
}
else {
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
+ import CorpseRemovalActor.SimilarCorpses
decomposition.cancel
//cumbersome partition
//a - find targets from corpses
val locatedTargets = for {
a <- targets
b <- corpses
- if b.corpse == a &&
- b.corpse.Continent.equals(a.Continent) &&
- b.corpse.HasGUID && a.HasGUID && b.corpse.GUID == a.GUID
+ if b.corpse.HasGUID && a.HasGUID && SimilarCorpses(b.corpse, a)
} yield b
- locatedTargets.foreach { BurialTask }
- buriedCorpses = locatedTargets ++ buriedCorpses
- //b - corpses, after the found targets are removed (cull any non-GUID entries while at it)
- corpses = (for {
- a <- locatedTargets.map { _.corpse }
- b <- corpses
- if b.corpse.HasGUID && a.HasGUID &&
- (b.corpse != a ||
- !b.corpse.Continent.equals(a.Continent) ||
- !b.corpse.HasGUID || !a.HasGUID || b.corpse.GUID != a.GUID)
- } yield b).sortBy(_.timeAlive)
- import scala.concurrent.ExecutionContext.Implicits.global
- decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
- }
- RetimeFirstTask()
+ if(locatedTargets.nonEmpty) {
+ decomposition.cancel
+ locatedTargets.foreach { BurialTask }
+ buriedCorpses = locatedTargets ++ buriedCorpses
+ import scala.concurrent.ExecutionContext.Implicits.global
+ decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
+ //b - corpses, after the found targets are removed (cull any non-GUID entries while at it)
+ corpses = (for {
+ a <- locatedTargets
+ b <- corpses
+ if b.corpse.HasGUID && a.corpse.HasGUID && !SimilarCorpses(b.corpse, a.corpse)
+ } yield b).sortBy(_.timeAlive)
+ }
}
+ RetimeFirstTask()
+ }
case CorpseRemovalActor.StartDelete() =>
burial.cancel
@@ -136,7 +136,9 @@ class CorpseRemovalActor extends Actor {
case CorpseRemovalActor.TryDelete() =>
decomposition.cancel
- val (decomposed, rotting) = buriedCorpses.partition(entry => { !entry.zone.Corpses.contains(entry.corpse) })
+ val (decomposed, rotting) = buriedCorpses.partition(entry => {
+ !entry.zone.Corpses.contains(entry.corpse)
+ })
buriedCorpses = rotting
decomposed.foreach { LastRitesTask }
if(rotting.nonEmpty) {
@@ -214,6 +216,10 @@ object CorpseRemovalActor {
private final case class TryDelete()
+ private def SimilarCorpses(obj1 : Player, obj2 : Player) : Boolean = {
+ obj1 == obj2 && obj1.Continent.equals(obj2.Continent) && obj1.GUID == obj2.GUID
+ }
+
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` objects
@@ -228,7 +234,7 @@ object CorpseRemovalActor {
}
else {
val corpse = iter.next.corpse
- if(corpse == player && corpse.Continent.equals(player.Continent) && corpse.GUID == player.GUID) {
+ if(corpse.HasGUID && player.HasGUID && SimilarCorpses(corpse, player)) {
Some(index)
}
else {
diff --git a/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala
index 2c508ad8d..7756b4153 100644
--- a/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala
+++ b/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala
@@ -33,37 +33,47 @@ class DelayedDeconstructionActor extends Actor {
def receive : Receive = {
case DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) =>
trace(s"delayed deconstruction order for $vehicle in $timeAlive")
- vehicles = vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L)
- if(vehicles.size == 1) { //we were the only entry so the event must be started from scratch
- import scala.concurrent.ExecutionContext.Implicits.global
- monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling)
+ val oldHead = vehicles.headOption
+ val now : Long = System.nanoTime
+ vehicles = (vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L))
+ .sortBy(entry => entry.survivalTime - (now - entry.logTime))
+ if(vehicles.size == 1 || oldHead != vehicles.headOption) { //we were the only entry so the event must be started from scratch
+ RetimePeriodicTest()
}
case DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) =>
//all tasks for this vehicle are cleared from the queue
//clear any task that is no longer valid by determination of unregistered GUID
val before = vehicles.length
- vehicles = vehicles.filter(entry => { !entry.vehicle.HasGUID || entry.vehicle.GUID != vehicle_guid })
+ val now : Long = System.nanoTime
+ vehicles = vehicles.filter(entry => { entry.vehicle.HasGUID && entry.vehicle.GUID != vehicle_guid })
+ .sortBy(entry => entry.survivalTime - (now - entry.logTime))
trace(s"attempting to clear deconstruction order for vehicle $vehicle_guid, found ${before - vehicles.length}")
- if(vehicles.isEmpty) {
- monitor.cancel
- }
+ RetimePeriodicTest()
case DelayedDeconstructionActor.PeriodicTaskCulling =>
//filter the list of deconstruction tasks for any that are need to be triggered
monitor.cancel
val now : Long = System.nanoTime
val (vehiclesToDecon, vehiclesRemain) = vehicles.partition(entry => { now - entry.logTime >= entry.survivalTime })
- vehicles = vehiclesRemain
- trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found")
+ vehicles = vehiclesRemain.sortBy(_.survivalTime)
+ trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found; ${vehiclesRemain.length} tasks remain")
vehiclesToDecon.foreach(entry => { context.parent ! VehicleServiceMessage.RequestDeleteVehicle(entry.vehicle, entry.zone) })
- if(vehiclesRemain.nonEmpty) {
- import scala.concurrent.ExecutionContext.Implicits.global
- monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling)
- }
+ RetimePeriodicTest()
case _ => ;
}
+
+ def RetimePeriodicTest() : Unit = {
+ monitor.cancel
+ vehicles.headOption match {
+ case None => ;
+ case Some(entry) =>
+ val retime = math.max(1, entry.survivalTime - (System.nanoTime - entry.logTime)) nanoseconds
+ import scala.concurrent.ExecutionContext.Implicits.global
+ monitor = context.system.scheduler.scheduleOnce(retime, self, DelayedDeconstructionActor.PeriodicTaskCulling)
+ }
+ }
}
object DelayedDeconstructionActor {
diff --git a/pslogin/src/test/scala/PacketCodingActorTest.scala b/pslogin/src/test/scala/PacketCodingActorTest.scala
index 5cd58b0b7..a2096f014 100644
--- a/pslogin/src/test/scala/PacketCodingActorTest.scala
+++ b/pslogin/src/test/scala/PacketCodingActorTest.scala
@@ -63,7 +63,7 @@ class PacketCodingActor4Test extends ActorTest {
val msg = ActorTest.MDCGamePacket(PacketCoding.CreateGamePacket(0, string_obj))
probe2 ! msg
- val reply1 = receiveOne(100 milli) //we get a MdcMsg message back
+ val reply1 = receiveOne(300 milli) //we get a MdcMsg message back
probe2 ! reply1 //by feeding the MdcMsg into the actor, we get normal output on the probe
probe1.expectMsg(string_hex)
}
@@ -83,7 +83,7 @@ class PacketCodingActor5Test extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveOne(100 milli)
+ val reply = probe1.receiveOne(300 milli)
reply match {
case GamePacket(_, _, msg) => ;
assert(msg == string_obj)
@@ -126,7 +126,7 @@ class PacketCodingActor7Test extends ActorTest {
val msg = ActorTest.MDCControlPacket(PacketCoding.CreateControlPacket(string_obj))
probe2 ! msg
- val reply1 = receiveOne(100 milli) //we get a MdcMsg message back
+ val reply1 = receiveOne(300 milli) //we get a MdcMsg message back
probe2 ! reply1 //by feeding the MdcMsg into the actor, we get normal output on the probe
probe1.expectMsg(string_hex)
}
@@ -146,7 +146,7 @@ class PacketCodingActor8Test extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveOne(100 milli)
+ val reply = probe1.receiveOne(300 milli)
reply match {
case ControlPacket(_, msg) => ;
assert(msg == string_obj)
@@ -200,7 +200,7 @@ class PacketCodingActorBTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
probe2 ! "unhandled message"
- val reply1 = receiveOne(100 milli) //we get a MdcMsg message back
+ val reply1 = receiveOne(300 milli) //we get a MdcMsg message back
probe2 ! reply1 //by feeding the MdcMsg into the actor, we get normal output on the probe
probe1.expectMsg("unhandled message")
}
@@ -379,10 +379,10 @@ class PacketCodingActorETest extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveN(2, 200 milli)
+ val reply = probe1.receiveN(2, 400 milli)
assert(reply.head == string_obj1)
assert(reply(1) == string_obj2)
- probe1.expectNoMsg(100 milli)
+ probe1.expectNoMsg(300 milli)
}
}
}
@@ -400,10 +400,10 @@ class PacketCodingActorFTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveN(1, 200 milli)
+ val reply = probe1.receiveN(1, 400 milli)
assert(reply.head == string_obj)
//the RelatedB message - 00 15 02 98 - is consumed by pca
- probe1.expectNoMsg(100 milli)
+ probe1.expectNoMsg(300 milli)
}
}
}
@@ -421,10 +421,10 @@ class PacketCodingActorGTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveN(1, 200 milli)
+ val reply = probe1.receiveN(1, 400 milli)
assert(reply.head == string_obj)
//the RelatedA message - 00 11 02 98 - is consumed by pca; should see error log message in console
- probe1.expectNoMsg(100 milli)
+ probe1.expectNoMsg(300 milli)
}
}
}
@@ -443,10 +443,10 @@ class PacketCodingActorHTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
pca ! string_hex
- val reply = probe1.receiveN(2, 200 milli)
+ val reply = probe1.receiveN(2, 400 milli)
assert(reply.head == string_obj1)
assert(reply(1) == string_obj2)
- probe1.expectNoMsg(100 milli)
+ probe1.expectNoMsg(300 milli)
}
}
}
@@ -498,10 +498,10 @@ class PacketCodingActorITest extends ActorTest {
probe1.receiveOne(100 milli) //consume
probe2 ! pkt
- val reply1 = receiveN(1, 200 milli) //we get a MdcMsg message back
+ val reply1 = receiveN(1, 400 milli) //we get a MdcMsg message back
probe1.receiveN(1, 200 milli) //flush contents
probe2 ! reply1.head //by feeding the MdcMsg into the actor, we get normal output on the probe
- probe1.receiveOne(100 milli) match {
+ probe1.receiveOne(300 milli) match {
case RawPacket(data) =>
assert(data == string_hex)
PacketCoding.DecodePacket(data).require match {
@@ -532,10 +532,10 @@ class PacketCodingActorJTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
probe2 ! pkt
- val reply1 = receiveN(1, 200 milli) //we get a MdcMsg message back
+ val reply1 = receiveN(1, 400 milli) //we get a MdcMsg message back
probe1.receiveN(1, 200 milli) //flush contents
probe2 ! reply1.head //by feeding the MdcMsg into the actor, we get normal output on the probe
- probe1.receiveOne(100 milli) match {
+ probe1.receiveOne(300 milli) match {
case RawPacket(data) =>
assert(data == string_hex)
case e =>
@@ -600,13 +600,13 @@ class PacketCodingActorKTest extends ActorTest {
probe1.receiveOne(100 milli) //consume
probe2 ! pkt
- val reply1 = receiveN(2, 200 milli)
+ val reply1 = receiveN(2, 400 milli)
probe1.receiveN(1, 200 milli) //flush contents
probe2 ! reply1.head //by feeding the MdcMsg into the actor, we get normal output on the probe
- val reply3 = probe1.receiveOne(100 milli).asInstanceOf[RawPacket]
+ val reply3 = probe1.receiveOne(300 milli).asInstanceOf[RawPacket]
pca ! reply3 //reconstruct original three packets from the first bundle
- val reply4 = probe1.receiveN(3, 200 milli)
+ val reply4 = probe1.receiveN(3, 400 milli)
var i = 0
reply4.foreach{
case GamePacket(_, _, packet) =>