Buildings now have simple type distinctions to work using the known output of a SpawnRequestMessage packet. Support for cross-continental respawning (actually a failure case for being unable to spawn). Corpse tuning and testing.

This commit is contained in:
FateJH 2018-03-24 00:28:02 -04:00
parent 20b7726653
commit ddc2450541
52 changed files with 2491 additions and 711 deletions

View file

@ -15,14 +15,19 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
private var cep : Long = 0
/** Certifications */
private val certs : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/** Implants */
/** Implants<br>
* Unlike other objects, the maximum number of `ImplantSlots` are built into the `Avatar`.
* Additionally, implants do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
* The `ImplantDefinition` objects themselves are moved around as if they were the implants.
* The terms externally used for the states of process is "installed" and "uninstalled."
* @see `ImplantSlot`
* @see `DetailedCharacterData.implants`
*/
private val implants : Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
/** Loadouts */
private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None)
/** Locker (fifth inventory slot) */
private val locker : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory) {
Equipment = new LockerContainer
}
private val locker : LockerContainer = LockerContainer()
def BEP : Long = bep
@ -67,7 +72,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
* @return the index of the `ImplantSlot` where the implant was installed
*/
def InstallImplant(implant : ImplantDefinition) : Option[Int] = {
implants.find({p => p.Installed.contains(implant)}) match { //try to find the installed implant
implants.find({p => p.Installed.contains(implant) || p.Implant == implant.Type}) match { //try to find the installed implant
case None =>
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
case Some(slot) =>
@ -138,29 +143,25 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
})
}
/**
* Unlike other objects, the maximum number of `ImplantSlots` are built into the `Player`.
* Additionally, "implants" do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
* The `ImplantDefinition` objects themselves are moved around as if they were the implants.
* The term internally used for this process is "installed" and "uninstalled."
* @see `ImplantSlot`
* @see `DetailedCharacterData.implants`
* @see `AvatarConverter.MakeImplantEntries`
*/
def SaveLoadout(owner : Player, label : String, line : Int) : Unit = {
loadouts(line) = Some(Loadout.Create(owner, label))
if(line > -1 && line < 10) {
loadouts(line) = Some(Loadout.Create(owner, label))
}
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line)
def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).getOrElse(None)
def DeleteLoadout(line : Int) : Unit = {
loadouts(line) = None
}
def Locker : LockerContainer = locker.Equipment.get.asInstanceOf[LockerContainer]
def Locker : LockerContainer = locker
def FifthSlot : EquipmentSlot = locker
def FifthSlot : EquipmentSlot = {
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
Equipment = locker
}
}
def Definition : AvatarDefinition = Avatar.definition
@ -191,7 +192,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
}
override def toString: String = s"$faction $name"
override def toString: String = Avatar.toString(this)
}
object Avatar {
@ -200,4 +201,6 @@ object Avatar {
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Avatar = {
new Avatar(name, faction, sex, head, voice)
}
def toString(avatar : Avatar) : String = s"${avatar.faction} ${avatar.name}"
}

View file

@ -487,7 +487,7 @@ object GlobalDefinitions {
*/
val order_terminal = new OrderTerminalDefinition
val ams_respawn_tube = new SpawnTubeDefinition(49) { Name = "ams_respawn_tube" }
val ams_respawn_tube = new SpawnTubeDefinition(49)
val matrix_terminalc = new MatrixTerminalDefinition(519)
@ -509,7 +509,11 @@ object GlobalDefinitions {
val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition
val spawn_terminal = new SpawnTerminalDefinition
val spawn_terminal = new MatrixTerminalDefinition(812)
val respawn_tube = new SpawnTubeDefinition(732)
val respawn_tube_tower = new SpawnTubeDefinition(733)
val spawn_pad = new VehicleSpawnPadDefinition

View file

@ -8,14 +8,6 @@ import net.psforever.types.ExoSuitType
import scala.annotation.tailrec
//trait Loadout {
// def Label : String
// def VisibleSlots : List[Loadout.SimplifiedEntry]
// def Inventory : List[Loadout.SimplifiedEntry]
// def ExoSuit : ExoSuitType.Value
// def Subtype : Int
//}
/**
* From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.<br>
* <br>
@ -113,7 +105,9 @@ object Loadout {
/**
* A basic `Trait` connecting all of the `Equipment` blueprints.
*/
sealed trait Simplification
sealed trait Simplification {
def definition : ObjectDefinition
}
/**
* An entry in the `Loadout`, wrapping around a slot index and what is in the slot index.
@ -125,17 +119,17 @@ object Loadout {
/**
* The simplified form of an `AmmoBox`.
* @param adef the `AmmoBoxDefinition` that describes this future object
* @param definition the `AmmoBoxDefinition` that describes this future object
* @param capacity the amount of ammunition, if any, to initialize;
* if `None`, then the previous `AmmoBoxDefinition` will be referenced for the amount later
*/
final case class ShorthandAmmoBox(adef : AmmoBoxDefinition, capacity : Int) extends Simplification
final case class ShorthandAmmoBox(definition : AmmoBoxDefinition, capacity : Int) extends Simplification
/**
* The simplified form of a `Tool`.
* @param tdef the `ToolDefinition` that describes this future object
* @param definition the `ToolDefinition` that describes this future object
* @param ammo the blueprints to construct the correct number of ammunition slots in the `Tool`
*/
final case class ShorthandTool(tdef : ToolDefinition, ammo : List[ShorthandAmmoSlot]) extends Simplification
final case class ShorthandTool(definition : ToolDefinition, ammo : List[ShorthandAmmoSlot]) extends Simplification
/**
* The simplified form of a `Tool` `FireMode`
* @param ammoIndex the index that points to the type of ammunition this slot currently uses
@ -144,19 +138,19 @@ object Loadout {
final case class ShorthandAmmoSlot(ammoIndex : Int, ammo : ShorthandAmmoBox)
/**
* The simplified form of a `ConstructionItem`.
* @param cdef the `ConstructionItemDefinition` that describes this future object
* @param definition the `ConstructionItemDefinition` that describes this future object
*/
final case class ShorthandConstructionItem(cdef : ConstructionItemDefinition) extends Simplification
final case class ShorthandConstructionItem(definition : ConstructionItemDefinition) extends Simplification
/**
* The simplified form of a `SimpleItem`.
* @param sdef the `SimpleItemDefinition` that describes this future object
* @param definition the `SimpleItemDefinition` that describes this future object
*/
final case class ShorthandSimpleItem(sdef : SimpleItemDefinition) extends Simplification
final case class ShorthandSimpleItem(definition : SimpleItemDefinition) extends Simplification
/**
* The simplified form of a `Kit`.
* @param kdef the `KitDefinition` that describes this future object
* @param definition the `KitDefinition` that describes this future object
*/
final case class ShorthandKit(kdef : KitDefinition) extends Simplification
final case class ShorthandKit(definition : KitDefinition) extends Simplification
def DetermineSubtype(player : Player) : Int = {
if(player.ExoSuit == ExoSuitType.MAX) {

View file

@ -200,7 +200,10 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
case Some(index) =>
Some(index)
case None =>
if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
if(Locker.Find(guid).isDefined) {
Some(5)
}
else if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
Some(Player.FreeHandSlot)
}
else {
@ -416,6 +419,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
}
object Player {
final val LockerSlot : Int = 5
final val FreeHandSlot : Int = 250
final val HandsDownSlot : Int = 255
@ -447,5 +451,8 @@ object Player {
}
}
def toString(obj : Player) : String = s"${obj.core} ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
def toString(obj : Player) : String = {
val guid = if(obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" } else { "" }
s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
}
}

View file

@ -1,7 +1,13 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.guid
import net.psforever.objects.vehicles.Utility
import akka.actor.ActorRef
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.{EquipmentSlot, LockerContainer, Player, Tool, Vehicle}
import net.psforever.objects.inventory.Container
import scala.annotation.tailrec
/**
* The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.<br>
@ -14,17 +20,13 @@ import net.psforever.objects.vehicles.Utility
* It will get passed from the more complicated functions down into the less complicated functions,
* until it has found the basic number assignment functionality.<br>
* <br>
* All functions produce a `TaskResolver.GiveTask` container object that is expected to be used by a `TaskResolver`.
* These "task containers" can also be unpackaged into their tasks, sorted into other containers,
* and combined with other "task containers" to enact more complicated sequences of operations.
* All functions produce a `TaskResolver.GiveTask` container object
* or a list of `TaskResolver.GiveTask` container objects that is expected to be used by a `TaskResolver` `Actor`.
* These "task containers" can also be unpackaged into their component tasks, sorted into other containers,
* and combined with other tasks to enact more complicated sequences of operations.
* Almost all tasks have an explicit registering and an unregistering activity defined for it.
*/
object GUIDTask {
import akka.actor.ActorRef
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.{EquipmentSlot, Player, Tool, Vehicle}
import scala.annotation.tailrec
/**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.<br>
* <br>
@ -76,9 +78,34 @@ object GUIDTask {
TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks)
}
/**
* Construct tasking that registers a `LockerContainer` object
* with a globally unique identifier selected from a pool of numbers.
* @param obj the object being registered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.UnregisterLocker`
* @return a `TaskResolver.GiveTask` message
*/
def RegisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
}
/**
* Construct tasking that registers the objects that are within the given container's inventory
* with a globally unique identifier selected from a pool of numbers for each object.
* @param container the storage unit in which objects can be found
* @param guid implicit reference to a unique number system
* @see `GUID.UnregisterInventory`<br>
* `Container`
* @return a list of `TaskResolver.GiveTask` messages
*/
def RegisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
container.Inventory.Items.values.map(entry => { RegisterEquipment(entry.obj)}).toList
}
/**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers,
* after determining whether the object is complex (`Tool`) or simple.<br>
* after determining whether the object is complex (`Tool` or `Locker`) or is simple.<br>
* <br>
* The objects in this case are specifically `Equipment`, a subclass of the basic register-able `IdentifiableEntity`.
* About five subclasses of `Equipment` exist, but they decompose into two groups - "complex objects" and "simple objects."
@ -96,6 +123,8 @@ object GUIDTask {
obj match {
case tool : Tool =>
RegisterTool(tool)
case locker : LockerContainer =>
RegisterLocker(locker)
case _ =>
RegisterObjectTask(obj)
}
@ -118,17 +147,24 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message
*/
def RegisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer
import net.psforever.objects.inventory.InventoryItem
val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, RegisterEquipment)
val fifthHolsterTask = tplayer.Slot(5).Equipment match {
case Some(locker) =>
RegisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)}).toList
case None =>
List.empty[TaskResolver.GiveTask];
}
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
val lockerTask = List(RegisterLocker(tplayer.Locker))
val inventoryTasks = RegisterInventory(tplayer)
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
}
/**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Player`.<br>
* <br>
* Similar to `RegisterAvatar` but the locker components are skipped.
* @param tplayer the `Player` object being registered
* @param guid implicit reference to a unique number system
* @return a `TaskResolver.GiveTask` message
*/
def RegisterPlayer(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
val inventoryTasks = RegisterInventory(tplayer)
TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
}
/**
@ -149,10 +185,9 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message
*/
def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.inventory.InventoryItem
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => RegisterEquipment(entry.Equipment.get)}).toList
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => RegisterObjectTask(util())}).toList
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, RegisterEquipment)
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { RegisterObjectTask(util())}).toList
val inventoryTasks = RegisterInventory(vehicle)
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
}
@ -201,9 +236,33 @@ object GUIDTask {
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks)
}
/**
* Construct tasking that unregisters a `LockerContainer` object from a globally unique identifier system.
* @param obj the object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterLocker`
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, UnregisterInventory(obj))
}
/**
* Construct tasking that unregisters the objects that are within the given container's inventory
* from a globally unique identifier system.
* @param container the storage unit in which objects can be found
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterInventory`<br>
* `Container`
* @return a list of `TaskResolver.GiveTask` messages
*/
def UnregisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
container.Inventory.Items.values.map(entry => { UnregisterEquipment(entry.obj)}).toList
}
/**
* Construct tasking that unregisters an object from a globally unique identifier system
* after determining whether the object is complex (`Tool`) or simple.<br>
* after determining whether the object is complex (`Tool` or `Locker`) or is simple.<br>
* <br>
* This task performs an operation that reverses the effect of `RegisterEquipment`.
* @param obj the `Equipment` object being unregistered
@ -215,6 +274,8 @@ object GUIDTask {
obj match {
case tool : Tool =>
UnregisterTool(tool)
case locker : LockerContainer =>
UnregisterLocker(locker)
case _ =>
UnregisterObjectTask(obj)
}
@ -230,36 +291,58 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer
import net.psforever.objects.inventory.InventoryItem
val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, UnregisterEquipment)
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
val fifthHolsterTask = tplayer.Slot(5).Equipment match {
case Some(locker) =>
UnregisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)}).toList
case None =>
List.empty[TaskResolver.GiveTask];
}
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
val lockerTask = List(UnregisterLocker(tplayer.Locker))
val inventoryTasks = UnregisterInventory(tplayer)
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
}
/**
* Construct tasking that unregisters a `Vehicle` object from a globally unique identifier system.<br>
* <br>
* This task performs an operation that reverses the effect of `RegisterVehicle`.
* @param vehicle the `Vehicle` object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterVehicle`
* @return a `TaskResolver.GiveTask` message
*/
/**
* Construct tasking that unregisters a portion of a `Player` object from a globally unique identifier system.<br>
* <br>
* Similar to `UnregisterAvatar` but the locker components are skipped.
* This task performs an operation that reverses the effect of `RegisterPlayer`.
* @param tplayer the `Player` object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterAvatar`
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterPlayer(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
val inventoryTasks = UnregisterInventory(tplayer)
TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(tplayer).task, holsterTasks ++ inventoryTasks)
}
/**
* Construct tasking that unregisters a `Vehicle` object from a globally unique identifier system.<br>
* <br>
* This task performs an operation that reverses the effect of `RegisterVehicle`.
* @param vehicle the `Vehicle` object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterVehicle`
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.inventory.InventoryItem
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => UnregisterTool(entry.Equipment.get.asInstanceOf[Tool]) }).toList
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => UnregisterObjectTask(util())}).toList
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, UnregisterEquipment)
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { UnregisterObjectTask(util())}).toList
val inventoryTasks = UnregisterInventory(vehicle)
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
}
/**
* Construct tasking that allocates work upon encountered `Equipment` objects
* in reference to a globally unique identifier system of a pool of numbers.
* "Visible slots" are locations that can be viewed by multiple observers across a number of clients.
* @param list an `Iterable` sequence of `EquipmentSlot` objects that may or may not have equipment
* @param func the function used to build tasking from any discovered `Equipment`;
* strictly either `RegisterEquipment` or `UnregisterEquipment`
* @param guid implicit reference to a unique number system
* @return a list of `TaskResolver.GiveTask` messages
*/
def VisibleSlotTaskBuilding(list : Iterable[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask))(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
recursiveVisibleSlotTaskBuilding(list.iterator, func)
}
/**
* Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
* Use `func` on any discovered `Equipment` to transform items into tasking, and add the tasking to a `List`.
@ -267,18 +350,19 @@ object GUIDTask {
* @param func the function used to build tasking from any discovered `Equipment`;
* strictly either `RegisterEquipment` or `UnregisterEquipment`
* @param list a persistent `List` of `Equipment` tasking
* @see `VisibleSlotTaskBuilding`
* @return a `List` of `Equipment` tasking
*/
@tailrec private def recursiveHolsterTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
@tailrec private def recursiveVisibleSlotTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
if(!iter.hasNext) {
list
}
else {
iter.next.Equipment match {
case Some(item) =>
recursiveHolsterTaskBuilding(iter, func, list :+ func(item))
recursiveVisibleSlotTaskBuilding(iter, func, list :+ func(item))
case None =>
recursiveHolsterTaskBuilding(iter, func, list)
recursiveVisibleSlotTaskBuilding(iter, func, list)
}
}
}

