mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-07 06:00:24 +00:00
Implant Terminals:
Implant terminals (mech) are now properly mountable and implant terminals (interface) are also properly interactive. Player can select to equip or to remove implants properly. Mountable: Vehicles and implant terminal mechs now use common Mountable logic. home3 Hart C: All doors, save for those to the shuttle, and all implant terminals in this building are now rigged to operate.
This commit is contained in:
parent
47a0aa3e0c
commit
f9beb47073
27 changed files with 1134 additions and 176 deletions
|
|
@ -7,6 +7,7 @@ import net.psforever.objects.serverobject.doors.DoorDefinition
|
|||
import net.psforever.objects.equipment.CItem.DeployedItem
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
|
||||
import net.psforever.objects.serverobject.locks.IFFLockDefinition
|
||||
import net.psforever.objects.serverobject.terminals._
|
||||
import net.psforever.objects.vehicles.SeatArmorRestriction
|
||||
|
|
@ -467,6 +468,10 @@ object GlobalDefinitions {
|
|||
|
||||
val cert_terminal = new CertTerminalDefinition
|
||||
|
||||
val implant_terminal_mech = new ImplantTerminalMechDefinition
|
||||
|
||||
val implant_terminal_interface = new ImplantTerminalInterfaceDefinition
|
||||
|
||||
val ground_vehicle_terminal = new GroundVehicleTerminalDefinition
|
||||
|
||||
val air_vehicle_terminal = new AirVehicleTerminalDefinition
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class ImplantSlot {
|
|||
def Unlocked : Boolean = unlocked
|
||||
|
||||
def Unlocked_=(lock : Boolean) : Boolean = {
|
||||
unlocked = lock || unlocked
|
||||
unlocked = lock || unlocked //do not let re-lock
|
||||
Unlocked
|
||||
}
|
||||
|
||||
|
|
@ -45,13 +45,13 @@ class ImplantSlot {
|
|||
Active
|
||||
}
|
||||
|
||||
def Implant : ImplantType.Value = if(Installed.isDefined) {
|
||||
implant.get.Type
|
||||
}
|
||||
else {
|
||||
Active = false
|
||||
Initialized = false
|
||||
ImplantType.None
|
||||
def Implant : ImplantType.Value = Installed match {
|
||||
case Some(idef) =>
|
||||
idef.Type
|
||||
case None =>
|
||||
Active = false
|
||||
Initialized = false
|
||||
ImplantType.None
|
||||
}
|
||||
|
||||
def Implant_=(anImplant : ImplantDefinition) : ImplantType.Value = {
|
||||
|
|
@ -64,6 +64,8 @@ class ImplantSlot {
|
|||
case Some(_) =>
|
||||
implant = anImplant
|
||||
case None =>
|
||||
Active = false
|
||||
Initialized = false
|
||||
implant = None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,15 @@ class Player(private val name : String,
|
|||
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
|
||||
|
|
@ -330,56 +339,94 @@ class Player(private val name : String,
|
|||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
def InstallImplant(implant : ImplantDefinition) : Boolean = {
|
||||
/**
|
||||
* 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)}) match { //try to find the installed implant
|
||||
case None =>
|
||||
//install in a free slot
|
||||
getAvailableImplantSlot(implants.iterator, implant.Type) match {
|
||||
case Some(slot) =>
|
||||
slot.Implant = implant
|
||||
true
|
||||
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
|
||||
case out @ Some(slot) =>
|
||||
implants(slot).Implant = implant
|
||||
out
|
||||
case None =>
|
||||
false
|
||||
None
|
||||
}
|
||||
case Some(_) =>
|
||||
false
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@tailrec private def getAvailableImplantSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value) : Option[ImplantSlot] = {
|
||||
/**
|
||||
* 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 out @ Some(slot) =>
|
||||
implants(slot).Implant = None
|
||||
out
|
||||
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) {
|
||||
None
|
||||
}
|
||||
else if(slot.Installed.isEmpty) {
|
||||
Some(slot)
|
||||
if(slot.Unlocked && slot.Implant == implantType) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
getAvailableImplantSlot(iter, implantType)
|
||||
recursiveFindImplantInSlot(iter, implantType, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def UninstallImplant(implantType : ImplantType.Value) : Boolean = {
|
||||
implants.find({slot => slot.Implant == implantType}) match {
|
||||
case Some(slot) =>
|
||||
slot.Implant = None
|
||||
true
|
||||
case None =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def ResetAllImplants() : Unit = {
|
||||
implants.foreach(slot => {
|
||||
slot.Installed match {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects
|
|||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
|
||||
import net.psforever.objects.inventory.{GridInventory, InventoryTile}
|
||||
import net.psforever.objects.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -26,7 +27,7 @@ import scala.collection.mutable
|
|||
* stores and unloads pertinent information about the `Vehicle`'s configuration;
|
||||
* used in the initialization process (`loadVehicleDefinition`)
|
||||
*/
|
||||
class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject {
|
||||
class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject with Mountable {
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
|
||||
private var owner : Option[PlanetSideGUID] = None
|
||||
private var health : Int = 1
|
||||
|
|
@ -97,7 +98,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
|||
}
|
||||
|
||||
def MaxHealth : Int = {
|
||||
this.vehicleDef.MaxHealth
|
||||
Definition.MaxHealth
|
||||
}
|
||||
|
||||
def Shields : Int = {
|
||||
|
|
@ -110,7 +111,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
|||
}
|
||||
|
||||
def MaxShields : Int = {
|
||||
vehicleDef.MaxShields
|
||||
Definition.MaxShields
|
||||
}
|
||||
|
||||
def Drive : DriveState.Value = {
|
||||
|
|
@ -118,7 +119,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
|||
}
|
||||
|
||||
def Drive_=(deploy : DriveState.Value) : DriveState.Value = {
|
||||
if(vehicleDef.Deployment) {
|
||||
if(Definition.Deployment) {
|
||||
this.deployed = deploy
|
||||
}
|
||||
Drive
|
||||
|
|
@ -153,9 +154,11 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
|||
* @return a seat number, or `None`
|
||||
*/
|
||||
def GetSeatFromMountPoint(mountPoint : Int) : Option[Int] = {
|
||||
vehicleDef.MountPoints.get(mountPoint)
|
||||
Definition.MountPoints.get(mountPoint)
|
||||
}
|
||||
|
||||
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
|
||||
|
||||
/**
|
||||
* What are the access permissions for a position on this vehicle, seats or trunk?
|
||||
* @param group the group index
|
||||
|
|
@ -225,8 +228,8 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
|
|||
}
|
||||
}
|
||||
|
||||
def Seats : List[Seat] = {
|
||||
seats.values.toList
|
||||
def Seats : Map[Int, Seat] = {
|
||||
seats
|
||||
}
|
||||
|
||||
def SeatPermissionGroup(seatNumber : Int) : Option[AccessPermissionGroup.Value] = {
|
||||
|
|
@ -428,27 +431,6 @@ object Vehicle {
|
|||
*/
|
||||
final case class PrepareForDeletion()
|
||||
|
||||
/**
|
||||
* This player wants to sit down in an available(?) seat.
|
||||
* @param seat_num the seat where the player is trying to occupy;
|
||||
* this is NOT the entry mount point index;
|
||||
* make certain to convert!
|
||||
* @param player the `Player` object
|
||||
*/
|
||||
final case class TrySeatPlayer(seat_num : Int, player : Player)
|
||||
/**
|
||||
* The recipient player of this packet is being allowed to sit in the assigned seat.
|
||||
* @param vehicle the `Vehicle` object that generated this message
|
||||
* @param seat_num the seat that the player will occupy
|
||||
*/
|
||||
final case class CanSeatPlayer(vehicle : Vehicle, seat_num : Int) extends Exchange
|
||||
/**
|
||||
* The recipient player of this packet is not allowed to sit in the requested seat.
|
||||
* @param vehicle the `Vehicle` object that generated this message
|
||||
* @param seat_num the seat that the player can not occupy
|
||||
*/
|
||||
final case class CannotSeatPlayer(vehicle : Vehicle, seat_num : Int) extends Exchange
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param vehicleDef the vehicle's definition entry
|
||||
|
|
@ -494,7 +476,7 @@ object Vehicle {
|
|||
* @return the string output
|
||||
*/
|
||||
def toString(obj : Vehicle) : String = {
|
||||
val occupancy = obj.Seats.count(seat => seat.isOccupied)
|
||||
val occupancy = obj.Seats.values.count(seat => seat.isOccupied)
|
||||
s"${obj.Definition.Name}, owned by ${obj.Owner}: (${obj.Health}/${obj.MaxHealth})(${obj.Shields}/${obj.MaxShields}) ($occupancy)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.objectcreate.CommonTerminalData
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class ImplantTerminalInterfaceConverter extends ObjectCreateConverter[Terminal]() {
|
||||
override def DetailedConstructorData(obj : Terminal) : Try[CommonTerminalData] =
|
||||
Failure(new Exception("ImplantTerminalInterfaceConverter should not be used to generate detailed CommonTerminalData"))
|
||||
|
||||
override def ConstructorData(obj : Terminal) : Try[CommonTerminalData] =
|
||||
Success(CommonTerminalData(net.psforever.types.PlanetSideEmpire.VS)) //TODO shortcut
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ import net.psforever.packet.game.objectcreate.{InventoryItemData, _}
|
|||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||
override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] = Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData"))
|
||||
override def DetailedConstructorData(obj : Vehicle) : Try[VehicleData] =
|
||||
Failure(new Exception("VehicleConverter should not be used to generate detailed VehicleData"))
|
||||
|
||||
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
||||
Success(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.mount
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
|
||||
/**
|
||||
* A `Trait` common to all game objects that permit players to
|
||||
* interact with established spatial locations external to the object ("mount points") and
|
||||
* attach to the object in internal indices ("seats") for an undefined length of time.
|
||||
* @see `Seat`
|
||||
*/
|
||||
trait Mountable {
|
||||
/**
|
||||
* Retrieve a mapping of each seat from its internal index.
|
||||
* @return the mapping of index to seat
|
||||
*/
|
||||
def Seats : Map[Int, Seat]
|
||||
|
||||
/**
|
||||
* Given a seat's index position, retrieve the internal `Seat` object.
|
||||
* @return the specific seat
|
||||
*/
|
||||
def Seat(seatNum : Int) : Option[Seat]
|
||||
|
||||
/**
|
||||
* Retrieve a mapping of each seat from its mount point index.
|
||||
* @return the mapping of mount point to seat
|
||||
*/
|
||||
def MountPoints : Map[Int, Int]
|
||||
|
||||
/**
|
||||
* Given a mount point index, return the associated seat index.
|
||||
* @param mount the mount point
|
||||
* @return the seat index
|
||||
*/
|
||||
def GetSeatFromMountPoint(mount : Int) : Option[Int]
|
||||
|
||||
/**
|
||||
* Given a player, determine if that player is seated.
|
||||
* @param user the player
|
||||
* @return the seat index
|
||||
*/
|
||||
def PassengerInSeat(user : Player) : Option[Int]
|
||||
|
||||
/**
|
||||
* A reference to an `Actor` that governs the logic of the object to accept `Mountable` messages.
|
||||
* Specifically, the `Actor` should intercept the logic of `MountableControl.`
|
||||
* @see `MountableControl`
|
||||
* @see `PlanetSideServerObject.Actor`
|
||||
* @return the internal `ActorRef`
|
||||
*/
|
||||
def Actor : ActorRef //TODO can we enforce this desired association to MountableControl?
|
||||
}
|
||||
|
||||
object Mountable {
|
||||
/**
|
||||
* Message used by the player to indicate the desire to board a `Mountable` object.
|
||||
* @param player the player who sent this request message
|
||||
* @param seat_num the seat index
|
||||
*/
|
||||
final case class TryMount(player : Player, seat_num : Int)
|
||||
|
||||
/**
|
||||
* A basic `Trait` connecting all of the actionable `Mountable` response messages.
|
||||
*/
|
||||
sealed trait Exchange
|
||||
|
||||
/**
|
||||
* Message that carries the result of the processed request message back to the original user (`player`).
|
||||
* @param player the player who sent this request message
|
||||
* @param response the result of the processed request
|
||||
*/
|
||||
final case class MountMessages(player : Player, response : Exchange)
|
||||
|
||||
/**
|
||||
* Message sent in response to the player succeeding to access a `Mountable` object.
|
||||
* The player should be seated at the given index.
|
||||
* @param obj the `Mountable` object
|
||||
* @param seat_num the seat index
|
||||
*/
|
||||
final case class CanMount(obj : Mountable, seat_num : Int) extends Exchange
|
||||
|
||||
/**
|
||||
* Message sent in response to the player failing to access a `Mountable` object.
|
||||
* The player would have been be seated at the given index.
|
||||
* @param obj the `Mountable` object
|
||||
* @param seat_num the seat index
|
||||
*/
|
||||
final case class CanNotMount(obj : Mountable, seat_num : Int) extends Exchange
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.mount
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
/**
|
||||
* The logic governing `Mountable` objects that use the `TryMount` message.
|
||||
* @see `Seat`
|
||||
* @see `Mountable`
|
||||
* @param obj the `Mountable` object governed beholden to this logic
|
||||
*/
|
||||
abstract class MountableControl(obj : Mountable) extends Actor {
|
||||
def receive : Receive = {
|
||||
case Mountable.TryMount(user, seat_num) =>
|
||||
obj.Seat(seat_num) match {
|
||||
case Some(seat) =>
|
||||
if((seat.Occupant = user).contains(user)) {
|
||||
sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num))
|
||||
}
|
||||
else {
|
||||
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
||||
}
|
||||
case None =>
|
||||
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl, ImplantTerminalMechDefinition}
|
||||
|
||||
/**
|
||||
* Wrapper `Class` designed to instantiate a `ImplantTerminalMech` server object.
|
||||
* @param idef a `ImplantTerminalMechDefinition` object, indicating the specific functionality of the resulting `Door`
|
||||
* @param id the globally unique identifier to which this "tube" will be registered
|
||||
*/
|
||||
class ImplantTerminalMechObjectBuilder(private val idef : ImplantTerminalMechDefinition, private val id : Int) extends ServerObjectBuilder[ImplantTerminalMech] {
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
def Build(implicit context : ActorContext, guid : NumberPoolHub) : ImplantTerminalMech = {
|
||||
val obj = ImplantTerminalMech(idef)
|
||||
guid.register(obj, id) //non-Actor GUID registration
|
||||
obj.Actor = context.actorOf(Props(classOf[ImplantTerminalMechControl], obj), s"${idef.Name}_${obj.GUID.guid}")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
object ImplantTerminalMechObjectBuilder {
|
||||
/**
|
||||
* Overloaded constructor for a `DoorObjectBuilder`.
|
||||
* @param idef a `DoorDefinition` object
|
||||
* @param id a globally unique identifier
|
||||
* @return a `DoorObjectBuilder` object
|
||||
*/
|
||||
def apply(idef : ImplantTerminalMechDefinition, id : Int) : ImplantTerminalMechObjectBuilder = {
|
||||
new ImplantTerminalMechObjectBuilder(idef, id)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
|
||||
/**
|
||||
* A structure-owned server object that is the visible and `Mountable` component of an implant terminal.
|
||||
* For the most part, it merely implements the support data structures indicated by `Mountable`.
|
||||
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends PlanetSideServerObject with Mountable {
|
||||
private val seats : Map[Int, Seat] = Map( 0 -> new Seat(idef.Seats(0)) )
|
||||
|
||||
def Seats : Map[Int, Seat] = seats
|
||||
|
||||
def Seat(seatNum : Int) : Option[Seat] = seats.get(seatNum)
|
||||
|
||||
def MountPoints : Map[Int, Int] = idef.MountPoints
|
||||
|
||||
def GetSeatFromMountPoint(mount : Int) : Option[Int] = idef.MountPoints.get(mount)
|
||||
|
||||
def PassengerInSeat(user : Player) : Option[Int] = {
|
||||
if(seats(0).Occupant.contains(user)) {
|
||||
Some(0)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def Definition : ObjectDefinition = idef
|
||||
}
|
||||
|
||||
object ImplantTerminalMech {
|
||||
def apply(idef : ImplantTerminalMechDefinition) : ImplantTerminalMech = {
|
||||
new ImplantTerminalMech(idef)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
|
||||
import net.psforever.objects.mount.MountableControl
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
|
||||
* @param mech the "mech" object being governed
|
||||
*/
|
||||
class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends MountableControl(mech) {
|
||||
override def receive : Receive = super[MountableControl].receive.orElse {
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
|
||||
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
|
||||
import net.psforever.objects.vehicles.SeatArmorRestriction
|
||||
|
||||
/**
|
||||
* The `Definition` for any `Terminal` that is of a type "implant_terminal_interface."
|
||||
* Implant terminals are composed of two components.
|
||||
* This `Definition` constructs the visible mechanical tube component that can be mounted.
|
||||
*/
|
||||
class ImplantTerminalMechDefinition extends ObjectDefinition(410) {
|
||||
/* key - seat index, value - seat object */
|
||||
private val seats : Map[Int, SeatDefinition] = Map(0 -> new SeatDefinition)
|
||||
/* key - entry point index, value - seat index */
|
||||
private val mountPoints : Map[Int, Int] = Map(1 -> 0)
|
||||
Name = "implant_terminal_mech"
|
||||
|
||||
def Seats : Map[Int, SeatDefinition] = seats
|
||||
|
||||
def MountPoints : Map[Int, Int] = mountPoints
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
import net.psforever.objects.definition.converter.ImplantTerminalInterfaceConverter
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
|
||||
/**
|
||||
* The `Definition` for any `Terminal` that is of a type "implant_terminal_interface."
|
||||
* Implant terminals are composed of two components.
|
||||
* This `Definition` constructs the invisible interface component (interacted with as a game window).
|
||||
* Unlike other `Terminal` objects in the game, this one must be constructed on the client and
|
||||
* attached as a child of the visible implant terminal component - the "implant_terminal_mech."
|
||||
*/
|
||||
class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) {
|
||||
private val implants : Map[String, ImplantDefinition] = Map (
|
||||
"advanced_regen" -> GlobalDefinitions.advanced_regen,
|
||||
"targeting" -> GlobalDefinitions.targeting,
|
||||
"audio_amplifier" -> GlobalDefinitions.audio_amplifier,
|
||||
"darklight_vision" -> GlobalDefinitions.darklight_vision,
|
||||
"melee_booster" -> GlobalDefinitions.melee_booster,
|
||||
"personal_shield" -> GlobalDefinitions.personal_shield,
|
||||
"range_magnifier" -> GlobalDefinitions.range_magnifier,
|
||||
"second_wind" -> GlobalDefinitions.second_wind,
|
||||
"silent_run" -> GlobalDefinitions.silent_run,
|
||||
"surge" -> GlobalDefinitions.surge
|
||||
)
|
||||
Packet = new ImplantTerminalInterfaceConverter
|
||||
Name = "implante_terminal_interface"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
implants.get(msg.item_name) match {
|
||||
case Some(implant) =>
|
||||
Terminal.LearnImplant(implant)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
implants.get(msg.item_name) match {
|
||||
case Some(implant) =>
|
||||
Terminal.SellImplant(implant)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
|
@ -151,7 +152,24 @@ object Terminal {
|
|||
*/
|
||||
final case class SellCertification(cert : CertificationType.Value, cost : Int) extends Exchange
|
||||
|
||||
/**
|
||||
* Provide the implant type unlocked by the player.
|
||||
* @param implant the implant (definition) requested
|
||||
*/
|
||||
final case class LearnImplant(implant : ImplantDefinition) extends Exchange
|
||||
|
||||
/**
|
||||
* Provide the implant type freed-up by the player.
|
||||
* @param implant the implant (definition) returned
|
||||
*/
|
||||
final case class SellImplant(implant : ImplantDefinition) extends Exchange
|
||||
|
||||
import net.psforever.objects.Vehicle
|
||||
/**
|
||||
* Provide a vehicle that was constructed for the player.
|
||||
* @param vehicle the vehicle
|
||||
* @param loadout the vehicle's trunk contents
|
||||
*/
|
||||
final case class BuyVehicle(vehicle : Vehicle, loadout: List[Any]) extends Exchange
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.mount.MountableControl
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||
|
|
@ -11,23 +11,11 @@ import net.psforever.objects.Vehicle
|
|||
* The latter is applicable only when the specific vehicle is being deconstructed.
|
||||
* @param vehicle the `Vehicle` object being governed
|
||||
*/
|
||||
class VehicleControl(private val vehicle : Vehicle) extends Actor {
|
||||
def receive : Receive = {
|
||||
class VehicleControl(private val vehicle : Vehicle) extends MountableControl(vehicle) {
|
||||
override def receive : Receive = super[MountableControl].receive.orElse {
|
||||
case Vehicle.PrepareForDeletion =>
|
||||
context.become(Disabled)
|
||||
|
||||
case Vehicle.TrySeatPlayer(seat_num, player) =>
|
||||
vehicle.Seat(seat_num) match {
|
||||
case Some(seat) =>
|
||||
if((seat.Occupant = player).contains(player)) {
|
||||
sender ! Vehicle.VehicleMessages(player, Vehicle.CanSeatPlayer(vehicle, seat_num))
|
||||
}
|
||||
else {
|
||||
sender ! Vehicle.VehicleMessages(player, Vehicle.CannotSeatPlayer(vehicle, seat_num))
|
||||
}
|
||||
case None =>
|
||||
sender ! Vehicle.VehicleMessages(player, Vehicle.CannotSeatPlayer(vehicle, seat_num))
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,16 +45,16 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a door at id $door_guid, but looking for uninitialized object")
|
||||
slog.error(s"expected a door at id $door_guid but no object is initialized")
|
||||
}
|
||||
try {
|
||||
if(!guid(lock_guid).get.isInstanceOf[IFFLock]) {
|
||||
slog.error(s"expected id $lock_guid to be an IFF locks, but it was not")
|
||||
slog.error(s"expected id $lock_guid to be an IFF locks but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected an IFF locks at id $lock_guid, but looking for uninitialized object")
|
||||
slog.error(s"expected an IFF locks at id $lock_guid but no object is initialized")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a terminal at id $term_guid, but looking for uninitialized object")
|
||||
slog.error(s"expected a terminal at id $term_guid but no object is initialized")
|
||||
}
|
||||
try {
|
||||
if(!guid(pad_guid).get.isInstanceOf[VehicleSpawnPad]) {
|
||||
|
|
@ -78,7 +78,30 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a spawn pad at id $pad_guid, but looking for uninitialized object")
|
||||
slog.error(s"expected a spawn pad at id $pad_guid but no object is initialized")
|
||||
}
|
||||
})
|
||||
|
||||
//check implant terminal mech to implant terminal interface association
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||
map.TerminalToInterface.foreach({case ((mech_guid, interface_guid)) =>
|
||||
try {
|
||||
if(!guid(mech_guid).get.isInstanceOf[ImplantTerminalMech]) {
|
||||
slog.error(s"expected id $mech_guid to be an implant terminal mech, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a implant terminal mech at id $mech_guid but no object is initialized")
|
||||
}
|
||||
try {
|
||||
if(!guid(interface_guid).get.isInstanceOf[Terminal]) { //TODO check is implant terminal
|
||||
slog.error(s"expected id $interface_guid to be an implant terminal interface, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a implant terminal interface at id $interface_guid but no object is initialized")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import net.psforever.objects.serverobject.builders.ServerObjectBuilder
|
|||
class ZoneMap(private val name : String) {
|
||||
private var localObjects : List[ServerObjectBuilder[_]] = List()
|
||||
private var linkTerminalPad : Map[Int, Int] = Map()
|
||||
private var linkTerminalInterface : Map[Int, Int] = Map()
|
||||
private var linkDoorLock : Map[Int, Int] = Map()
|
||||
private var linkObjectBase : Map[Int, Int] = Map()
|
||||
private var numBases : Int = 0
|
||||
|
|
@ -74,4 +75,10 @@ class ZoneMap(private val name : String) {
|
|||
def TerminalToSpawnPad(terminal_guid : Int, pad_guid : Int) : Unit = {
|
||||
linkTerminalPad = linkTerminalPad ++ Map(terminal_guid -> pad_guid)
|
||||
}
|
||||
|
||||
def TerminalToInterface : Map[Int, Int] = linkTerminalInterface
|
||||
|
||||
def TerminalToInterface(interface_guid : Int, terminal_guid : Int) : Unit = {
|
||||
linkTerminalInterface = linkTerminalInterface ++ Map(interface_guid -> terminal_guid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1268,7 +1268,7 @@ object ObjectClass {
|
|||
case ObjectClass.ams_respawn_tube => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||
case ObjectClass.avatar => ConstructorData.genericCodec(CharacterData.codec, "avatar")
|
||||
case ObjectClass.capture_flag => ConstructorData.genericCodec(CaptureFlagData.codec, "capture flag")
|
||||
case ObjectClass.implant_terminal_interface => DroppedItemData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
||||
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
|
||||
case ObjectClass.locker_container => ConstructorData.genericCodec(LockerContainerData.codec, "locker container")
|
||||
case ObjectClass.matrix_terminala => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||
case ObjectClass.matrix_terminalb => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
|
||||
|
|
|
|||
87
common/src/test/scala/objects/MountableTest.scala
Normal file
87
common/src/test/scala/objects/MountableTest.scala
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
|
||||
import net.psforever.objects.mount.{Mountable, MountableControl}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class MountableControl1Test extends ActorTest() {
|
||||
"MountableControl" should {
|
||||
"construct" in {
|
||||
val obj = new MountableTest.MountableTestObject
|
||||
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mech")
|
||||
assert(obj.Actor != ActorRef.noSender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MountableControl2Test extends ActorTest() {
|
||||
"MountableControl" should {
|
||||
"let a player mount" in {
|
||||
val player = Player("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)
|
||||
|
||||
obj.Actor ! msg
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
val reply2 = reply.asInstanceOf[Mountable.MountMessages]
|
||||
assert(reply2.player == player)
|
||||
assert(reply2.response.isInstanceOf[Mountable.CanMount])
|
||||
val reply3 = reply2.response.asInstanceOf[Mountable.CanMount]
|
||||
assert(reply3.obj == obj)
|
||||
assert(reply3.seat_num == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 obj = new MountableTest.MountableTestObject
|
||||
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
|
||||
obj.Actor ! Mountable.TryMount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //consume reply
|
||||
|
||||
obj.Actor ! Mountable.TryMount(player2, 0)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
val reply2 = reply.asInstanceOf[Mountable.MountMessages]
|
||||
assert(reply2.player == player2)
|
||||
assert(reply2.response.isInstanceOf[Mountable.CanNotMount])
|
||||
val reply3 = reply2.response.asInstanceOf[Mountable.CanNotMount]
|
||||
assert(reply3.obj == obj)
|
||||
assert(reply3.seat_num == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MountableTest {
|
||||
class MountableTestObject extends PlanetSideServerObject with Mountable {
|
||||
private val seats : Map[Int, Seat] = Map( 0 -> new Seat(new SeatDefinition()) )
|
||||
def Seats : Map[Int, Seat] = seats
|
||||
def Seat(seatNum : Int) : Option[Seat] = seats.get(seatNum)
|
||||
def MountPoints : Map[Int, Int] = Map(1 -> 0)
|
||||
def GetSeatFromMountPoint(mount : Int) : Option[Int] = MountPoints.get(mount)
|
||||
def PassengerInSeat(user : Player) : Option[Int] = {
|
||||
if(seats(0).Occupant.contains(user)) {
|
||||
Some(0)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
def Definition : ObjectDefinition = null //eh whatever
|
||||
}
|
||||
|
||||
class MountableTestControl(obj : Mountable) extends MountableControl(obj)
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ class PlayerTest extends Specification {
|
|||
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 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)
|
||||
|
|
@ -126,15 +126,15 @@ class PlayerTest extends Specification {
|
|||
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 true
|
||||
obj.InstallImplant(testplant2) mustEqual false
|
||||
obj.InstallImplant(testplant1) mustEqual Some(0)
|
||||
obj.InstallImplant(testplant2) mustEqual Some(1)
|
||||
}
|
||||
|
||||
"uninstall implants" in {
|
||||
val testplant : ImplantDefinition = ImplantDefinition(1)
|
||||
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
||||
obj.Implants(0).Unlocked = true
|
||||
obj.InstallImplant(testplant) mustEqual true
|
||||
obj.InstallImplant(testplant) mustEqual Some(0)
|
||||
obj.Implants(0).Installed mustEqual Some(testplant)
|
||||
|
||||
obj.UninstallImplant(testplant.Type)
|
||||
|
|
|
|||
116
common/src/test/scala/objects/ServerObjectBuilderTest.scala
Normal file
116
common/src/test/scala/objects/ServerObjectBuilderTest.scala
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.objects.serverobject.builders.ServerObjectBuilder
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class DoorObjectBuilderTest extends ActorTest {
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.builders.DoorObjectBuilder
|
||||
"DoorObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], DoorObjectBuilder(GlobalDefinitions.door, 1), hub), "door")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Door])
|
||||
assert(reply.asInstanceOf[Door].HasGUID)
|
||||
assert(reply.asInstanceOf[Door].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IFFLockObjectBuilderTest extends ActorTest {
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.builders.IFFLockObjectBuilder
|
||||
"IFFLockObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], IFFLockObjectBuilder(GlobalDefinitions.lock_external, 1), hub), "lock")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[IFFLock])
|
||||
assert(reply.asInstanceOf[IFFLock].HasGUID)
|
||||
assert(reply.asInstanceOf[IFFLock].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImplantTerminalMechObjectBuilderTest extends ActorTest {
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.builders.ImplantTerminalMechObjectBuilder
|
||||
"IFFLockObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ImplantTerminalMechObjectBuilder(GlobalDefinitions.implant_terminal_mech, 1), hub), "mech")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[ImplantTerminalMech])
|
||||
assert(reply.asInstanceOf[ImplantTerminalMech].HasGUID)
|
||||
assert(reply.asInstanceOf[ImplantTerminalMech].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TerminalObjectBuilderTest extends ActorTest {
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.builders.TerminalObjectBuilder
|
||||
"TerminalObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], TerminalObjectBuilder(GlobalDefinitions.order_terminal, 1), hub), "term")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Terminal])
|
||||
assert(reply.asInstanceOf[Terminal].HasGUID)
|
||||
assert(reply.asInstanceOf[Terminal].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnPadObjectBuilderTest extends ActorTest {
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.builders.VehicleSpawnPadObjectBuilder
|
||||
"TerminalObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], VehicleSpawnPadObjectBuilder(GlobalDefinitions.spawn_pad, 1), hub), "pad")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[VehicleSpawnPad])
|
||||
assert(reply.asInstanceOf[VehicleSpawnPad].HasGUID)
|
||||
assert(reply.asInstanceOf[VehicleSpawnPad].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ServerObjectBuilderTest {
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
def NumberPoolHub : NumberPoolHub = {
|
||||
val obj = new NumberPoolHub(new LimitedNumberSource(2))
|
||||
obj
|
||||
}
|
||||
|
||||
class BuilderTestActor(builder : ServerObjectBuilder[_], hub : NumberPoolHub) extends Actor {
|
||||
def receive : Receive = {
|
||||
case _ =>
|
||||
sender ! builder.Build(context, hub)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,11 +128,11 @@ class VehicleTest extends Specification {
|
|||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.Owner mustEqual None
|
||||
fury_vehicle.Seats.size mustEqual 1
|
||||
fury_vehicle.Seats.head.ArmorRestriction mustEqual SeatArmorRestriction.NoMax
|
||||
fury_vehicle.Seats.head.isOccupied mustEqual false
|
||||
fury_vehicle.Seats.head.Occupant mustEqual None
|
||||
fury_vehicle.Seats.head.Bailable mustEqual true
|
||||
fury_vehicle.Seats.head.ControlledWeapon mustEqual Some(1)
|
||||
fury_vehicle.Seats(0).ArmorRestriction mustEqual SeatArmorRestriction.NoMax
|
||||
fury_vehicle.Seats(0).isOccupied mustEqual false
|
||||
fury_vehicle.Seats(0).Occupant mustEqual None
|
||||
fury_vehicle.Seats(0).Bailable mustEqual true
|
||||
fury_vehicle.Seats(0).ControlledWeapon mustEqual Some(1)
|
||||
fury_vehicle.PermissionGroup(0) mustEqual Some(VehicleLockState.Locked) //driver
|
||||
fury_vehicle.PermissionGroup(1) mustEqual Some(VehicleLockState.Empire) //gunner
|
||||
fury_vehicle.PermissionGroup(2) mustEqual Some(VehicleLockState.Empire) //passenger
|
||||
|
|
|
|||
|
|
@ -44,6 +44,24 @@ class ZoneTest extends Specification {
|
|||
map.DoorToLock(3, 4)
|
||||
map.DoorToLock mustEqual Map(1 -> 2, 3 -> 4)
|
||||
}
|
||||
|
||||
"associates terminals to spawn pads (doesn't check numbers)" in {
|
||||
val map = new ZoneMap("map13")
|
||||
map.TerminalToSpawnPad mustEqual Map.empty
|
||||
map.TerminalToSpawnPad(1, 2)
|
||||
map.TerminalToSpawnPad mustEqual Map(1 -> 2)
|
||||
map.TerminalToSpawnPad(3, 4)
|
||||
map.TerminalToSpawnPad mustEqual Map(1 -> 2, 3 -> 4)
|
||||
}
|
||||
|
||||
"associates mechanical components to implant terminals (doesn't check numbers)" in {
|
||||
val map = new ZoneMap("map13")
|
||||
map.TerminalToInterface mustEqual Map.empty
|
||||
map.TerminalToInterface(1, 2)
|
||||
map.TerminalToInterface mustEqual Map(1 -> 2)
|
||||
map.TerminalToInterface(3, 4)
|
||||
map.TerminalToInterface mustEqual Map(1 -> 2, 3 -> 4)
|
||||
}
|
||||
}
|
||||
|
||||
val map13 = new ZoneMap("map13")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class ImplantTerminalInterfaceTest extends Specification {
|
||||
"Implant_Terminal_Interface" should {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
|
||||
"construct" in {
|
||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||
terminal.Actor mustEqual ActorRef.noSender
|
||||
}
|
||||
|
||||
"player can learn an implant ('darklight_vision')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "darklight_vision", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.LearnImplant] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.LearnImplant]
|
||||
reply2.implant mustEqual GlobalDefinitions.darklight_vision
|
||||
}
|
||||
|
||||
"player can not learn a fake implant ('aimbot')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "aimbot", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
|
||||
"player can surrender an implant ('darklight_vision')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "darklight_vision", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.SellImplant] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.SellImplant]
|
||||
reply2.implant mustEqual GlobalDefinitions.darklight_vision
|
||||
}
|
||||
|
||||
"player can not surrender a fake implant ('aimbot')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "aimbot", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.definition.SeatDefinition
|
||||
import net.psforever.objects.mount.Mountable
|
||||
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
import objects.ActorTest
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class ImplantTerminalMechTest extends Specification {
|
||||
"Implant_Terminal_Mech" should {
|
||||
"define" in {
|
||||
val implant_terminal_mech = GlobalDefinitions.implant_terminal_mech
|
||||
implant_terminal_mech.ObjectId mustEqual 410
|
||||
implant_terminal_mech.MountPoints mustEqual Map(1 -> 0)
|
||||
implant_terminal_mech.Seats.keySet mustEqual Set(0)
|
||||
implant_terminal_mech.Seats(0).isInstanceOf[SeatDefinition] mustEqual true
|
||||
implant_terminal_mech.Seats(0).ArmorRestriction mustEqual net.psforever.objects.vehicles.SeatArmorRestriction.NoMax
|
||||
implant_terminal_mech.Seats(0).Bailable mustEqual false
|
||||
implant_terminal_mech.Seats(0).ControlledWeapon mustEqual None
|
||||
}
|
||||
}
|
||||
|
||||
"VehicleSpawnPad" should {
|
||||
"construct" in {
|
||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.Actor mustEqual ActorRef.noSender
|
||||
obj.Definition mustEqual GlobalDefinitions.implant_terminal_mech
|
||||
obj.Seats.keySet mustEqual Set(0)
|
||||
obj.Seats(0).isInstanceOf[Seat] mustEqual true
|
||||
}
|
||||
|
||||
"get seat from mount points" in {
|
||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.GetSeatFromMountPoint(0) mustEqual None
|
||||
obj.GetSeatFromMountPoint(1) mustEqual Some(0)
|
||||
obj.GetSeatFromMountPoint(2) mustEqual None
|
||||
}
|
||||
|
||||
"get passenger in a seat" in {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.PassengerInSeat(player) mustEqual None
|
||||
obj.Seats(0).Occupant = player
|
||||
obj.PassengerInSeat(player) mustEqual Some(0)
|
||||
obj.Seats(0).Occupant = None
|
||||
obj.PassengerInSeat(player) mustEqual None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImplantTerminalMechControl1Test extends ActorTest() {
|
||||
"ImplantTerminalMechControl" should {
|
||||
"construct" in {
|
||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], obj), "mech")
|
||||
assert(obj.Actor != ActorRef.noSender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImplantTerminalMechControl2Test extends ActorTest() {
|
||||
"ImplantTerminalMechControl" should {
|
||||
"let a player mount" in {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], obj), "mech")
|
||||
val msg = Mountable.TryMount(player, 0)
|
||||
|
||||
obj.Actor ! msg
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
val reply2 = reply.asInstanceOf[Mountable.MountMessages]
|
||||
assert(reply2.player == player)
|
||||
assert(reply2.response.isInstanceOf[Mountable.CanMount])
|
||||
val reply3 = reply2.response.asInstanceOf[Mountable.CanMount]
|
||||
assert(reply3.obj == obj)
|
||||
assert(reply3.seat_num == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImplantTerminalMechControl3Test extends ActorTest() {
|
||||
"ImplantTerminalMechControl" 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 obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
|
||||
obj.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], obj), "mech")
|
||||
obj.Actor ! Mountable.TryMount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //consume reply
|
||||
|
||||
obj.Actor ! Mountable.TryMount(player2, 0)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
val reply2 = reply.asInstanceOf[Mountable.MountMessages]
|
||||
assert(reply2.player == player2)
|
||||
assert(reply2.response.isInstanceOf[Mountable.CanNotMount])
|
||||
val reply3 = reply2.response.asInstanceOf[Mountable.CanNotMount]
|
||||
assert(reply3.obj == obj)
|
||||
assert(reply3.seat_num == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue