Merge pull request #213 from Fate-JH/is-fixes-things

Is Fixes Things
This commit is contained in:
Fate-JH 2018-05-21 10:50:14 -04:00 committed by GitHub
commit 63cc183031
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1245 additions and 615 deletions

View file

@ -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)

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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 = {

View file

@ -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
}

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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
}
}

View file

@ -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
}
}
}

View file

@ -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)

View file

@ -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"
}
}
}

View file

@ -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)

View file

@ -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.<br>
* <br>
* Things whose configuration should not be changed:<br>
* - if the player is seated
* - if the player is seated<br>
* - if anchored
*/
def PlayerActionsToCancel() : Unit = {
progressBarUpdate.cancel

View file

@ -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 {

View file

@ -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 {

View file

@ -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) =>