View file

@ -287,12 +287,7 @@ class UniqueNumberSystem(private val guid : NumberPoolHub, private val poolActor
* @see `UniqueNumberSystem.UnregistrationProcess(Option[GUIDRequest], Int, Int)`
*/
private def NoCallbackReturnNumber(number : Int, poolName : String) : Unit = {
poolActors.get(poolName) match {
case Some(pool) =>
pool ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
case None =>
log.error(s"critical: tried to return number $number but did not find pool $poolName")
}
poolActors(poolName) ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
}
/**
@ -316,12 +311,7 @@ class UniqueNumberSystem(private val guid : NumberPoolHub, private val poolActor
* @see `UniqueNumberSystem.RegistrationProcess(Option[GUIDRequest], Int, Int)`
*/
private def NoCallbackGetSpecificNumber(number : Int, poolName : String) : Unit = {
poolActors.get(poolName) match {
case Some(pool) =>
pool ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
case None =>
log.error(s"critical: tried to re-register number $number but did not find pool $poolName")
}
poolActors(poolName) ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
}
}

View file

@ -6,9 +6,9 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
import net.psforever.types.{PlanetSideEmpire, Vector3}
class Building(private val mapId : Int, private val zone : Zone) extends PlanetSideServerObject {
class Building(private val mapId : Int, private val zone : Zone, private val buildingType : StructureType.Value) extends PlanetSideServerObject {
/**
* The mapId is the identifier number used in BuildingInfoUpdateMessage.
* The modelId is the identifier number used in SetEmpireMessage.
@ -46,25 +46,35 @@ class Building(private val mapId : Int, private val zone : Zone) extends PlanetS
ModelId
}
def BuildingType : StructureType.Value = buildingType
def Definition: ObjectDefinition = Building.BuildingDefinition
}
object Building {
final val NoBuilding : Building = new Building(0, Zone.Nowhere) {
final val NoBuilding : Building = new Building(0, Zone.Nowhere, StructureType.Platform) {
override def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
override def Amenities_=(obj : Amenity) : List[Amenity] = Nil
}
final val BuildingDefinition : ObjectDefinition = new ObjectDefinition(0) { Name = "building" }
def apply(id : Int, zone : Zone) : Building = {
new Building(id, zone)
def apply(id : Int, zone : Zone, buildingType : StructureType.Value) : Building = {
new Building(id, zone, buildingType)
}
def Structure(id : Int, zone : Zone, context : ActorContext) : Building = {
def Structure(buildingType : StructureType.Value, location : Vector3)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
val obj = new Building(id, zone)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-building")
val obj = new Building(id, zone, buildingType)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj
}
def Structure(buildingType : StructureType.Value)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
val obj = new Building(id, zone, buildingType)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj
}
}

View file

@ -0,0 +1,20 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.structures
/**
* An `Enumeration` of the kinds of building structures found in the game.
* This is merely a kludge for more a future, more complicated internal object that handles base operations.
*/
object StructureType extends Enumeration {
type Type = Value
val
Bridge,
Building, //generic
Bunker,
Facility,
Platform, //outdoor amenities like the spawn pads in sanctuary
Tower,
WarpGate
= Value
}

View file

@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.structures
import akka.actor.ActorContext
import net.psforever.objects.zones.Zone
class WarpGate(id : Int, zone : Zone) extends Building(id, zone) {
class WarpGate(id : Int, zone : Zone) extends Building(id, zone, StructureType.WarpGate) {
//TODO stuff later
}

View file

@ -339,8 +339,8 @@ object EquipmentTerminalDefinition {
import net.psforever.objects.Loadout._
entry match {
case obj : ShorthandTool =>
val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef })
val tool = Tool(obj.tdef)
val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.definition })
val tool = Tool(obj.definition)
//makes Tools where an ammo slot may have one of its alternate ammo types
(0 until tool.MaxAmmoSlot).foreach(index => {
val slot = tool.AmmoSlots(index)
@ -350,16 +350,16 @@ object EquipmentTerminalDefinition {
tool
case obj : ShorthandAmmoBox =>
MakeAmmoBox(obj.adef, Some(obj.capacity))
MakeAmmoBox(obj.definition, Some(obj.capacity))
case obj : ShorthandConstructionItem =>
MakeConstructionItem(obj.cdef)
MakeConstructionItem(obj.definition)
case obj : ShorthandSimpleItem =>
MakeSimpleItem(obj.sdef)
MakeSimpleItem(obj.definition)
case obj : ShorthandKit =>
MakeKit(obj.kdef)
MakeKit(obj.definition)
}
}

View file

@ -19,8 +19,11 @@ class MatrixTerminalDefinition(object_id : Int) extends TerminalDefinition(objec
else if(object_id == 519) {
"matrix_terminalc"
}
else if(object_id == 812) {
"spawn_terminal"
}
else {
throw new IllegalArgumentException("terminal must be object id 517-519")
throw new IllegalArgumentException("terminal must be object id 517-519 or 812")
}
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()

View file

@ -1,15 +0,0 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
import net.psforever.objects.Player
import net.psforever.packet.game.ItemTransactionMessage
/**
* The definition for any `Terminal` that is of a type "spawn_terminal."
* A "spawn_terminal" is somewhat like the `matrix_terminalc` of an advanced mobile spawn unit, but inside of facilities.
*/
class SpawnTerminalDefinition extends TerminalDefinition(812) {
Name = "spawn_terminal"
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
}

View file

@ -1,16 +1,26 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.tube
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.structures.Amenity
class SpawnTube(tubeDef : ObjectDefinition) extends Amenity {
def Definition : ObjectDefinition = tubeDef
/**
* An owned server object that is used as a placeholder for the position and direction
* that infantry will be arranged upon spawning into the game world.
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class SpawnTube(tDef : SpawnTubeDefinition) extends Amenity {
def Definition : SpawnTubeDefinition = tDef
}
object SpawnTube {
def apply(tubeDef : ObjectDefinition) : SpawnTube = {
new SpawnTube(tubeDef)
/**
* Overloaded constructor.
* @param tDef the spawn tube's definition entry
* @return a `SpawnTube` object
*/
def apply(tDef : SpawnTubeDefinition) : SpawnTube = {
new SpawnTube(tDef)
}
import akka.actor.ActorContext
@ -24,13 +34,25 @@ object SpawnTube {
* @return the `SpawnTube` object
*/
def Constructor(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
import akka.actor.Props
import net.psforever.objects.GlobalDefinitions
Constructor(GlobalDefinitions.respawn_tube, pos, orient)(id, context)
}
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
/**
* Instantiate an configure a `SpawnTube` object with the given definition
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
* @param pos the position (used to determine spawn point)
* @param orient the orientation (used to indicate spawn direction)
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `SpawnTube` object
*/
def Constructor(tdef : SpawnTubeDefinition, pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
import akka.actor.Props
val obj = SpawnTube(tdef)
obj.Position = pos
obj.Orientation = orient
obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), s"${GlobalDefinitions.ams_respawn_tube.Name}_$id")
obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), s"${tdef.Name}_$id")
obj
}
}

