mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-07 14:30:29 +00:00
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:
parent
20b7726653
commit
ddc2450541
52 changed files with 2491 additions and 711 deletions
|
|
@ -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}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) ::
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
397
common/src/test/scala/objects/AvatarTest.scala
Normal file
397
common/src/test/scala/objects/AvatarTest.scala
Normal 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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
59
common/src/test/scala/objects/SpawnTubeTest.scala
Normal file
59
common/src/test/scala/objects/SpawnTubeTest.scala
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)`
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
|||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
|
||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.types.Vector3
|
||||
|
|
@ -23,7 +23,7 @@ object Maps {
|
|||
val map5 = new ZoneMap("map05")
|
||||
|
||||
val map6 = new ZoneMap("map06") {
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure)) //Anguta
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(3974.2344f, 4287.914f, 0)))) //Anguta
|
||||
LocalObject(222, Door.Constructor) //air term building, bay door
|
||||
LocalObject(370, Door.Constructor) //courtyard
|
||||
LocalObject(371, Door.Constructor) //courtyard
|
||||
|
|
@ -193,17 +193,17 @@ object Maps {
|
|||
TerminalToSpawnPad(224, 501)
|
||||
TerminalToSpawnPad(2419, 500)
|
||||
|
||||
LocalBuilding(38, FoundationBuilder(Building.Structure)) //Anguta, West Bunker
|
||||
LocalBuilding(38, FoundationBuilder(Building.Structure(StructureType.Bunker))) //Anguta, West Bunker
|
||||
LocalObject(362, Door.Constructor)
|
||||
ObjectToBuilding(362, 38)
|
||||
|
||||
LocalBuilding(42, FoundationBuilder(Building.Structure)) //Anguta, East Bunker(s)
|
||||
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Bunker))) //Anguta, East Bunker(s)
|
||||
LocalObject(407, Door.Constructor)
|
||||
LocalObject(408, Door.Constructor)
|
||||
ObjectToBuilding(407, 42)
|
||||
ObjectToBuilding(408, 42)
|
||||
|
||||
LocalBuilding(48, FoundationBuilder(Building.Structure)) //North Anguta Watchtower
|
||||
LocalBuilding(48, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(3864.2266f, 4518.0234f, 0)))) //North Anguta Watchtower
|
||||
LocalObject(364, Door.Constructor(Vector3(3871.9688f, 4509.992f, 269.65625f), Vector3(0f, 0f, 180f))) //s1
|
||||
LocalObject(365, Door.Constructor(Vector3(3871.9688f, 4509.992f, 279.57812f), Vector3(0f, 0f, 180f))) //s2
|
||||
LocalObject(366, Door.Constructor(Vector3(3871.9688f, 4509.992f, 299.57812f), Vector3(0f, 0f, 180f))) //s3
|
||||
|
|
@ -227,8 +227,8 @@ object Maps {
|
|||
LocalObject(1561, Terminal.Constructor(order_terminal))
|
||||
LocalObject(1562, Terminal.Constructor(order_terminal))
|
||||
LocalObject(1563, Terminal.Constructor(order_terminal))
|
||||
LocalObject(2138, SpawnTube.Constructor(Vector3(3870.9688f, 4505.7266f, 259.875f), Vector3(0, 0, 90)))
|
||||
LocalObject(2139, SpawnTube.Constructor(Vector3(3870.9688f, 4522.1562f, 259.875f), Vector3(0, 0, 90)))
|
||||
LocalObject(2138, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4505.7266f, 259.875f), Vector3(0, 0, 90)))
|
||||
LocalObject(2139, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4522.1562f, 259.875f), Vector3(0, 0, 90)))
|
||||
LocalObject(2315, Door.Constructor) //spawn tube door
|
||||
LocalObject(2316, Door.Constructor) //spawn tube door
|
||||
ObjectToBuilding(364, 48)
|
||||
|
|
@ -287,15 +287,13 @@ object Maps {
|
|||
LocalObject(1081, Terminal.Constructor(implant_terminal_interface)) //tube 520
|
||||
TerminalToInterface(520, 1081)
|
||||
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building))) //HART building C
|
||||
LocalObject(186, Terminal.Constructor(cert_terminal))
|
||||
LocalObject(187, Terminal.Constructor(cert_terminal))
|
||||
LocalObject(188, Terminal.Constructor(cert_terminal))
|
||||
LocalObject(362, Door.Constructor)
|
||||
LocalObject(370, Door.Constructor)
|
||||
LocalObject(371, Door.Constructor)
|
||||
LocalObject(372, Door.Constructor)
|
||||
LocalObject(373, Door.Constructor)
|
||||
LocalObject(374, Door.Constructor)
|
||||
LocalObject(375, Door.Constructor)
|
||||
LocalObject(394, Door.Constructor)
|
||||
|
|
@ -303,6 +301,8 @@ object Maps {
|
|||
LocalObject(396, Door.Constructor)
|
||||
LocalObject(397, Door.Constructor)
|
||||
LocalObject(398, Door.Constructor)
|
||||
LocalObject(462, Door.Constructor)
|
||||
LocalObject(463, Door.Constructor)
|
||||
LocalObject(522, ImplantTerminalMech.Constructor)
|
||||
LocalObject(523, ImplantTerminalMech.Constructor)
|
||||
LocalObject(524, ImplantTerminalMech.Constructor)
|
||||
|
|
@ -337,8 +337,6 @@ object Maps {
|
|||
ObjectToBuilding(362, 2)
|
||||
ObjectToBuilding(370, 2)
|
||||
ObjectToBuilding(371, 2)
|
||||
ObjectToBuilding(372, 2)
|
||||
ObjectToBuilding(373, 2)
|
||||
ObjectToBuilding(374, 2)
|
||||
ObjectToBuilding(375, 2)
|
||||
ObjectToBuilding(394, 2)
|
||||
|
|
@ -346,6 +344,8 @@ object Maps {
|
|||
ObjectToBuilding(396, 2)
|
||||
ObjectToBuilding(397, 2)
|
||||
ObjectToBuilding(398, 2)
|
||||
ObjectToBuilding(462, 2)
|
||||
ObjectToBuilding(463, 2)
|
||||
ObjectToBuilding(522, 2)
|
||||
ObjectToBuilding(523, 2)
|
||||
ObjectToBuilding(524, 2)
|
||||
|
|
@ -383,7 +383,7 @@ object Maps {
|
|||
TerminalToInterface(528, 1088)
|
||||
TerminalToInterface(529, 1089)
|
||||
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.Tower))) //South Villa Gun Tower
|
||||
LocalObject(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))
|
||||
LocalObject(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))
|
||||
LocalObject(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0)))
|
||||
|
|
@ -405,7 +405,61 @@ object Maps {
|
|||
DoorToLock(332, 556)
|
||||
DoorToLock(333, 557)
|
||||
|
||||
LocalBuilding(51, FoundationBuilder(Building.Structure))
|
||||
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0)))) //spawn building south of HART C
|
||||
LocalObject(258, Door.Constructor) //spawn tube door
|
||||
LocalObject(259, Door.Constructor) //spawn tube door
|
||||
LocalObject(260, Door.Constructor) //spawn tube door
|
||||
LocalObject(261, Door.Constructor) //spawn tube door
|
||||
LocalObject(262, Door.Constructor) //spawn tube door
|
||||
LocalObject(263, Door.Constructor) //spawn tube door
|
||||
LocalObject(372, Door.Constructor) //entrance
|
||||
LocalObject(373, Door.Constructor) //entrance
|
||||
LocalObject(430, Door.Constructor) //vr door
|
||||
LocalObject(431, Door.Constructor) //vr door
|
||||
LocalObject(432, Door.Constructor) //vr door
|
||||
LocalObject(433, Door.Constructor) //vr door
|
||||
LocalObject(434, Door.Constructor) //vr door
|
||||
LocalObject(435, Door.Constructor) //vr door
|
||||
LocalObject(744, SpawnTube.Constructor(Vector3(3684.336f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(746, SpawnTube.Constructor(Vector3(3691.0703f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(748, SpawnTube.Constructor(Vector3(3697.711f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(852, Terminal.Constructor(order_terminal)) //s. wall
|
||||
LocalObject(853, Terminal.Constructor(order_terminal)) //n. wall
|
||||
LocalObject(854, Terminal.Constructor(order_terminal)) //s. wall
|
||||
LocalObject(855, Terminal.Constructor(order_terminal)) //n. wall
|
||||
LocalObject(859, Terminal.Constructor(order_terminal)) //s. wall
|
||||
LocalObject(860, Terminal.Constructor(order_terminal)) //n. wall
|
||||
ObjectToBuilding(258, 42)
|
||||
ObjectToBuilding(259, 42)
|
||||
ObjectToBuilding(260, 42)
|
||||
ObjectToBuilding(261, 42)
|
||||
ObjectToBuilding(262, 42)
|
||||
ObjectToBuilding(263, 42)
|
||||
ObjectToBuilding(372, 42)
|
||||
ObjectToBuilding(373, 42)
|
||||
ObjectToBuilding(430, 42)
|
||||
ObjectToBuilding(431, 42)
|
||||
ObjectToBuilding(432, 42)
|
||||
ObjectToBuilding(433, 42)
|
||||
ObjectToBuilding(434, 42)
|
||||
ObjectToBuilding(435, 42)
|
||||
ObjectToBuilding(744, 42)
|
||||
ObjectToBuilding(745, 42)
|
||||
ObjectToBuilding(746, 42)
|
||||
ObjectToBuilding(747, 42)
|
||||
ObjectToBuilding(748, 42)
|
||||
ObjectToBuilding(749, 42)
|
||||
ObjectToBuilding(852, 42)
|
||||
ObjectToBuilding(853, 42)
|
||||
ObjectToBuilding(854, 42)
|
||||
ObjectToBuilding(855, 42)
|
||||
ObjectToBuilding(859, 42)
|
||||
ObjectToBuilding(860, 42)
|
||||
|
||||
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform))) //air terminal west of HART C
|
||||
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(292,
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
|
||||
|
|
@ -414,7 +468,7 @@ object Maps {
|
|||
ObjectToBuilding(292, 51)
|
||||
TerminalToSpawnPad(304, 292)
|
||||
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure))
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform))) //ground terminal west of HART C
|
||||
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(706,
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
|
||||
|
|
@ -422,18 +476,6 @@ object Maps {
|
|||
ObjectToBuilding(1063, 77)
|
||||
ObjectToBuilding(706, 77)
|
||||
TerminalToSpawnPad(1063, 706)
|
||||
|
||||
//TODO check building id: these belong to a spawn building in HART C campus
|
||||
LocalObject(462, Door.Constructor)
|
||||
LocalObject(463, Door.Constructor)
|
||||
LocalObject(853, Terminal.Constructor(order_terminal))
|
||||
LocalObject(855, Terminal.Constructor(order_terminal))
|
||||
LocalObject(860, Terminal.Constructor(order_terminal))
|
||||
ObjectToBuilding(462, 2)
|
||||
ObjectToBuilding(463, 2)
|
||||
ObjectToBuilding(853, 2)
|
||||
ObjectToBuilding(855, 2)
|
||||
ObjectToBuilding(860, 2)
|
||||
}
|
||||
|
||||
val map14 = new ZoneMap("map13")
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
|||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, SpawnTerminalDefinition, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
|
||||
import net.psforever.objects.serverobject.structures.{Building, WarpGate}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
|
||||
|
|
@ -56,6 +56,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var taskResolver : ActorRef = Actor.noSender
|
||||
var galaxy : ActorRef = Actor.noSender
|
||||
var continent : Zone = null
|
||||
var player : Player = null
|
||||
var avatar : Avatar = null
|
||||
var progressBarValue : Option[Float] = None
|
||||
var shooting : Option[PlanetSideGUID] = None
|
||||
var accessedContainer : Option[PlanetSideGameObject with Container] = None
|
||||
|
|
@ -66,9 +68,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
var clientKeepAlive : Cancellable = DefaultCancellable.obj
|
||||
var progressBarUpdate : Cancellable = DefaultCancellable.obj
|
||||
var reviveTimer : Cancellable = DefaultCancellable.obj
|
||||
|
||||
override def postStop() = {
|
||||
clientKeepAlive.cancel()
|
||||
clientKeepAlive.cancel
|
||||
progressBarUpdate.cancel
|
||||
reviveTimer.cancel
|
||||
localService ! Service.Leave()
|
||||
vehicleService ! Service.Leave()
|
||||
avatarService ! Service.Leave()
|
||||
|
|
@ -282,7 +287,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case AvatarResponse.Release(tplayer) =>
|
||||
if(tplayer_guid != guid) {
|
||||
turnPlayerIntoCorpse(tplayer)
|
||||
TurnPlayerIntoCorpse(tplayer)
|
||||
}
|
||||
|
||||
case AvatarResponse.Reload(item_guid) =>
|
||||
|
|
@ -920,7 +925,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.Transport ! Zone.SpawnVehicle(vehicle)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata))
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid)) //fte and ownership?
|
||||
//sendResponse(ObjectAttachMessage(vehicle_guid, player_guid, 0))
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
|
||||
|
|
@ -1002,6 +1006,64 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.info(s"$tplayer has left zone ${zone.Id}")
|
||||
}
|
||||
|
||||
case Zone.Lattice.SpawnPoint(zone_id, building, spawn_tube) =>
|
||||
log.info(s"Zone.Lattice.SpawnPoint: spawn point on $zone_id in ${building.Id} @ ${spawn_tube.GUID.guid} selected")
|
||||
reviveTimer.cancel
|
||||
val sameZone = zone_id == continent.Id
|
||||
val backpack = player.isBackpack
|
||||
val respawnTime : Long = if(sameZone) { 10 } else { 0 } //s
|
||||
val respawnTimeMillis = respawnTime * 1000 //ms
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, respawnTimeMillis, respawnTimeMillis, Vector3.Zero, 2, true))
|
||||
val tplayer = if(backpack) {
|
||||
RespawnClone(player) //new player
|
||||
}
|
||||
else {
|
||||
val player_guid = player.GUID
|
||||
sendResponse(ObjectDeleteMessage(player_guid, 4))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 4))
|
||||
player //player is deconstructing self
|
||||
}
|
||||
|
||||
tplayer.Position = spawn_tube.Position
|
||||
tplayer.Orientation = spawn_tube.Orientation
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val (target, msg) : (ActorRef, Any) = if(sameZone) {
|
||||
if(backpack) {
|
||||
//respawning from unregistered player
|
||||
(taskResolver, RegisterAvatar(tplayer))
|
||||
}
|
||||
else {
|
||||
//move existing player
|
||||
(self, PlayerLoaded(tplayer))
|
||||
}
|
||||
}
|
||||
else {
|
||||
continent.Population ! Zone.Population.Leave(avatar)
|
||||
val original = player
|
||||
//TODO check player orientation upon spawn not polluted
|
||||
if(backpack) {
|
||||
//unregister avatar locker + GiveWorld
|
||||
player = tplayer
|
||||
(taskResolver, TaskBeforeZoneChange(GUIDTask.UnregisterLocker(original.Locker)(continent.GUID), zone_id))
|
||||
}
|
||||
else {
|
||||
//unregister avatar whole + GiveWorld
|
||||
(taskResolver, TaskBeforeZoneChange(GUIDTask.UnregisterAvatar(original)(continent.GUID), zone_id))
|
||||
}
|
||||
}
|
||||
context.system.scheduler.scheduleOnce(respawnTime seconds, target, msg)
|
||||
|
||||
case Zone.Lattice.NoValidSpawnPoint(zone_number, None) =>
|
||||
log.warn(s"Zone.Lattice.SpawnPoint: zone $zone_number could not be accessed as requested")
|
||||
reviveTimer.cancel
|
||||
RequestSanctuaryZoneSpawn(player, zone_number)
|
||||
|
||||
case Zone.Lattice.NoValidSpawnPoint(zone_number, Some(spawn_group)) =>
|
||||
log.warn(s"Zone.Lattice.SpawnPoint: zone $zone_number has no available ${player.Faction} targets in spawn group $spawn_group")
|
||||
reviveTimer.cancel
|
||||
RequestSanctuaryZoneSpawn(player, zone_number)
|
||||
|
||||
case InterstellarCluster.ClientInitializationComplete() =>
|
||||
LivePlayerList.Add(sessionId, avatar)
|
||||
//PropertyOverrideMessage
|
||||
|
|
@ -1012,21 +1074,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
galaxy ! InterstellarCluster.GetWorld("z6")
|
||||
|
||||
case InterstellarCluster.GiveWorld(zoneId, zone) =>
|
||||
log.info(s"Zone $zoneId has been loaded")
|
||||
log.info(s"Zone $zoneId will now load")
|
||||
player.Continent = zoneId
|
||||
continent = zone
|
||||
continent.Population ! Zone.Population.Join(avatar)
|
||||
taskResolver ! RegisterNewAvatar(player)
|
||||
|
||||
case NewPlayerLoaded(tplayer) =>
|
||||
log.info(s"Player $tplayer has been loaded")
|
||||
log.info(s"Player ${tplayer.Name} has been loaded")
|
||||
player = tplayer
|
||||
//LoadMapMessage will cause the client to send back a BeginZoningMessage packet (see below)
|
||||
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))
|
||||
AvatarCreate() //important! the LoadMapMessage must be processed by the client before the avatar is created
|
||||
|
||||
case PlayerLoaded(tplayer) =>
|
||||
log.info(s"Player $tplayer has been loaded")
|
||||
log.info(s"Player ${tplayer.Name} will respawn")
|
||||
player = tplayer
|
||||
AvatarCreate()
|
||||
self ! SetCurrentAvatar(tplayer)
|
||||
|
|
@ -1034,22 +1096,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case PlayerFailedToLoad(tplayer) =>
|
||||
player.Continent match {
|
||||
case _ =>
|
||||
failWithError(s"$tplayer failed to load anywhere")
|
||||
failWithError(s"${tplayer.Name} failed to load anywhere")
|
||||
}
|
||||
|
||||
case SetCurrentAvatar(tplayer) =>
|
||||
player = tplayer
|
||||
val guid = tplayer.GUID
|
||||
sendResponse(SetCurrentAvatarMessage(guid,0,0))
|
||||
if(spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
|
||||
}
|
||||
|
||||
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
|
||||
sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
|
||||
sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant
|
||||
//TODO: if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
|
||||
//TODO if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
|
||||
})
|
||||
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
|
||||
//TODO: if Medkit does not have shortcut, add to a free slot or write over slot 64
|
||||
//TODO if Medkit does not have shortcut, add to a free slot or write over slot 64
|
||||
sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
|
||||
sendResponse(ChangeShortcutBankMessage(guid, 0))
|
||||
//FavoritesMessage
|
||||
|
|
@ -1195,10 +1260,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
|
||||
var player : Player = null
|
||||
var avatar : Avatar = null
|
||||
var spawnZones : Map[Int, Building] = null
|
||||
|
||||
def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
|
||||
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
|
||||
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
|
||||
|
|
@ -1272,7 +1333,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
//TODO check if can spawn on last continent/location from player?
|
||||
//TODO if yes, get continent guid accessors
|
||||
//TODO if no, get sanctuary guid accessors and reset the player's expectations
|
||||
//galaxy ! InterstellarCluster.GetWorld("z6")
|
||||
galaxy ! InterstellarCluster.RequestClientInitialization()
|
||||
case default =>
|
||||
log.error("Unsupported " + default + " in " + msg)
|
||||
|
|
@ -1283,18 +1343,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ BeginZoningMessage() =>
|
||||
log.info("Reticulating splines ...")
|
||||
configZone(continent) //todo density
|
||||
configZone(continent)
|
||||
sendResponse(TimeOfDayMessage(1191182336))
|
||||
/** WIP */
|
||||
spawnZones = Map(
|
||||
7 -> continent.Building(2).get,
|
||||
6 -> continent.Building(48).get
|
||||
)
|
||||
|
||||
//custom
|
||||
sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1)) //common
|
||||
(0 to 255).foreach(i => { sendResponse(SetEmpireMessage(PlanetSideGUID(i), PlanetSideEmpire.VS)) })
|
||||
|
||||
//render Equipment that was dropped into zone before the player arrived
|
||||
continent.EquipmentOnGround.foreach(item => {
|
||||
|
|
@ -1309,20 +1365,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
})
|
||||
//load active players in zone
|
||||
continent.LivePlayers.filterNot(_.GUID == player.GUID).foreach(char => {
|
||||
sendResponse(
|
||||
ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
|
||||
)
|
||||
sendResponse(ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get))
|
||||
})
|
||||
//load corpses in zone
|
||||
continent.Corpses.foreach( turnPlayerIntoCorpse(_) )
|
||||
continent.Corpses.foreach { TurnPlayerIntoCorpse }
|
||||
//load active vehicles in zone
|
||||
continent.Vehicles.foreach(vehicle => {
|
||||
val definition = vehicle.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get)
|
||||
)
|
||||
sendResponse(ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get))
|
||||
//seat vehicle occupants
|
||||
vehicle.Definition.MountPoints.values.foreach(seat_num => {
|
||||
definition.MountPoints.values.foreach(seat_num => {
|
||||
vehicle.Seat(seat_num).get.Occupant match {
|
||||
case Some(tplayer) =>
|
||||
if(tplayer.HasGUID) {
|
||||
|
|
@ -1339,14 +1391,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.GUID(interface_guid) match {
|
||||
case Some(obj : Terminal) =>
|
||||
val objDef = obj.Definition
|
||||
val obj_uid = objDef.ObjectId
|
||||
val obj_data = objDef.Packet.ConstructorData(obj).get
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
obj_uid,
|
||||
ObjectClass.implant_terminal_interface,
|
||||
PlanetSideGUID(interface_guid),
|
||||
ObjectCreateMessageParent(parent_guid, 1),
|
||||
obj_data
|
||||
objDef.Packet.ConstructorData(obj).get
|
||||
)
|
||||
)
|
||||
case _ => ;
|
||||
|
|
@ -1444,36 +1494,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ ReleaseAvatarRequestMessage() =>
|
||||
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
|
||||
//TODO is it easier to delete the player, then re-create them as a corpse?
|
||||
reviveTimer.cancel
|
||||
player.Release
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
continent.Population ! Zone.Corpse.Add(player)
|
||||
val knife = player.Slot(4).Equipment.get
|
||||
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
|
||||
turnPlayerIntoCorpse(player)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
player.VehicleSeated match {
|
||||
case None =>
|
||||
continent.Population ! Zone.Corpse.Add(player) //TODO move back out of this match case when changing below issue
|
||||
val knife = player.Slot(4).Equipment.get
|
||||
player.Slot(4).Equipment = None
|
||||
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
|
||||
TurnPlayerIntoCorpse(player)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
|
||||
|
||||
case Some(_) =>
|
||||
//TODO we do not want to delete the player if he is seated in a vehicle when releasing
|
||||
//TODO it is necessary for now until we know how to juggle ownership properly
|
||||
val player_guid = player.GUID
|
||||
sendResponse(ObjectDeleteMessage(player_guid, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
|
||||
self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, 0, true)) //let vehicle try to clean up its fields
|
||||
taskResolver ! GUIDTask.UnregisterPlayer(player)(continent.GUID)
|
||||
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0, 0, 0))
|
||||
//sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
|
||||
}
|
||||
|
||||
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
|
||||
log.info(s"SpawnRequestMessage: $msg")
|
||||
spawnZones.get(u2.toInt) match {
|
||||
case Some(building) =>
|
||||
scala.util.Random.shuffle(building.Amenities.filter(_.isInstanceOf[SpawnTube])).headOption match { //TODO temporary shuffle
|
||||
case Some(tube) =>
|
||||
val tplayer = SpawnRequest(player) //new player
|
||||
tplayer.Position = tube.Position
|
||||
tplayer.Orientation = tube.Orientation
|
||||
log.info(s"SpawnRequestMessage: new player will spawn in ${building.Id} @ ${tube.GUID.guid}")
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, 10000, 10000, Vector3.Zero, 2, true))
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(10 seconds, taskResolver, RegisterAvatar(tplayer))
|
||||
case None =>
|
||||
log.warn(s"SpawnRequestMessage: can not find a spawn point in this spawn group - $u2")
|
||||
}
|
||||
case None =>
|
||||
log.warn(s"SpawnRequestMessage: can not find somewhere to spawn on ${continent.Id}")
|
||||
}
|
||||
//TODO just focus on u5 and u2 for now
|
||||
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
|
||||
|
||||
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
|
||||
log.info("SetChatFilters: " + msg)
|
||||
|
|
@ -1516,15 +1565,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
if(messagetype == ChatMessageType.CMT_SUICIDE) {
|
||||
val player_guid = player.GUID
|
||||
val pos = player.Position
|
||||
player.Die
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 2, 0))
|
||||
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, pos, 2, true))
|
||||
KillPlayer(player)
|
||||
}
|
||||
|
||||
if(messagetype == ChatMessageType.CMT_DESTROY) {
|
||||
self ! PacketCoding.CreateGamePacket(0, RequestDestroyMessage(PlanetSideGUID(contents.toInt)))
|
||||
}
|
||||
|
||||
if (messagetype == ChatMessageType.CMT_VOICE) {
|
||||
|
|
@ -1853,7 +1898,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
// TODO: Make sure this is the correct response for all cases
|
||||
continent.GUID(object_guid) match {
|
||||
case Some(vehicle : Vehicle) =>
|
||||
if(player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) {
|
||||
if((player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID))
|
||||
|| (player.Faction == vehicle.Faction && (vehicle.Owner.isEmpty || vehicle.Health == 0))) {
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(object_guid)
|
||||
vehicleService ! VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent)
|
||||
log.info(s"RequestDestroy: vehicle $object_guid")
|
||||
|
|
@ -2084,7 +2130,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
case Some(obj : Terminal) =>
|
||||
if(obj.Definition.isInstanceOf[MatrixTerminalDefinition] || obj.Definition.isInstanceOf[SpawnTerminalDefinition]) {
|
||||
if(obj.Definition.isInstanceOf[MatrixTerminalDefinition]) {
|
||||
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
|
||||
sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, obj.Position))
|
||||
}
|
||||
|
|
@ -2092,6 +2138,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||
}
|
||||
|
||||
case Some(obj : SpawnTube) =>
|
||||
//deconstruction
|
||||
PlayerActionsToCancel()
|
||||
player.Release
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
|
||||
case Some(obj : PlanetSideGameObject) =>
|
||||
if(itemType != 121) {
|
||||
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
|
||||
|
|
@ -2217,7 +2270,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
def dismountWarning(msg : String) : Unit = {
|
||||
log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it")
|
||||
}
|
||||
if(player.GUID == player_guid) {
|
||||
if(player.HasGUID && player.GUID == player_guid) {
|
||||
//normally disembarking from a seat
|
||||
player.VehicleSeated match {
|
||||
case Some(obj_guid) =>
|
||||
|
|
@ -2562,32 +2615,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
override def onFailure(ex : Throwable) : Unit = {
|
||||
localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WSA
|
||||
}
|
||||
}, List(RegisterLightweightAvatar(tplayer)(continent.GUID))
|
||||
}, List(GUIDTask.RegisterPlayer(tplayer)(continent.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
//TODO temporary function for registering avatar without locker contents
|
||||
def RegisterLightweightAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.LockerContainer
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
val holsterTasks = tplayer.Holsters().filter(_.Equipment.isDefined).map(slot =>{
|
||||
GUIDTask.RegisterEquipment(slot.Equipment.get)(guid)
|
||||
}).toList
|
||||
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => GUIDTask.RegisterEquipment(entry.obj)(guid)})
|
||||
TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
|
||||
}
|
||||
|
||||
//TODO temporary function for unregistering avatar without locker contents
|
||||
def UnregisterLightweightAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.LockerContainer
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
val holsterTasks = tplayer.Holsters().filter(_.Equipment.isDefined).map(slot =>{
|
||||
GUIDTask.UnregisterEquipment(slot.Equipment.get)(guid)
|
||||
}).toList
|
||||
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => GUIDTask.UnregisterEquipment(entry.obj)(guid)})
|
||||
TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that adds a completed and registered vehicle into the scene.
|
||||
* Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
|
||||
|
|
@ -2739,6 +2770,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
}
|
||||
|
||||
def TaskBeforeZoneChange(priorTask : TaskResolver.GiveTask, zoneId : String) : TaskResolver.GiveTask = {
|
||||
TaskResolver.GiveTask(
|
||||
new Task() {
|
||||
private val localService = galaxy
|
||||
private val localMsg = InterstellarCluster.GetWorld(zoneId)
|
||||
|
||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
localService ! localMsg
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
}, List(priorTask)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* After a client has connected to the server, their account is used to generate a list of characters.
|
||||
* On the character selection screen, each of these characters is made to exist temporarily when one is selected.
|
||||
|
|
@ -3223,10 +3270,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
* @param building the building object
|
||||
*/
|
||||
def initBuilding(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
|
||||
building match {
|
||||
case _ : WarpGate =>
|
||||
building.BuildingType match {
|
||||
case StructureType.WarpGate =>
|
||||
initGate(continentNumber, buildingNumber, building)
|
||||
case _ : Building =>
|
||||
case _ =>
|
||||
initFacility(continentNumber, buildingNumber, building)
|
||||
}
|
||||
}
|
||||
|
|
@ -3309,6 +3356,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(BroadcastWarpgateUpdateMessage(continentNumber, buildingNumber, false, false, true))
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the buildings and each specific amenity for that building in a given zone by sending the client packets.
|
||||
* These actions are performed during the loading of a zone.
|
||||
* @see `SetEmpireMessage`<br>
|
||||
* `PlanetsideAttributeMessage`<br>
|
||||
* `HackMessage`
|
||||
* @param zone the zone being loaded
|
||||
*/
|
||||
def configZone(zone : Zone) : Unit = {
|
||||
zone.Buildings.values.foreach(building => {
|
||||
sendResponse(SetEmpireMessage(PlanetSideGUID(building.ModelId), building.Faction))
|
||||
|
|
@ -3322,11 +3377,90 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO write
|
||||
* The player has lost all his vitality and must be killed.<br>
|
||||
* <br>
|
||||
* Shift directly into a state of being dead on the client by setting health to zero points,
|
||||
* whereupon the player will perform a dramatic death animation.
|
||||
* Stamina is also set to zero points.
|
||||
* If the player was in a vehicle at the time of demise, special conditions apply and
|
||||
* the model must be manipulated so it behaves correctly.
|
||||
* Do not move or completely destroy the `Player` object as its coordinates of death will be important.<br>
|
||||
* <br>
|
||||
* A maximum revive waiting timer is started.
|
||||
* When this timer reaches zero, the avatar will attempt to spawn back on its faction-specific sanctuary continent.
|
||||
* @pararm tplayer the player to be killed
|
||||
*/
|
||||
def KillPlayer(tplayer : Player) : Unit = {
|
||||
val player_guid = tplayer.GUID
|
||||
val pos = tplayer.Position
|
||||
val respawnTimer = 300000 //milliseconds
|
||||
tplayer.Die
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
|
||||
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, 2, true))
|
||||
if(tplayer.VehicleSeated.nonEmpty) {
|
||||
//make player invisible (if not, the cadaver sticks out the side in a seated position)
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
|
||||
}
|
||||
PlayerActionsToCancel()
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, galaxy, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
|
||||
}
|
||||
|
||||
/**
|
||||
* An event has occurred that would cause the player character to stop certain stateful activities.
|
||||
* These activities include shooting, hacking, accessing (a container), flying, and running.
|
||||
* Other players in the same zone must be made aware that the player has stopped as well.<br>
|
||||
* <br>
|
||||
* Things whose configuration should not be changed:<br>
|
||||
* - if the player is seated
|
||||
*/
|
||||
def PlayerActionsToCancel() : Unit = {
|
||||
progressBarUpdate.cancel
|
||||
progressBarValue = None
|
||||
accessedContainer match {
|
||||
case Some(obj : Vehicle) =>
|
||||
if(obj.AccessingTrunk.contains(player.GUID)) {
|
||||
obj.AccessingTrunk = None
|
||||
UnAccessContents(obj)
|
||||
}
|
||||
accessedContainer = None
|
||||
|
||||
case Some(_) =>
|
||||
accessedContainer = None
|
||||
|
||||
case None => ;
|
||||
}
|
||||
shooting match {
|
||||
case Some(guid) =>
|
||||
sendResponse(ChangeFireStateMessage_Stop(guid))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, guid))
|
||||
shooting = None
|
||||
case None => ;
|
||||
}
|
||||
if(flying) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_FLY, false, "", "off", None))
|
||||
flying = false
|
||||
}
|
||||
if(speed > 1) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_SPEED, false, "", "1.000", None))
|
||||
speed = 1f
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A part of the process of spawning the player into the game world.
|
||||
* The function should work regardless of whether the player is alive or dead - it will make them alive.
|
||||
* It adds the `WSA`-current `Player` to the current zone and sends out the expected packets.
|
||||
*/
|
||||
def AvatarCreate() : Unit = {
|
||||
player.Spawn
|
||||
player.Health = 50
|
||||
player.Health = 50 //TODO temp
|
||||
val packet = player.Definition.Packet
|
||||
val dcdata = packet.DetailedConstructorData(player).get
|
||||
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
|
||||
|
|
@ -3335,11 +3469,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.debug(s"ObjectCreateDetailedMessage: $dcdata")
|
||||
}
|
||||
|
||||
def SpawnRequest(tplayer : Player) : Player = {
|
||||
/**
|
||||
* Produce a clone of the player that is equipped with the default infantry loadout.
|
||||
* The loadout is hardcoded.
|
||||
* The player is expected to be in a Standard Exo-Suit.
|
||||
* @param tplayer the original player
|
||||
* @return the duplication of the player, in Standard Exo-Suit and with default equipment loadout
|
||||
*/
|
||||
def RespawnClone(tplayer : Player) : Player = {
|
||||
val faction = tplayer.Faction
|
||||
val obj = Player.Respawn(tplayer)
|
||||
//obj.VehicleOwned = tplayer.VehicleOwned
|
||||
//obj.Continent = tplayer.Continent
|
||||
obj.Slot(0).Equipment = Tool(StandardPistol(faction))
|
||||
obj.Slot(2).Equipment = Tool(suppressor)
|
||||
obj.Slot(4).Equipment = Tool(StandardMelee(faction))
|
||||
|
|
@ -3352,13 +3491,38 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
obj
|
||||
}
|
||||
|
||||
def turnPlayerIntoCorpse(tplayer : Player) : Unit = {
|
||||
//sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 6, 1))
|
||||
/**
|
||||
* Creates a player that has the characteristics of a corpse.
|
||||
* To the game, that is a backpack (or some pastry, festive graphical modification allowing).
|
||||
* @see `CorpseConverter.converter`
|
||||
* @param tplayer the player
|
||||
*/
|
||||
def TurnPlayerIntoCorpse(tplayer : Player) : Unit = {
|
||||
sendResponse(
|
||||
ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, CorpseConverter.converter.DetailedConstructorData(tplayer).get)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to tranfer to the player's faction-specific sanctuary continent.
|
||||
* If the server thinks the player is already on his sanctuary continent,
|
||||
* it will disconnect the player under the assumption that an error has occurred.
|
||||
* Eventually, this functionality should support better error-handling before it jumps to the conclusion:
|
||||
* "Disconnecting the client is the safest option."
|
||||
* @see `Zones.SanctuaryZoneNumber`
|
||||
* @param tplayer the player
|
||||
* @param currentZone the current cone number
|
||||
*/
|
||||
def RequestSanctuaryZoneSpawn(tplayer : Player, currentZone : Int) : Unit = {
|
||||
val sanctNumber = Zones.SanctuaryZoneNumber(tplayer.Faction)
|
||||
if(currentZone == sanctNumber) {
|
||||
sendResponse(DisconnectMessage("Player failed to load on faction's sanctuary continent. Please relog."))
|
||||
}
|
||||
else {
|
||||
galaxy ! Zone.Lattice.RequestSpawnPoint(sanctNumber, tplayer, 7)
|
||||
}
|
||||
}
|
||||
|
||||
def failWithError(error : String) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
object Zones {
|
||||
val z1 = new Zone("z1", Maps.map1, 1)
|
||||
|
|
@ -86,4 +87,32 @@ object Zones {
|
|||
val i3 = new Zone("i3", Maps.map98, 31)
|
||||
|
||||
val i4 = new Zone("i4", Maps.map99, 32)
|
||||
|
||||
/**
|
||||
* Get the zone identifier name for the sanctuary continent of a given empire.
|
||||
* @param faction the empire
|
||||
* @return the zone id, with a blank string as an invalidating result
|
||||
*/
|
||||
def SanctuaryZoneId(faction : PlanetSideEmpire.Value) : String = {
|
||||
faction match {
|
||||
case PlanetSideEmpire.TR => "home1"
|
||||
case PlanetSideEmpire.NC => "home2"
|
||||
case PlanetSideEmpire.VS => "home3"
|
||||
case PlanetSideEmpire.NEUTRAL => "" //invalid, not black ops
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the zone number for the sanctuary continent of a given empire.
|
||||
* @param faction the empire
|
||||
* @return the zone number, within the sequence 1-32, and with 0 as an invalidating result
|
||||
*/
|
||||
def SanctuaryZoneNumber(faction : PlanetSideEmpire.Value) : Int = {
|
||||
faction match {
|
||||
case PlanetSideEmpire.TR => 11
|
||||
case PlanetSideEmpire.NC => 12
|
||||
case PlanetSideEmpire.VS => 13
|
||||
case PlanetSideEmpire.NEUTRAL => 0 //invalid, not black ops
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class UndertakerActor extends Actor {
|
|||
override def onFailure(ex : Throwable): Unit = {
|
||||
localAnnounce ! UndertakerActor.FailureToWork(localCorpse, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterAvatar(corpse)(zone.GUID))
|
||||
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue