Merge pull request #196 from Fate-JH/spawn-tube

The Real Resurrection Update
This commit is contained in:
Fate-JH 2018-04-19 07:45:47 -04:00 committed by GitHub
commit d8fe6bab28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 4562 additions and 1410 deletions

View file

@ -0,0 +1,206 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.types.{CertificationType, CharacterGender, ImplantType, PlanetSideEmpire}
import scala.annotation.tailrec
import scala.collection.mutable
class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : Int) {
/** Battle Experience Points */
private var bep : Long = 0
/** Command Experience Points */
private var cep : Long = 0
/** Certifications */
private val certs : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/** 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 : LockerContainer = LockerContainer()
def BEP : Long = bep
def BEP_=(battleExperiencePoints : Long) : Long = {
bep = math.max(0L, math.min(battleExperiencePoints, 4294967295L))
BEP
}
def Certifications : mutable.Set[CertificationType.Value] = certs
def CEP : Long = cep
def CEP_=(commandExperiencePoints : Long) : Long = {
cep = math.max(0L, math.min(commandExperiencePoints, 4294967295L))
CEP
}
/**
* Retrieve the three implant slots for this player.
* @return an `Array` of `ImplantSlot` objects
*/
def Implants : Array[ImplantSlot] = implants
/**
* What kind of implant is installed into the given slot number?
* @see `ImplantType`
* @param slot the slot number
* @return the tye of implant
*/
def Implant(slot : Int) : ImplantType.Value = {
if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
}
/**
* Given a new implant, assign it into a vacant implant slot on this player.<br>
* <br>
* The implant must be unique in terms of which implants have already been assigned to this player.
* Multiple of a type of implant being assigned at once is not supported.
* Additionally, the implant is inserted into the earliest yet-unknown but vacant slot.
* Implant slots are vacant by just being unlocked or by having their previous implant uninstalled.
* @param implant the implant being installed
* @return the index of the `ImplantSlot` where the implant was installed
*/
def InstallImplant(implant : ImplantDefinition) : Option[Int] = {
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) =>
implants(slot).Implant = implant
Some(slot)
case None =>
None
}
case Some(_) =>
None
}
}
/**
* Remove a specific implant from a player's allocated installed implants.<br>
* <br>
* Due to the exclusiveness of installed implants,
* any implant slot with a matching `Definition` can be uninstalled safely.
* (There will never be any doubles.)
* This operation can lead to an irregular pattern of installed and uninstalled `ImplantSlot` objects.
* Despite that breach of pattern, the logic here is consistent as demonstrated by the client and by packets.
* The client also assigns and removes implants based on slot numbers that only express availability of a "slot."
* @see `AvatarImplantMessage.implantSlot`
* @param implantType the type of implant being uninstalled
* @return the index of the `ImplantSlot` where the implant was found and uninstalled
*/
def UninstallImplant(implantType : ImplantType.Value) : Option[Int] = {
recursiveFindImplantInSlot(implants.iterator, implantType) match {
case Some(slot) =>
implants(slot).Implant = None
Some(slot)
case None =>
None
}
}
/**
* Locate the index of the encountered implant type.
* Functional implants may be exclusive in as far as the input `Iterator`'s source is concerned,
* but any number of `ImplantType.None` values are alway allowed in the source in any order.
* @param iter an `Iterator` of `ImplantSlot` objects
* @param implantType the target implant being sought
* @param index a defaulted index value representing the structure underlying the `Iterator` param
* @return the index where the target implant is installed
*/
@tailrec private def recursiveFindImplantInSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val slot = iter.next
if(slot.Unlocked && slot.Implant == implantType) {
Some(index)
}
else {
recursiveFindImplantInSlot(iter, implantType, index + 1)
}
}
}
def ResetAllImplants() : Unit = {
implants.foreach(slot => {
slot.Installed match {
case Some(_) =>
slot.Initialized = false
case None => ;
}
})
}
def SaveLoadout(owner : Player, label : String, line : Int) : Unit = {
if(line > -1 && line < 10) {
loadouts(line) = Some(Loadout.Create(owner, label))
}
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).getOrElse(None)
def DeleteLoadout(line : Int) : Unit = {
loadouts(line) = None
}
def Locker : LockerContainer = locker
def FifthSlot : EquipmentSlot = {
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
Equipment = locker
}
}
def Definition : AvatarDefinition = Avatar.definition
/*
Merit Commendations and Ribbons
*/
// private var tosRibbon : MeritCommendation.Value = MeritCommendation.None
// private var upperRibbon : MeritCommendation.Value = MeritCommendation.None
// private var middleRibbon : MeritCommendation.Value = MeritCommendation.None
// private var lowerRibbon : MeritCommendation.Value = MeritCommendation.None
def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar]
override def equals(other : Any) : Boolean = other match {
case that: Avatar =>
(that canEqual this) &&
name == that.name &&
faction == that.faction &&
sex == that.sex &&
head == that.head &&
voice == that.voice
case _ =>
false
}
override def hashCode() : Int = {
val state = Seq(name, faction, sex, head, voice)
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
}
override def toString: String = Avatar.toString(this)
}
object Avatar {
final private val definition : AvatarDefinition = new AvatarDefinition(121)
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Avatar = {
new Avatar(name, faction, sex, head, voice)
}
def toString(avatar : Avatar) : String = s"${avatar.faction} ${avatar.name}"
}

View file