View file

@ -6,7 +6,24 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.definition.converter.SpawnTubeConverter
import net.psforever.objects.serverobject.structures.Amenity
/**
* The definition for any `VehicleSpawnPad`.
* Currently, all tubes identify as object id 49 - `ams_respawn_tube` - configured manually.
* @see `GlobalDefinitions.ams_respawn_tube`
*/
class SpawnTubeDefinition(object_id : Int) extends ObjectDefinition(object_id) {
Name = if(object_id == 49) {
"ams_respawn_tube"
}
else if(object_id == 732) {
"respawn_tube"
}
else if(object_id == 733) {
"respawn_tube_tower"
}
else {
throw new IllegalArgumentException("terminal must be object id 49, 732, or 733")
}
Packet = new SpawnTubeConverter
}

View file

@ -41,7 +41,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
def receive : Receive = {
case InterstellarCluster.GetWorld(zoneId) =>
log.info(s"Asked to find $zoneId")
findWorldInCluster(zones.iterator, zoneId) match {
recursiveFindWorldInCluster(zones.iterator, _.Id == zoneId) match {
case Some(continent) =>
sender ! InterstellarCluster.GiveWorld(zoneId, continent)
case None =>
@ -52,26 +52,35 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
sender ! InterstellarCluster.ClientInitializationComplete() //will be processed after all Zones
case msg @ Zone.Lattice.RequestSpawnPoint(zone_number, _, _) =>
recursiveFindWorldInCluster(zones.iterator, _.Number == zone_number) match {
case Some(zone) =>
zone.Actor forward msg
case None => //zone_number does not exist
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
}
case _ => ;
}
/**
* Search through the `List` of `Zone` entities and find the one with the matching designation.
* @param iter an `Iterator` of `Zone` entities
* @param zoneId the name of the `Zone`
* @param predicate a condition to check against to determine when the appropriate `Zone` is discovered
* @return the discovered `Zone`
*/
@tailrec private def findWorldInCluster(iter : Iterator[Zone], zoneId : String) : Option[Zone] = {
@tailrec private def recursiveFindWorldInCluster(iter : Iterator[Zone], predicate : Zone=>Boolean) : Option[Zone] = {
if(!iter.hasNext) {
None
}
else {
val cont = iter.next
if(cont.Id == zoneId) {
if(predicate.apply(cont)) {
Some(cont)
}
else {
findWorldInCluster(iter, zoneId)
recursiveFindWorldInCluster(iter, predicate)
}
}
}
@ -104,3 +113,31 @@ object InterstellarCluster {
*/
final case class ClientInitializationComplete()
}
/*
// List[Building] --> List[List[(Amenity, Building)]] --> List[(SpawnTube*, Building)]
zone.LocalLattice.Buildings.values
.filter(_.Faction == player.Faction)
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
*/
/*
zone.Buildings.values.filter(building => {
(
if(spawn_zone == 6) { Set(StructureType.Tower) }
else if(spawn_zone == 7) { Set(StructureType.Facility, StructureType.Building) }
else { Set.empty[StructureType.Value] }
).contains(building.BuildingType) &&
building.Amenities.exists(_.isInstanceOf[SpawnTube]) &&
building.Faction == player.Faction &&
building.Position != Vector3.Zero
})
.toSeq
.sortBy(building => {
Vector3.DistanceSquared(player.Position, building.Position) < Vector3.DistanceSquared(player.Position, building.Position)
})
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
).headOption
*/

View file

@ -10,6 +10,7 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3
@ -58,11 +59,13 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
/** */
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]()
/** */
private var corpses : List[Player] = List[Player]()
private val corpses : ListBuffer[Player] = ListBuffer[Player]()
/** */
private var population : ActorRef = ActorRef.noSender
private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building]
/** key - spawn zone id, value - buildings belonging to spawn zone */
private var spawnGroups : Map[Building, List[SpawnTube]] = PairMap[Building, List[SpawnTube]]()
/**
* Establish the basic accessible conditions necessary for a functional `Zone`.<br>
@ -87,11 +90,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this), s"$Id-vehicles")
population = context.actorOf(Props(classOf[ZonePopulationActor], this), s"$Id-players")
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
Map.LocalObjects.foreach({ builderObject => builderObject.Build })
MakeBuildings(context)
AssignAmenities()
CreateSpawnGroups()
}
}
@ -191,7 +195,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def LivePlayers : List[Player] = players.values.collect( { case Some(tplayer) => tplayer }).toList
def Corpses : List[Player] = corpses
def Corpses : List[Player] = corpses.toList
def AddVehicle(vehicle : Vehicle) : List[Vehicle] = {
vehicles = vehicles :+ vehicle
@ -222,78 +226,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}
}
def PopulationJoin(avatar : Avatar) : Boolean = {
players.get(avatar) match {
case Some(_) =>
false
case None =>
players += avatar -> None
true
}
}
def PopulationLeave(avatar : Avatar) : Option[Player] = {
players.remove(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer
}
}
def PopulationSpawn(avatar : Avatar, player : Player) : Option[Player] = {
players.get(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer match {
case Some(aplayer) =>
Some(aplayer)
case None =>
players(avatar) = Some(player)
Some(player)
}
}
}
def PopulationRelease(avatar : Avatar) : Option[Player] = {
players.get(avatar) match {
case None =>
None
case Some(tplayer) =>
players(avatar) = None
tplayer
}
}
def CorpseAdd(player : Player) : Unit = {
if(player.isBackpack && recursiveFindCorpse(players.values.filter(_.nonEmpty).map(_.get).iterator, player.GUID).isEmpty) {
corpses = corpses :+ player
}
}
def CorpseRemove(player : Player) : Unit = {
recursiveFindCorpse(corpses.iterator, player.GUID) match {
case Some(index) =>
corpses = corpses.take(index-1) ++ corpses.drop(index)
case None => ;
}
}
@tailrec final def recursiveFindCorpse(iter : Iterator[Player], guid : PlanetSideGUID, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
if(iter.next.GUID == guid) {
Some(index)
}
else {
recursiveFindCorpse(iter, guid, index + 1)
}
}
}
/**
* Coordinate `Equipment` that has been dropped on the ground or to-be-dropped on the ground.
* @return synchronized reference to the ground
@ -326,26 +258,55 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
})
}
private def CreateSpawnGroups() : Unit = {
buildings.values
.filterNot { _.Position == Vector3.Zero }
.map(building => { building -> building.Amenities.collect { case(obj : SpawnTube) => obj } })
.filter( { case((_, spawns)) => spawns.nonEmpty })
.foreach { SpawnGroups }
}
def SpawnGroups() : Map[Building, List[SpawnTube]] = spawnGroups
def SpawnGroups(building : Building) : List[SpawnTube] = SpawnGroups(building.Id)
def SpawnGroups(buildingId : Int) : List[SpawnTube] = {
spawnGroups.find({ case((building, _)) => building.Id == buildingId }) match {
case Some((_, list)) =>
list
case None =>
List.empty[SpawnTube]
}
}
def SpawnGroups(spawns : (Building, List[SpawnTube])) : Map[Building, List[SpawnTube]] = {
val (building, tubes) = spawns
val entry : Map[Building, List[SpawnTube]] = PairMap(building -> tubes)
spawnGroups = spawnGroups ++ entry
entry
}
/**
* Provide bulk correspondence on all map entities that can be composed into packet messages and reported to a client.
* These messages are sent in this fashion at the time of joining the server:<br>
* - `BroadcastWarpgateUpdateMessage`<br>
* - `BuildingInfoUpdateMessage`<br>
* - `DensityLevelUpdateMessage`<br>
* - `BroadcastWarpgateUpdateMessage`<br>
* - `CaptureFlagUpdateMessage`<br>
* - `ContinentalLockUpdateMessage`<br>
* - `DensityLevelUpdateMessage`<br>
* - `ModuleLimitsMessage`<br>
* - `VanuModuleUpdateMessage`<br>
* - `ZoneForcedCavernConnectionMessage`<br>
* - `ZoneInfoMessage`<br>
* - `ZoneLockInfoMessage`<br>
* - `ZonePopulationUpdateMessage`
* @return a `List` of `GamePacket` messages
* @return the `Zone` object
*/
def ClientInitialization() : Zone = this
}
object Zone {
/** Default value, non-zone area. */
final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99)
/**
@ -355,20 +316,96 @@ object Zone {
final case class Init()
object Population {
/**
* Message that introduces a user, by their `Avatar`, into a `Zone`.
* That user will be counted as part of that zone's population.
* The `avatar` may associate `Player` objects with itself in the future.
* @param avatar the `Avatar` object
*/
final case class Join(avatar : Avatar)
/**
* Message that excuses a user, by their `Avatar`, into a `Zone`.
* That user will not longer be counted as part of that zone's population.
* @see `PlayerHasLeft`
* @param avatar the `Avatar` object
*/
final case class Leave(avatar : Avatar)
/**
* Message that instructs the zone to disassociate a `Player` from this `Actor`.
* @see `PlayerAlreadySpawned`<br>
* `PlayerCanNotSpawn`
* @param avatar the `Avatar` object
* @param player the `Player` object
*/
final case class Spawn(avatar : Avatar, player : Player)
/**
* Message that instructs the zone to disassociate a `Player` from this `Actor`.
* @see `PlayerHasLeft`
* @param avatar the `Avatar` object
*/
final case class Release(avatar : Avatar)
/**
* Message that acts in reply to `Leave(avatar)` or `Release(avatar)`.
* In the former case, the avatar will have successfully left the zone, and `player` may be defined.
* In the latter case, the avatar did not initially `Join` the zone, and `player` is `None`.
* This message should not be considered a failure or a success case.
* @see `Release`<br>
* `Leave`
* @param zone the `Zone` object
* @param player the `Player` object
*/
final case class PlayerHasLeft(zone : Zone, player : Option[Player]) //Leave(avatar), but still has a player
final case class PlayerAlreadySpawned(player : Player) //Spawn(avatar, player), but avatar already has a player
final case class PlayerCanNotSpawn(zone : Zone, player : Player) //Spawn(avatar, player), but avatar did not initially Join(avatar)
/**
* Message that acts in reply to `Spawn(avatar, player)`, but the avatar already has a player.
* @param player the `Player` object
*/
final case class PlayerAlreadySpawned(player : Player)
/**
* Message that acts in reply to `Spawn(avatar, player)`, but the avatar did not initially `Join` this zone.
* @param zone the `Zone` object
* @param player the `Player` object
*/
final case class PlayerCanNotSpawn(zone : Zone, player : Player)
}
object Corpse {
/**
* Message that reports to the zone of a freshly dead player.
* @param player the dead `Player`
*/
final case class Add(player : Player)
/**
* Message that tells the zone to no longer mind the dead player.
* @param player the dead `Player`
*/
final case class Remove(player : Player)
}
object Lattice {
/**
* Message requesting that the current zone determine where a `player` can spawn.
* @param zone_number this zone's numeric identifier
* @param player the `Player` object
* @param spawn_group the category of spawn points the request wants searched
*/
final case class RequestSpawnPoint(zone_number : Int, player : Player, spawn_group : Int)
/**
* Message that returns a discovered spawn point to a request source.
* @param zone_id the zone's text identifier
* @param building the `Building` in which the spawnpoint is located
* @param spawn_tube the spawn point holding object
*/
final case class SpawnPoint(zone_id : String, building : Building, spawn_tube : SpawnTube)
/**
* Message that informs a request source that a spawn point could not be discovered with the previous criteria.
* @param zone_number this zone's numeric identifier
* @param spawn_group the spawn point holding object;
* if `None`, then the previous `zone_number` could not be found;
* otherwise, no spawn points could be found in the zone
*/
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
}
/**
* Message to relinguish an item and place in on the ground.
* @param item the piece of `Equipment`

View file

@ -1,8 +1,12 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.zones
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.types.Vector3
import org.log4s.Logger
/**
@ -12,29 +16,108 @@ import org.log4s.Logger
class ZoneActor(zone : Zone) extends Actor {
private[this] val log = org.log4s.getLogger
def receive : Receive = {
def receive : Receive = Init
def Init : Receive = {
case Zone.Init() =>
zone.Init
ZoneSetupCheck()
context.become(Processing)
case _ => ;
}
def Processing : Receive = {
//frwd to Population Actor
case msg @ Zone.Population.Join =>
zone.Population forward msg
case msg @ Zone.Population.Leave =>
zone.Population forward msg
case msg @ Zone.Population.Spawn =>
zone.Population forward msg
case msg @ Zone.Population.Release =>
zone.Population forward msg
case msg @ Zone.Corpse.Add =>
zone.Population forward msg
case msg @ Zone.Corpse.Remove =>
zone.Population forward msg
//frwd to Ground Actor
case msg @ Zone.DropItemOnGround =>
zone.Ground forward msg
case msg @ Zone.GetItemOnGround =>
zone.Ground forward msg
//frwd to Vehicle Actor
case msg @ Zone.SpawnVehicle =>
zone.Transport forward msg
case msg @ Zone.DespawnVehicle =>
zone.Transport forward msg
//own
case Zone.Lattice.RequestSpawnPoint(zone_number, player, spawn_group) =>
if(zone_number == zone.Number) {
val buildingTypeSet = if(spawn_group == 6) {
Set(StructureType.Tower)
}
else if(spawn_group == 7) {
Set(StructureType.Facility, StructureType.Building)
}
else {
Set.empty[StructureType.Value]
}
val playerPosition = player.Position.xy
zone.SpawnGroups()
.filter({ case((building, _)) =>
building.Faction == player.Faction && buildingTypeSet.contains(building.BuildingType)
})
.toSeq
.sortBy({ case ((building, _)) =>
Vector3.DistanceSquared(playerPosition, building.Position.xy)
})
.headOption match {
case Some((building, List(tube))) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, tube)
case Some((building, tubes)) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, scala.util.Random.shuffle(tubes).head)
case None =>
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, Some(spawn_group))
}
}
else { //wrong zone_number
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
}
case msg =>
log.warn(s"Received unexpected message - $msg")
}
def ZoneSetupCheck(): Unit = {
def ZoneSetupCheck() : Int = {
import ZoneActor._
def guid(id : Int) = zone.GUID(id)
val map = zone.Map
def guid(id : Int) = zone.GUID(id)
val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity")
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog)
val errors = new AtomicInteger(0)
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
//check base to object associations
map.ObjectToBuilding.foreach({ case((object_guid, building_id)) =>
if(zone.Building(building_id).isEmpty) {
slog.error(s"expected a building at id #$building_id")
errors.incrementAndGet()
}
if(guid(object_guid).isEmpty) {
slog.error(s"expected object id $object_guid to exist, but it did not")
errors.incrementAndGet()
}
})
@ -55,11 +138,11 @@ class ZoneActor(zone : Zone) extends Actor {
validateObject(mech_guid, ImplantMechCheck, "implant terminal mech")
validateObject(interface_guid, TerminalCheck, "implant terminal interface")
})
errors.intValue()
}
}
object ZoneActor {
/**
* Recover an object from a collection and perform any number of validating tests upon it.
* If the object fails any tests, log an error.
@ -73,11 +156,12 @@ object ZoneActor {
* @return `true` if the object was discovered and validates correctly;
* `false` if the object failed any tests
*/
def ValidateObject(guid : (Int)=>Option[PlanetSideGameObject], elog : Logger)
def ValidateObject(guid : (Int)=>Option[PlanetSideGameObject], elog : Logger, errorCounter : AtomicInteger)
(object_guid : Int, test : (PlanetSideGameObject)=>Boolean, description : String) : Boolean = {
try {
if(!test(guid(object_guid).get)) {
elog.error(s"expected id $object_guid to be a $description, but it was not")
errorCounter.incrementAndGet()
false
}
else {
@ -87,6 +171,7 @@ object ZoneActor {
catch {
case e : Exception =>
elog.error(s"expected a $description at id $object_guid but no object is initialized - $e")
errorCounter.incrementAndGet()
false
}
}

View file

@ -2,42 +2,183 @@
package net.psforever.objects.zones
import akka.actor.Actor
import net.psforever.objects.{Avatar, Player}
import scala.annotation.tailrec
import scala.collection.concurrent.TrieMap
import scala.collection.mutable.ListBuffer
/**
* A support `Actor` that sequences adding and removing `Avatar` and `Player` objects to mappings and lists.
* The former mapping is considered to represent every user connect to the `zone` (`as Avatar` objects)
* and their current representation (as `Player` objects).
* The latter list keeps track of a group of former user representations.
* @param zone the `Zone` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @param corpseList a list of `Player` objects
*/
class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player]], corpseList : ListBuffer[Player]) extends Actor {
import ZonePopulationActor._
class ZonePopulationActor(zone : Zone) extends Actor {
def receive : Receive = {
case Zone.Population.Join(avatar) =>
zone.PopulationJoin(avatar)
PopulationJoin(avatar, playerMap)
case Zone.Population.Leave(avatar) =>
zone.PopulationLeave(avatar) match {
PopulationLeave(avatar, playerMap) match {
case None => ;
case player @ Some(_) =>
sender ! Zone.Population.PlayerHasLeft(zone, player)
}
case Zone.Population.Spawn(avatar, player) =>
zone.PopulationSpawn(avatar, player) match {
PopulationSpawn(avatar, player, playerMap) match {
case Some(tplayer) =>
if(tplayer != player) {
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
if(tplayer ne player) {
sender ! Zone.Population.PlayerAlreadySpawned(player)
}
case None =>
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
}
case Zone.Population.Release(avatar) =>
zone.PopulationRelease(avatar) match {
PopulationRelease(avatar, playerMap) match {
case Some(_) => ;
case None =>
sender ! Zone.Population.PlayerHasLeft(zone, None)
}
case Zone.Corpse.Add(player) =>
zone.CorpseAdd(player)
CorpseAdd(player, corpseList)
case Zone.Corpse.Remove(player) =>
zone.CorpseRemove(player)
CorpseRemove(player, corpseList)
case _ => ;
}
}
object ZonePopulationActor {
/**
* Add an `avatar` as the key of an `Avatar` to `Player` object pair in the given collection.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return true, if the mapping is for a new key;
* false, if the key already exists
*/
def PopulationJoin(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Boolean = {
playerMap.get(avatar) match {
case Some(_) =>
false
case None =>
playerMap += avatar -> None
true
}
}
/**
* Remove an `avatar` from the key of an `Avatar` to `Player` object pair in the given collection.
* If a `Player` object is associated at the time, return it safely.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that was associated at the time the `avatar` was removed
*/
def PopulationLeave(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.remove(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer
}
}
/**
* Associate a `Player` object as a value to an existing `Avatar` object that will be its key.
* Do not overwrite players that are already associated.
* @param avatar an `Avatar` object
* @param player a `Player` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return the `Player` object that is associated with the `Avatar` key
*/
def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer match {
case Some(aplayer) =>
Some(aplayer)
case None =>
playerMap(avatar) = Some(player)
Some(player)
}
}
}
/**
* Disassociate a `Player` object from an existing `Avatar` object that was be its key.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that is associated at the time
*/
def PopulationRelease(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
playerMap(avatar) = None
tplayer
}
}
/**
* If the given `player` passes a condition check, add it to the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
* @return true, if the `player` was added to the list;
* false, otherwise
*/
def CorpseAdd(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
if(player.isBackpack) {
corpseList += player
true
}
else {
false
}
}
/**
* Remove the given `player` from the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
*/
def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = {
recursiveFindCorpse(corpseList.iterator, player) match {
case None => ;
case Some(index) =>
corpseList.remove(index)
}
}
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `Player` objects
* @param player the target `Player`
* @param index the index of the discovered `Player` object
* @return the index of the `Player` object in the list to be removed;
* `None`, otherwise
*/
@tailrec final def recursiveFindCorpse(iter : Iterator[Player], player : Player, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
if(iter.next == player) {
Some(index)
}
else {
recursiveFindCorpse(iter, player, index + 1)
}
}
}
}

View file

@ -20,8 +20,8 @@ import scodec.codecs._
* @param unk3 na
*/
final case class DisconnectMessage(msg : String,
unk2 : String = "",
unk3 : String = "")
unk2 : String,
unk3 : String)
extends PlanetSideGamePacket {
type Packet = DisconnectMessage
def opcode = GamePacketOpcode.DisconnectMessage
@ -29,6 +29,15 @@ final case class DisconnectMessage(msg : String,
}
object DisconnectMessage extends Marshallable[DisconnectMessage] {
/**
* Overloaded constructor that focuses only on the visible disconnection message
* @param msg the displayed message
* @return a `DisconnectMessage` object
*/
def apply(msg : String) : DisconnectMessage = {
new DisconnectMessage(msg, "", "")
}
implicit val codec : Codec[DisconnectMessage] = (
("msg" | PacketHelpers.encodedString) ::
("unk2" | PacketHelpers.encodedString) ::

View file

@ -6,12 +6,16 @@ import scodec.Codec
import scodec.codecs._
/**
*
* @param unk1 na
* @param unk2 na
* na
* @param unk1 when defined, na;
* non-zero when selecting the sanctuary option from a non-sanctuary continent deployment map
* @param unk2 when defined, indicates type of spawn point by destination;
* 0 is unknown (may refer to all available spawns regardless of last position);
* 6 is towers;
* 7 is facilities
* @param unk3 na
* @param unk4 na
* @param unk5 continent?
* @param unk5 when defined, the continent number
*/
final case class SpawnRequestMessage(unk1 : Int,
unk2 : Long,

View file

@ -9,8 +9,8 @@ final case class Vector3(x : Float,
y : Float,
z : Float) {
/**
* Operator override for vector addition, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector1 + vector2."
* Operator for vector addition, treating `Vector3` objects as actual mathematical vectors.
* The application of this definition is "vector1 + vector2."
* @param vec the other `Vector3` object
* @return a new `Vector3` object with the summed values
*/
@ -19,8 +19,8 @@ final case class Vector3(x : Float,
}
/**
* Operator override for vector subtraction, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector1 - vector2."
* Operator for vector subtraction, treating `Vector3` objects as actual mathematical vectors.
* The application of this definition is "vector1 - vector2."
* @param vec the other `Vector3` object
* @return a new `Vector3` object with the difference values
*/
@ -29,7 +29,7 @@ final case class Vector3(x : Float,
}
/**
* Operator override for vector scaling, treating `Vector3` objects as actual mathematical vectors.
* Operator for vector scaling, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector * scalar" exclusively.
* "scalar * vector" is invalid.
* @param scalar the value to multiply this vector
@ -38,6 +38,14 @@ final case class Vector3(x : Float,
def *(scalar : Float) : Vector3 = {
Vector3(x*scalar, y*scalar, z*scalar)
}
/**
* Operator for returning the ground-planar coordinates
* and ignoring the perpendicular distance from the world floor.
* The application of this definition is "vector.xy" or "vector xy."
* @return a new `Vector3` object with only two of the components of the original
*/
def xy : Vector3 = Vector3(x, y, 0)
}
object Vector3 {

View file

@ -64,6 +64,11 @@ class Vector3Test extends Specification {
vec * 3f mustEqual Vector3(3.8999999f, -7.7999997f, 11.700001f)
}
"separate into x-component and y-component only" in {
val obj = Vector3(1.1f, 2.2f, 3.3f)
obj.xy mustEqual Vector3(1.1f, 2.2f, 0f)
}
"calculate the unit vector (zero)" in {
Vector3.Unit(Vector3.Zero) mustEqual Vector3(0,0,0)
}

View file

@ -0,0 +1,397 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.definition.ImplantDefinition
import net.psforever.types.{CharacterGender, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._
class AvatarTest extends Specification {
def CreatePlayer() : (Player, Avatar) = {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
val
player = Player(avatar)
player.Slot(0).Equipment = Tool(beamer)
player.Slot(2).Equipment = Tool(suppressor)
player.Slot(4).Equipment = Tool(forceblade)
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
player.Slot(36).Equipment = AmmoBox(energy_cell)
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
(player, avatar)
}
"construct" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.name mustEqual "Chord"
av.faction mustEqual PlanetSideEmpire.TR
av.sex mustEqual CharacterGender.Male
av.head mustEqual 0
av.voice mustEqual 5
av.BEP mustEqual 0
av.CEP mustEqual 0
av.Certifications mustEqual Set.empty
av.Definition.ObjectId mustEqual 121
}
"can maintain cumulative battle experience point values" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = 100
av.BEP mustEqual 100
av.BEP = 700
av.BEP mustEqual 700
}
"can maintain battle experience point values up to a maximum (Long.MaxValue)" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = 4294967295L
av.BEP mustEqual 4294967295L
}
"can not maintain battle experience point values below zero" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = -1
av.BEP mustEqual 0
av.BEP = 100
av.BEP mustEqual 100
av.BEP = -1
av.BEP mustEqual 0
}
"can maintain cumulative command experience point values" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = 100
av.CEP mustEqual 100
av.CEP = 700
av.CEP mustEqual 700
}
"can maintain command experience point values up to a maximum (Long.MaxValue)" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = 4294967295L
av.CEP mustEqual 4294967295L
}
"can not maintain command experience point values below zero" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = -1
av.CEP mustEqual 0
av.CEP = 100
av.CEP mustEqual 100
av.CEP = -1
av.CEP mustEqual 0
}
"can tell the difference between avatars" in {
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
}
//refer to ImplantTest.scala for more tests
"maximum of three implant slots" in {
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants.length mustEqual 3
obj.Implants(0).Unlocked mustEqual false
obj.Implants(0).Initialized mustEqual false
obj.Implants(0).Active mustEqual false
obj.Implants(0).Implant mustEqual ImplantType.None
obj.Implant(0) mustEqual ImplantType.None
obj.Implants(0).Installed mustEqual None
obj.Implants(1).Unlocked mustEqual false
obj.Implants(1).Initialized mustEqual false
obj.Implants(1).Active mustEqual false
obj.Implants(1).Implant mustEqual ImplantType.None
obj.Implant(1) mustEqual ImplantType.None
obj.Implants(1).Installed mustEqual None
obj.Implants(2).Unlocked mustEqual false
obj.Implants(2).Initialized mustEqual false
obj.Implants(2).Active mustEqual false
obj.Implants(2).Implant mustEqual ImplantType.None
obj.Implant(2) mustEqual ImplantType.None
obj.Implants(2).Installed mustEqual None
obj.Implant(3) mustEqual ImplantType.None //invalid slots beyond the third always reports as ImplantType.None
}
"can install an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
case Some(slot) =>
slot.Installed mustEqual Some(testplant)
case _ =>
ko
}
ok
}
"can install implants in sequential slots" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
}
"can not install the same type of implant twice" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual None
}
"can not install more implants than slots available (two unlocked)" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual None
}
"can not install more implants than slots available (four implants)" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val testplant4 : ImplantDefinition = ImplantDefinition(4)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.InstallImplant(testplant4) mustEqual None
}
"can uninstall an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants(0).Installed mustEqual Some(testplant)
obj.UninstallImplant(testplant.Type) mustEqual Some(0)
obj.Implants(0).Installed mustEqual None
}
"can uninstall just a specific implant" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual testplant2.Type
obj.Implant(2) mustEqual testplant3.Type
obj.UninstallImplant(testplant2.Type) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual ImplantType.None
obj.Implant(2) mustEqual testplant3.Type
}
"can install implants to any available slot" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.UninstallImplant(testplant2.Type) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual ImplantType.None
obj.Implant(2) mustEqual testplant3.Type
val testplant4 : ImplantDefinition = ImplantDefinition(4)
obj.InstallImplant(testplant4) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual testplant4.Type
obj.Implant(2) mustEqual testplant3.Type
}
"can reset implants to uninitialized state" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.Implants(0).Initialized = true
obj.Implants(0).Active = true
obj.Implants(1).Initialized = true
obj.Implants(0).Initialized mustEqual true
obj.Implants(0).Active mustEqual true
obj.Implants(1).Initialized mustEqual true
obj.ResetAllImplants()
obj.Implants(0).Initialized mustEqual false
obj.Implants(0).Active mustEqual false
obj.Implants(1).Initialized mustEqual false
}
"does not have any loadout specifications by default" in {
val (_, avatar) = CreatePlayer()
(0 to 9).foreach { avatar.LoadLoadout(_) mustEqual None }
ok
}
"save player's current inventory as a loadout" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment.get.asInstanceOf[Tool].Magazine = 1 //non-standard but legal
obj.Slot(2).Equipment.get.asInstanceOf[Tool].AmmoSlot.Magazine = 100 //non-standard (and out of range, real=25)
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
val holsters = items.VisibleSlots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1 //we changed this
holsters(1).index mustEqual 2
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual suppressor
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 100 //we changed this
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
items.Inventory.length mustEqual 6
val inventory = items.Inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm_AP
inventory(4).index mustEqual 36
inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual energy_cell
inventory(5).index mustEqual 39
inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit
case None =>
ko
}
}
"save player's current inventory as a loadout, only found in the called-out slot number" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(1).isDefined mustEqual false
avatar.LoadLoadout(0).isDefined mustEqual true
}
"try to save player's current inventory as a loadout, but will not save to an invalid slot" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 10)
avatar.LoadLoadout(10) mustEqual None
}
"save player's current inventory as a loadout, without inventory contents" in {
val (obj, avatar) = CreatePlayer()
obj.Inventory.Clear()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
items.Inventory.length mustEqual 0 //empty
case None =>
ko
}
}
"save player's current inventory as a loadout, without visible slot contents" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment = None
obj.Slot(2).Equipment = None
obj.Slot(4).Equipment = None
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 0 //empty
items.Inventory.length mustEqual 6
case None =>
ko
}
}
"save, load, delete; rapidly" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0).isDefined mustEqual true
avatar.DeleteLoadout(0)
avatar.LoadLoadout(0) mustEqual None
}
"the fifth slot is the locker wrapped in an EquipmentSlot" in {
val (_, avatar) = CreatePlayer()
avatar.FifthSlot.Equipment.contains(avatar.Locker)
}
"toString" in {
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5).toString mustEqual "TR Chord"
}
}

View file

@ -6,7 +6,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl, WarpGate}
import net.psforever.objects.serverobject.structures._
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
@ -27,7 +27,7 @@ class AmenityTest extends Specification {
"can be owned by a building" in {
val ao = new AmenityObject()
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
ao.Owner = bldg
ao.Owner mustEqual bldg
@ -51,7 +51,7 @@ class AmenityTest extends Specification {
"confer faction allegiance through ownership" in {
//see FactionAffinityTest
val ao = new AmenityObject()
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
ao.Owner = bldg
bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
ao.Faction mustEqual PlanetSideEmpire.NEUTRAL
@ -66,7 +66,7 @@ class AmenityTest extends Specification {
class BuildingTest extends Specification {
"Building" should {
"construct" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Id mustEqual 10
bldg.Actor mustEqual ActorRef.noSender
bldg.Amenities mustEqual Nil
@ -75,7 +75,7 @@ class BuildingTest extends Specification {
}
"change faction affinity" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
bldg.Faction = PlanetSideEmpire.TR
@ -83,7 +83,7 @@ class BuildingTest extends Specification {
}
"keep track of amenities" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
val door1 = Door(GlobalDefinitions.door)
val door2 = Door(GlobalDefinitions.door)
@ -114,7 +114,7 @@ class WarpGateTest extends Specification {
class BuildingControl1Test extends ActorTest {
"Building Control" should {
"construct" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
assert(bldg.Actor != ActorRef.noSender)
}
@ -124,7 +124,7 @@ class BuildingControl1Test extends ActorTest {
class BuildingControl2Test extends ActorTest {
"Building Control" should {
"convert and assert faction affinity on convert request" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
assert(bldg.Faction == PlanetSideEmpire.TR)
@ -142,7 +142,7 @@ class BuildingControl2Test extends ActorTest {
class BuildingControl3Test extends ActorTest {
"Building Control" should {
"convert and assert faction affinity on convert request, and for each of its amenities" in {
val bldg = Building(10, Zone.Nowhere)
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "building-test")
val door1 = Door(GlobalDefinitions.door)

View file

@ -4,7 +4,7 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
@ -121,7 +121,7 @@ object DoorControlTest {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Door) = {
val door = Door(GlobalDefinitions.door)
door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
door.Owner = new Building(0, Zone.Nowhere)
door.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
door.Owner.Faction = faction
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), door)
}

View file

@ -5,7 +5,7 @@ import akka.actor.{Actor, ActorSystem, Props}
import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideEmpire
import org.specs2.mutable.Specification
@ -42,7 +42,7 @@ class FactionAffinityTest extends Specification {
"inherits affinity from owner 2" in {
val obj = new Door(GlobalDefinitions.door)
val bldg = new Building(1, Zone.Nowhere)
val bldg = new Building(1, Zone.Nowhere, StructureType.Building)
obj.Owner = bldg
obj.Faction mustEqual PlanetSideEmpire.NEUTRAL

View file

@ -5,7 +5,7 @@ import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
@ -67,7 +67,7 @@ object IFFLockControlTest {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, IFFLock) = {
val lock = IFFLock(GlobalDefinitions.lock_external)
lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
lock.Owner = new Building(0, Zone.Nowhere)
lock.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
lock.Owner.Faction = faction
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), lock)
}

View file

@ -9,190 +9,82 @@ import org.specs2.mutable._
class LoadoutTest extends Specification {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
def CreatePlayer() : (Player, Avatar) = {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
val
player = Player(avatar)
player.Slot(0).Equipment = Tool(beamer)
player.Slot(2).Equipment = Tool(suppressor)
player.Slot(4).Equipment = Tool(forceblade)
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
player.Slot(36).Equipment = AmmoBox(energy_cell)
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
(player, avatar)
def CreatePlayer() : Player = {
new Player(avatar) {
Slot(0).Equipment = Tool(beamer)
Slot(2).Equipment = Tool(suppressor)
Slot(4).Equipment = Tool(forceblade)
Slot(6).Equipment = ConstructionItem(ace)
Slot(9).Equipment = AmmoBox(bullet_9mm)
Slot(12).Equipment = AmmoBox(bullet_9mm)
Slot(33).Equipment = Kit(medkit)
Slot(39).Equipment = SimpleItem(remote_electronics_kit)
}
}
"Player Loadout" should {
"test sample player" in {
val (obj, _) = CreatePlayer()
obj.Holsters()(0).Equipment.get.Definition mustEqual beamer
obj.Holsters()(2).Equipment.get.Definition mustEqual suppressor
obj.Holsters()(4).Equipment.get.Definition mustEqual forceblade
obj.Slot(6).Equipment.get.Definition mustEqual bullet_9mm
obj.Slot(9).Equipment.get.Definition mustEqual bullet_9mm
obj.Slot(12).Equipment.get.Definition mustEqual bullet_9mm
obj.Slot(33).Equipment.get.Definition mustEqual bullet_9mm_AP
obj.Slot(36).Equipment.get.Definition mustEqual energy_cell
obj.Slot(39).Equipment.get.Definition mustEqual remote_electronics_kit
}
"test sample player" in {
val player = CreatePlayer()
player.Holsters()(0).Equipment.get.Definition mustEqual beamer
player.Holsters()(2).Equipment.get.Definition mustEqual suppressor
player.Holsters()(4).Equipment.get.Definition mustEqual forceblade
player.Slot(6).Equipment.get.Definition mustEqual ace
player.Slot(9).Equipment.get.Definition mustEqual bullet_9mm
player.Slot(12).Equipment.get.Definition mustEqual bullet_9mm
player.Slot(33).Equipment.get.Definition mustEqual medkit
player.Slot(39).Equipment.get.Definition mustEqual remote_electronics_kit
}
"do not load, if never saved" in {
CreatePlayer()._2.LoadLoadout(0) mustEqual None
}
"create a loadout that contains a player's inventory" in {
val player = CreatePlayer()
val obj = Loadout.Create(player, "test")
"save but incorrect load" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
obj.Label mustEqual "test"
obj.ExoSuit mustEqual obj.ExoSuit
obj.Subtype mustEqual 0
avatar.LoadLoadout(1) mustEqual None
}
obj.VisibleSlots.length mustEqual 3
val holsters = obj.VisibleSlots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters(1).index mustEqual 2
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual suppressor
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
"save and load" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment.get.asInstanceOf[Tool].Magazine = 1 //non-standard but legal
obj.Slot(2).Equipment.get.asInstanceOf[Tool].AmmoSlot.Magazine = 100 //non-standard (and out of range, real=25)
avatar.SaveLoadout(obj, "test", 0)
obj.Inventory.length mustEqual 5
val inventory = obj.Inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].definition mustEqual ace
inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandKit].definition mustEqual medkit
inventory(4).index mustEqual 39
inventory(4).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit
}
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
"distinguish MAX subtype information" in {
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)
items.VisibleSlots.length mustEqual 3
val holsters = items.VisibleSlots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual beamer
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1
holsters(1).index mustEqual 2
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual suppressor
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 100
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual forceblade
val ldout1 = Loadout.Create(player, "weaponless")
slot.Equipment = None
slot.Equipment = Tool(trhev_dualcycler)
val ldout2 = Loadout.Create(player, "cycler")
slot.Equipment = None
slot.Equipment = Tool(trhev_pounder)
val ldout3 = Loadout.Create(player, "pounder")
slot.Equipment = None
slot.Equipment = Tool(trhev_burster)
val ldout4 = Loadout.Create(player, "burster")
items.Inventory.length mustEqual 6
val inventory = items.Inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm_AP
inventory(4).index mustEqual 36
inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual energy_cell
inventory(5).index mustEqual 39
inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].sdef mustEqual remote_electronics_kit
case None =>
ko
}
}
"save without inventory contents" in {
val (obj, avatar) = CreatePlayer()
obj.Inventory.Clear()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
items.Inventory.length mustEqual 0 //empty
case None =>
ko
}
}
"save without visible slot contents" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment = None
obj.Slot(2).Equipment = None
obj.Slot(4).Equipment = None
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 0 //empty
items.Inventory.length mustEqual 6
case None =>
ko
}
}
"save (a construction item) and load" in {
val (obj, avatar) = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = ConstructionItem(advanced_ace)
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Inventory.length mustEqual 1
items.Inventory.head.index mustEqual 6
items.Inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].cdef mustEqual advanced_ace
case None =>
ko
}
}
"save (a kit) and load" in {
val (obj, avatar) = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = Kit(medkit)
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Inventory.length mustEqual 1
items.Inventory.head.index mustEqual 6
items.Inventory.head.item.asInstanceOf[Loadout.ShorthandKit].kdef mustEqual medkit
case None =>
ko
}
}
"save, load, delete" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case None =>
ko
case Some(_) => ; //good; keep going
}
avatar.DeleteLoadout(0)
avatar.LoadLoadout(0) mustEqual None
}
"distinguish MAX subtype information" in {
val (obj, avatar) = CreatePlayer()
val slot = obj.Slot(0)
slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max)
Player.SuitSetup(obj, ExoSuitType.MAX)
avatar.SaveLoadout(obj, "generic", 0) //weaponless
slot.Equipment = None
slot.Equipment = Tool(trhev_dualcycler)
avatar.SaveLoadout(obj, "cycler", 1)
slot.Equipment = None
slot.Equipment = Tool(trhev_pounder)
avatar.SaveLoadout(obj, "pounder", 2)
slot.Equipment = None
slot.Equipment = Tool(trhev_burster)
avatar.SaveLoadout(obj, "burster", 3)
avatar.LoadLoadout(0).get.Subtype mustEqual 0
avatar.LoadLoadout(1).get.Subtype mustEqual 1
avatar.LoadLoadout(2).get.Subtype mustEqual 2
avatar.LoadLoadout(3).get.Subtype mustEqual 3
}
ldout1.Subtype mustEqual 0
ldout2.Subtype mustEqual 1
ldout3.Subtype mustEqual 2
ldout4.Subtype mustEqual 3
}
}