@ -487,7 +487,7 @@ object GlobalDefinitions {
*/
val order_terminal = new OrderTerminalDefinition
val ams_respawn_tube = new SpawnTubeDefinition(49) { Name = "ams_respawn_tube" }
val ams_respawn_tube = new SpawnTubeDefinition(49)
val matrix_terminalc = new MatrixTerminalDefinition(519)
@ -509,6 +509,12 @@ object GlobalDefinitions {
val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition
val spawn_terminal = new MatrixTerminalDefinition(812)
val respawn_tube = new SpawnTubeDefinition(732)
val respawn_tube_tower = new SpawnTubeDefinition(733)
val spawn_pad = new VehicleSpawnPadDefinition
val mb_locker = new LockerDefinition
@ -643,7 +649,7 @@ object GlobalDefinitions {
* @param faction the faction
* @return the `ToolDefinition` for the launcher
*/
def AntiVehicular(faction : PlanetSideEmpire.Value) : ToolDefinition = {
def AntiVehicularLauncher(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match {
case PlanetSideEmpire.TR => striker
case PlanetSideEmpire.NC => hunterseeker
@ -683,6 +689,17 @@ object GlobalDefinitions {
}
}
def isMaxArms(tdef : ToolDefinition) : Boolean = {
tdef match {
case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar`
| `trhev_pounder` | `nchev_falcon` | `vshev_comet`
| `trhev_burster` | `nchev_sparrow` | `vshev_starfire` =>
true
case _ =>
false
}
}
def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match {
case PlanetSideEmpire.TR => trhev_dualcycler

View file

@ -1,9 +1,6 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.packet.game.PlanetSideGUID
import scala.annotation.tailrec
import scala.collection.concurrent.{Map, TrieMap}
/**
@ -12,157 +9,42 @@ import scala.collection.concurrent.{Map, TrieMap}
*/
private class LivePlayerList {
/** key - the session id; value - a `Player` object */
private val sessionMap : Map[Long, Player] = new TrieMap[Long, Player]
/** the index of the List corresponds to zone number 1-32 with 0 being "Nowhere" */
/** each mapping: key - the global unique identifier; value - the session id */
private val zoneMap : List[Map[Int, Long]] = List.fill(33)(new TrieMap[Int,Long])
private val sessionMap : Map[Long, Avatar] = new TrieMap[Long, Avatar]
def WorldPopulation(predicate : ((_, Player)) => Boolean) : List[Player] = {
def WorldPopulation(predicate : ((_, Avatar)) => Boolean) : List[Avatar] = {
sessionMap.filter(predicate).values.toList
}
def ZonePopulation(zone : Int, predicate : ((_, Player)) => Boolean) : List[Player] = {
zoneMap.lift(zone) match {
case Some(map) =>
val list = map.values.toList
sessionMap.filter({ case ((sess, _)) => list.contains(sess) }).filter(predicate).values.toList
def Add(sessionId : Long, avatar : Avatar) : Boolean = {
sessionMap.values.find(char => char.equals(avatar)) match {
case None =>
Nil
}
}
def Add(sessionId : Long, player : Player) : Boolean = {
sessionMap.values.find(char => char.equals(player)) match {
case None =>
sessionMap.putIfAbsent(sessionId, player).isEmpty
sessionMap.putIfAbsent(sessionId, avatar).isEmpty
case Some(_) =>
false
}
}
def Remove(sessionId : Long) : Option[Player] = {
sessionMap.remove(sessionId) match {
case Some(char) =>
zoneMap.foreach(zone => {
recursiveRemoveSession(zone.iterator, sessionId) match {
case Some(guid) =>
zone.remove(guid)
case None => ;
}
})
Some(char)
case None =>
None
}
def Remove(sessionId : Long) : Option[Avatar] = {
sessionMap.remove(sessionId)
}
@tailrec private def recursiveRemoveSession(iter : Iterator[(Int, Long)], sessionId : Long) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val (guid : Int, sess : Long) = iter.next
if(sess == sessionId) {
Some(guid)
}
else {
recursiveRemoveSession(iter, sessionId)
}
}
}
def Get(zone : Int, guid : PlanetSideGUID) : Option[Player] = {
Get(zone, guid.guid)
}
def Get(zone : Int, guid : Int) : Option[Player] = {
zoneMap.lift(zone) match {
case Some(map) =>
map.get(guid) match {
case Some(sessionId) =>
sessionMap.get(sessionId)
case _ =>
None
}
case None =>
None
}
}
def Assign(zone: Int, sessionId : Long, guid : PlanetSideGUID) : Boolean = Assign(zone, sessionId, guid.guid)
def Assign(zone : Int, sessionId : Long, guid : Int) : Boolean = {
sessionMap.get(sessionId) match {
case Some(_) =>
zoneMap.lift(zone) match {
case Some(zn) =>
AssignToZone(zn, sessionId, guid)
case None =>
false
}
case None =>
false
}
}
private def AssignToZone(zone : Map[Int, Long], sessionId : Long, guid : Int) : Boolean = {
zone.get(guid) match {
case Some(_) =>
false
case None =>
zone(guid) = sessionId
true
}
}
def Drop(zone : Int, guid : PlanetSideGUID) : Option[Player] = Drop(zone, guid.guid)
def Drop(zone : Int, guid : Int) : Option[Player] = {
zoneMap.lift(zone) match {
case Some(map) =>
map.remove(guid) match {
case Some(sessionId) =>
sessionMap.get(sessionId)
case None =>
None
}
case None =>
None
}
}
def Shutdown : List[Player] = {
def Shutdown : List[Avatar] = {
val list = sessionMap.values.toList
sessionMap.clear
zoneMap.foreach(map => map.clear())
list
}
}
/**
* A class for storing `Player` mappings for users that are currently online.
* The mapping system is tightly coupled between the `Player` class and to an instance of `WorldSessionActor`.
* Looser couplings exist between the instance of `WorldSessionActor` and a given `Player`'s globally unique id.
* These looser couplings are zone-specific.
* Though the user may have local knowledge of the zone they inhabit on their `Player` object,
* it should not be trusted.<br>
* The mapping system is tightly coupled between the `Avatar` class and to an instance of `WorldSessionActor`.
* <br>
* Use:<br>
* 1) When a users logs in during `WorldSessionActor`, associate that user's session id and the character.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Add(session, player)`<br>
* 2) When that user's chosen character is declared his avatar using `SetCurrentAvatarMessage`,
* also associate the user's session with their current GUID.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Assign(zone, session, guid)`<br>
* 3) Repeat the previous step for as many times the user's GUID changes, especially during the aforementioned condition.<br>
* 4a) In between the previous two steps, a user's character may be referenced by their current GUID.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Get(zone, guid)`<br>
* 4b) Also in between those same previous steps, a range of characters may be queried based on provided statistics.<br>
* 1) When a users logs in during `WorldSessionActor`, associate that user's session id and their character (avatar).<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Add(session, avatar)`<br>
* 2) In between the previous two steps, a range of characters may be queried based on provided statistics.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.WorldPopulation(...)`<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.ZonePopulation(zone, ...)`<br>
* 5) When the user navigates away from a region completely, their entry is forgotten.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Drop(zone, guid)`<br>
* 6) When the user leaves the game entirely, his character's entries are removed from the mappings.<br>
* 3) When the user leaves the game entirely, his character's entry is removed from the mapping.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Remove(session)`
*/
object LivePlayerList {
@ -179,100 +61,28 @@ object LivePlayerList {
* @param predicate the conditions for filtering the live `Player`s
* @return a list of users's `Player`s that fit the criteria
*/
def WorldPopulation(predicate : ((_, Player)) => Boolean) : List[Player] = Instance.WorldPopulation(predicate)
/**
* Given some criteria, examine the mapping of user characters for a zone and find the ones that fulfill the requirements.<br>
* <br>
* Note the signature carefully.
* A two-element tuple is checked, but only the second element of that tuple - a `Player` - is eligible for being queried.
* The first element is ignored.
* Even a predicate as simple as `{ case ((x : Long, _)) => x > 0 }` will not work for that reason.
* @param zone the number of the zone
* @param predicate the conditions for filtering the live `Player`s
* @return a list of users's `Player`s that fit the criteria
*/
def ZonePopulation(zone : Int, predicate : ((_, Player)) => Boolean) : List[Player] = Instance.ZonePopulation(zone, predicate)
def WorldPopulation(predicate : ((_, Avatar)) => Boolean) : List[Avatar] = Instance.WorldPopulation(predicate)
/**
* Create a mapped entry between the user's session and a user's character.
* Neither the player nor the session may exist in the current mappings if this is to work.
* @param sessionId the session
* @param player the character
* @param avatar the character
* @return `true`, if the session was association was made; `false`, otherwise
*/
def Add(sessionId : Long, player : Player) : Boolean = Instance.Add(sessionId, player)
def Add(sessionId : Long, avatar : Avatar) : Boolean = Instance.Add(sessionId, avatar)
/**
* Remove all entries related to the given session identifier from the mappings.
* The player no longer counts as "online."
* This function cleans up __all__ associations - those created by `Add`, and those created by `Assign`.
* The character no longer counts as "online."
* @param sessionId the session
* @return any character that was afffected by the mapping removal
*/
def Remove(sessionId : Long) : Option[Player] = Instance.Remove(sessionId)
/**
* Get a user's character from the mappings.
* @param zone the number of the zone
* @param guid the current GUID of the character
* @return the character, if it can be found using the GUID
*/
def Get(zone : Int, guid : PlanetSideGUID) : Option[Player] = Instance.Get(zone, guid)
/**
* Get a user's character from the mappings.
* @param zone the number of the zone
* @param guid the current GUID of the character
* @return the character, if it can be found using the GUID
*/
def Get(zone : Int, guid : Int) : Option[Player] = Instance.Get(zone, guid)
/**
* Given a session that maps to a user's character, create a mapping between the character's current GUID and the session.
* If the user already has a GUID in the mappings, remove it and assert the new one.
* @param zone the number of the zone
* @param sessionId the session
* @param guid the GUID to associate with the character;
* technically, it has already been assigned and should be findable using `{character}.GUID.guid`
* @return `true`, if the mapping was created;
* `false`, if the session can not be found or if the character's GUID doesn't match the one provided
*/
def Assign(zone : Int, sessionId : Long, guid : PlanetSideGUID) : Boolean = Instance.Assign(zone, sessionId, guid)
/**
* Given a session that maps to a user's character, create a mapping between the character's current GUID and the session.
* If the user already has a GUID in the mappings, remove it and assert the new one.
* @param zone the number of the zone
* @param sessionId the session
* @param guid the GUID to associate with the character;
* technically, it has already been assigned and should be findable using `{character}.GUID.guid`
* @return `true`, if the mapping was created;
* `false`, if the session can not be found or if the character's GUID doesn't match the one provided
*/
def Assign(zone : Int, sessionId : Long, guid : Int) : Boolean = Instance.Assign(zone, sessionId, guid)
/**
* Given a GUID, remove any record of it.
* @param zone the number of the zone
* @param guid a GUID associated with the character;
* it does not have to be findable using `{character}.GUID.guid`
* @return any `Player` that may have been associated with this GUID
*/
def Drop(zone : Int, guid : PlanetSideGUID) : Option[Player] = Instance.Drop(zone, guid)
/**
* Given a GUID, remove any record of it.
* @param zone the number of the zone
* @param guid a GUID associated with the character;
* it does not have to be findable using `{character}.GUID.guid`
* @return any `Player` that may have been associated with this GUID
*/
def Drop(zone : Int, guid : Int) : Option[Player] = Instance.Drop(zone, guid)
def Remove(sessionId : Long) : Option[Avatar] = Instance.Remove(sessionId)
/**
* Hastily remove all mappings and ids.
* @return an unsorted list of the characters that were still online
*/
def Shutdown : List[Player] = Instance.Shutdown
def Shutdown : List[Avatar] = Instance.Shutdown
}

View file

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

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.definition.AvatarDefinition
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.serverobject.affinity.FactionAffinity
@ -9,15 +9,9 @@ import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._
import scala.annotation.tailrec
import scala.collection.mutable
import scala.util.{Success, Try}
class Player(private val name : String,
private val faction : PlanetSideEmpire.Value,
private val sex : CharacterGender.Value,
private val head : Int,
private val voice : Int
) extends PlanetSideGameObject with FactionAffinity with Container {
class Player(private val core : Avatar) extends PlanetSideGameObject with FactionAffinity with Container {
private var alive : Boolean = false
private var backpack : Boolean = false
private var health : Int = 0
@ -29,46 +23,20 @@ class Player(private val name : String,
private var exosuit : ExoSuitType.Value = ExoSuitType.Standard
private val freeHand : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
private val holsters : Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
private val fifthSlot : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
private val inventory : GridInventory = GridInventory()
private var drawnSlot : Int = Player.HandsDownSlot
private var lastDrawnSlot : Int = Player.HandsDownSlot
private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None)
private var bep : Long = 0
private var cep : Long = 0
private val certifications : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/**
* 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`
*/
private val implants : Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
// private var tosRibbon : MeritCommendation.Value = MeritCommendation.None
// private var upperRibbon : MeritCommendation.Value = MeritCommendation.None
// private var middleRibbon : MeritCommendation.Value = MeritCommendation.None
// private var lowerRibbon : MeritCommendation.Value = MeritCommendation.None
private var facingYawUpper : Float = 0f
private var crouching : Boolean = false
private var jumping : Boolean = false
private var cloaked : Boolean = false
private var backpackAccess : Option[PlanetSideGUID] = None
private var admin : Boolean = false
private var spectator : Boolean = false
private var vehicleSeated : Option[PlanetSideGUID] = None
private var vehicleOwned : Option[PlanetSideGUID] = None
private var continent : String = "home2" //the zone id
private val playerDef : AvatarDefinition = Player.definition //TODO could be a var
//SouNourS things
/** Last medkituse. */
@ -76,23 +44,20 @@ class Player(private val name : String,
var death_by : Int = 0
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time : Int = -1
/** The player is shooting. */
var shooting : Boolean = false
/** From PlanetsideAttributeMessage */
var PlanetsideAttribute : Array[Long] = Array.ofDim(120)
Player.SuitSetup(this, ExoSuit)
fifthSlot.Equipment = new LockerContainer //the fifth slot is the player's "locker"
def Name : String = name
def Name : String = core.name
def Faction : PlanetSideEmpire.Value = faction
def Faction : PlanetSideEmpire.Value = core.faction
def Sex : CharacterGender.Value = sex
def Sex : CharacterGender.Value = core.sex
def Voice : Int = voice
def Head : Int = core.head
def Head : Int = head
def Voice : Int = core.voice
def isAlive : Boolean = alive
@ -173,9 +138,7 @@ class Player(private val name : String,
holsters(slot)
}
else if(slot == 5) {
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
Equipment = fifthSlot.Equipment
}
core.FifthSlot
}
else if(slot == Player.FreeHandSlot) {
freeHand
@ -189,7 +152,7 @@ class Player(private val name : String,
def Inventory : GridInventory = inventory
def Locker : LockerContainer = fifthSlot.Equipment.get.asInstanceOf[LockerContainer]
def Locker : LockerContainer = core.Locker
def Fit(obj : Equipment) : Option[Int] = {
recursiveHolsterFit(holsters.iterator, obj.Size) match {
@ -220,20 +183,6 @@ class Player(private val name : String,
}
}
def Equip(slot : Int, obj : Equipment) : Boolean = {
if(-1 < slot && slot < 5) {
holsters(slot).Equipment = obj
true
}
else if(slot == Player.FreeHandSlot) {
freeHand.Equipment = obj
true
}
else {
inventory += slot -> obj
}
}
def FreeHand = freeHand
def FreeHand_=(item : Option[Equipment]) : Option[Equipment] = {
@ -243,33 +192,22 @@ class Player(private val name : String,
FreeHand.Equipment
}
def SaveLoadout(label : String, line : Int) : Unit = {
loadouts(line) = Some(Loadout.Create(this, label))
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line)
def DeleteLoadout(line : Int) : Unit = {
loadouts(line) = None
}
def Find(obj : Equipment) : Option[Int] = Find(obj.GUID)
def Find(guid : PlanetSideGUID) : Option[Int] = {
findInHolsters(holsters.iterator, guid) match {
findInHolsters(holsters.iterator, guid)
.orElse(findInInventory(inventory.Items.values.iterator, guid)) match {
case Some(index) =>
Some(index)
case None =>
findInInventory(inventory.Items.values.iterator, guid) match {
case Some(index) =>
Some(index)
case None =>
if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
Some(Player.FreeHandSlot)
}
else {
None
}
if(Locker.Find(guid).isDefined) {
Some(5)
}
else if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
Some(Player.FreeHandSlot)
}
else {
None
}
}
}
@ -349,27 +287,13 @@ class Player(private val name : String,
exosuit = suit
}
def BEP : Long = bep
def LoadLoadout(line : Int) : Option[Loadout] = core.LoadLoadout(line)
def BEP_=(battleExperiencePoints : Long) : Long = {
bep = math.max(0L, math.min(battleExperiencePoints, 4294967295L))
BEP
}
def BEP : Long = core.BEP
def CEP : Long = cep
def CEP : Long = core.CEP
def CEP_=(commandExperiencePoints : Long) : Long = {
cep = math.max(0L, math.min(commandExperiencePoints, 4294967295L))
CEP
}
def Certifications : mutable.Set[CertificationType.Value] = certifications
/**
* Retrieve the three implant slots for this player.
* @return an `Array` of `ImplantSlot` objects
*/
def Implants : Array[ImplantSlot] = implants
def Certifications : Set[CertificationType.Value] = core.Certifications.toSet
/**
* What kind of implant is installed into the given slot number?
@ -377,91 +301,17 @@ class Player(private val name : String,
* @param slot the slot number
* @return the tye of implant
*/
def Implant(slot : Int) : ImplantType.Value = {
if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
}
def Implant(slot : Int) : ImplantType.Value = core.Implant(slot)
/**
* Given a new implant, assign it into a vacant implant slot on this player.<br>
* <br>
* The implant must be unique in terms of which implants have already been assigned to this player.
* Multiple of a type of implant being assigned at once is not supported.
* Additionally, the implant is inserted into the earliest yet-unknown but vacant slot.
* Implant slots are vacant by just being unlocked or by having their previous implant uninstalled.
* @param implant the implant being installed
* @return the index of the `ImplantSlot` where the implant was installed
* A read-only `Array` of tuples representing important information about all unlocked implant slots.
* @return a maximum of three implant types, initialization times, and active flags
*/
def InstallImplant(implant : ImplantDefinition) : Option[Int] = {
implants.find({p => p.Installed.contains(implant)}) match { //try to find the installed implant
case None =>
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
case Some(slot) =>
implants(slot).Implant = implant
Some(slot)
case None =>
None
}
case Some(_) =>
None
}
def Implants : Array[(ImplantType.Value, Long, Boolean)] = {
core.Implants.takeWhile(_.Unlocked).map( implant => { (implant.Implant, implant.MaxTimer, implant.Active) })
}
/**
* Remove a specific implant from a player's allocated installed implants.<br>
* <br>
* Due to the exclusiveness of installed implants,
* any implant slot with a matching `Definition` can be uninstalled safely.
* (There will never be any doubles.)
* This operation can lead to an irregular pattern of installed and uninstalled `ImplantSlot` objects.
* Despite that breach of pattern, the logic here is consistent as demonstrated by the client and by packets.
* The client also assigns and removes implants based on slot numbers that only express availability of a "slot."
* @see `AvatarImplantMessage.implantSlot`
* @param implantType the type of implant being uninstalled
* @return the index of the `ImplantSlot` where the implant was found and uninstalled
*/
def UninstallImplant(implantType : ImplantType.Value) : Option[Int] = {
recursiveFindImplantInSlot(implants.iterator, implantType) match {
case Some(slot) =>
implants(slot).Implant = None
Some(slot)
case None =>
None
}
}
/**
* Locate the index of the encountered implant type.
* Functional implants may be exclusive in as far as the input `Iterator`'s source is concerned,
* but any number of `ImplantType.None` values are alway allowed in the source in any order.
* @param iter an `Iterator` of `ImplantSlot` objects
* @param implantType the target implant being sought
* @param index a defaulted index value representing the structure underlying the `Iterator` param
* @return the index where the target implant is installed
*/
@tailrec private def recursiveFindImplantInSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val slot = iter.next
if(slot.Unlocked && slot.Implant == implantType) {
Some(index)
}
else {
recursiveFindImplantInSlot(iter, implantType, index + 1)
}
}
}
def ResetAllImplants() : Unit = {
implants.foreach(slot => {
slot.Installed match {
case Some(_) =>
slot.Initialized = false
case None => ;
}
})
}
def ResetAllImplants() : Unit = core.ResetAllImplants()
def FacingYawUpper : Float = facingYawUpper
@ -524,10 +374,6 @@ class Player(private val name : String,
isBackpack && (backpackAccess.isEmpty || backpackAccess.contains(player.GUID))
}
def Admin : Boolean = admin
def Spectator : Boolean = spectator
def VehicleSeated : Option[PlanetSideGUID] = vehicleSeated
def VehicleSeated_=(guid : PlanetSideGUID) : Option[PlanetSideGUID] = VehicleSeated_=(Some(guid))
@ -553,52 +399,34 @@ class Player(private val name : String,
Continent
}
def Definition : AvatarDefinition = playerDef
override def toString : String = {
Player.toString(this)
}
def Definition : AvatarDefinition = core.Definition
def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
override def equals(other : Any) : Boolean = other match {
case that: Player =>
(that canEqual this) &&
name == that.name &&
faction == that.faction &&
sex == that.sex &&
voice == that.voice &&
head == that.head
core == that.core
case _ =>
false
}
override def hashCode() : Int = {
val state = Seq(name, faction, sex, voice, head)
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
core.hashCode()
}
override def toString : String = Player.toString(this)
}
object Player {
final private val definition : AvatarDefinition = new AvatarDefinition(121)
final val LockerSlot : Int = 5
final val FreeHandSlot : Int = 250
final val HandsDownSlot : Int = 255
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
new Player(name, faction, sex, head, voice)
def apply(core : Avatar) : Player = {
new Player(core)
}
// /**
// * Change the type of `AvatarDefinition` is used to define the player.
// * @param player the player
// * @param avatarDef the player's new definition entry
// * @return the changed player
// */
// def apply(player : Player, avatarDef : AvatarDefinition) : Player = {
// player.playerDef = avatarDef
// player
// }
def SuitSetup(player : Player, eSuit : ExoSuitType.Value) : Unit = {
val esuitDef : ExoSuitDefinition = ExoSuitDefinition.Select(eSuit)
//exosuit
@ -611,37 +439,11 @@ object Player {
(0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) })
}
def Administrate(player : Player, isAdmin : Boolean) : Player = {
player.admin = isAdmin
player
}
def Spectate(player : Player, isSpectator : Boolean) : Player = {
player.spectator = isSpectator
player
}
def Release(player : Player) : Player = {
def Respawn(player : Player) : Player = {
if(player.Release) {
val obj = new Player(player.Name, player.Faction, player.Sex, player.Voice, player.Head)
val obj = new Player(player.core)
obj.VehicleOwned = player.VehicleOwned
obj.Continent = player.Continent
//hand over loadouts
(0 until 10).foreach(index => {
obj.loadouts(index) = player.loadouts(index)
})
//hand over implants
(0 until 3).foreach(index => {
if(obj.Implants(index).Unlocked = player.Implants(index).Unlocked) {
obj.Implants(index).Implant = player.Implants(index).Installed
}
})
//hand over knife
obj.Slot(4).Equipment = player.Slot(4).Equipment
player.Slot(4).Equipment = None
//hand over ???
obj.fifthSlot.Equipment = player.fifthSlot.Equipment
player.fifthSlot.Equipment = None
obj
}
else {
@ -650,7 +452,7 @@ object Player {
}
def toString(obj : Player) : String = {
val name : String = if(obj.VehicleSeated.isDefined) { s"[${obj.name}, ${obj.VehicleSeated.get.guid}]" } else { obj.Name }
s"[player $name, ${obj.Faction} (${obj.Health}/${obj.MaxHealth})(${obj.Armor}/${obj.MaxArmor})]"
val guid = if(obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" } else { "" }
s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
}
}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, ImplantSlot, Player}
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
import net.psforever.types.{GrenadeState, ImplantType}
@ -136,18 +136,12 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = {
val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
val implants = obj.Implants
(0 until numImplants).map(index => {
val slot = implants(index)
slot.Installed match {
case Some(implant) =>
if(slot.Initialized) {
ImplantEntry(slot.Implant, None)
}
else {
ImplantEntry(slot.Implant, Some(implant.Initialization.toInt))
}
case None =>
ImplantEntry(ImplantType.None, None)
obj.Implants.map({ case(implant, initialization, active) =>
if(initialization == 0) {
ImplantEntry(implant, None)
}
else {
ImplantEntry(implant, Some(math.max(0,initialization).toInt))
}
}).toList
}
@ -157,14 +151,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @param iter an `Iterator` of `ImplantSlot` objects
* @return the effect of an active implant
*/
@tailrec private def recursiveMakeImplantEffects(iter : Iterator[ImplantSlot]) : Option[ImplantEffects.Value] = {
@tailrec private def recursiveMakeImplantEffects(iter : Iterator[(ImplantType.Value, Long, Boolean)]) : Option[ImplantEffects.Value] = {
if(!iter.hasNext) {
None
}
else {
val slot = iter.next
if(slot.Active) {
slot.Implant match {
val(implant, _, active) = iter.next
if(active) {
implant match {
case ImplantType.AdvancedRegen =>
Some(ImplantEffects.RegenEffects)
case ImplantType.DarklightVision =>

View file

@ -0,0 +1,124 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
class CorpseConverter extends AvatarConverter {
override def ConstructorData(obj : Player) : Try[CharacterData] =
Failure(new Exception("CorpseConverter should not be used to generate CharacterData"))
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
Success(
DetailedCharacterData(
MakeAppearanceData(obj),
0, 0, 0, 0, 0, 0, 0,
Nil, Nil, Nil, Nil,
None,
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
DrawnSlot.None
)
)
}
/**
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
CharacterAppearanceData(
PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
0,
false,
false,
obj.ExoSuit,
"",
0,
true,
obj.Orientation.y, //TODO is this important?
0,
true,
GrenadeState.None,
false,
false,
false,
RibbonBars()
)
}
/**
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
* It will always be "`Detailed`".
* @param obj the `Player` game object
* @return a list of all items that were in the inventory in decoded packet form
*/
private def MakeInventory(obj : Player) : List[InternalSlot] = {
obj.Inventory.Items
.map({
case(_, item) =>
val equip : Equipment = item.obj
BuildEquipment(item.start, equip)
}).toList
}
/**
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
* with exception to the contents of the fifth slot.
* The fifth slot is only represented if the `Player` is an `0x18` type.
* @param obj the `Player` game object
* @return a list of all items that were in the holsters in decoded packet form
*/
private def MakeHolsters(obj : Player) : List[InternalSlot] = {
recursiveMakeHolsters(obj.Holsters().iterator)
}
/**
* Given some equipment holsters, convert the contents of those holsters into converted-decoded packet data.
* @param iter an `Iterator` of `EquipmentSlot` objects that are a part of the player's holsters
* @param list the current `List` of transformed data
* @param index which holster is currently being explored
* @return the `List` of inventory data created from the holsters
*/
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
if(!iter.hasNext) {
list
}
else {
val slot : EquipmentSlot = iter.next
if(slot.Equipment.isDefined) {
val equip : Equipment = slot.Equipment.get
recursiveMakeHolsters(
iter,
list :+ BuildEquipment(index, equip),
index + 1
)
}
else {
recursiveMakeHolsters(iter, list, index + 1)
}
}
}
/**
* A builder method for turning an object into `0x17` decoded packet form.
* @param index the position of the object
* @param equip the game object
* @return the game object in decoded packet form
*/
private def BuildEquipment(index : Int, equip : Equipment) : InternalSlot = {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
}
}
object CorpseConverter {
val converter = new CorpseConverter
}

View file

@ -22,7 +22,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
),
0,
obj.Health / obj.MaxHealth * 255, //TODO not precise
255 * obj.Health / obj.MaxHealth, //TODO not precise
false, false,
obj.DeploymentState,
false,

View file

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

View file

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

View file

@ -18,7 +18,7 @@ import net.psforever.objects.guid.NumberPoolHub
* a closed number space, which is also the `Zone`.
* It utilizes those qualities of the enclosing region to construct the entity within that region.<br>
* <br>
* Example: `ServerObjectBuilder(n, function)`
* Example: `ServerObjectBuilder(n, function)`<br>
* Example: `new ServerBuilderObject[A](n, function)`, where `function` is a `(Int,Context)=>A`
* @see `ZoneMap`
* @see `Zone.Init`
@ -28,7 +28,7 @@ import net.psforever.objects.guid.NumberPoolHub
* can be inferred from the output of `constructor`
*/
class ServerObjectBuilder[A <: PlanetSideServerObject](private val id : Int,
private val constructor : (Int, ActorContext) => A
private val constructor : ServerObjectBuilder.ConstructorType[A]
) {
/**
* Instantiate and configure the given server object.
@ -49,6 +49,8 @@ class ServerObjectBuilder[A <: PlanetSideServerObject](private val id : Int,
}
object ServerObjectBuilder {
type ConstructorType[A <: PlanetSideServerObject] = (Int, ActorContext)=>A
/**
* Overloaded constructor.
* @param id the unqiue id that will be assigned to this entity
@ -56,7 +58,7 @@ object ServerObjectBuilder {
* @tparam A any object that extends from PlanetSideServerObject that will be produced by this class
* @return a `ServerObjectBuilder` object
*/
def apply[A <: PlanetSideServerObject](id : Int, constructor : (Int, ActorContext) => A) : ServerObjectBuilder[A] = {
def apply[A <: PlanetSideServerObject](id : Int, constructor : ConstructorType[A]) : ServerObjectBuilder[A] = {
new ServerObjectBuilder[A](id, constructor)
}
}

View file

@ -6,14 +6,19 @@ 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 id : 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.
*/
private var modelId : Option[Int] = None
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var amenities : List[Amenity] = List.empty
GUID = PlanetSideGUID(0)
def Id : Int = id
def Id : Int = mapId
def Faction : PlanetSideEmpire.Value = faction
@ -32,25 +37,44 @@ class Building(private val id : Int, private val zone : Zone) extends PlanetSide
def Zone : Zone = zone
def ModelId : Int = modelId.getOrElse(Id)
def ModelId_=(id : Int) : Int = ModelId_=(Some(id))
def ModelId_=(id : Option[Int]) : Int = {
modelId = id
ModelId
}
def BuildingType : StructureType.Value = buildingType
def Definition: ObjectDefinition = Building.BuildingDefinition
}
object Building {
final val NoBuilding : Building = new Building(0, Zone.Nowhere) {
final val NoBuilding : Building = new Building(0, Zone.Nowhere, StructureType.Platform) {
override def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
override def Amenities_=(obj : Amenity) : List[Amenity] = Nil
}
final val BuildingDefinition : ObjectDefinition = new ObjectDefinition(0) { Name = "building" }
def apply(id : Int, zone : Zone) : Building = {
new Building(id, zone)
def apply(id : Int, zone : Zone, buildingType : StructureType.Value) : Building = {
new Building(id, zone, buildingType)
}
def Structure(id : Int, zone : Zone, context : ActorContext) : Building = {
def Structure(buildingType : StructureType.Value, location : Vector3)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
val obj = new Building(id, zone)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-building")
val obj = new Building(id, zone, buildingType)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj
}
def Structure(buildingType : StructureType.Value)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
val obj = new Building(id, zone, buildingType)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,34 +1,58 @@
// 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
// import net.psforever.types.Vector3
// /**
// * Instantiate an configure a `SpawnTube` object
// * @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(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
// import net.psforever.objects.GlobalDefinitions
//
// val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
// obj.Position = pos
// obj.Orientation = orient
// obj
// }
import akka.actor.ActorContext
import net.psforever.types.Vector3
/**
* Instantiate an configure a `SpawnTube` object
* @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(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
Constructor(GlobalDefinitions.respawn_tube, pos, orient)(id, context)
}
/**
* 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"${tdef.Name}_$id")
obj
}
}

View file

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

View file

@ -2,7 +2,6 @@
package net.psforever.objects.zones
import akka.actor.{Actor, Props}
import net.psforever.objects.Player
import scala.annotation.tailrec
@ -42,16 +41,25 @@ 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 =>
log.error(s"Requested zone $zoneId could not be found")
}
case InterstellarCluster.RequestClientInitialization(tplayer) =>
case InterstellarCluster.RequestClientInitialization() =>
zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones
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 _ => ;
}
@ -59,20 +67,20 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
/**
* 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)
}
}
}
@ -95,17 +103,41 @@ object InterstellarCluster {
/**
* Signal to the cluster that a new client needs to be initialized for all listed `Zone` destinations.
* @param tplayer the `Player` belonging to the client;
* may be superfluous
* @see `Zone`
*/
final case class RequestClientInitialization(tplayer : Player)
final case class RequestClientInitialization()
/**
* Return signal intended to inform the original sender that all `Zone`s have finished being initialized.
* @param tplayer the `Player` belonging to the client;
* may be superfluous
* @see `WorldSessionActor`
*/
final case class ClientInitializationComplete(tplayer : Player)
final case class ClientInitializationComplete()
}
/*
// List[Building] --> List[List[(Amenity, Building)]] --> List[(SpawnTube*, Building)]
zone.LocalLattice.Buildings.values
.filter(_.Faction == player.Faction)
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
*/
/*
zone.Buildings.values.filter(building => {
(
if(spawn_zone == 6) { Set(StructureType.Tower) }
else if(spawn_zone == 7) { Set(StructureType.Facility, StructureType.Building) }
else { Set.empty[StructureType.Value] }
).contains(building.BuildingType) &&
building.Amenities.exists(_.isInstanceOf[SpawnTube]) &&
building.Faction == player.Faction &&
building.Position != Vector3.Zero
})
.toSeq
.sortBy(building => {
Vector3.DistanceSquared(player.Position, building.Position) < Vector3.DistanceSquared(player.Position, building.Position)
})
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
).headOption
*/

View file

@ -3,17 +3,19 @@ package net.psforever.objects.zones
import akka.actor.{ActorContext, ActorRef, Props}
import akka.routing.RandomPool
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.{Avatar, PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
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
import scala.annotation.tailrec
import scala.collection.concurrent.TrieMap
import scala.collection.mutable.ListBuffer
import scala.collection.immutable.{Map => PairMap}
@ -44,8 +46,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private var accessor : ActorRef = ActorRef.noSender
/** The basic support structure for the globally unique number system used by this `Zone`. */
private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
guid.AddPool("environment", (0 to 2000).toList)
guid.AddPool("dynamic", (2001 to 10000).toList).Selector = new RandomSelector //TODO unlump pools later; do not make too big
guid.AddPool("environment", (0 to 3000).toList) //TODO tailer ro suit requirements of zone
guid.AddPool("dynamic", (3001 to 10000).toList).Selector = new RandomSelector //TODO unlump pools later; do not make too big
/** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
@ -54,8 +56,16 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private var vehicles : List[Vehicle] = List[Vehicle]()
/** */
private var transport : ActorRef = ActorRef.noSender
/** */
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[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>
@ -66,7 +76,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
* First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized.
* Second, all supporting `Actor` agents are created, e.g., `ground`.
* Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system.
* To avoid being called more than once, there is a test whether the `accessor` for the globally unique identifier system has been changed.
* To avoid being called more than once, there is a test whether the `accessor` for the globally unique identifier system has been changed.<br>
* <br>
* Execution of this operation should be fail-safe.
* The chances of failure should be mitigated or skipped.
* An testing routine should be run after the fact on the results of the process.
* @see `ZoneActor.ZoneSetupCheck`
* @param context a reference to an `ActorContext` necessary for `Props`
*/
def Init(implicit context : ActorContext) : Unit = {
@ -75,12 +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, players, corpses), s"$Id-players")
Map.LocalObjects.foreach({ builderObject =>
builderObject.Build
})
Map.LocalObjects.foreach({ builderObject => builderObject.Build })
MakeBuildings(context)
AssignAmenities()
CreateSpawnGroups()
}
}
@ -176,6 +191,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Vehicles : List[Vehicle] = vehicles
def Players : List[Avatar] = players.keys.toList
def LivePlayers : List[Player] = players.values.collect( { case Some(tplayer) => tplayer }).toList
def Corpses : List[Player] = corpses.toList
def AddVehicle(vehicle : Vehicle) : List[Vehicle] = {
vehicles = vehicles :+ vehicle
Vehicles
@ -217,6 +238,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Transport : ActorRef = transport
def Population : ActorRef = population
def Buildings : Map[Int, Building] = buildings
def Building(id : Int) : Option[Building] = {
@ -235,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)
/**
@ -263,6 +315,97 @@ 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
/**
* 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(zone : Zone, player : Player)
/**
* Message that acts in reply to `Spawn(avatar, player)`, but the avatar did not initially `Join` this zone.
* @param zone the `Zone` object
* @param player the `Player` object
*/
final case class PlayerCanNotSpawn(zone : Zone, player : Player)
}
object Corpse {
/**
* Message that reports to the zone of a freshly dead player.
* @param player the dead `Player`
*/
final case class Add(player : Player)
/**
* Message that tells the zone to no longer mind the dead player.
* @param player the dead `Player`
*/
final case class Remove(player : Player)
}
object Lattice {
/**
* Message requesting that the current zone determine where a `player` can spawn.
* @param zone_number this zone's numeric identifier
* @param player the `Player` object
* @param spawn_group the category of spawn points the request wants searched
*/
final case class RequestSpawnPoint(zone_number : Int, player : Player, spawn_group : Int)
/**
* Message that returns a discovered spawn point to a request source.
* @param zone_id the zone's text identifier
* @param building the `Building` in which the spawnpoint is located
* @param spawn_tube the spawn point holding object
*/
final case class SpawnPoint(zone_id : String, building : Building, spawn_tube : SpawnTube)
/**
* Message that informs a request source that a spawn point could not be discovered with the previous criteria.
* @param zone_number this zone's numeric identifier
* @param spawn_group the spawn point holding object;
* if `None`, then the previous `zone_number` could not be found;
* otherwise, no spawn points could be found in the zone
*/
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
}
/**
* Message to relinguish an item and place in on the ground.
* @param item the piece of `Equipment`

View file

@ -1,8 +1,12 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.zones
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.types.Vector3
import org.log4s.Logger
/**
@ -12,29 +16,108 @@ import org.log4s.Logger
class ZoneActor(zone : Zone) extends Actor {
private[this] val log = org.log4s.getLogger
def receive : Receive = {
def receive : Receive = Init
def Init : Receive = {
case Zone.Init() =>
zone.Init
ZoneSetupCheck()
context.become(Processing)
case _ => ;
}
def Processing : Receive = {
//frwd to Population Actor
case msg @ Zone.Population.Join =>
zone.Population forward msg
case msg @ Zone.Population.Leave =>
zone.Population forward msg
case msg @ Zone.Population.Spawn =>
zone.Population forward msg
case msg @ Zone.Population.Release =>
zone.Population forward msg
case msg @ Zone.Corpse.Add =>
zone.Population forward msg
case msg @ Zone.Corpse.Remove =>
zone.Population forward msg
//frwd to Ground Actor
case msg @ Zone.DropItemOnGround =>
zone.Ground forward msg
case msg @ Zone.GetItemOnGround =>
zone.Ground forward msg
//frwd to Vehicle Actor
case msg @ Zone.SpawnVehicle =>
zone.Transport forward msg
case msg @ Zone.DespawnVehicle =>
zone.Transport forward msg
//own
case Zone.Lattice.RequestSpawnPoint(zone_number, player, spawn_group) =>
if(zone_number == zone.Number) {
val buildingTypeSet = if(spawn_group == 6) {
Set(StructureType.Tower)
}
else if(spawn_group == 7) {
Set(StructureType.Facility, StructureType.Building)
}
else {
Set.empty[StructureType.Value]
}
val playerPosition = player.Position.xy
zone.SpawnGroups()
.filter({ case((building, _)) =>
building.Faction == player.Faction && buildingTypeSet.contains(building.BuildingType)
})
.toSeq
.sortBy({ case ((building, _)) =>
Vector3.DistanceSquared(playerPosition, building.Position.xy)
})
.headOption match {
case Some((building, List(tube))) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, tube)
case Some((building, tubes)) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, scala.util.Random.shuffle(tubes).head)
case None =>
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, Some(spawn_group))
}
}
else { //wrong zone_number
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
}
case msg =>
log.warn(s"Received unexpected message - $msg")
}
def ZoneSetupCheck(): Unit = {
def ZoneSetupCheck() : Int = {
import ZoneActor._
def guid(id : Int) = zone.GUID(id)
val map = zone.Map
def guid(id : Int) = zone.GUID(id)
val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity")
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog)
val errors = new AtomicInteger(0)
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
//check base to object associations
map.ObjectToBuilding.foreach({ case((object_guid, base_id)) =>
if(zone.Building(base_id).isEmpty) {
slog.error(s"expected a building at id #$base_id")
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 {
@ -85,8 +169,9 @@ object ZoneActor {
}
}
catch {
case _ : Exception =>
elog.error(s"expected a $description at id $object_guid but no object is initialized")
case e : Exception =>
elog.error(s"expected a $description at id $object_guid but no object is initialized - $e")
errorCounter.incrementAndGet()
false
}
}

View file

@ -2,7 +2,7 @@
package net.psforever.objects.zones
import net.psforever.objects.serverobject.structures.FoundationBuilder
import net.psforever.objects.serverobject.ServerObjectBuilder
import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObjectBuilder}
/**
* The fixed instantiation and relation of a series of server objects.<br>
@ -44,10 +44,15 @@ class ZoneMap(private val name : String) {
/**
* Append the builder for a server object to the list of builders known to this `ZoneMap`.
* @param obj the builder for a server object
* @param id the unique id that will be assigned to this entity
* @param constructor the logic that initializes the emitted entity
* @return the current number of builders
*/
def LocalObject(obj : ServerObjectBuilder[_]) : Unit = {
localObjects = localObjects :+ obj
def LocalObject[A <: PlanetSideServerObject](id : Int, constructor : ServerObjectBuilder.ConstructorType[A]) : Int = {
if(id > 0) {
localObjects = localObjects :+ ServerObjectBuilder[A](id, constructor)
}
localObjects.size
}
def LocalBuildings : Map[Int, FoundationBuilder] = buildings

View file

@ -0,0 +1,184 @@
// Copyright (c) 2017 PSForever
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._
def receive : Receive = {
case Zone.Population.Join(avatar) =>
PopulationJoin(avatar, playerMap)
case Zone.Population.Leave(avatar) =>
PopulationLeave(avatar, playerMap) match {
case None => ;
case player @ Some(_) =>
sender ! Zone.Population.PlayerHasLeft(zone, player)
}
case Zone.Population.Spawn(avatar, player) =>
PopulationSpawn(avatar, player, playerMap) match {
case Some(tplayer) =>
if(tplayer ne player) {
sender ! Zone.Population.PlayerAlreadySpawned(zone, player)
}
case None =>
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
}
case Zone.Population.Release(avatar) =>
PopulationRelease(avatar, playerMap) match {
case Some(_) => ;
case None =>
sender ! Zone.Population.PlayerHasLeft(zone, None)
}
case Zone.Corpse.Add(player) =>
CorpseAdd(player, corpseList)
case Zone.Corpse.Remove(player) =>
CorpseRemove(player, corpseList)
case _ => ;
}
}
object ZonePopulationActor {
/**
* Add an `avatar` as the key of an `Avatar` to `Player` object pair in the given collection.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return true, if the mapping is for a new key;
* false, if the key already exists
*/
def PopulationJoin(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Boolean = {
playerMap.get(avatar) match {
case Some(_) =>
false
case None =>
playerMap += avatar -> None
true
}
}
/**
* Remove an `avatar` from the key of an `Avatar` to `Player` object pair in the given collection.
* If a `Player` object is associated at the time, return it safely.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that was associated at the time the `avatar` was removed
*/
def PopulationLeave(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.remove(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer
}
}
/**
* Associate a `Player` object as a value to an existing `Avatar` object that will be its key.
* Do not overwrite players that are already associated.
* @param avatar an `Avatar` object
* @param player a `Player` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return the `Player` object that is associated with the `Avatar` key
*/
def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer match {
case Some(aplayer) =>
Some(aplayer)
case None =>
playerMap(avatar) = Some(player)
Some(player)
}
}
}
/**
* Disassociate a `Player` object from an existing `Avatar` object that was be its key.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that is associated at the time
*/
def PopulationRelease(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
playerMap(avatar) = None
tplayer
}
}
/**
* If the given `player` passes a condition check, add it to the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
* @return true, if the `player` was added to the list;
* false, otherwise
*/
def CorpseAdd(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
if(player.isBackpack) {
corpseList += player
true
}
else {
false
}
}
/**
* Remove the given `player` from the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
*/
def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = {
recursiveFindCorpse(corpseList.iterator, player) match {
case None => ;
case Some(index) =>
corpseList.remove(index)
}
}
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `Player` objects
* @param player the target `Player`
* @param index the index of the discovered `Player` object
* @return the index of the `Player` object in the list to be removed;
* `None`, otherwise
*/
@tailrec final def recursiveFindCorpse(iter : Iterator[Player], player : Player, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
if(iter.next == player) {
Some(index)
}
else {
recursiveFindCorpse(iter, player, index + 1)
}
}
}
}

View file

@ -2,15 +2,19 @@
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.Vector3
import scodec.Codec
import net.psforever.types.{PlanetSideEmpire, Vector3}
import scodec.Attempt.{Failure, Successful}
import scodec.{Codec, Err}
import scodec.codecs._
/**
* An `Enumeration` of the various states a `Player` may possess in the cycle of nanite life and death.
*/
object DeadState extends Enumeration {
type Type = Value
val
Nothing,
Alive,
Dead,
Release,
RespawnTime
@ -20,19 +24,41 @@ object DeadState extends Enumeration {
}
/**
* na
* @param state avatar's relationship with the world
* Dispatched by the server to manipulate the client's management of the `Player` object owned by the user as his "avatar."<br>
* <br>
* The cycle of a player is generally `Alive` to `Dead` and `Dead` to `Release` and `Release` to `RespawnTimer` to `Alive`.
* When deconstructing oneself, the user makes a jump between `Alive` and `Release`;
* and, he may make a further jump from `Release` to `Alive` depending on spawning choices.
* Being `Alive` is the most common state.
* (Despite what anyone says.)
* Being `Dead` is just a technical requirement to initialize the revive timer.
* The player should be sufficiently "dead" by having his health points decreased to zero.
* If the timer is reduced to zero, the player is sent back to their faction-appropriate sanctuary continent.<br>
* <br>
* `Release` causes a "dead" player to have its character model converted into a backpack or a form of pastry.
* This cancels the revival timer - the player may no longer be revived - and brings the user to the deployment map.
* From the deployment map, the user may select a place where they may respawn a new character.
* The options available form this spawn are not only related to the faction affinity of the bases compared to the user's player(s)
* but also to the field `faction` as is provided in the packet.
* If the player is converted to a state of `Release` while being alive, the deployment map is still displayed.
* Their character model is not replaced by a backpack or pastry.<br>
* <br>
* `RespawnTimer` is like `Dead` as it is just a formal distinction to cause the client to display a timer.
* The state indicates that the player is being resurrected at a previously-selected location in the state `Alive`.
* @param state avatar's mortal relationship with the world;
* the following timers are applicable during `Death` and `RespawnTimer`;
* `faction` is applicable mainly during `Release`
* @param timer_max total length of respawn countdown, in milliseconds
* @param timer initial length of the respawn timer, in milliseconds
* @param pos last position
* @param unk4 na
* @param pos player's last position
* @param faction spawn points available for this faction on redeployment map
* @param unk5 na
*/
final case class AvatarDeadStateMessage(state : DeadState.Value,
timer_max : Long,
timer : Long,
pos : Vector3,
unk4 : Long,
faction : PlanetSideEmpire.Value,
unk5 : Boolean)
extends PlanetSideGamePacket {
type Packet = AvatarDeadStateMessage
@ -41,12 +67,32 @@ final case class AvatarDeadStateMessage(state : DeadState.Value,
}
object AvatarDeadStateMessage extends Marshallable[AvatarDeadStateMessage] {
/**
* allocate all values from the `PlanetSideEmpire` `Enumeration`
*/
private val factionLongValues = PlanetSideEmpire.values map { _.id.toLong }
/**
* `Codec` for converting between the limited `PlanetSideEmpire` `Enumeration` and a `Long` value.
*/
private val factionLongCodec = uint32L.exmap[PlanetSideEmpire.Value] (
fv =>
if(factionLongValues.contains(fv)) {
Successful(PlanetSideEmpire(fv.toInt))
}
else {
Failure(Err(s"$fv is not mapped to a PlanetSideEmpire value"))
},
f =>
Successful(f.id.toLong)
)
implicit val codec : Codec[AvatarDeadStateMessage] = (
("state" | DeadState.codec) ::
("timer_max" | uint32L) ::
("timer" | uint32L) ::
("pos" | Vector3.codec_pos) ::
("unk4" | uint32L) ::
("unk4" | factionLongCodec) ::
("unk5" | bool)
).as[AvatarDeadStateMessage]
}

View file

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

View file

@ -5,7 +5,18 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
import scodec.Codec
import scodec.codecs._
/**
* 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 when defined, the continent number
*/
final case class SpawnRequestMessage(unk1 : Int,
unk2 : Long,
unk3 : Int,

View file

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

View file

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

View file

@ -4,11 +4,12 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.Vector3
import net.psforever.types.{PlanetSideEmpire, Vector3}
import scodec.bits._
class AvatarDeadStateMessageTest extends Specification {
val string = hex"ad3c1260801c12608009f99861fb0741e040000010"
val string_invalid = hex"ad3c1260801c12608009f99861fb0741e0400000F0"
"decode" in {
PacketCoding.DecodePacket(string).require match {
@ -17,15 +18,19 @@ class AvatarDeadStateMessageTest extends Specification {
unk2 mustEqual 300000
unk3 mustEqual 300000
pos mustEqual Vector3(6552.617f,4602.375f,60.90625f)
unk4 mustEqual 2
unk4 mustEqual PlanetSideEmpire.VS
unk5 mustEqual true
case _ =>
ko
}
}
"decode (failure)" in {
PacketCoding.DecodePacket(string_invalid).isFailure mustEqual true
}
"encode" in {
val msg = AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), 2, true)
val msg = AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), PlanetSideEmpire.VS, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string

View file

@ -26,4 +26,8 @@ class DisconnectMessageTest extends Specification {
pkt mustEqual string
}
"comparison" in {
DisconnectMessage("First") mustEqual DisconnectMessage("First", "", "")
}
}

View file

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

View file

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

View file

@ -151,6 +151,7 @@ class ConverterTest extends Specification {
}
"Player" should {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj : Player = {
/*
Create an AmmoBoxDefinition with which to build two AmmoBoxes
@ -171,7 +172,7 @@ class ConverterTest extends Specification {
val tool = Tool(tdef)
tool.GUID = PlanetSideGUID(92)
tool.AmmoSlot.Box.GUID = PlanetSideGUID(90)
val obj = Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = Player(avatar)
obj.GUID = PlanetSideGUID(93)
obj.Slot(2).Equipment = tool
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(94)
@ -182,7 +183,7 @@ class ConverterTest extends Specification {
val converter = new CharacterSelectConverter
"convert to packet (BR < 24)" in {
obj.BEP = 0
avatar.BEP = 0
obj.Definition.Packet.DetailedConstructorData(obj) match {
case Success(_) =>
ok
@ -198,7 +199,7 @@ class ConverterTest extends Specification {
}
"convert to packet (BR >= 24)" in {
obj.BEP = 10000000
avatar.BEP = 10000000
obj.Definition.Packet.DetailedConstructorData(obj) match {
case Success(_) =>
ok
@ -214,7 +215,7 @@ class ConverterTest extends Specification {
}
"convert to simple packet (BR < 24)" in {
obj.BEP = 0
avatar.BEP = 0
converter.DetailedConstructorData(obj) match {
case Success(_) =>
ok
@ -226,7 +227,7 @@ class ConverterTest extends Specification {
}
"convert to simple packet (BR >= 24)" in {
obj.BEP = 10000000
avatar.BEP = 10000000
converter.DetailedConstructorData(obj) match {
case Success(_) =>
ok

View file

@ -2,9 +2,9 @@
package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.{GlobalDefinitions, Player}
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}
@ -13,7 +13,7 @@ import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration
class DoorTest extends Specification {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
"Door" should {
"construct" in {
@ -121,8 +121,8 @@ 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("test", faction, CharacterGender.Male, 0, 0), door)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), door)
}
}

View file

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

View file

@ -3,10 +3,9 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{GlobalDefinitions, Player}
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.terminals.{Terminal, TerminalControl, TerminalDefinition}
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}
@ -68,8 +67,8 @@ 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("test", faction, CharacterGender.Male, 0, 0), lock)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), lock)
}
}

View file

@ -4,193 +4,87 @@ package objects
import net.psforever.objects._
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import org.specs2.mutable._
class LoadoutTest extends Specification {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
def CreatePlayer() : Player = {
val
player = Player("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
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
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 : Player = 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().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 : Player = CreatePlayer()
obj.SaveLoadout("test", 0)
obj.Label mustEqual "test"
obj.ExoSuit mustEqual obj.ExoSuit
obj.Subtype mustEqual 0
obj.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 : Player = 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)
obj.SaveLoadout("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
}
obj.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 : Player = CreatePlayer()
obj.Inventory.Clear()
obj.SaveLoadout("test", 0)
obj.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 : Player = CreatePlayer()
obj.Slot(0).Equipment = None
obj.Slot(2).Equipment = None
obj.Slot(4).Equipment = None
obj.SaveLoadout("test", 0)
obj.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 : Player = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = ConstructionItem(advanced_ace)
obj.SaveLoadout("test", 0)
obj.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 : Player = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = Kit(medkit)
obj.SaveLoadout("test", 0)
obj.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 : Player = CreatePlayer()
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case None =>
ko
case Some(_) => ; //good; keep going
}
obj.DeleteLoadout(0)
obj.LoadLoadout(0) mustEqual None
}
"distinguish MAX subtype information" in {
val obj : Player = 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)
obj.SaveLoadout("generic", 0) //weaponless
slot.Equipment = None
slot.Equipment = Tool(trhev_dualcycler)
obj.SaveLoadout("cycler", 1)
slot.Equipment = None
slot.Equipment = Tool(trhev_pounder)
obj.SaveLoadout("pounder", 2)
slot.Equipment = None
slot.Equipment = Tool(trhev_burster)
obj.SaveLoadout("burster", 3)
obj.LoadLoadout(0).get.Subtype mustEqual 0
obj.LoadLoadout(1).get.Subtype mustEqual 1
obj.LoadLoadout(2).get.Subtype mustEqual 2
obj.LoadLoadout(3).get.Subtype mustEqual 3
}
ldout1.Subtype mustEqual 0
ldout2.Subtype mustEqual 1
ldout3.Subtype mustEqual 2
ldout4.Subtype mustEqual 3
}
}

View file

@ -2,7 +2,7 @@
package objects
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.Player
import net.psforever.objects.{Avatar, Player}
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.PlanetSideServerObject
@ -25,7 +25,7 @@ class MountableControl1Test extends ActorTest() {
class MountableControl2Test extends ActorTest() {
"MountableControl" should {
"let a player mount" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
val msg = Mountable.TryMount(player, 0)
@ -46,8 +46,8 @@ class MountableControl2Test extends ActorTest() {
class MountableControl3Test extends ActorTest() {
"MountableControl" should {
"block a player from mounting" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
obj.Actor ! Mountable.TryMount(player1, 0)

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize
@ -8,38 +9,53 @@ import net.psforever.packet.game.PlanetSideGUID
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 = new 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 {
(Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
(Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
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 = new 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 = new 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 = new 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 = new 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 = new 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 = new 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 = new 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 = new 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 = new 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 satore equipment" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Inventory.Resize(3,3)
"search for the smallest available slot in which to store equipment" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Inventory.Resize(3,3) //fits one item
obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
@ -176,46 +255,180 @@ class PlayerTest extends Specification {
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
}
"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)
"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
}
ok
} //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
}
"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)
"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
}
"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)
"command experience point values of the avatar" in {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
obj.UninstallImplant(testplant.Type)
obj.Implants(0).Installed mustEqual None
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 = new 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))
@ -224,7 +437,7 @@ class PlayerTest extends Specification {
}
"own in a vehicle" in {
val obj = new 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))
@ -233,27 +446,18 @@ class PlayerTest extends Specification {
}
"remember what zone he is in" in {
val obj = new 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"
}
"administrate" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Admin mustEqual false
Player.Administrate(obj, true)
obj.Admin mustEqual true
Player.Administrate(obj, false)
obj.Admin mustEqual false
}
"toString" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.toString mustEqual "TR Chord 0/100 0/50"
"spectate" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Spectator mustEqual false
Player.Spectate(obj, true)
obj.Spectator mustEqual true
Player.Spectate(obj, false)
obj.Spectator mustEqual false
obj.GUID = PlanetSideGUID(455)
obj.Continent = "z3"
obj.toString mustEqual "TR Chord z3-455 0/100 0/50"
}
}

View file

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

View file

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

View file

@ -3,10 +3,10 @@ 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.{GlobalDefinitions, Player, Vehicle}
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
@ -109,8 +109,8 @@ 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("test", faction, CharacterGender.Male, 0, 0), pad)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), pad)
}
}

View file

@ -2,17 +2,18 @@
package objects
import akka.actor.Props
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles._
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
import net.psforever.types.ExoSuitType
import org.specs2.mutable._
import scala.concurrent.duration.Duration
class VehicleTest extends Specification {
import VehicleTest._
"SeatDefinition" should {
val seat = new SeatDefinition
@ -73,7 +74,7 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1
seat.Occupant.isDefined mustEqual true
@ -84,13 +85,13 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1
seat.Occupant.isDefined mustEqual true
seat.Occupant.contains(player1) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(avatar1)
player2.ExoSuit = ExoSuitType.MAX
seat.Occupant = player2
seat.Occupant.isDefined mustEqual true
@ -101,13 +102,13 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1
seat.Occupant.isDefined mustEqual true
seat.Occupant.contains(player1) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(avatar2)
player2.ExoSuit = ExoSuitType.MAX
seat.Occupant = player2
seat.Occupant.isDefined mustEqual true
@ -160,7 +161,7 @@ class VehicleTest extends Specification {
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
fury_vehicle.Owner.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1)
fury_vehicle.Owner = player1
fury_vehicle.Owner.isDefined mustEqual true
@ -171,13 +172,13 @@ class VehicleTest extends Specification {
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
fury_vehicle.Owner.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1)
fury_vehicle.Owner = player1
fury_vehicle.Owner.isDefined mustEqual true
fury_vehicle.Owner.contains(PlanetSideGUID(1)) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(avatar2)
player2.GUID = PlanetSideGUID(2)
fury_vehicle.Owner = player2
fury_vehicle.Owner.isDefined mustEqual true
@ -234,9 +235,9 @@ class VehicleTest extends Specification {
"can find a passenger in a seat" in {
val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(avatar2)
player2.GUID = PlanetSideGUID(2)
harasser_vehicle.Seat(0).get.Occupant = player1 //don't worry about ownership for now
harasser_vehicle.Seat(1).get.Occupant = player2
@ -282,9 +283,9 @@ class VehicleTest extends Specification {
class VehicleControl1Test extends ActorTest {
"Vehicle Control" should {
"deactivate and stop handling mount messages" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(VehicleTest.avatar1)
player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(VehicleTest.avatar2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.GUID = PlanetSideGUID(3)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
@ -303,9 +304,9 @@ class VehicleControl1Test extends ActorTest {
class VehicleControl2Test extends ActorTest {
"Vehicle Control" should {
"reactivate and resume handling mount messages" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player1 = Player(VehicleTest.avatar1)
player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(VehicleTest.avatar2)
player2.GUID = PlanetSideGUID(2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.GUID = PlanetSideGUID(3)
@ -324,3 +325,10 @@ class VehicleControl2Test extends ActorTest {
}
}
}
object VehicleTest {
import net.psforever.objects.Avatar
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
}

View file

@ -1,16 +1,24 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorContext, ActorRef}
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{Actor, ActorContext, ActorRef, Props}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.objects._
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,422 @@ 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)
}
}
}
class ZoneGroundTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
item.GUID = PlanetSideGUID(10)
"ZoneGroundActor" should {
"drop item on ground" in {
val zone = new Zone("test", new ZoneMap(""), 0)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-item-test") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.EquipmentOnGround.isEmpty)
assert(item.Position == Vector3.Zero)
assert(item.Orientation == Vector3.Zero)
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
assert(item.Position == Vector3(1.1f, 2.2f, 3.3f))
assert(item.Orientation == Vector3(4.4f, 5.5f, 6.6f))
}
"get item from ground (success)" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10))
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround.isEmpty)
assert(reply.isInstanceOf[Zone.ItemFromGround])
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player)
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
}
"get item from ground (failure)" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid
expectNoMsg(Duration.create(500, "ms"))
}
}
}
object ZoneTest {
class ZoneInitActor(zone : Zone) extends Actor {
def receive : Receive = {
case "!" =>
zone.Init(context)
sender ! "!"
case _ => ;
}
}
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
@ -10,7 +9,7 @@ import objects.ActorTest
class GUIDTaskRegister5Test extends ActorTest() {
"RegisterAvatar" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
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)

View file

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

View file

@ -9,7 +9,7 @@ import objects.ActorTest
class GUIDTaskUnregister5Test extends ActorTest() {
"UnregisterAvatar" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
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)

View file

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

View file

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

View file

@ -2,8 +2,8 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{GlobalDefinitions, Player}
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
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class AirVehicleTerminalTest extends Specification {
"Air_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,19 +2,19 @@
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.{GlobalDefinitions, Player}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._
import org.specs2.mutable.Specification
class CertTerminalTest extends Specification {
"Cert_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.cert_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{GlobalDefinitions, Player}
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
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class DropshipVehicleTerminalTest extends Specification {
"Dropship_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{GlobalDefinitions, Player}
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
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class GroundVehicleTerminalTest extends Specification {
"Ground_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{GlobalDefinitions, Player}
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
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class ImplantTerminalInterfaceTest extends Specification {
"Implant_Terminal_Interface" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -5,8 +5,9 @@ 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.{GlobalDefinitions, Player}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
import objects.ActorTest
import org.specs2.mutable.Specification
@ -44,7 +45,7 @@ class ImplantTerminalMechTest extends Specification {
}
"get passenger in a seat" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
obj.PassengerInSeat(player) mustEqual None
obj.Seats(0).Occupant = player
@ -89,7 +90,7 @@ class ImplantTerminalMechControl3Test extends ActorTest() {
"ImplantTerminalMechControl" should {
"block a player from mounting" in {
val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
mech.Actor ! Mountable.TryMount(player1, 0)
receiveOne(Duration.create(100, "ms")) //consume reply
@ -160,9 +161,9 @@ 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("test", faction, CharacterGender.Male, 0, 0), terminal)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
}
}

View file

@ -3,7 +3,7 @@ package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._
import org.specs2.mutable.Specification
@ -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
}
@ -54,7 +60,7 @@ class MatrixTerminalTest extends Specification {
}
"player can not buy (anything)" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()

View file

@ -2,10 +2,10 @@
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.{GlobalDefinitions, Player}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._
import org.specs2.mutable.Specification
@ -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 {
@ -47,14 +47,14 @@ class OrderTerminalABTest extends Specification {
}
"player can buy different armor ('lite_armor')" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
}
"player can buy max armor ('trhev_antiaircraft')" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
@ -62,10 +62,11 @@ class OrderTerminalABTest extends Specification {
//TODO loudout tests
"player can not load max loadout" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
player.SaveLoadout("test1", 0)
val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(avatar)
avatar.SaveLoadout(player, "test1", 0)
player.ExoSuit = ExoSuitType.MAX
player.SaveLoadout("test2", 1)
avatar.SaveLoadout(player, "test2", 1)
val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 0, PlanetSideGUID(0))
terminal.Request(player, msg1) mustEqual Terminal.InfantryLoadout(ExoSuitType.Standard, 0, Nil, Nil)

View file

@ -2,19 +2,19 @@
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, GlobalDefinitions, Player, Tool}
import net.psforever.objects.{AmmoBox, Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._
import org.specs2.mutable.Specification
class OrderTerminalTest extends Specification {
"Order_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.order_terminal)
terminal.Owner = new Building(0, Zone.Nowhere)
terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in {

View file

@ -2,10 +2,10 @@
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.{GlobalDefinitions, Player}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._
import objects.ActorTest
@ -122,8 +122,8 @@ 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("test", faction, CharacterGender.Male, 0, 0), terminal)
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
}
}

View file

@ -2,8 +2,8 @@
package objects.terminal
import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.{GlobalDefinitions, Player}
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
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class VehicleTerminalCombinedTest extends Specification {
"Ground_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
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 {

View file

@ -1,14 +1,14 @@
// Copyright (c) 2017 PSForever
import net.psforever.objects.zones.ZoneMap
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.serverobject.ServerObjectBuilder
import net.psforever.objects.serverobject.doors.Door
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
object Maps {
@ -23,16 +23,362 @@ object Maps {
val map5 = new ZoneMap("map05")
val map6 = new ZoneMap("map06") {
//TODO TEST ceryshen
LocalObject(ServerObjectBuilder(3353, Terminal.Constructor(ground_vehicle_terminal)))
LocalObject(ServerObjectBuilder(500,
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 268.0f), Vector3(0f, 0f, 180.0f))
)) //TODO guid not correct
Building2()
Building38()
Building42()
Building48()
Building49()
LocalBuilding(2, FoundationBuilder(Building.Structure))
ObjectToBuilding(3353, 2)
ObjectToBuilding(500, 2)
TerminalToSpawnPad(3353, 500)
def Building2() : Unit = {
//Anguta
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(3974.2344f, 4287.914f, 0))))
LocalObject(222, Door.Constructor) //air term building, bay door
LocalObject(370, Door.Constructor) //courtyard
LocalObject(371, Door.Constructor) //courtyard
LocalObject(372, Door.Constructor) //courtyard
LocalObject(373, Door.Constructor) //courtyard
LocalObject(375, Door.Constructor(Vector3(3924.0f, 4231.2656f, 271.82812f), Vector3(0, 0, 180))) //2nd level door, south
LocalObject(376, Door.Constructor(Vector3(3924.0f, 4240.2656f, 271.82812f), Vector3(0, 0, 0))) //2nd level door, north
LocalObject(383, Door.Constructor) //courtyard
LocalObject(384, Door.Constructor(Vector3(3939.6328f, 4232.547f, 279.26562f), Vector3(0, 0, 270))) //3rd floor door
LocalObject(385, Door.Constructor) //courtyard
LocalObject(387, Door.Constructor(Vector3(3951.9531f, 4260.008f, 271.82812f), Vector3(0, 0, 270))) //2nd level door, stairwell
LocalObject(391, Door.Constructor) //courtyard
LocalObject(393, Door.Constructor(Vector3(3997.8984f, 4344.3203f, 271.8125f), Vector3(0, 0, 0))) //air term building, upstairs door
LocalObject(394, Door.Constructor(Vector3(3999.9766f, 4314.3203f, 266.82812f), Vector3(0, 0, 270))) //air term building, f.door
LocalObject(396, Door.Constructor) //courtyard
LocalObject(398, Door.Constructor) //courtyard
LocalObject(399, Door.Constructor) //courtyard
LocalObject(402, Door.Constructor) //courtyard
LocalObject(403, Door.Constructor) //courtyard
LocalObject(404, Door.Constructor(Vector3(4060.0078f, 4319.9766f, 266.8125f), Vector3(0, 0, 0))) //b.door
LocalObject(603, Door.Constructor)
LocalObject(604, Door.Constructor)
LocalObject(605, Door.Constructor)
LocalObject(606, Door.Constructor)
LocalObject(607, Door.Constructor)
LocalObject(610, Door.Constructor)
LocalObject(611, Door.Constructor)
LocalObject(614, Door.Constructor)
LocalObject(619, Door.Constructor)
LocalObject(620, Door.Constructor(Vector3(3983.9531f, 4299.992f, 249.29688f), Vector3(0, 0, 90))) //generator room door
LocalObject(621, Door.Constructor)
LocalObject(622, Door.Constructor(Vector3(3988.0078f, 4248.0156f, 256.82812f), Vector3(0, 0, 180))) //spawn room door
LocalObject(623, Door.Constructor(Vector3(3988.0078f, 4271.9766f, 256.79688f), Vector3(0, 0, 0))) //spawn room door
LocalObject(630, Door.Constructor(Vector3(4000.0078f, 4252.0f, 249.29688f), Vector3(0, 0, 270))) //spawn room door
LocalObject(631, Door.Constructor) //spawn room door, kitchen
LocalObject(634, Door.Constructor) //air term building, interior
LocalObject(638, Door.Constructor(Vector3(4016.0078f, 4212.008f, 249.29688f), Vector3(0, 0, 270))) //cc door
LocalObject(642, Door.Constructor(Vector3(4023.9844f, 4212.008f, 249.32812f), Vector3(0, 0, 90))) //cc door, interior
LocalObject(643, Door.Constructor) //cc door, exterior
LocalObject(645, Door.Constructor) //b.door, interior
LocalObject(646, Door.Constructor) //b.door, interior
LocalObject(715, Door.Constructor(Vector3(3961.5938f ,4235.8125f, 266.84375f), Vector3(0, 0, 90))) //f.door
LocalObject(751, IFFLock.Constructor)
LocalObject(860, IFFLock.Constructor)
LocalObject(863, IFFLock.Constructor)
LocalObject(866, IFFLock.Constructor)
LocalObject(868, IFFLock.Constructor)
LocalObject(873, IFFLock.Constructor)
LocalObject(874, IFFLock.Constructor)
LocalObject(875, IFFLock.Constructor)
LocalObject(876, IFFLock.Constructor)
LocalObject(878, IFFLock.Constructor)
LocalObject(879, IFFLock.Constructor)
LocalObject(882, IFFLock.Constructor)
LocalObject(884, IFFLock.Constructor)
LocalObject(885, IFFLock.Constructor)
LocalObject(1177, Locker.Constructor)
LocalObject(1178, Locker.Constructor)
LocalObject(1179, Locker.Constructor)
LocalObject(1180, Locker.Constructor)
LocalObject(1181, Locker.Constructor)
LocalObject(1182, Locker.Constructor)
LocalObject(1183, Locker.Constructor)
LocalObject(1184, Locker.Constructor)
LocalObject(1185, Locker.Constructor)
LocalObject(1186, Locker.Constructor)
LocalObject(1187, Locker.Constructor)
LocalObject(1188, Locker.Constructor)
LocalObject(1564, Terminal.Constructor(order_terminal))
LocalObject(1568, Terminal.Constructor(order_terminal))
LocalObject(1569, Terminal.Constructor(order_terminal))
LocalObject(1570, Terminal.Constructor(order_terminal))
LocalObject(1571, Terminal.Constructor(order_terminal))
LocalObject(1576, Terminal.Constructor(order_terminal))
LocalObject(1577, Terminal.Constructor(order_terminal))
LocalObject(1578, Terminal.Constructor(order_terminal))
LocalObject(2145, SpawnTube.Constructor(Vector3(3980.4062f, 4252.7656f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2146, SpawnTube.Constructor(Vector3(3980.4062f, 4259.992f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2147, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2239, Terminal.Constructor(spawn_terminal))
LocalObject(2244, Terminal.Constructor(spawn_terminal))
LocalObject(2245, Terminal.Constructor(spawn_terminal))
LocalObject(2246, Terminal.Constructor(spawn_terminal))
LocalObject(2248, Terminal.Constructor(spawn_terminal))
LocalObject(2250, Terminal.Constructor(spawn_terminal))
LocalObject(2251, Terminal.Constructor(spawn_terminal))
LocalObject(2253, Terminal.Constructor(spawn_terminal))
LocalObject(2254, Terminal.Constructor(spawn_terminal))
LocalObject(2322, Door.Constructor) //spawn tube door
LocalObject(2323, Door.Constructor) //spawn tube door
LocalObject(2324, Door.Constructor) //spawn tube door
LocalObject(2419, Terminal.Constructor(ground_vehicle_terminal))
LocalObject(500,
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 267.75f), Vector3(0f, 0f, 180.0f))
) //TODO guid not correct
LocalObject(224, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(501,
VehicleSpawnPad.Constructor(Vector3(4012.3594f, 4364.8047f, 271.90625f), Vector3(0f, 0f, 180.0f))
) //TODO guid not correct
ObjectToBuilding(222, 2)
ObjectToBuilding(224, 2)
ObjectToBuilding(370, 2)
ObjectToBuilding(371, 2)
ObjectToBuilding(372, 2)
ObjectToBuilding(373, 2)
ObjectToBuilding(375, 2)
ObjectToBuilding(376, 2)
ObjectToBuilding(383, 2)
ObjectToBuilding(384, 2)
ObjectToBuilding(385, 2)
ObjectToBuilding(387, 2)
ObjectToBuilding(391, 2)
ObjectToBuilding(393, 2)
ObjectToBuilding(394, 2)
ObjectToBuilding(396, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(399, 2)
ObjectToBuilding(402, 2)
ObjectToBuilding(403, 2)
ObjectToBuilding(404, 2)
ObjectToBuilding(603, 2)
ObjectToBuilding(604, 2)
ObjectToBuilding(605, 2)
ObjectToBuilding(606, 2)
ObjectToBuilding(607, 2)
ObjectToBuilding(610, 2)
ObjectToBuilding(611, 2)
ObjectToBuilding(614, 2)
ObjectToBuilding(619, 2)
ObjectToBuilding(620, 2)
ObjectToBuilding(621, 2)
ObjectToBuilding(622, 2)
ObjectToBuilding(623, 2)
ObjectToBuilding(630, 2)
ObjectToBuilding(631, 2)
ObjectToBuilding(634, 2)
ObjectToBuilding(638, 2)
ObjectToBuilding(642, 2)
ObjectToBuilding(643, 2)
ObjectToBuilding(645, 2)
ObjectToBuilding(646, 2)
ObjectToBuilding(715, 2)
ObjectToBuilding(751, 2)
ObjectToBuilding(860, 2)
ObjectToBuilding(863, 2)
ObjectToBuilding(866, 2)
ObjectToBuilding(868, 2)
ObjectToBuilding(873, 2)
ObjectToBuilding(874, 2)
ObjectToBuilding(875, 2)
ObjectToBuilding(876, 2)
ObjectToBuilding(878, 2)
ObjectToBuilding(879, 2)
ObjectToBuilding(882, 2)
ObjectToBuilding(884, 2)
ObjectToBuilding(885, 2)
ObjectToBuilding(1177, 2)
ObjectToBuilding(1178, 2)
ObjectToBuilding(1179, 2)
ObjectToBuilding(1180, 2)
ObjectToBuilding(1181, 2)
ObjectToBuilding(1182, 2)
ObjectToBuilding(1183, 2)
ObjectToBuilding(1184, 2)
ObjectToBuilding(1185, 2)
ObjectToBuilding(1186, 2)
ObjectToBuilding(1187, 2)
ObjectToBuilding(1188, 2)
ObjectToBuilding(1564, 2)
ObjectToBuilding(1568, 2)
ObjectToBuilding(1569, 2)
ObjectToBuilding(1570, 2)
ObjectToBuilding(1571, 2)
ObjectToBuilding(1576, 2)
ObjectToBuilding(1577, 2)
ObjectToBuilding(1578, 2)
ObjectToBuilding(2145, 2)
ObjectToBuilding(2146, 2)
ObjectToBuilding(2147, 2)
ObjectToBuilding(2239, 2)
ObjectToBuilding(2244, 2)
ObjectToBuilding(2245, 2)
ObjectToBuilding(2246, 2)
ObjectToBuilding(2248, 2)
ObjectToBuilding(2250, 2)
ObjectToBuilding(2251, 2)
ObjectToBuilding(2253, 2)
ObjectToBuilding(2254, 2)
ObjectToBuilding(2322, 2)
ObjectToBuilding(2323, 2)
ObjectToBuilding(2324, 2)
ObjectToBuilding(2419, 2)
ObjectToBuilding(500, 2)
ObjectToBuilding(501, 2)
DoorToLock(375, 863)
DoorToLock(376, 860)
DoorToLock(384, 866)
DoorToLock(387, 868)
DoorToLock(393, 876)
DoorToLock(394, 879)
DoorToLock(404, 885)
DoorToLock(620, 873)
DoorToLock(622, 876)
DoorToLock(623, 874)
DoorToLock(630, 878)
DoorToLock(638, 882)
DoorToLock(642, 884)
DoorToLock(715, 751)
TerminalToSpawnPad(224, 501)
TerminalToSpawnPad(2419, 500)
}
def Building38() : Unit = {
//Anguta, West Bunker
LocalBuilding(38, FoundationBuilder(Building.Structure(StructureType.Bunker)))
LocalObject(362, Door.Constructor)
ObjectToBuilding(362, 38)
}
def Building42() : Unit = {
//Anguta, East Bunker(s)
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Bunker)))
LocalObject(407, Door.Constructor)
LocalObject(408, Door.Constructor)
ObjectToBuilding(407, 42)
ObjectToBuilding(408, 42)
}
def Building48() : Unit = {
//North Anguta Watchtower
LocalBuilding(48, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(3864.2266f, 4518.0234f, 0))))
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
LocalObject(367, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 269.65625f), Vector3(0f, 0f, 0f))) //n1
LocalObject(368, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 279.57812f), Vector3(0f, 0f, 0f))) //n2
LocalObject(369, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 299.57812f), Vector3(0f, 0f, 0f))) //n3
LocalObject(854, IFFLock.Constructor)
LocalObject(855, IFFLock.Constructor)
LocalObject(856, IFFLock.Constructor)
LocalObject(857, IFFLock.Constructor)
LocalObject(858, IFFLock.Constructor)
LocalObject(859, IFFLock.Constructor)
LocalObject(1140, Locker.Constructor)
LocalObject(1141, Locker.Constructor)
LocalObject(1142, Locker.Constructor)
LocalObject(1143, Locker.Constructor)
LocalObject(1144, Locker.Constructor)
LocalObject(1145, Locker.Constructor)
LocalObject(1146, Locker.Constructor)
LocalObject(1147, Locker.Constructor)
LocalObject(1561, Terminal.Constructor(order_terminal))
LocalObject(1562, Terminal.Constructor(order_terminal))
LocalObject(1563, Terminal.Constructor(order_terminal))
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)
ObjectToBuilding(365, 48)
ObjectToBuilding(366, 48)
ObjectToBuilding(367, 48)
ObjectToBuilding(368, 48)
ObjectToBuilding(369, 48)
ObjectToBuilding(854, 48)
ObjectToBuilding(855, 48)
ObjectToBuilding(856, 48)
ObjectToBuilding(857, 48)
ObjectToBuilding(858, 48)
ObjectToBuilding(859, 48)
ObjectToBuilding(1140, 48)
ObjectToBuilding(1141, 48)
ObjectToBuilding(1142, 48)
ObjectToBuilding(1143, 48)
ObjectToBuilding(1144, 48)
ObjectToBuilding(1145, 48)
ObjectToBuilding(1146, 48)
ObjectToBuilding(1147, 48)
ObjectToBuilding(1561, 48)
ObjectToBuilding(1562, 48)
ObjectToBuilding(1563, 48)
ObjectToBuilding(2138, 48)
ObjectToBuilding(2139, 48)
ObjectToBuilding(2315, 48)
ObjectToBuilding(2316, 48)
DoorToLock(364, 857)
DoorToLock(365, 858)
DoorToLock(366, 859)
DoorToLock(367, 854)
DoorToLock(368, 855)
DoorToLock(369, 856)
}
def Building49() : Unit = {
//North Akna Air Tower
LocalBuilding(49, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4358.3203f, 3989.5625f, 0))))
LocalObject(430, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 237.96875f), Vector3(0f, 0f, 180f))) //s1
LocalObject(431, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 257.89062f), Vector3(0f, 0f, 180f))) //s2
LocalObject(432, Door.Constructor(Vector3(4366.0156f, 3997.9297f, 237.96875f), Vector3(0f, 0f, 0f))) //n1
LocalObject(433, Door.Constructor(Vector3(4366.0156f, 3997.9297f, 257.89062f), Vector3(0f, 0f, 0f))) //n2
LocalObject(902, IFFLock.Constructor)
LocalObject(903, IFFLock.Constructor)
LocalObject(906, IFFLock.Constructor)
LocalObject(907, IFFLock.Constructor)
LocalObject(1217, Locker.Constructor)
LocalObject(1218, Locker.Constructor)
LocalObject(1219, Locker.Constructor)
LocalObject(1220, Locker.Constructor)
LocalObject(1225, Locker.Constructor)
LocalObject(1226, Locker.Constructor)
LocalObject(1227, Locker.Constructor)
LocalObject(1228, Locker.Constructor)
LocalObject(1591, Terminal.Constructor(order_terminal))
LocalObject(1592, Terminal.Constructor(order_terminal))
LocalObject(1593, Terminal.Constructor(order_terminal))
LocalObject(2156, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3994.125f, 228.1875f), Vector3(0, 0, 90)))
LocalObject(2157, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3977.7266f, 228.1875f), Vector3(0, 0, 90)))
LocalObject(2333, Door.Constructor) //spawn tube door
LocalObject(2334, Door.Constructor) //spawn tube door
ObjectToBuilding(430, 49)
ObjectToBuilding(431, 49)
ObjectToBuilding(432, 49)
ObjectToBuilding(433, 49)
ObjectToBuilding(902, 49)
ObjectToBuilding(903, 49)
ObjectToBuilding(906, 49)
ObjectToBuilding(907, 49)
ObjectToBuilding(1217, 49)
ObjectToBuilding(1218, 49)
ObjectToBuilding(1219, 49)
ObjectToBuilding(1220, 49)
ObjectToBuilding(1225, 49)
ObjectToBuilding(1226, 49)
ObjectToBuilding(1227, 49)
ObjectToBuilding(1228, 49)
ObjectToBuilding(1591, 49)
ObjectToBuilding(1592, 49)
ObjectToBuilding(1593, 49)
ObjectToBuilding(2156, 49)
ObjectToBuilding(2157, 49)
ObjectToBuilding(2333, 49)
ObjectToBuilding(2334, 49)
DoorToLock(430, 906)
DoorToLock(431, 907)
DoorToLock(432, 902)
DoorToLock(433, 903)
}
}
val map7 = new ZoneMap("map07")
@ -48,154 +394,241 @@ object Maps {
val map12 = new ZoneMap("map12")
val map13 = new ZoneMap("map13") {
LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
LocalBuilding(2, FoundationBuilder(WarpGate.Structure))
LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
Building1()
Building2()
Building3()
Building29()
Building42()
Building51()
Building77()
LocalObject(ServerObjectBuilder(372, Door.Constructor))
LocalObject(ServerObjectBuilder(373, Door.Constructor))
def Building1() : Unit = {
//warpgate?
LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
}
LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
TerminalToInterface(520, 1081)
def Building3() : Unit = {
//warpgate?
LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
}
LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C
LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(362, Door.Constructor))
LocalObject(ServerObjectBuilder(370, Door.Constructor))
LocalObject(ServerObjectBuilder(371, Door.Constructor))
LocalObject(ServerObjectBuilder(374, Door.Constructor))
LocalObject(ServerObjectBuilder(375, Door.Constructor))
LocalObject(ServerObjectBuilder(394, Door.Constructor))
LocalObject(ServerObjectBuilder(395, Door.Constructor))
LocalObject(ServerObjectBuilder(396, Door.Constructor))
LocalObject(ServerObjectBuilder(397, Door.Constructor))
LocalObject(ServerObjectBuilder(398, Door.Constructor))
LocalObject(ServerObjectBuilder(462, Door.Constructor))
LocalObject(ServerObjectBuilder(463, Door.Constructor))
LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(524, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(525, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(526, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(527, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(529, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(686, Locker.Constructor))
LocalObject(ServerObjectBuilder(687, Locker.Constructor))
LocalObject(ServerObjectBuilder(688, Locker.Constructor))
LocalObject(ServerObjectBuilder(689, Locker.Constructor))
LocalObject(ServerObjectBuilder(690, Locker.Constructor))
LocalObject(ServerObjectBuilder(691, Locker.Constructor))
LocalObject(ServerObjectBuilder(692, Locker.Constructor))
LocalObject(ServerObjectBuilder(693, Locker.Constructor))
LocalObject(ServerObjectBuilder(842, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(845, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(1082, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1083, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1084, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1085, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1086, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1087, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1088, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1089, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
ObjectToBuilding(522, 2)
ObjectToBuilding(523, 2)
ObjectToBuilding(524, 2)
ObjectToBuilding(525, 2)
ObjectToBuilding(526, 2)
ObjectToBuilding(527, 2)
ObjectToBuilding(528, 2)
ObjectToBuilding(529, 2)
ObjectToBuilding(686, 2)
ObjectToBuilding(687, 2)
ObjectToBuilding(688, 2)
ObjectToBuilding(689, 2)
ObjectToBuilding(690, 2)
ObjectToBuilding(691, 2)
ObjectToBuilding(692, 2)
ObjectToBuilding(693, 2)
ObjectToBuilding(842, 2)
ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2)
ObjectToBuilding(1082, 2)
ObjectToBuilding(1083, 2)
ObjectToBuilding(1084, 2)
ObjectToBuilding(1085, 2)
ObjectToBuilding(1086, 2)
ObjectToBuilding(1087, 2)
ObjectToBuilding(1088, 2)
ObjectToBuilding(1089, 2)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
TerminalToInterface(525, 1085)
TerminalToInterface(526, 1086)
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
// LocalBuilding(2, FoundationBuilder(WarpGate.Structure)) //TODO might be wrong?
LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower
LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180))))
LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180))))
LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
ObjectToBuilding(330, 29)
ObjectToBuilding(331, 29)
ObjectToBuilding(332, 29)
ObjectToBuilding(333, 29)
ObjectToBuilding(556, 29)
ObjectToBuilding(557, 29)
ObjectToBuilding(558, 29)
ObjectToBuilding(559, 29)
DoorToLock(330, 558)
DoorToLock(331, 559)
DoorToLock(332, 556)
DoorToLock(333, 557)
// LocalObject(520, ImplantTerminalMech.Constructor) //Hart B
// LocalObject(1081, Terminal.Constructor(implant_terminal_interface)) //tube 520
// TerminalToInterface(520, 1081)
LocalBuilding(51, FoundationBuilder(Building.Structure))
LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal)))
LocalObject(ServerObjectBuilder(292,
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
))
ObjectToBuilding(304, 51)
ObjectToBuilding(292, 51)
TerminalToSpawnPad(304, 292)
def Building2() : Unit = {
//HART building C
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
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(374, Door.Constructor)
LocalObject(375, Door.Constructor)
LocalObject(394, Door.Constructor)
LocalObject(395, Door.Constructor)
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)
LocalObject(525, ImplantTerminalMech.Constructor)
LocalObject(526, ImplantTerminalMech.Constructor)
LocalObject(527, ImplantTerminalMech.Constructor)
LocalObject(528, ImplantTerminalMech.Constructor)
LocalObject(529, ImplantTerminalMech.Constructor)
LocalObject(686, Locker.Constructor)
LocalObject(687, Locker.Constructor)
LocalObject(688, Locker.Constructor)
LocalObject(689, Locker.Constructor)
LocalObject(690, Locker.Constructor)
LocalObject(691, Locker.Constructor)
LocalObject(692, Locker.Constructor)
LocalObject(693, Locker.Constructor)
LocalObject(842, Terminal.Constructor(order_terminal))
LocalObject(843, Terminal.Constructor(order_terminal))
LocalObject(844, Terminal.Constructor(order_terminal))
LocalObject(845, Terminal.Constructor(order_terminal))
LocalObject(1082, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1083, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1084, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1085, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1086, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1087, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1088, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1089, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
ObjectToBuilding(362, 2)
ObjectToBuilding(370, 2)
ObjectToBuilding(371, 2)
ObjectToBuilding(374, 2)
ObjectToBuilding(375, 2)
ObjectToBuilding(394, 2)
ObjectToBuilding(395, 2)
ObjectToBuilding(396, 2)
ObjectToBuilding(397, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(522, 2)
ObjectToBuilding(523, 2)
ObjectToBuilding(524, 2)
ObjectToBuilding(525, 2)
ObjectToBuilding(526, 2)
ObjectToBuilding(527, 2)
ObjectToBuilding(528, 2)
ObjectToBuilding(529, 2)
ObjectToBuilding(686, 2)
ObjectToBuilding(687, 2)
ObjectToBuilding(688, 2)
ObjectToBuilding(689, 2)
ObjectToBuilding(690, 2)
ObjectToBuilding(691, 2)
ObjectToBuilding(692, 2)
ObjectToBuilding(693, 2)
ObjectToBuilding(842, 2)
ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2)
ObjectToBuilding(1082, 2)
ObjectToBuilding(1083, 2)
ObjectToBuilding(1084, 2)
ObjectToBuilding(1085, 2)
ObjectToBuilding(1086, 2)
ObjectToBuilding(1087, 2)
ObjectToBuilding(1088, 2)
ObjectToBuilding(1089, 2)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
TerminalToInterface(525, 1085)
TerminalToInterface(526, 1086)
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
}
LocalBuilding(77, FoundationBuilder(Building.Structure))
LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal)))
LocalObject(ServerObjectBuilder(706,
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
))
ObjectToBuilding(1063, 77)
ObjectToBuilding(706, 77)
TerminalToSpawnPad(1063, 706)
def Building29() : Unit = {
//South Villa Gun Tower
LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.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)))
LocalObject(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0)))
LocalObject(556, IFFLock.Constructor)
LocalObject(557, IFFLock.Constructor)
LocalObject(558, IFFLock.Constructor)
LocalObject(559, IFFLock.Constructor)
ObjectToBuilding(330, 29)
ObjectToBuilding(331, 29)
ObjectToBuilding(332, 29)
ObjectToBuilding(333, 29)
ObjectToBuilding(556, 29)
ObjectToBuilding(557, 29)
ObjectToBuilding(558, 29)
ObjectToBuilding(559, 29)
DoorToLock(330, 558)
DoorToLock(331, 559)
DoorToLock(332, 556)
DoorToLock(333, 557)
}
ObjectToBuilding(853, 2) //TODO check building_id
ObjectToBuilding(855, 2) //TODO check building_id
ObjectToBuilding(860, 2) //TODO check building_id
def Building42() : Unit = {
//spawn building south of HART C
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0))))
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.9f), Vector3(0, 0, 180)))
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.75f, 91.9f), Vector3(0, 0, 0)))
LocalObject(746, SpawnTube.Constructor(Vector3(3690.9062f, 2708.4219f, 91.9f), Vector3(0, 0, 180)))
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.8672f, 91.9f), Vector3(0, 0, 0)))
LocalObject(748, SpawnTube.Constructor(Vector3(3697.664f, 2708.3984f, 91.9f), Vector3(0, 0, 180)))
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.9f), 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)
}
def Building51() : Unit = {
//air terminal west of HART C
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform)))
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(292,
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
)
ObjectToBuilding(304, 51)
ObjectToBuilding(292, 51)
TerminalToSpawnPad(304, 292)
}
def Building77() : Unit = {
//ground terminal west of HART C
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform)))
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
LocalObject(706,
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
)
ObjectToBuilding(1063, 77)
ObjectToBuilding(706, 77)
TerminalToSpawnPad(1063, 706)
}
}
val map14 = new ZoneMap("map13")
val map14 = new ZoneMap("map14")
val map15 = new ZoneMap("map13")
val map15 = new ZoneMap("map15")
val map16 = new ZoneMap("map13")
val map16 = new ZoneMap("map16")
val ugd01 = new ZoneMap("ugd01")
@ -209,7 +642,7 @@ object Maps {
val ugd06 = new ZoneMap("ugd06")
val map96 = new ZoneMap("ugd06")
val map96 = new ZoneMap("map96")
val map97 = new ZoneMap("map97")

File diff suppressed because it is too large Load diff

View file

@ -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)
@ -19,6 +20,13 @@ object Zones {
import net.psforever.types.PlanetSideEmpire
Building(2).get.Faction = PlanetSideEmpire.VS
Building(2).get.ModelId = 20
Building(38).get.ModelId = 0
Building(42).get.ModelId = 0
Building(48).get.Faction = PlanetSideEmpire.VS
Building(48).get.ModelId = 59
Building(49).get.Faction = PlanetSideEmpire.VS
Building(49).get.ModelId = 69
}
}
@ -39,7 +47,7 @@ object Zones {
super.Init(context)
import net.psforever.types.PlanetSideEmpire
Buildings.values.foreach(building => { building.Faction = PlanetSideEmpire.VS })
Buildings.values.foreach { _.Faction = PlanetSideEmpire.VS }
Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
}
}
@ -81,4 +89,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.NC => "home1"
case PlanetSideEmpire.TR => "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.NC => 11
case PlanetSideEmpire.TR => 12
case PlanetSideEmpire.VS => 13
case PlanetSideEmpire.NEUTRAL => 0 //invalid, not black ops
}
}
}

View file

@ -1,7 +1,9 @@
// Copyright (c) 2017 PSForever
package services.avatar
import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.types.{ExoSuitType, Vector3}
@ -24,6 +26,7 @@ object AvatarAction {
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
final case class Release(player : Player, zone : Zone, time : Option[Long] = None) extends Action
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package services.avatar
import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData
@ -24,6 +25,7 @@ object AvatarResponse {
final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
final case class Release(player : Player) extends Response
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response

View file

@ -1,11 +1,14 @@
// Copyright (c) 2017 PSForever
package services.avatar
import akka.actor.Actor
import akka.actor.{Actor, ActorRef, Props}
import services.avatar.support.CorpseRemovalActor
import services.{GenericEventBus, Service}
class AvatarService extends Actor {
//import AvatarServiceResponse._
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
undertaker ! "startup"
private [this] val log = org.log4s.getLogger
override def preStart = {
@ -87,6 +90,14 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
)
case AvatarAction.Release(player, zone, time) =>
undertaker ! (time match {
case Some(t) => CorpseRemovalActor.AddCorpse(player, zone, t)
case None => CorpseRemovalActor.AddCorpse(player, zone)
})
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
)
case AvatarAction.Reload(player_guid, weapon_guid) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
@ -95,9 +106,14 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid))
)
case _ => ;
}
//message to Undertaker
case AvatarServiceMessage.RemoveSpecificCorpse(corpses) =>
undertaker ! AvatarServiceMessage.RemoveSpecificCorpse( corpses.filter(corpse => {corpse.HasGUID && corpse.isBackpack}) )
/*
case AvatarService.PlayerStateMessage(msg) =>
// log.info(s"NEW: ${m}")

View file

@ -1,4 +1,10 @@
// Copyright (c) 2017 PSForever
package services.avatar
import net.psforever.objects.Player
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
object AvatarServiceMessage {
final case class RemoveSpecificCorpse(corpse : List[Player])
}

View file

@ -0,0 +1,199 @@
// Copyright (c) 2017 PSForever
package services.avatar.support
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.{DefaultCancellable, Player}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
import services.{Service, ServiceManager}
import services.ServiceManager.Lookup
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.annotation.tailrec
import scala.concurrent.duration._
class CorpseRemovalActor extends Actor {
private var burial : Cancellable = DefaultCancellable.obj
private var corpses : List[CorpseRemovalActor.Entry] = List()
private var taskResolver : ActorRef = Actor.noSender
private[this] val log = org.log4s.getLogger
override def postStop() = {
//Cart Master: See you on Thursday.
corpses.foreach { BurialTask }
corpses = Nil
}
def receive : Receive = {
case "startup" =>
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
case ServiceManager.LookupResult("taskResolver", endpoint) =>
//Cart Master: Bring out your dead!
taskResolver = endpoint
context.become(Processing)
case _ => ;
}
def Processing : Receive = {
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
if(corpse.isBackpack) {
if(corpses.isEmpty) {
//we were the only entry so the event must be started from scratch
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
RetimeFirstTask()
}
else {
//unknown number of entries; append, sort, then re-time tasking
val oldHead = corpses.head
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
if(oldHead != corpses.head) {
RetimeFirstTask()
}
}
}
else {
//Cart Master: 'Ere. He says he's not dead!
log.warn(s"$corpse does not qualify as a corpse; ignored queueing request")
}
case AvatarServiceMessage.RemoveSpecificCorpse(targets) =>
if(targets.nonEmpty) {
//Cart Master: No, I've got to go to the Robinsons'. They've lost nine today.
burial.cancel
if(targets.size == 1) {
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
//simple selection
CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
case None => ;
case Some(index) =>
BurialTask(corpses(index))
corpses = corpses.take(index) ++ corpses.drop(index+1)
}
}
else {
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
//cumbersome partition
//a - find targets from corpses
(for {
a <- targets
b <- corpses
if b.corpse == a &&
b.corpse.Continent.equals(a.Continent) &&
b.corpse.HasGUID && a.HasGUID && b.corpse.GUID == a.GUID
} yield b).foreach { BurialTask }
//b - corpses after the found targets are
//removed (note: cull any non-GUID entries while at it)
corpses = (for {
a <- targets
b <- corpses
if b.corpse.HasGUID && a.HasGUID &&
(b.corpse != a ||
!b.corpse.Continent.equals(a.Continent) ||
!b.corpse.HasGUID || !a.HasGUID || b.corpse.GUID != a.GUID)
} yield b).sortBy(_.timeAlive)
}
RetimeFirstTask()
}
case CorpseRemovalActor.Dispose() =>
burial.cancel
val now : Long = System.nanoTime
val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive })
corpses = rotting
buried.foreach { BurialTask }
RetimeFirstTask()
case CorpseRemovalActor.FailureToWork(target, zone, ex) =>
//Cart Master: Oh, I can't take him like that. It's against regulations.
log.error(s"corpse $target from $zone not properly unregistered - $ex")
case _ => ;
}
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
//Cart Master: Thursday.
burial.cancel
if(corpses.nonEmpty) {
val short_timeout : FiniteDuration = math.max(1, corpses.head.timeAlive - (now - corpses.head.time)) nanoseconds
import scala.concurrent.ExecutionContext.Implicits.global
burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.Dispose())
}
}
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
//Cart master: Nine pence.
val target = entry.corpse
val zone = entry.zone
target.Position = Vector3.Zero //somewhere it will not disturb anything
entry.zone.Population ! Zone.Corpse.Remove(target)
context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
taskResolver ! BurialTask(target, zone)
}
def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
import net.psforever.objects.guid.{GUIDTask, Task}
TaskResolver.GiveTask (
new Task() {
private val localCorpse = corpse
private val localZone = zone
private val localAnnounce = self
override def isComplete : Task.Resolution.Value = if(!localCorpse.HasGUID) {
Task.Resolution.Success
}
else {
Task.Resolution.Incomplete
}
def Execute(resolver : ActorRef) : Unit = {
resolver ! scala.util.Success(this)
}
override def onFailure(ex : Throwable): Unit = {
localAnnounce ! CorpseRemovalActor.FailureToWork(localCorpse, localZone, ex)
}
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
)
}
}
object CorpseRemovalActor {
final val time : Long = 180000000000L //3 min (180s)
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = CorpseRemovalActor.time)
final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime())
final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
final case class Dispose()
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` 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[CorpseRemovalActor.Entry], player : Player, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val corpse = iter.next.corpse
if(corpse == player && corpse.Continent.equals(player.Continent) && corpse.GUID == player.GUID) {
Some(index)
}
else {
recursiveFindCorpse(iter, player, index + 1)
}
}
}
}

View file

@ -7,6 +7,7 @@ import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.vehicles.Seat
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3
import services.ServiceManager
import services.ServiceManager.Lookup
import services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -80,6 +81,7 @@ class DeconstructionActor extends Actor {
vehiclesToScrap.foreach(entry => {
val vehicle = entry.vehicle
val zone = entry.zone
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager

View file

@ -1,15 +1,21 @@
// Copyright (c) 2017 PSForever
import akka.actor.Props
import akka.routing.RandomPool
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
import services.Service
import services.{Service, ServiceManager}
import services.avatar._
import scala.concurrent.duration._
class AvatarService1Test extends ActorTest {
"AvatarService" should {
"construct" in {
system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
assert(true)
}
}
@ -18,7 +24,8 @@ class AvatarService1Test extends ActorTest {
class AvatarService2Test extends ActorTest {
"AvatarService" should {
"subscribe" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
assert(true)
}
@ -27,8 +34,9 @@ class AvatarService2Test extends ActorTest {
class AvatarService3Test extends ActorTest {
"AvatarService" should {
ServiceManager.boot(system)
"subscribe to a specific channel" in {
val service = system.actorOf(Props[AvatarService], "service")
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! Service.Leave()
assert(true)
@ -39,7 +47,8 @@ class AvatarService3Test extends ActorTest {
class AvatarService4Test extends ActorTest {
"AvatarService" should {
"subscribe" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! Service.LeaveAll()
assert(true)
@ -50,7 +59,8 @@ class AvatarService4Test extends ActorTest {
class AvatarService5Test extends ActorTest {
"AvatarService" should {
"pass an unhandled message" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! "hello"
expectNoMsg()
@ -61,7 +71,8 @@ class AvatarService5Test extends ActorTest {
class ArmorChangedTest extends ActorTest {
"AvatarService" should {
"pass ArmorChanged" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ArmorChanged(PlanetSideGUID(10), ExoSuitType.Reinforced, 0))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ArmorChanged(ExoSuitType.Reinforced, 0)))
@ -72,7 +83,8 @@ class ArmorChangedTest extends ActorTest {
class ConcealPlayerTest extends ActorTest {
"AvatarService" should {
"pass ConcealPlayer" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ConcealPlayer(PlanetSideGUID(10)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ConcealPlayer()))
@ -85,7 +97,8 @@ class EquipmentInHandTest extends ActorTest {
"AvatarService" should {
"pass EquipmentInHand" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), 2, tool))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(2, tool)))
@ -101,7 +114,8 @@ class EquipmentOnGroundTest extends ActorTest {
"AvatarService" should {
"pass EquipmentOnGround" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)))
@ -110,14 +124,15 @@ class EquipmentOnGroundTest extends ActorTest {
}
class LoadPlayerTest extends ActorTest {
val obj = Player("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1)
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
val pdata = obj.Definition.Packet.DetailedConstructorData(obj).get
"AvatarService" should {
"pass LoadPlayer" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata)))
@ -128,7 +143,8 @@ class LoadPlayerTest extends ActorTest {
class ObjectDeleteTest extends ActorTest {
"AvatarService" should {
"pass ObjectDelete" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(10), PlanetSideGUID(11)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(PlanetSideGUID(11), 0)))
@ -142,7 +158,8 @@ class ObjectDeleteTest extends ActorTest {
class ObjectHeldTest extends ActorTest {
"AvatarService" should {
"pass ObjectHeld" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ObjectHeld(PlanetSideGUID(10), 1))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectHeld(1)))
@ -153,7 +170,8 @@ class ObjectHeldTest extends ActorTest {
class PlanetsideAttributeTest extends ActorTest {
"AvatarService" should {
"pass PlanetsideAttribute" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.PlanetsideAttribute(PlanetSideGUID(10), 5, 1200L))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlanetsideAttribute(5, 1200L)))
@ -166,7 +184,8 @@ class PlayerStateTest extends ActorTest {
"AvatarService" should {
"pass PlayerState" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), msg, false, false))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(msg, false, false)))
@ -177,7 +196,8 @@ class PlayerStateTest extends ActorTest {
class ReloadTest extends ActorTest {
"AvatarService" should {
"pass Reload" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40))))
@ -191,7 +211,8 @@ class ChangeAmmoTest extends ActorTest {
"AvatarService" should {
"pass ChangeAmmo" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeAmmo(PlanetSideGUID(10), PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeAmmo(PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get)))
@ -205,7 +226,8 @@ class ChangeFireModeTest extends ActorTest {
"AvatarService" should {
"pass ChangeFireMode" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireMode(PlanetSideGUID(10), PlanetSideGUID(40), 0))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireMode(PlanetSideGUID(40), 0)))
@ -216,7 +238,8 @@ class ChangeFireModeTest extends ActorTest {
class ChangeFireStateStartTest extends ActorTest {
"AvatarService" should {
"pass ChangeFireState_Start" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Start(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Start(PlanetSideGUID(40))))
@ -227,7 +250,8 @@ class ChangeFireStateStartTest extends ActorTest {
class ChangeFireStateStopTest extends ActorTest {
"AvatarService" should {
"pass ChangeFireState_Stop" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Stop(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Stop(PlanetSideGUID(40))))
@ -238,7 +262,8 @@ class ChangeFireStateStopTest extends ActorTest {
class WeaponDryFireTest extends ActorTest {
"AvatarService" should {
"pass WeaponDryFire" in {
val service = system.actorOf(Props[AvatarService], "service")
ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.WeaponDryFire(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.WeaponDryFire(PlanetSideGUID(40))))
@ -246,6 +271,177 @@ class WeaponDryFireTest extends ActorTest {
}
}
object AvatarServiceTest {
//decoy
/*
Preparation for these three Release tests is involved.
The ServiceManager must not only be set up correctly, but must be given a TaskResolver.
The AvatarService is started and that starts CorpseRemovalActor, an essential part of this test.
The CorpseRemovalActor needs that TaskResolver created by the ServiceManager;
but, another independent TaskResolver will be needed for manual parts of the test.
(The ServiceManager's TaskResolver can be "borrowed" but that requires writing code to intercept it.)
The Zone needs to be set up and initialized properly with a ZoneActor.
The ZoneActor builds the GUID Actor and the ZonePopulationActor.
ALL of these Actors will talk to each other.
The lines of communication can short circuit if the next Actor does not have the correct information.
Putting Actor startup in the main class, outside of the body of the test, helps.
Frequent pauses to allow everything to sort their messages also helps.
Even with all this work, the tests have a high chance of failure just due to being asynchronous.
*/
class AvatarReleaseTest extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1000000000))) //alive for one second
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
val reply2 = receiveOne(2 seconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
class AvatarReleaseEarly1Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(obj)) //IMPORTANT: ONE ENTRY
val reply2 = receiveOne(100 milliseconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
class AvatarReleaseEarly2Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, 1)) //necessary clutter
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(objAlt, obj)) //IMPORTANT: TWO ENTRIES
val reply2 = receiveOne(100 milliseconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
object AvatarServiceTest {
import java.util.concurrent.atomic.AtomicInteger
private val number = new AtomicInteger(1)
def TestName : String = {
s"service${number.getAndIncrement()}"
}
}