View file

@ -1,45 +1,61 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.definition.SimpleItemDefinition
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._
import scala.util.Success
class PlayerTest extends Specification {
def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
new Player(Avatar(name, faction, sex, head, voice))
}
"construct" in {
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
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 {
(PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
(PlayerTest.Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
(PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
(PlayerTest.Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(PlayerTest.Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(PlayerTest.Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
PlayerTest.Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
}
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
"become a backpack" in {
val obj = PlayerTest.Player("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
(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 = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.isAlive mustEqual false
obj.Health mustEqual 0
obj.Stamina mustEqual 0
@ -54,32 +70,24 @@ class PlayerTest extends Specification {
obj.Armor mustEqual 50
}
"set new maximum values (health, stamina)" in {
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.MaxHealth mustEqual 100
obj.MaxStamina mustEqual 100
obj.MaxHealth = 123
obj.MaxStamina = 456
"will not (re)spawn if not dead" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Spawn
obj.Health mustEqual 123
obj.Stamina mustEqual 456
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
}
"init (Standard Exo-Suit)" in {
val obj = PlayerTest.Player("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
}
"die" in {
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
"can die" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Spawn
obj.Armor = 35 //50 -> 35
obj.isAlive mustEqual true
@ -93,9 +101,80 @@ class PlayerTest extends Specification {
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 = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
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
@ -108,7 +187,7 @@ class PlayerTest extends Specification {
"remember the last drawn holster" in {
val wep1 = SimpleItem(SimpleItemDefinition(149))
val wep2 = SimpleItem(SimpleItemDefinition(149))
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
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
@ -147,7 +226,7 @@ class PlayerTest extends Specification {
"hold something in their free hand" in {
val wep = SimpleItem(SimpleItemDefinition(149))
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
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
@ -155,15 +234,15 @@ class PlayerTest extends Specification {
"provide an invalid hand that can not hold anything" in {
val wep = SimpleItem(SimpleItemDefinition(149))
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
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 = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Inventory.Resize(3,3)
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)
@ -171,52 +250,185 @@ class PlayerTest extends Specification {
val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
val ammo2 = AmmoBox(GlobalDefinitions.bullet_9mm)
//val ammo3 = 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(ammo2) mustEqual None
obj.Fit(ammo3) mustEqual None
}
//TODO move to Avatar tests
// "install an implant" in {
// val testplant : ImplantDefinition = ImplantDefinition(1)
// val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
// obj.Implants(0).Unlocked = true
// obj.InstallImplant(testplant) mustEqual Some(0)
// obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
// case Some(slot) =>
// slot.Installed mustEqual Some(testplant)
// case _ =>
// ko
// }
// ok
// }
//
// "can not install the same type of implant twice" in {
// val testplant1 : ImplantDefinition = ImplantDefinition(1)
// val testplant2 : ImplantDefinition = ImplantDefinition(1)
// val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
// obj.Implants(0).Unlocked = true
// obj.Implants(1).Unlocked = true
// obj.InstallImplant(testplant1) mustEqual Some(0)
// obj.InstallImplant(testplant2) mustEqual Some(1)
// }
//
// "uninstall implants" in {
// val testplant : ImplantDefinition = ImplantDefinition(1)
// val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
// obj.Implants(0).Unlocked = true
// obj.InstallImplant(testplant) mustEqual Some(0)
// obj.Implants(0).Installed mustEqual Some(testplant)
//
// obj.UninstallImplant(testplant.Type)
// obj.Implants(0).Installed 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 Some(Player.LockerSlot) //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 = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.VehicleSeated mustEqual None
obj.VehicleSeated = PlanetSideGUID(65)
obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
@ -225,7 +437,7 @@ class PlayerTest extends Specification {
}
"own in a vehicle" in {
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.VehicleOwned mustEqual None
obj.VehicleOwned = PlanetSideGUID(65)
obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
@ -234,15 +446,18 @@ class PlayerTest extends Specification {
}
"remember what zone he is in" in {
val obj = PlayerTest.Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Continent mustEqual "home2"
obj.Continent = "ugd01"
obj.Continent mustEqual "ugd01"
}
}
object PlayerTest {
def Player(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
new Player(Avatar(name, faction, sex, head, voice))
"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

@ -5,7 +5,7 @@ import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
@ -14,7 +14,7 @@ import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest {
"Building object" should {
"build" in {
val structure : (Int,Zone,ActorContext)=>Building = Building.Structure
val structure : (Int,Zone,ActorContext)=>Building = Building.Structure(StructureType.Building)
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "building")
actor ! "!"
@ -169,6 +169,26 @@ class LockerObjectBuilderTest extends ActorTest {
}
}
class SpawnTubeObjectBuilderTest extends ActorTest {
import net.psforever.objects.serverobject.tube.SpawnTube
"LockerObjectBuilder" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))), hub), "spawn-tube")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[SpawnTube])
assert(reply.asInstanceOf[SpawnTube].HasGUID)
assert(reply.asInstanceOf[SpawnTube].GUID == PlanetSideGUID(1))
assert(reply.asInstanceOf[SpawnTube].Position == Vector3(3980.4062f, 4267.3047f, 257.5625f))
assert(reply.asInstanceOf[SpawnTube].Orientation == Vector3(0, 0, 90))
assert(reply == hub(1).get)
}
}
}
object ServerObjectBuilderTest {
import net.psforever.objects.guid.source.LimitedNumberSource
def NumberPoolHub : NumberPoolHub = {

View file

@ -0,0 +1,59 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorRef, Props}
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeControl, SpawnTubeDefinition}
import org.specs2.mutable.Specification
class SpawnTubeTest extends Specification {
"SpawnTubeDefinition" should {
"define (ams_respawn_tube)" in {
val obj = new SpawnTubeDefinition(49)
obj.ObjectId mustEqual 49
obj.Name mustEqual "ams_respawn_tube"
}
"define (respawn_tube)" in {
val obj = new SpawnTubeDefinition(732)
obj.ObjectId mustEqual 732
obj.Name mustEqual "respawn_tube"
}
"define (respawn_tube_tower)" in {
val obj = new SpawnTubeDefinition(733)
obj.ObjectId mustEqual 733
obj.Name mustEqual "respawn_tube_tower"
}
"define (invalid)" in {
var id : Int = (math.random * Int.MaxValue).toInt
if(id == 49 || id == 733) {
id += 1
}
else if(id == 732) {
id += 2
}
new SpawnTubeDefinition(id) must throwA[IllegalArgumentException]
}
}
"SpawnTube" should {
"construct" in {
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
obj.Actor mustEqual ActorRef.noSender
obj.Definition mustEqual GlobalDefinitions.ams_respawn_tube
}
}
}
class SpawnTubeControlTest extends ActorTest() {
"SpawnTubeControl" should {
"construct" in {
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
obj.Actor = system.actorOf(Props(classOf[SpawnTubeControl], obj), "spawn-tube")
assert(obj.Actor != ActorRef.noSender)
}
}
}

View file

@ -3,7 +3,7 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.vehicles.VehicleControl
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
@ -109,7 +109,7 @@ object VehicleSpawnPadControl {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, VehicleSpawnPad) = {
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), "test-pad")
pad.Owner = new Building(0, Zone.Nowhere)
pad.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
pad.Owner.Faction = faction
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), pad)
}

View file

@ -1,16 +1,24 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorContext, ActorRef}
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{Actor, ActorContext, ActorRef, Props}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration
class ZoneTest extends Specification {
def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding }
@ -82,6 +90,8 @@ class ZoneTest extends Specification {
//zone also has a unique identifier system but it can't be accessed without its the Actor GUID being initialized
zone.EquipmentOnGround mustEqual List.empty[Equipment]
zone.Vehicles mustEqual List.empty[Vehicle]
zone.Players mustEqual List.empty[Player]
zone.Corpses mustEqual List.empty[Player]
}
"can have its unique identifier system changed if no objects were added to it" in {
@ -124,3 +134,368 @@ class ZoneTest extends Specification {
}
}
}
class ZoneActorTest extends ActorTest {
"Zone" should {
"have an Actor" in {
val zone = new Zone("test", new ZoneMap("map6"), 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-actor")
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Actor != ActorRef.noSender)
}
"set up spawn groups based on buildings" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(1, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
LocalObject(2, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
LocalObject(3, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(1, 1)
ObjectToBuilding(2, 1)
ObjectToBuilding(3, 1)
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
LocalObject(7, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(7, 2)
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(4, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
LocalObject(6, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
ObjectToBuilding(4, 3)
ObjectToBuilding(5, 3)
ObjectToBuilding(6, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-init")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val groups = zone.SpawnGroups()
assert(groups.size == 2)
zone.SpawnGroups().foreach({ case(building, tubes) =>
if(building.Id == 1) {
val building1 = zone.SpawnGroups(building)
assert(tubes.length == 2)
assert(tubes.head == building1.head)
assert(tubes.head.GUID == PlanetSideGUID(1))
assert(tubes(1) == building1(1))
assert(tubes(1).GUID == PlanetSideGUID(3))
}
else if(building.Id == 3) {
val building2 = zone.SpawnGroups(building)
assert(tubes.length == 1)
assert(tubes.head == building2.head)
assert(tubes.head.GUID == PlanetSideGUID(5))
}
else {
assert(false)
}
})
}
"select spawn points based on the position of the player in reference to buildings" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(1, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(1, 1)
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(4,4,4))))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
val bldg1 = zone.Building(1).get
val bldg3 = zone.Building(3).get
player.Position = Vector3(1,1,1) //closer to bldg1
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply1 = receiveOne(Duration.create(200, "ms"))
assert(reply1.isInstanceOf[Zone.Lattice.SpawnPoint])
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].zone_id == "test")
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].building == bldg1)
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].spawn_tube.Owner == bldg1)
player.Position = Vector3(3,3,3) //closer to bldg3
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply3 = receiveOne(Duration.create(200, "ms"))
assert(reply3.isInstanceOf[Zone.Lattice.SpawnPoint])
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].zone_id == "test")
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].building == bldg3)
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].spawn_tube.Owner == bldg3)
}
"will report if no spawn points have been found in a zone" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4,4,4))))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply = receiveOne(Duration.create(200, "ms"))
assert(reply.isInstanceOf[Zone.Lattice.NoValidSpawnPoint])
assert(reply.asInstanceOf[Zone.Lattice.NoValidSpawnPoint].zone_number == 1)
assert(reply.asInstanceOf[Zone.Lattice.NoValidSpawnPoint].spawn_group.contains(7))
}
}
}
class ZonePopulationTest extends ActorTest {
val testNum = new AtomicInteger(1)
def TestName : String = s"test${testNum.getAndIncrement()}"
"ZonePopulationActor" should {
"add new user to zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
}
"remove user from zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
zone.Population ! Zone.Population.Leave(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
}
"associate user with a character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
}
"disassociate character from a user" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
zone.Population ! Zone.Population.Release(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
}
"user tries to Leave, but still has an associated character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(500, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
zone.Population ! Zone.Population.Leave(avatar)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerHasLeft])
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].player.contains(player))
}
"user tries to Spawn a character, but an associated character already exists" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player1 = Player(avatar)
val player2 = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player1)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player1)
zone.Population ! Zone.Population.Spawn(avatar, player2)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player1)
assert(reply.isInstanceOf[Zone.Population.PlayerAlreadySpawned])
assert(reply.asInstanceOf[Zone.Population.PlayerAlreadySpawned].player == player1)
}
"user tries to Spawn a character, but did not Join first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Spawn(avatar, player)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerCanNotSpawn])
assert(reply.asInstanceOf[Zone.Population.PlayerCanNotSpawn].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerCanNotSpawn].player == player)
}
"user tries to Release a character, but did not Spawn a character first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Release(avatar)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerHasLeft])
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].player.isEmpty)
}
"user adds character to list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
}
"user removes character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
zone.Population ! Zone.Corpse.Remove(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.isEmpty)
}
"user removes THE CORRECT character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player1.Release
val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player2.Release
val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player3.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Corpse.Add(player1)
zone.Population ! Zone.Corpse.Add(player2)
zone.Population ! Zone.Corpse.Add(player3)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 3)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player2)
assert(zone.Corpses(2) == player3)
zone.Population ! Zone.Corpse.Remove(player2)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 2)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player3)
}
"user tries to add character to list of retired characters, but is not in correct state" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
//player.Release !!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
receiveOne(Duration.create(500, "ms")) //consume
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.isEmpty)
}
}
}
object ZoneTest {
class ZoneInitActor(zone : Zone) extends Actor {
def receive : Receive = {
case "!" =>
zone.Init(context)
sender ! "!"
case _ => ;
}
}
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}

View file

@ -0,0 +1,38 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister6Test extends ActorTest() {
"RegisterPlayer" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj.Slot(6).Equipment = obj_inv_ammo
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterPlayer(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
assert(obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
}
}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister6Test extends ActorTest() {
"UnregisterPlayer" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj.Slot(6).Equipment = obj_inv_ammo
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
guid.register(obj, "dynamic")
guid.register(obj_wep, "dynamic")
guid.register(obj_wep_ammo, "dynamic")
guid.register(obj_inv_ammo, "dynamic")
guid.register(obj_locker, "dynamic")
guid.register(obj_locker_ammo, "dynamic")
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
assert(obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterPlayer(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
}
}

View file

@ -275,6 +275,40 @@ class UniqueNumberSystemTest8 extends ActorTest() {
}
}
class UniqueNumberSystemTest9 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Failures (manually walking the failure cases)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val excp = new Exception("EXCEPTION MESSAGE")
expectNoMsg(Duration.create(200, "ms"))
//GiveNumber
uns ! NumberPoolActor.GiveNumber(1001, Some("test")) //no task associated with id="test"
uns ! NumberPoolActor.GiveNumber(1000, Some("test")) //no task associated with id="test" and number is not pooled
uns ! NumberPoolActor.GiveNumber(1000, Some(1)) //the task could theoretically exist, but does not
//NoNumber
uns ! NumberPoolActor.NoNumber(excp, Some(1))
uns ! NumberPoolActor.NoNumber(excp, None)
uns ! NumberPoolActor.NoNumber(excp, Some("test"))
//ReturnNumberResult A
uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some(1))
uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some(1))
//ReturnNumberResult B
uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some(1))
}
}
}
object UniqueNumberSystemTest {
/**
* @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
@ -14,7 +14,7 @@ class AirVehicleTerminalTest extends Specification {
"Air_Vehicle_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
@ -14,7 +14,7 @@ class CertTerminalTest extends Specification {
"Cert_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.cert_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
@ -14,7 +14,7 @@ class DropshipVehicleTerminalTest extends Specification {
"Dropship_Vehicle_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
@ -14,7 +14,7 @@ class GroundVehicleTerminalTest extends Specification {
"Ground_Vehicle_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
@ -14,7 +14,7 @@ class ImplantTerminalInterfaceTest extends Specification {
"Implant_Terminal_Interface" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -5,6 +5,7 @@ import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.vehicles.Seat
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
@ -160,7 +161,7 @@ object ImplantTerminalMechTest {
val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "mech")
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction
terminal.GUID = PlanetSideGUID(1)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)

View file

@ -22,12 +22,18 @@ class MatrixTerminalTest extends Specification {
b.Name mustEqual "matrix_terminalb"
}
"define (b)" in {
"define (c)" in {
val b = new MatrixTerminalDefinition(519)
b.ObjectId mustEqual 519
b.Name mustEqual "matrix_terminalc"
}
"define (d)" in {
val b = new MatrixTerminalDefinition(812)
b.ObjectId mustEqual 812
b.Name mustEqual "spawn_terminal"
}
"define (invalid)" in {
var id : Int = (math.random * Int.MaxValue).toInt
if(id == 517) {
@ -36,7 +42,7 @@ class MatrixTerminalTest extends Specification {
else if(id == 518) {
id += 2
}
else if(id == 519) {
else if(id == 519 | id == 812) {
id += 1
}

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal}
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
@ -39,7 +39,7 @@ class OrderTerminalABTest extends Specification {
"Order_Terminal" should {
val terminal = Terminal(GlobalDefinitions.order_terminala)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.objects.{AmmoBox, Avatar, GlobalDefinitions, Player, Tool}
@ -14,7 +14,7 @@ class OrderTerminalTest extends Specification {
"Order_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.order_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.{ActorSystem, Props}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
@ -122,7 +122,7 @@ object TerminalControlTest {
def SetUpAgents(tdef : TerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Terminal) = {
val terminal = Terminal(tdef)
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term")
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
}

View file

@ -2,7 +2,7 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
@ -14,7 +14,7 @@ class VehicleTerminalCombinedTest extends Specification {
"Ground_Vehicle_Terminal" should {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {