From eefe4d2e20ecb99a7bb902303829c6f52b00fcf6 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 26 Jan 2018 15:32:08 -0500 Subject: [PATCH] Building: Replaces class object/serverobject/door/Base.scala. It performs nearly the exact same purpose but now has a list of owned objects called Amenities. Buildings are now a PlanetSideServerObject (PSSO), which allows them to have accept a *Control Actor and possess FactionAffinity. FoundationBuilder: FoundationBuilder : Building :: ServerObjectBuilder : [T <: PlanetSideServerObject] Amenity: Most PSSO's now accept Amenity as their parent in class hierarchy. Flagged PSSO's like Building and Vehicle are, on the other hand, capable of becoming the owner for these Amenity PSSOs, which allows them to inherit the same FactionAffinity. FactionAffinity: A trait that connects objects that are intended to communicate PlanetSideEmpire values. MountableBhevaior: Split between Mount and Dismount behavior. Passes appropriate messages to ensure coherent workflows. Control Actors: FactionAffinityBehavior and MountableBehavior are PartialFunctions that get processed in series. VehicleControl: Distinguished behavior allowed between an operational vehicle and a deactivated one. WSA: Tightened up DismountVehicleMsg handling code, since MountableBehavior has been enhanced. Minor: Shotgun shell stacking goes from 32 to 16. Various PSSO classes now have reliable Definition objects. Tests: We now have 1012 tests, some of them useful. --- .../psforever/objects/GlobalDefinitions.scala | 10 +- .../psforever/objects/LockerContainer.scala | 6 + .../scala/net/psforever/objects/Player.scala | 3 +- .../scala/net/psforever/objects/Vehicle.scala | 18 +- .../objects/mount/MountableBehavior.scala | 31 --- .../serverobject/PlanetSideServerObject.scala | 3 +- .../affinity/FactionAffinity.scala | 35 +++ .../affinity/FactionAffinityBehavior.scala | 42 ++++ .../objects/serverobject/doors/Base.scala | 21 -- .../objects/serverobject/doors/Door.scala | 4 +- .../serverobject/doors/DoorControl.scala | 7 +- .../implantmech/ImplantTerminalMech.scala | 6 +- .../ImplantTerminalMechControl.scala | 19 +- .../objects/serverobject/locks/IFFLock.scala | 4 +- .../serverobject/locks/IFFLockControl.scala | 7 +- .../serverobject/mblocker/Locker.scala | 12 +- .../serverobject/mblocker/LockerControl.scala | 17 ++ .../mblocker/LockerDefinition.scala | 12 ++ .../{ => serverobject}/mount/Mountable.scala | 19 +- .../mount/MountableBehavior.scala | 68 ++++++ .../pad/VehicleSpawnControl.scala | 6 +- .../serverobject/pad/VehicleSpawnPad.scala | 9 +- .../pad/VehicleSpawnPadDefinition.scala | 12 ++ .../serverobject/structures/Amenity.scala | 60 ++++++ .../serverobject/structures/Building.scala | 56 +++++ .../structures/BuildingControl.scala | 20 ++ .../structures/FoundationBuilder.scala | 30 +++ .../serverobject/terminals/Terminal.scala | 27 ++- .../terminals/TerminalControl.scala | 7 +- .../objects/vehicles/VehicleControl.scala | 38 +++- .../net/psforever/objects/zones/Zone.scala | 28 ++- .../psforever/objects/zones/ZoneActor.scala | 134 ++++++------ .../net/psforever/objects/zones/ZoneMap.scala | 19 +- .../src/test/scala/objects/BuildingTest.scala | 169 +++++++++++++++ common/src/test/scala/objects/DoorTest.scala | 25 ++- .../scala/objects/FactionAffinityTest.scala | 131 ++++++++++++ .../src/test/scala/objects/IFFLockTest.scala | 23 +- .../src/test/scala/objects/LockerTest.scala | 36 ++++ .../test/scala/objects/MountableTest.scala | 12 +- .../objects/ServerObjectBuilderTest.scala | 23 ++ .../scala/objects/VehicleSpawnPadTest.scala | 34 +-- .../src/test/scala/objects/VehicleTest.scala | 52 ++++- common/src/test/scala/objects/ZoneTest.scala | 32 +-- .../terminal/AirVehicleTerminalTest.scala | 8 +- .../objects/terminal/CertTerminalTest.scala | 9 +- .../DropshipVehicleTerminalTest.scala | 8 +- .../terminal/GroundVehicleTerminalTest.scala | 8 +- .../ImplantTerminalInterfaceTest.scala | 10 +- .../terminal/ImplantTerminalMechTest.scala | 88 ++++++-- .../objects/terminal/OrderTerminalTest.scala | 12 +- .../terminal/TerminalControlTest.scala | 39 ++-- .../VehicleTerminalCombinedTest.scala | 10 +- pslogin/src/main/scala/Maps.scala | 63 ++++-- .../src/main/scala/WorldSessionActor.scala | 199 +++++++++--------- pslogin/src/main/scala/Zones.scala | 4 +- .../vehicle/support/DeconstructionActor.scala | 2 +- 56 files changed, 1362 insertions(+), 425 deletions(-) delete mode 100644 common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinity.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinityBehavior.scala delete mode 100644 common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerDefinition.scala rename common/src/main/scala/net/psforever/objects/{ => serverobject}/mount/Mountable.scala (80%) create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPadDefinition.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/structures/FoundationBuilder.scala create mode 100644 common/src/test/scala/objects/BuildingTest.scala create mode 100644 common/src/test/scala/objects/FactionAffinityTest.scala create mode 100644 common/src/test/scala/objects/LockerTest.scala diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 4c9b542b..d22bea55 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -9,6 +9,8 @@ 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.mblocker.LockerDefinition +import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.vehicles.SeatArmorRestriction import net.psforever.types.PlanetSideEmpire @@ -498,9 +500,9 @@ object GlobalDefinitions { val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition - val spawn_pad = new ObjectDefinition(800) { Name = "spawn_pad" } + val spawn_pad = new VehicleSpawnPadDefinition - val mb_locker = new ObjectDefinition(524) { Name = "mb_locker" } + val mb_locker = new LockerDefinition val lock_external = new IFFLockDefinition @@ -827,10 +829,10 @@ object GlobalDefinitions { bullet_9mm_AP.Capacity = 50 bullet_9mm_AP.Tile = InventoryTile.Tile33 - shotgun_shell.Capacity = 32 + shotgun_shell.Capacity = 16 shotgun_shell.Tile = InventoryTile.Tile33 - shotgun_shell_AP.Capacity = 32 + shotgun_shell_AP.Capacity = 16 shotgun_shell_AP.Tile = InventoryTile.Tile33 energy_cell.Capacity = 50 diff --git a/common/src/main/scala/net/psforever/objects/LockerContainer.scala b/common/src/main/scala/net/psforever/objects/LockerContainer.scala index 91f13590..0282b8a3 100644 --- a/common/src/main/scala/net/psforever/objects/LockerContainer.scala +++ b/common/src/main/scala/net/psforever/objects/LockerContainer.scala @@ -8,6 +8,12 @@ import net.psforever.packet.game.PlanetSideGUID import scala.annotation.tailrec +/** + * The companion of a `Locker` that is carried with a player + * masquerading as their sixth `EquipmentSlot` object and a sub-inventory item. + * The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`. + * The inventory of this object is accessed using a game world `Locker` object (`mb_locker`). + */ class LockerContainer extends Equipment with Container { private val inventory = GridInventory(30, 20) diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala index 710a3845..6da1a64c 100644 --- a/common/src/main/scala/net/psforever/objects/Player.scala +++ b/common/src/main/scala/net/psforever/objects/Player.scala @@ -4,6 +4,7 @@ package net.psforever.objects import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition} import net.psforever.objects.equipment.{Equipment, EquipmentSize} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} +import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.packet.game.PlanetSideGUID import net.psforever.types._ @@ -16,7 +17,7 @@ class Player(private val name : String, private val sex : CharacterGender.Value, private val head : Int, private val voice : Int - ) extends PlanetSideGameObject with Container { + ) extends PlanetSideGameObject with FactionAffinity with Container { private var alive : Boolean = false private var backpack : Boolean = false private var health : Int = 0 diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala index 98e0f485..eb2bd636 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala @@ -4,8 +4,9 @@ package net.psforever.objects import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.equipment.{Equipment, EquipmentSize} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem, InventoryTile} -import net.psforever.objects.mount.Mountable +import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState} import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate.DriveState @@ -27,7 +28,10 @@ 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 with Mountable with Container { +class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject + with FactionAffinity + with Mountable + with Container { private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR private var owner : Option[PlanetSideGUID] = None private var health : Int = 1 @@ -63,7 +67,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ this.faction } - def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = { + override def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = { this.faction = faction faction } @@ -465,11 +469,17 @@ object Vehicle { /** * The `Vehicle` will become unresponsive to player activity. - * Usually, it does this to await deconstruction and clean-up + * Usually, it does this to await deconstruction and clean-up. * @see `VehicleControl` */ final case class PrepareForDeletion() + /** + * The `Vehicle` will resume previous unresponsiveness to player activity. + * @see `VehicleControl` + */ + final case class Reactivate() + /** * Overloaded constructor. * @param vehicleDef the vehicle's definition entry diff --git a/common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala b/common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala deleted file mode 100644 index 6ccd0dfb..00000000 --- a/common/src/main/scala/net/psforever/objects/mount/MountableBehavior.scala +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.objects.mount - -import akka.actor.Actor - -/** - * The logic governing `Mountable` objects that use the `TryMount` message. - * This is a mix-in trait for combining the `Receive` logic. - * @see `Seat` - * @see `Mountable` - */ -trait MountableBehavior { - this : Actor => - - def MountableObject : Mountable - - val mountableBehavior : Receive = { - case Mountable.TryMount(user, seat_num) => - MountableObject.Seat(seat_num) match { - case Some(seat) => - if((seat.Occupant = user).contains(user)) { - sender ! Mountable.MountMessages(user, Mountable.CanMount(MountableObject, seat_num)) - } - else { - sender ! Mountable.MountMessages(user, Mountable.CanNotMount(MountableObject, seat_num)) - } - case None => - sender ! Mountable.MountMessages(user, Mountable.CanNotMount(MountableObject, seat_num)) - } - } -} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala index b81d8e0f..3f8436b6 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala @@ -3,12 +3,13 @@ package net.psforever.objects.serverobject import akka.actor.ActorRef import net.psforever.objects.PlanetSideGameObject +import net.psforever.objects.serverobject.affinity.FactionAffinity /** * An object layered on top of the standard game object class that maintains an internal `ActorRef`. * A measure of synchronization can be managed using this `Actor`. */ -abstract class PlanetSideServerObject extends PlanetSideGameObject { +abstract class PlanetSideServerObject extends PlanetSideGameObject with FactionAffinity { private var actor = ActorRef.noSender /** diff --git a/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinity.scala b/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinity.scala new file mode 100644 index 00000000..8fecc4de --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinity.scala @@ -0,0 +1,35 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.affinity + +import net.psforever.types.PlanetSideEmpire + +/** + * Keep track of the allegiance of the object in terms of its association to a `PlanetSideEmpire` value. + */ +trait FactionAffinity { + def Faction : PlanetSideEmpire.Value + + def Faction_=(fac : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = Faction +} + +object FactionAffinity { + /** + * Message that makes the server object transmit IFF feedback. + * @see AssertFactionAffinity + */ + final case class ConfirmFactionAffinity() + /** + * Message that makes the server object change allegiance to the specified faction value. + * Transmit IFF feedback when done. + * @param faction the allegiance to which to change + */ + final case class ConvertFactionAffinity(faction : PlanetSideEmpire.Value) + /** + * Message that responds to an IFF feedback request. + * Transmit IFF feedback when done. + * @see ConfirmFactionAffinity + * @param obj the governed object + * @param faction the allegiance to which the object belongs + */ + final case class AssertFactionAffinity(obj : FactionAffinity, faction : PlanetSideEmpire.Value) +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinityBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinityBehavior.scala new file mode 100644 index 00000000..21ab5b73 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/affinity/FactionAffinityBehavior.scala @@ -0,0 +1,42 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.affinity + +import akka.actor.Actor + +object FactionAffinityBehavior { + + /** + * A `trait` for inheritance of common implementable methods. + */ + sealed trait BasicAffinity { + def FactionObject : FactionAffinity + } + + /** + * The logic governing `FactionAffinity` objects that use the `ConvertFactionAffinity` message. + * This is a mix-in trait for combining with existing `Receive` logic. + */ + trait Convert extends BasicAffinity { + this : Actor => + + val convertBehavior : Receive = { + case FactionAffinity.ConvertFactionAffinity(faction) => + FactionObject.Faction = faction + sender ! FactionAffinity.AssertFactionAffinity(FactionObject, faction) + } + } + + /** + * The logic governing `FactionAffinity` objects that use the `ConfirmFactionAffinity` message. + * A case exists to catch `AssertFactionAffinity` messages for the same ends though they should not be used this way. + * This is a mix-in trait for combining with existing `Receive` logic. + */ + trait Check extends BasicAffinity { + this : Actor => + + val checkBehavior : Receive = { + case FactionAffinity.ConfirmFactionAffinity() | FactionAffinity.AssertFactionAffinity(_, _) => + sender ! FactionAffinity.AssertFactionAffinity(FactionObject, FactionObject.Faction) + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala deleted file mode 100644 index 90e6c490..00000000 --- a/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.objects.serverobject.doors - -import net.psforever.types.PlanetSideEmpire - -/** - * A temporary class to represent "facilities" and "structures." - * @param id the map id of the base - */ -class Base(private val id : Int) { - private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL - - def Id : Int = id - - def Faction : PlanetSideEmpire.Value = faction - - def Faction_=(emp : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = { - faction = emp - Faction - } -} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala index 5f9b26de..562ce8d0 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.doors -import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.Player +import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.UseItemMessage import net.psforever.types.Vector3 @@ -10,7 +10,7 @@ import net.psforever.types.Vector3 * A structure-owned server object that is a "door" that can open and can close. * @param ddef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class Door(private val ddef : DoorDefinition) extends PlanetSideServerObject { +class Door(private val ddef : DoorDefinition) extends Amenity { private var openState : Option[Player] = None /** a vector in the direction of the "outside" of a room; * typically, any locking utility is on that same "outside" */ diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala index 8f116331..0b112345 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala @@ -2,13 +2,16 @@ package net.psforever.objects.serverobject.doors import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** * An `Actor` that handles messages being dispatched to a specific `Door`. * @param door the `Door` object being governed */ -class DoorControl(door : Door) extends Actor { - def receive : Receive = { +class DoorControl(door : Door) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = door + + def receive : Receive = checkBehavior.orElse { case Door.Use(player, msg) => sender ! Door.DoorMessage(player, msg, door.Use(player, msg)) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala index 114a20f3..d9d9af33 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMech.scala @@ -3,8 +3,8 @@ 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.serverobject.mount.Mountable +import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.vehicles.Seat /** @@ -12,7 +12,7 @@ import net.psforever.objects.vehicles.Seat * 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 { +class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends Amenity with Mountable { private val seats : Map[Int, Seat] = Map( 0 -> new Seat(idef.Seats(0)) ) def Seats : Map[Int, Seat] = seats diff --git a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala index e47a675b..67f5d14c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala @@ -2,16 +2,23 @@ package net.psforever.objects.serverobject.implantmech import akka.actor.Actor -import net.psforever.objects.mount.MountableBehavior +import net.psforever.objects.serverobject.mount.MountableBehavior +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** * An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`. * @param mech the "mech" object being governed */ -class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with MountableBehavior { - override def MountableObject = mech +class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with FactionAffinityBehavior.Check + with MountableBehavior.Mount with MountableBehavior.Dismount { + def MountableObject = mech //do not add type! - def receive : Receive = mountableBehavior.orElse { - case _ => ; - } + def FactionObject : FactionAffinity = mech + + def receive : Receive = checkBehavior + .orElse(mountBehavior) + .orElse(dismountBehavior) + .orElse { + case _ => ; + } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala index a1c3380d..8142fd9a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala @@ -2,7 +2,7 @@ package net.psforever.objects.serverobject.locks import net.psforever.objects.Player -import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.Vector3 @@ -15,7 +15,7 @@ import net.psforever.types.Vector3 * The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper. * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class IFFLock(private val idef : IFFLockDefinition) extends PlanetSideServerObject { +class IFFLock(private val idef : IFFLockDefinition) extends Amenity { /** * An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */ diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala index bf9d1b81..0af05811 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala @@ -3,14 +3,17 @@ package net.psforever.objects.serverobject.locks import akka.actor.Actor import net.psforever.objects.serverobject.CommonMessages +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** * An `Actor` that handles messages being dispatched to a specific `IFFLock`. * @param lock the `IFFLock` object being governed * @see `CommonMessages` */ -class IFFLockControl(lock : IFFLock) extends Actor { - def receive : Receive = { +class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = lock + + def receive : Receive = checkBehavior.orElse { case CommonMessages.Hack(player) => lock.HackedBy = player diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala index 60ee7979..d614b53a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala @@ -1,19 +1,18 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.mblocker -import akka.actor.ActorContext +import akka.actor.{ActorContext, Props} import net.psforever.objects.GlobalDefinitions -import net.psforever.objects.definition.ObjectDefinition -import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.structures.Amenity -class Locker extends PlanetSideServerObject { - def Definition : ObjectDefinition = GlobalDefinitions.mb_locker +class Locker extends Amenity { + def Definition : LockerDefinition = GlobalDefinitions.mb_locker } object Locker { /** * Overloaded constructor. - * @return a `VehicleSpawnPad` object + * @return the `Locker` object */ def apply() : Locker = { new Locker() @@ -28,6 +27,7 @@ object Locker { */ def Constructor(id : Int, context : ActorContext) : Locker = { val obj = Locker() + obj.Actor = context.actorOf(Props(classOf[LockerControl], obj), s"${obj.Definition.Name}_$id") obj } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala new file mode 100644 index 00000000..2e037e64 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala @@ -0,0 +1,17 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.mblocker + +import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} + +/** + * An `Actor` that handles messages being dispatched to a specific `Locker`. + * @param locker the `Locker` object being governed + */ +class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = locker + + def receive : Receive = checkBehavior.orElse { + case _ => ; + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerDefinition.scala new file mode 100644 index 00000000..b73a9e53 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerDefinition.scala @@ -0,0 +1,12 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.mblocker + +import net.psforever.objects.definition.ObjectDefinition + +/** + * The definition for any `Locker`. + * Object Id 524. + */ +class LockerDefinition extends ObjectDefinition(524) { + Name = "mb_locker" +} diff --git a/common/src/main/scala/net/psforever/objects/mount/Mountable.scala b/common/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala similarity index 80% rename from common/src/main/scala/net/psforever/objects/mount/Mountable.scala rename to common/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala index 06b63faf..b65a251f 100644 --- a/common/src/main/scala/net/psforever/objects/mount/Mountable.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mount/Mountable.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects.mount +package net.psforever.objects.serverobject.mount import akka.actor.ActorRef import net.psforever.objects.Player @@ -62,6 +62,8 @@ object Mountable { */ final case class TryMount(player : Player, seat_num : Int) + final case class TryDismount(player : Player, seat_num : Int) + /** * A basic `Trait` connecting all of the actionable `Mountable` response messages. */ @@ -89,4 +91,19 @@ object Mountable { * @param seat_num the seat index */ final case class CanNotMount(obj : Mountable, seat_num : Int) extends Exchange + + /** + * Message sent in response to the player succeeding to disembark a `Mountable` object. + * The player was previously seated at the given index. + * @param obj the `Mountable` object + * @param seat_num the seat index + */ + final case class CanDismount(obj : Mountable, seat_num : Int) extends Exchange + /** + * Message sent in response to the player failing to disembark a `Mountable` object. + * The player is still seated at the given index. + * @param obj the `Mountable` object + * @param seat_num the seat index + */ + final case class CanNotDismount(obj : Mountable, seat_num : Int) extends Exchange } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala new file mode 100644 index 00000000..33f8aa3f --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/mount/MountableBehavior.scala @@ -0,0 +1,68 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.mount + +import akka.actor.Actor +import net.psforever.objects.entity.{Identifiable, WorldEntity} +import net.psforever.objects.serverobject.affinity.FactionAffinity +import net.psforever.types.Vector3 + +object MountableBehavior { + /** + * The logic governing `Mountable` objects that use the `TryMount` message. + * This is a mix-in trait for combining with existing `Receive` logic. + * @see `Seat` + * @see `Mountable` + */ + trait Mount { + this : Actor => + + def MountableObject : Mountable with Identifiable with WorldEntity with FactionAffinity + + val mountBehavior : Receive = { + case Mountable.TryMount(user, seat_num) => + val obj = MountableObject + obj.Seat(seat_num) match { + case Some(seat) => + if(user.Faction == obj.Faction && (seat.Occupant = user).contains(user)) { + user.VehicleSeated = obj.GUID + 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)) + } + } + } + + /** + * The logic governing `Mountable` objects that use the `TryDismount` message. + * This is a mix-in trait for combining with existing `Receive` logic. + * @see `Seat` + * @see `Mountable` + */ + trait Dismount { + this : Actor => + + def MountableObject : Mountable with Identifiable with WorldEntity with FactionAffinity + + val dismountBehavior : Receive = { + case Mountable.TryDismount(user, seat_num) => + val obj = MountableObject + obj.Seat(seat_num) match { + case Some(seat) => + if(seat.Bailable || obj.Velocity.isEmpty || Vector3.MagnitudeSquared(obj.Velocity.get).toInt == 0) { + seat.Occupant = None + user.VehicleSeated = None + sender ! Mountable.MountMessages(user, Mountable.CanDismount(obj, seat_num)) + } + else { + sender ! Mountable.MountMessages(user, Mountable.CanNotDismount(obj, seat_num)) + } + case None => + sender ! Mountable.MountMessages(user, Mountable.CanNotDismount(obj, seat_num)) + } + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala index 412774bf..8f1e75a4 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.pad import akka.actor.{Actor, ActorRef, Cancellable} +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.{DefaultCancellable, Player, Vehicle} import net.psforever.types.Vector3 @@ -29,7 +30,7 @@ import scala.concurrent.duration._ * 3. a callback location for sending messages. * @param pad the `VehicleSpawnPad` object being governed */ -class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor { +class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor with FactionAffinityBehavior.Check { /** an executor for progressing a vehicle order through the normal spawning logic */ private var process : Cancellable = DefaultCancellable.obj /** a list of vehicle orders that have been submitted for this spawn pad */ @@ -41,8 +42,9 @@ class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor { private[this] val log = org.log4s.getLogger private[this] def trace(msg : String) : Unit = log.trace(msg) + def FactionObject : FactionAffinity = pad - def receive : Receive = { + def receive : Receive = checkBehavior.orElse { case VehicleSpawnPad.VehicleOrder(player, vehicle) => trace(s"order from $player for $vehicle received") orders = orders :+ VehicleSpawnControl.OrderEntry(player, vehicle, sender) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala index 16c1357c..19df456d 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala @@ -2,8 +2,7 @@ package net.psforever.objects.serverobject.pad import net.psforever.objects.{Player, Vehicle} -import net.psforever.objects.definition.ObjectDefinition -import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.PlanetSideGUID /** @@ -16,8 +15,8 @@ import net.psforever.packet.game.PlanetSideGUID * @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields * @see `VehicleSpawnControl` */ -class VehicleSpawnPad(spDef : ObjectDefinition) extends PlanetSideServerObject { - def Definition : ObjectDefinition = spDef +class VehicleSpawnPad(spDef : VehicleSpawnPadDefinition) extends Amenity { + def Definition : VehicleSpawnPadDefinition = spDef } object VehicleSpawnPad { @@ -83,7 +82,7 @@ object VehicleSpawnPad { * @param spDef the spawn pad's definition entry * @return a `VehicleSpawnPad` object */ - def apply(spDef : ObjectDefinition) : VehicleSpawnPad = { + def apply(spDef : VehicleSpawnPadDefinition) : VehicleSpawnPad = { new VehicleSpawnPad(spDef) } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPadDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPadDefinition.scala new file mode 100644 index 00000000..aa797207 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPadDefinition.scala @@ -0,0 +1,12 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.pad + +import net.psforever.objects.definition.ObjectDefinition + +/** + * The definition for any `VehicleSpawnPad`. + * Object Id 800. + */ +class VehicleSpawnPadDefinition extends ObjectDefinition(800) { + Name = "spawn_pad" +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala new file mode 100644 index 00000000..6458757d --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala @@ -0,0 +1,60 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.structures + +import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.types.PlanetSideEmpire + +/** + * Amenities are elements of the game that belong to other elements of the game.
+ *
+ * Normal `PlanetSideServerObject` entities (server objects) tend to have properties that are completely internalized. + * An `Amenity` is a server object that maintains a fixed association with another server object. + * This association strips away at the internalization and redirects a reference to some properties somewhere else. + * An `Amenity` object belongs to its `Owner` object; + * the `Amenity` objects looks to its `Owner` object for some of its properties. + * @see `FactionAffinity` + */ +abstract class Amenity extends PlanetSideServerObject { + private var owner : PlanetSideServerObject = Building.NoBuilding + + def Faction : PlanetSideEmpire.Value = Owner.Faction + + /** + * Reference the object that is in direct association with (is superior to) this one. + * @return the object associated as this object's "owner" + */ + def Owner : PlanetSideServerObject = owner + + /** + * Set an object to have a direct association with (be superior to) this one. + * @see `Amenity.AmenityTarget` + * @param obj the object trying to become associated as this object's "owner" + * @tparam T a validation of the type of object that can be an owner + * @return the object associated as this object's "owner" + */ + def Owner_=[T : Amenity.AmenityTarget](obj : T) : PlanetSideServerObject = { + owner = obj.asInstanceOf[PlanetSideServerObject] + Owner + } +} + +object Amenity { + /** + * A `trait` for validating the type of object that can be allowed to become an `Amenity` object's `Owner`.
+ *
+ * The `Owner` defaults to a type of `PlanetSideServerObject` in reference type; + * but, that distinction is mainly to allow for a common ancestor with appropriate methods. + * Only certain types of `PlanetSideServerObject` are formally allowed to be owners. + * In execution, the `T` is the type of object that implicitly converts into an acceptable type of sub-object. + * The companion object maintains the hardcoded conversions. + * If such an implicit conversion does not exist, the assignment is unacceptable at compile time. + * @tparam T the permitted type of object + */ + sealed trait AmenityTarget[T] + + object AmenityTarget { + import net.psforever.objects.Vehicle + implicit object BuildingTarget extends AmenityTarget[Building] { } + implicit object VehicleTarget extends AmenityTarget[Vehicle] { } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala new file mode 100644 index 00000000..84a14c2c --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -0,0 +1,56 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.structures + +import akka.actor.ActorContext +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 + +class Building(private val id : Int, private val zone : Zone) extends PlanetSideServerObject { + private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL + private var amenities : List[Amenity] = List.empty + GUID = PlanetSideGUID(0) + + def Id : Int = id + + def Faction : PlanetSideEmpire.Value = faction + + override def Faction_=(fac : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = { + faction = fac + Faction + } + + def Amenities : List[Amenity] = amenities + + def Amenities_=(obj : Amenity) : List[Amenity] = { + amenities = amenities :+ obj + obj.Owner = this + amenities + } + + def Zone : Zone = zone + + def Definition: ObjectDefinition = Building.BuildingDefinition +} + +object Building { + final val NoBuilding : Building = new Building(0, Zone.Nowhere) { + 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 Structure(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") + obj + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala new file mode 100644 index 00000000..2f7b7b7c --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala @@ -0,0 +1,20 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.structures + +import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} + +class BuildingControl(building : Building) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = building + + def receive : Receive = checkBehavior.orElse { + case FactionAffinity.ConvertFactionAffinity(faction) => + val originalAffinity = building.Faction + if(originalAffinity != (building.Faction = faction)) { + building.Amenities.foreach(_.Actor forward FactionAffinity.ConfirmFactionAffinity()) + } + sender ! FactionAffinity.AssertFactionAffinity(building, faction) + + case _ => ; + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/FoundationBuilder.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/FoundationBuilder.scala new file mode 100644 index 00000000..fdf247e7 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/FoundationBuilder.scala @@ -0,0 +1,30 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.structures + +import akka.actor.ActorContext +import net.psforever.objects.zones.Zone + +/** + * Defer establishment of a `Building` object until the location for the object is correct (in the correct zone) + * and a `context` in the proper `Actor` hierarchy of that zone exists in scope. + * @see `ServerObjectBuilder` + * @see `Building` + * @param constructor a curried function that eventually constructs a `Building` object + */ +class FoundationBuilder(private val constructor : (Int, Zone, ActorContext)=>Building) { + def Build(id : Int, zone : Zone)(implicit context : ActorContext = null) : Building = { + val obj : Building = constructor(id, zone, context) + obj + } +} + +object FoundationBuilder { + /** + * Overloaded constructor. + * @param constructor a curried function that eventually constructs a `Building` object + * @return a `FoundationBuilder` object + */ + def apply(constructor : (Int, Zone, ActorContext)=>Building) : FoundationBuilder = { + new FoundationBuilder(constructor) + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala index c7c9b5bd..3f6f6389 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala @@ -2,7 +2,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player -import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{TransactionType, Vector3} @@ -10,7 +10,7 @@ import net.psforever.types.{TransactionType, Vector3} * A structure-owned server object that is a "terminal" that can be accessed for amenities and services. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class Terminal(tdef : TerminalDefinition) extends PlanetSideServerObject { +class Terminal(tdef : TerminalDefinition) extends Amenity { /** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */ private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None @@ -65,18 +65,23 @@ class Terminal(tdef : TerminalDefinition) extends PlanetSideServerObject { * @return an actionable message that explains what resulted from interacting with this `Terminal` */ def Request(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { - msg.transaction_type match { - case TransactionType.Buy | TransactionType.Learn => - tdef.Buy(player, msg) + if(Faction == player.Faction || HackedBy.isDefined) { + msg.transaction_type match { + case TransactionType.Buy | TransactionType.Learn => + tdef.Buy(player, msg) - case TransactionType.Sell => - tdef.Sell(player, msg) + case TransactionType.Sell => + tdef.Sell(player, msg) - case TransactionType.InfantryLoadout => - tdef.Loadout(player, msg) + case TransactionType.InfantryLoadout => + tdef.Loadout(player, msg) - case _ => - Terminal.NoDeal() + case _ => + Terminal.NoDeal() + } + } + else { + Terminal.NoDeal() } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index c74ada79..ad63babf 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -2,13 +2,16 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** * An `Actor` that handles messages being dispatched to a specific `Terminal`. * @param term the `Terminal` object being governed */ -class TerminalControl(term : Terminal) extends Actor { - def receive : Receive = { +class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = term + + def receive : Receive = checkBehavior.orElse { case Terminal.Request(player, msg) => sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala index 016ce631..70639d45 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -3,7 +3,8 @@ package net.psforever.objects.vehicles import akka.actor.Actor import net.psforever.objects.Vehicle -import net.psforever.objects.mount.MountableBehavior +import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** * An `Actor` that handles messages being dispatched to a specific `Vehicle`.
@@ -12,17 +13,32 @@ import net.psforever.objects.mount.MountableBehavior * 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 with MountableBehavior { - override def MountableObject = vehicle +class VehicleControl(private val vehicle : Vehicle) extends Actor + with FactionAffinityBehavior.Check + with MountableBehavior.Mount + with MountableBehavior.Dismount { + def MountableObject = vehicle //do not add type! - def receive : Receive = mountableBehavior.orElse { - case Vehicle.PrepareForDeletion => - context.become(Disabled) + def FactionObject : FactionAffinity = vehicle - case _ => ; - } + def receive : Receive = Enabled - def Disabled : Receive = { - case _ => ; - } + def Enabled : Receive = checkBehavior + .orElse(mountBehavior) + .orElse(dismountBehavior) + .orElse { + case Vehicle.PrepareForDeletion => + context.become(Disabled) + + case _ => ; + } + + def Disabled : Receive = checkBehavior + .orElse(dismountBehavior) + .orElse { + case Vehicle.Reactivate => + context.become(Enabled) + + case _ => ; + } } diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala index dbd2501d..13e05504 100644 --- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -3,19 +3,20 @@ package net.psforever.objects.zones import akka.actor.{ActorContext, ActorRef, Props} import akka.routing.RandomPool -import net.psforever.objects.serverobject.doors.Base import net.psforever.objects.{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.packet.GamePacket import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.Vector3 import scala.annotation.tailrec import scala.collection.mutable.ListBuffer +import scala.collection.immutable.{Map => PairMap} /** * A server object representing the one-landmass planets as well as the individual subterranean caverns.
@@ -55,7 +56,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { /** */ private var transport : ActorRef = ActorRef.noSender - private var bases : List[Base] = List() + private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building] /** * Establish the basic accessible conditions necessary for a functional `Zone`.
@@ -79,8 +80,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { Map.LocalObjects.foreach({ builderObject => builderObject.Build }) - - MakeBases(Map.LocalBases) + MakeBuildings(context) + AssignAmenities() } } @@ -217,13 +218,20 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { def Transport : ActorRef = transport - def MakeBases(num : Int) : List[Base] = { - bases = (1 to num).map(id => new Base(id)).toList - bases + def Building(id : Int) : Option[Building] = { + buildings.get(id) } - def Base(id : Int) : Option[Base] = { - bases.lift(id) + private def MakeBuildings(implicit context : ActorContext) : PairMap[Int, Building] = { + val buildingList = Map.LocalBuildings + buildings = buildingList.map({case(building_id, constructor) => building_id -> constructor.Build(building_id, this) }) + buildings + } + + private def AssignAmenities() : Unit = { + Map.ObjectToBuilding.foreach({ case(object_guid, building_id) => + buildings(building_id).Amenities = guid(object_guid).get.asInstanceOf[Amenity] + }) } /** @@ -264,6 +272,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { } object Zone { + final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99) + /** * Message to initialize the `Zone`. * @see `Zone.Init(implicit ActorContext)` diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala index d72d7db4..af5c06ea 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala @@ -2,6 +2,8 @@ package net.psforever.objects.zones import akka.actor.Actor +import net.psforever.objects.PlanetSideGameObject +import org.log4s.Logger /** * na @@ -20,14 +22,16 @@ class ZoneActor(zone : Zone) extends Actor { } def ZoneSetupCheck(): Unit = { + import ZoneActor._ def guid(id : Int) = zone.GUID(id) val map = zone.Map val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity") + val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog) //check base to object associations - map.ObjectToBase.foreach({ case((object_guid, base_id)) => - if(zone.Base(base_id).isEmpty) { - slog.error(s"expected a base #$base_id") + map.ObjectToBuilding.foreach({ case((object_guid, base_id)) => + if(zone.Building(base_id).isEmpty) { + slog.error(s"expected a building at id #$base_id") } if(guid(object_guid).isEmpty) { slog.error(s"expected object id $object_guid to exist, but it did not") @@ -35,74 +39,80 @@ class ZoneActor(zone : Zone) extends Actor { }) //check door to lock association - import net.psforever.objects.serverobject.doors.Door - import net.psforever.objects.serverobject.locks.IFFLock map.DoorToLock.foreach({ case((door_guid, lock_guid)) => - try { - if(!guid(door_guid).get.isInstanceOf[Door]) { - slog.error(s"expected id $door_guid to be a door, but it was not") - } - } - catch { - case _ : Exception => - 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") - } - } - catch { - case _ : Exception => - slog.error(s"expected an IFF locks at id $lock_guid but no object is initialized") - } + validateObject(door_guid, DoorCheck, "door") + validateObject(lock_guid, LockCheck, "IFF lock") }) //check vehicle terminal to spawn pad association - import net.psforever.objects.serverobject.pad.VehicleSpawnPad - import net.psforever.objects.serverobject.terminals.Terminal map.TerminalToSpawnPad.foreach({ case ((term_guid, pad_guid)) => - try { - if(!guid(term_guid).get.isInstanceOf[Terminal]) { //TODO check is vehicle terminal - slog.error(s"expected id $term_guid to be a terminal, but it was not") - } - } - catch { - case _ : Exception => - slog.error(s"expected a terminal at id $term_guid but no object is initialized") - } - try { - if(!guid(pad_guid).get.isInstanceOf[VehicleSpawnPad]) { - slog.error(s"expected id $pad_guid to be a spawn pad, but it was not") - } - } - catch { - case _ : Exception => - slog.error(s"expected a spawn pad at id $pad_guid but no object is initialized") - } + validateObject(term_guid, TerminalCheck, "vehicle terminal") + validateObject(pad_guid, VehicleSpawnPadCheck, "vehicle spawn pad") }) //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") - } + validateObject(mech_guid, ImplantMechCheck, "implant terminal mech") + validateObject(interface_guid, TerminalCheck, "implant terminal interface") }) } } + +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. + * @param guid access to an association between unique numbers and objects using some of those unique numbers + * @param elog a contraction of "error log;" + * accepts `String` data + * @param object_guid the unique indentifier being checked against the `guid` access point + * @param test a test for the discovered object; + * expects at least `Type` checking + * @param description an explanation of how the object, if not discovered, should be identified + * @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) + (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") + false + } + else { + true + } + } + catch { + case _ : Exception => + elog.error(s"expected a $description at id $object_guid but no object is initialized") + false + } + } + + def LockCheck(obj : PlanetSideGameObject) : Boolean = { + import net.psforever.objects.serverobject.locks.IFFLock + obj.isInstanceOf[IFFLock] + } + + def DoorCheck(obj : PlanetSideGameObject) : Boolean = { + import net.psforever.objects.serverobject.doors.Door + obj.isInstanceOf[Door] + } + + def TerminalCheck(obj : PlanetSideGameObject) : Boolean = { + import net.psforever.objects.serverobject.terminals.Terminal + obj.isInstanceOf[Terminal] + } + + def ImplantMechCheck(obj : PlanetSideGameObject) : Boolean = { + import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech + obj.isInstanceOf[ImplantTerminalMech] + } + + def VehicleSpawnPadCheck(obj : PlanetSideGameObject) : Boolean = { + import net.psforever.objects.serverobject.pad.VehicleSpawnPad + obj.isInstanceOf[VehicleSpawnPad] + } +} diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala index 36a5bbe5..d8019274 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.zones +import net.psforever.objects.serverobject.structures.FoundationBuilder import net.psforever.objects.serverobject.ServerObjectBuilder /** @@ -29,7 +30,7 @@ class ZoneMap(private val name : String) { 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 + private var buildings : Map[Int, FoundationBuilder] = Map() def Name : String = name @@ -49,19 +50,19 @@ class ZoneMap(private val name : String) { localObjects = localObjects :+ obj } - def LocalBases : Int = numBases + def LocalBuildings : Map[Int, FoundationBuilder] = buildings - def LocalBases_=(num : Int) : Int = { - if(num > 0) { - numBases = num + def LocalBuilding(building_id : Int, constructor : FoundationBuilder) : Int = { + if(building_id > 0) { + buildings = buildings ++ Map(building_id -> constructor) } - LocalBases + buildings.size } - def ObjectToBase : Map[Int, Int] = linkObjectBase + def ObjectToBuilding : Map[Int, Int] = linkObjectBase - def ObjectToBase(object_guid : Int, base_id : Int) : Unit = { - linkObjectBase = linkObjectBase ++ Map(object_guid -> base_id) + def ObjectToBuilding(object_guid : Int, building_id : Int) : Unit = { + linkObjectBase = linkObjectBase ++ Map(object_guid -> building_id) } def DoorToLock : Map[Int, Int] = linkDoorLock diff --git a/common/src/test/scala/objects/BuildingTest.scala b/common/src/test/scala/objects/BuildingTest.scala new file mode 100644 index 00000000..122f3d99 --- /dev/null +++ b/common/src/test/scala/objects/BuildingTest.scala @@ -0,0 +1,169 @@ +// Copyright (c) 2017 PSForever +package objects + +import akka.actor.{ActorRef, Props} +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} +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types.PlanetSideEmpire +import org.specs2.mutable.Specification + +import scala.concurrent.duration.Duration + +class AmenityTest extends Specification { + class AmenityObject extends Amenity { + def Definition : ObjectDefinition = null + } + + "Amenity" should { + "construct" in { + val ao = new AmenityObject() + ao.Owner mustEqual Building.NoBuilding + } + + "can be owned by a building" in { + val ao = new AmenityObject() + val bldg = Building(10, Zone.Nowhere) + + ao.Owner = bldg + ao.Owner mustEqual bldg + } + + "be owned by a vehicle" in { + import net.psforever.objects.Vehicle + val ao = new AmenityObject() + val veh = Vehicle(GlobalDefinitions.quadstealth) + + ao.Owner = veh + ao.Owner mustEqual veh + } + + "not be owned by an unexpected object" in { + val ao = new AmenityObject() + //ao.Owner = net.psforever.objects.serverobject.mblocker.Locker() //will not compile + ok + } + + "confer faction allegiance through ownership" in { + //see FactionAffinityTest + val ao = new AmenityObject() + val bldg = Building(10, Zone.Nowhere) + ao.Owner = bldg + bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL + ao.Faction mustEqual PlanetSideEmpire.NEUTRAL + + bldg.Faction = PlanetSideEmpire.TR + bldg.Faction mustEqual PlanetSideEmpire.TR + ao.Faction mustEqual PlanetSideEmpire.TR + } + } +} + +class BuildingTest extends Specification { + "Building" should { + "construct" in { + val bldg = Building(10, Zone.Nowhere) + bldg.Id mustEqual 10 + bldg.Actor mustEqual ActorRef.noSender + bldg.Amenities mustEqual Nil + bldg.Zone mustEqual Zone.Nowhere + bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL + } + + "change faction affinity" in { + val bldg = Building(10, Zone.Nowhere) + bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL + + bldg.Faction = PlanetSideEmpire.TR + bldg.Faction mustEqual PlanetSideEmpire.TR + } + + "keep track of amenities" in { + val bldg = Building(10, Zone.Nowhere) + val door1 = Door(GlobalDefinitions.door) + val door2 = Door(GlobalDefinitions.door) + + bldg.Amenities mustEqual Nil + bldg.Amenities = door2 + bldg.Amenities mustEqual List(door2) + bldg.Amenities = door1 + bldg.Amenities mustEqual List(door2, door1) + door1.Owner mustEqual bldg + door2.Owner mustEqual bldg + } + } +} + +class BuildingControl1Test extends ActorTest { + "Building Control" should { + "construct" in { + val bldg = Building(10, Zone.Nowhere) + bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") + assert(bldg.Actor != ActorRef.noSender) + } + } +} + +class BuildingControl2Test extends ActorTest { + "Building Control" should { + "convert and assert faction affinity on convert request" in { + val bldg = Building(10, Zone.Nowhere) + bldg.Faction = PlanetSideEmpire.TR + bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") + assert(bldg.Faction == PlanetSideEmpire.TR) + + bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS) + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[FactionAffinity.AssertFactionAffinity]) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].obj == bldg) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].faction == PlanetSideEmpire.VS) + assert(bldg.Faction == PlanetSideEmpire.VS) + } + } +} + +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) + bldg.Faction = PlanetSideEmpire.TR + bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "building-test") + val door1 = Door(GlobalDefinitions.door) + door1.GUID = PlanetSideGUID(1) + door1.Actor = system.actorOf(Props(classOf[DoorControl], door1), "door1-test") + val door2 = Door(GlobalDefinitions.door) + door2.GUID = PlanetSideGUID(2) + door2.Actor = system.actorOf(Props(classOf[DoorControl], door2), "door2-test") + bldg.Amenities = door2 + bldg.Amenities = door1 + assert(bldg.Faction == PlanetSideEmpire.TR) + assert(bldg.Amenities.length == 2) + assert(bldg.Amenities.head == door2) + assert(bldg.Amenities(1) == door1) + + bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS) + val reply = receiveN(3, Duration.create(500, "ms")) + assert(reply.length == 3) + var building_count = 0 + var door_count = 0 + reply.foreach(item => { + assert(item.isInstanceOf[FactionAffinity.AssertFactionAffinity]) + val item2 = item.asInstanceOf[FactionAffinity.AssertFactionAffinity] + item2.obj match { + case _ : Building => + building_count += 1 + case _ : Door => + door_count += 1 + case _ => + assert(false) + } + assert(item2.faction == PlanetSideEmpire.VS) + }) + assert(building_count == 1 && door_count == 2) + } + } +} diff --git a/common/src/test/scala/objects/DoorTest.scala b/common/src/test/scala/objects/DoorTest.scala index d95b77ca..228d44ab 100644 --- a/common/src/test/scala/objects/DoorTest.scala +++ b/common/src/test/scala/objects/DoorTest.scala @@ -1,9 +1,11 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.{ActorRef, ActorSystem, Props} import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.serverobject.doors.{Door, DoorControl} +import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage} import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} import org.specs2.mutable.Specification @@ -11,6 +13,8 @@ import org.specs2.mutable.Specification import scala.concurrent.duration.Duration class DoorTest extends Specification { + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + "Door" should { "construct" in { Door(GlobalDefinitions.door) @@ -24,7 +28,6 @@ class DoorTest extends Specification { } "be opened and closed (1; manual)" in { - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val door = Door(GlobalDefinitions.door) door.isOpen mustEqual false door.Open mustEqual None @@ -39,7 +42,6 @@ class DoorTest extends Specification { } "be opened and closed (2; toggle)" in { - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val msg = UseItemMessage(PlanetSideGUID(6585), 0, PlanetSideGUID(372), 4294967295L, false, Vector3(5.0f,0.0f,0.0f), Vector3(0.0f,0.0f,0.0f), 11, 25, 0, 364) val door = Door(GlobalDefinitions.door) door.Open mustEqual None @@ -85,9 +87,7 @@ class DoorControl1Test extends ActorTest() { class DoorControl2Test extends ActorTest() { "DoorControl" should { "open on use" in { - val door = Door(GlobalDefinitions.door) - door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR) val msg = UseItemMessage(PlanetSideGUID(1), 0, PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked assert(door.Open.isEmpty) @@ -106,8 +106,7 @@ class DoorControl2Test extends ActorTest() { class DoorControl3Test extends ActorTest() { "DoorControl" should { "do nothing if given garbage" in { - val door = Door(GlobalDefinitions.door) - door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door") + val (_, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR) assert(door.Open.isEmpty) door.Actor ! "trash" @@ -117,3 +116,13 @@ class DoorControl3Test extends ActorTest() { } } } + +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.Faction = faction + (Player("test", faction, CharacterGender.Male, 0, 0), door) + } +} diff --git a/common/src/test/scala/objects/FactionAffinityTest.scala b/common/src/test/scala/objects/FactionAffinityTest.scala new file mode 100644 index 00000000..c4598896 --- /dev/null +++ b/common/src/test/scala/objects/FactionAffinityTest.scala @@ -0,0 +1,131 @@ +// Copyright (c) 2017 PSForever +package objects + +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.zones.Zone +import net.psforever.types.PlanetSideEmpire +import org.specs2.mutable.Specification + +import scala.concurrent.duration.Duration + +class FactionAffinityTest extends Specification { + "FactionAffinity" should { + "construct (basic)" in { + val obj = new FactionAffinity { def Faction = PlanetSideEmpire.TR } + obj.Faction mustEqual PlanetSideEmpire.TR + } + + "construct (part of)" in { + val obj = new Door(GlobalDefinitions.door) + obj.Faction mustEqual PlanetSideEmpire.NEUTRAL + } + + "can not change affinity directly (basic)" in { + val obj = new FactionAffinity { def Faction = PlanetSideEmpire.TR } + (obj.Faction = PlanetSideEmpire.NC) mustEqual PlanetSideEmpire.TR + } + + "can not change affinity directly (part of)" in { + val obj = new Door(GlobalDefinitions.door) + (obj.Faction = PlanetSideEmpire.TR) mustEqual PlanetSideEmpire.NEUTRAL + } + + "inherits affinity from owner 1" in { + val obj = new Door(GlobalDefinitions.door) + obj.Owner.Faction mustEqual PlanetSideEmpire.NEUTRAL + (obj.Faction = PlanetSideEmpire.TR) mustEqual PlanetSideEmpire.NEUTRAL + } + + "inherits affinity from owner 2" in { + val obj = new Door(GlobalDefinitions.door) + val bldg = new Building(1, Zone.Nowhere) + obj.Owner = bldg + obj.Faction mustEqual PlanetSideEmpire.NEUTRAL + + bldg.Faction = PlanetSideEmpire.TR + obj.Faction mustEqual PlanetSideEmpire.TR + + bldg.Faction = PlanetSideEmpire.NC + obj.Faction mustEqual PlanetSideEmpire.NC + } + } +} + +class FactionAffinity1Test extends ActorTest() { + "FactionAffinity" should { + "assert affinity on confirm request" in { + val obj = FactionAffinityTest.SetUpAgent + obj.Faction = PlanetSideEmpire.VS //object is a type that can be changed directly + assert(obj.Faction == PlanetSideEmpire.VS) + + obj.Actor ! FactionAffinity.ConfirmFactionAffinity() + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[FactionAffinity.AssertFactionAffinity]) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].obj == obj) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].faction == PlanetSideEmpire.VS) + } + } +} + +class FactionAffinity2Test extends ActorTest() { + "FactionAffinity" should { + "assert affinity on assert request" in { + val obj = FactionAffinityTest.SetUpAgent + obj.Faction = PlanetSideEmpire.VS //object is a type that can be changed directly + assert(obj.Faction == PlanetSideEmpire.VS) + + obj.Actor ! FactionAffinity.AssertFactionAffinity(obj, PlanetSideEmpire.NEUTRAL) + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[FactionAffinity.AssertFactionAffinity]) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].obj == obj) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].faction == PlanetSideEmpire.VS) + } + } +} + +class FactionAffinity3Test extends ActorTest() { + "FactionAffinity" should { + "convert and assert affinity on convert request" in { + val obj = FactionAffinityTest.SetUpAgent + obj.Faction = PlanetSideEmpire.VS //object is a type that can be changed directly + assert(obj.Faction == PlanetSideEmpire.VS) + + obj.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.TR) + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[FactionAffinity.AssertFactionAffinity]) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].obj == obj) + assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].faction == PlanetSideEmpire.TR) + assert(obj.Faction == PlanetSideEmpire.TR) + } + } +} + +object FactionAffinityTest { + import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior + + private class AffinityControl(obj : FactionAffinity) extends Actor + with FactionAffinityBehavior.Check + with FactionAffinityBehavior.Convert { + override def FactionObject = obj + def receive = checkBehavior.orElse(convertBehavior).orElse { case _ => } + } + + def SetUpAgent(implicit system : ActorSystem) = { + val obj = new Vehicle(GlobalDefinitions.quadstealth) + obj.Actor = system.actorOf(Props(classOf[FactionAffinityTest.AffinityControl], obj), "test") + obj + } + + def FreeFactionObject : FactionAffinity = new FactionAffinity() { + private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL + def Faction : PlanetSideEmpire.Value = faction + override def Faction_=(fac : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = { + faction = fac + faction + } + } +} \ No newline at end of file diff --git a/common/src/test/scala/objects/IFFLockTest.scala b/common/src/test/scala/objects/IFFLockTest.scala index 54be04d1..05eb1e2f 100644 --- a/common/src/test/scala/objects/IFFLockTest.scala +++ b/common/src/test/scala/objects/IFFLockTest.scala @@ -1,10 +1,13 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.{ActorRef, ActorSystem, Props} import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.{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.zones.Zone import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.{CharacterGender, PlanetSideEmpire} import org.specs2.mutable.Specification @@ -33,9 +36,7 @@ class IFFLockControl1Test extends ActorTest() { class IFFLockControl2Test extends ActorTest() { "IFFLockControl" should { "can hack" in { - val lock = IFFLock(GlobalDefinitions.lock_external) - lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR) player.GUID = PlanetSideGUID(1) assert(lock.HackedBy.isEmpty) @@ -49,9 +50,7 @@ class IFFLockControl2Test extends ActorTest() { class IFFLockControl3Test extends ActorTest() { "IFFLockControl" should { "can hack" in { - val lock = IFFLock(GlobalDefinitions.lock_external) - lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR) player.GUID = PlanetSideGUID(1) assert(lock.HackedBy.isEmpty) @@ -64,3 +63,13 @@ class IFFLockControl3Test extends ActorTest() { } } } + +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.Faction = faction + (Player("test", faction, CharacterGender.Male, 0, 0), lock) + } +} diff --git a/common/src/test/scala/objects/LockerTest.scala b/common/src/test/scala/objects/LockerTest.scala new file mode 100644 index 00000000..ae5ffb3a --- /dev/null +++ b/common/src/test/scala/objects/LockerTest.scala @@ -0,0 +1,36 @@ +// Copyright (c) 2017 PSForever +package objects + +import akka.actor.{ActorRef, Props} +import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.serverobject.affinity.FactionAffinity +import net.psforever.objects.serverobject.mblocker.{Locker, LockerControl} +import net.psforever.types.PlanetSideEmpire +import org.specs2.mutable._ + +class LockerTest extends Specification { + "LockerDefinition" should { + "define" in { + GlobalDefinitions.mb_locker.ObjectId mustEqual 524 + GlobalDefinitions.mb_locker.Name mustEqual "mb_locker" + } + } + + "Locker" should { + "construct" in { + val locker = new Locker() + locker.Actor mustEqual ActorRef.noSender + } + } +} + +class LockerControlTest extends ActorTest { + "LockerControl" should { + "construct" in { + val locker = new Locker() + locker.Actor = system.actorOf(Props(classOf[LockerControl], locker), "test") + locker.Actor ! FactionAffinity.ConfirmFactionAffinity() + expectMsg(FactionAffinity.AssertFactionAffinity(locker, PlanetSideEmpire.NEUTRAL)) + } + } +} diff --git a/common/src/test/scala/objects/MountableTest.scala b/common/src/test/scala/objects/MountableTest.scala index 3b33ecd3..83ba5bab 100644 --- a/common/src/test/scala/objects/MountableTest.scala +++ b/common/src/test/scala/objects/MountableTest.scala @@ -4,9 +4,10 @@ package objects import akka.actor.{Actor, ActorRef, Props} import net.psforever.objects.Player import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition} -import net.psforever.objects.mount.{Mountable, MountableBehavior} +import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.vehicles.Seat +import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.{CharacterGender, PlanetSideEmpire} import scala.concurrent.duration.Duration @@ -80,12 +81,15 @@ object MountableTest { None } } - def Definition : ObjectDefinition = null //eh whatever + GUID = PlanetSideGUID(1) + //eh whatever + def Faction = PlanetSideEmpire.TR + def Definition : ObjectDefinition = null } - class MountableTestControl(obj : Mountable) extends Actor with MountableBehavior { + class MountableTestControl(obj : PlanetSideServerObject with Mountable) extends Actor with MountableBehavior.Mount with MountableBehavior.Dismount { override def MountableObject = obj - def receive : Receive = mountableBehavior + def receive : Receive = mountBehavior.orElse(dismountBehavior) } } diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala index 863278f4..73f80203 100644 --- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala +++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala @@ -5,10 +5,26 @@ import akka.actor.{Actor, 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} +import net.psforever.objects.zones.Zone import net.psforever.types.Vector3 import scala.concurrent.duration.Duration +class BuildingBuilderTest extends ActorTest { + "Building object" should { + "build" in { + val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], 10, Zone.Nowhere), "building") + actor ! "!" + + val reply = receiveOne(Duration.create(1000, "ms")) + assert(reply.isInstanceOf[Building]) + assert(reply.asInstanceOf[Building].Id == 10) + assert(reply.asInstanceOf[Building].Zone == Zone.Nowhere) + } + } +} + class DoorObjectBuilderTest1 extends ActorTest { import net.psforever.objects.serverobject.doors.Door "Door object" should { @@ -150,4 +166,11 @@ object ServerObjectBuilderTest { sender ! builder.Build(context, hub) } } + + class BuildingTestActor(building_id : Int, zone : Zone) extends Actor { + def receive : Receive = { + case _ => + sender ! FoundationBuilder(Building.Structure).Build(building_id, zone)(context) + } + } } diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala index 61f1deeb..f595da65 100644 --- a/common/src/test/scala/objects/VehicleSpawnPadTest.scala +++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala @@ -1,9 +1,11 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +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.vehicles.VehicleControl +import net.psforever.objects.zones.Zone import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} @@ -40,22 +42,20 @@ class VehicleSpawnControl1Test extends ActorTest() { class VehicleSpawnControl2Test extends ActorTest() { "VehicleSpawnControl" should { "spawn a vehicle" in { - val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) - obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR) player.Spawn val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) vehicle.GUID = PlanetSideGUID(1) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle") - obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) val reply = receiveOne(Duration.create(10000, "ms")) assert(reply == VehicleSpawnPad.ConcealPlayer) //explicit: isInstanceOf does not work val reply2 = receiveOne(Duration.create(10000, "ms")) assert(reply2.isInstanceOf[VehicleSpawnPad.LoadVehicle]) assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].vehicle == vehicle) - assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].pad == obj) + assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].pad == pad) player.VehicleOwned = Some(vehicle.GUID) val reply3 = receiveOne(Duration.create(10000, "ms")) @@ -78,14 +78,12 @@ class VehicleSpawnControl2Test extends ActorTest() { class VehicleSpawnControl3Test extends ActorTest() { "VehicleSpawnControl" should { "not spawn a vehicle if player is dead" in { - val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) - obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR) val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) vehicle.GUID = PlanetSideGUID(1) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle") - obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) val reply = receiveOne(Duration.create(5000, "ms")) assert(reply == null) } @@ -95,16 +93,24 @@ class VehicleSpawnControl3Test extends ActorTest() { class VehicleSpawnControl4Test extends ActorTest() { "VehicleSpawnControl" should { "not spawn a vehicle if vehicle Actor is missing" in { - val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) - obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, pad) = VehicleSpawnPadControl.SetUpAgents(PlanetSideEmpire.TR) player.Spawn val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) vehicle.GUID = PlanetSideGUID(1) - obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) val reply = receiveOne(Duration.create(5000, "ms")) assert(reply == null) } } } + +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.Faction = faction + (Player("test", faction, CharacterGender.Male, 0, 0), pad) + } +} diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala index eba1463a..1bd7abfa 100644 --- a/common/src/test/scala/objects/VehicleTest.scala +++ b/common/src/test/scala/objects/VehicleTest.scala @@ -1,13 +1,17 @@ // Copyright (c) 2017 PSForever package objects +import akka.actor.Props import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.objects.definition.SeatDefinition -import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, SeatArmorRestriction, VehicleLockState} +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 org.specs2.mutable._ +import scala.concurrent.duration.Duration + class VehicleTest extends Specification { "SeatDefinition" should { @@ -254,3 +258,49 @@ 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) + player1.GUID = PlanetSideGUID(1) + val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(3) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + + vehicle.Actor ! Mountable.TryMount(player1, 0) + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[Mountable.MountMessages]) + + vehicle.Actor ! Vehicle.PrepareForDeletion + vehicle.Actor ! Mountable.TryMount(player2, 1) + expectNoMsg(Duration.create(200, "ms")) + } + } +} + +class VehicleControl2Test extends ActorTest { + "Vehicle Control" should { + "reactivate and resume handling mount messages" in { + val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + player1.GUID = PlanetSideGUID(1) + val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + player2.GUID = PlanetSideGUID(2) + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(3) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + + vehicle.Actor ! Mountable.TryMount(player1, 0) + receiveOne(Duration.create(100, "ms")) //discard + vehicle.Actor ! Vehicle.PrepareForDeletion + vehicle.Actor ! Mountable.TryMount(player2, 1) + expectNoMsg(Duration.create(200, "ms")) + + vehicle.Actor ! Vehicle.Reactivate + vehicle.Actor ! Mountable.TryMount(player2, 1) + val reply = receiveOne(Duration.create(100, "ms")) + assert(reply.isInstanceOf[Mountable.MountMessages]) + } + } +} diff --git a/common/src/test/scala/objects/ZoneTest.scala b/common/src/test/scala/objects/ZoneTest.scala index a16eeb10..024c8aef 100644 --- a/common/src/test/scala/objects/ZoneTest.scala +++ b/common/src/test/scala/objects/ZoneTest.scala @@ -1,39 +1,44 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.ActorRef +import akka.actor.{ActorContext, ActorRef} 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 org.specs2.mutable.Specification class ZoneTest extends Specification { + def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding } + "ZoneMap" should { - //TODO these are temporary tests as the current ZoneMap is a kludge "construct" in { new ZoneMap("map13") ok } "references bases by a positive building id (defaults to 0)" in { + def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding } + val map = new ZoneMap("map13") - map.LocalBases mustEqual 0 - map.LocalBases = 10 - map.LocalBases mustEqual 10 - map.LocalBases = -1 - map.LocalBases mustEqual 10 + map.LocalBuildings mustEqual Map.empty + map.LocalBuilding(10, FoundationBuilder(test)) + map.LocalBuildings.keySet.contains(10) mustEqual true + map.LocalBuilding(-1, FoundationBuilder(test)) + map.LocalBuildings.keySet.contains(10) mustEqual true + map.LocalBuildings.keySet.contains(-1) mustEqual false } "associates objects to bases (doesn't check numbers)" in { val map = new ZoneMap("map13") - map.ObjectToBase mustEqual Nil - map.ObjectToBase(1, 2) - map.ObjectToBase mustEqual List((1, 2)) - map.ObjectToBase(3, 4) - map.ObjectToBase mustEqual List((1, 2), (3, 4)) + map.ObjectToBuilding mustEqual Nil + map.ObjectToBuilding(1, 2) + map.ObjectToBuilding mustEqual Map(1 -> 2) + map.ObjectToBuilding(3, 4) + map.ObjectToBuilding mustEqual Map(1 -> 2, 3 -> 4) } "associates doors to door locks (doesn't check numbers)" in { @@ -65,11 +70,10 @@ class ZoneTest extends Specification { } val map13 = new ZoneMap("map13") - map13.LocalBases = 10 + map13.LocalBuilding(10, FoundationBuilder(test)) class TestObject extends IdentifiableEntity "Zone" should { - //TODO these are temporary tests as the current Zone is a kludge "construct" in { val zone = new Zone("home3", map13, 13) zone.GUID mustEqual ActorRef.noSender diff --git a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala index ffab2595..65436e49 100644 --- a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala @@ -2,8 +2,10 @@ 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.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} import org.specs2.mutable.Specification @@ -11,6 +13,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 terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) @@ -18,8 +23,8 @@ class AirVehicleTerminalTest extends Specification { } "player can buy a reaver ('lightgunship')" in { - val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] @@ -35,7 +40,6 @@ class AirVehicleTerminalTest extends Specification { } "player can not buy a fake vehicle ('reaver')" in { - val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "reaver", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/common/src/test/scala/objects/terminal/CertTerminalTest.scala b/common/src/test/scala/objects/terminal/CertTerminalTest.scala index 96b83668..c3d000d0 100644 --- a/common/src/test/scala/objects/terminal/CertTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/CertTerminalTest.scala @@ -2,7 +2,9 @@ package objects.terminal import akka.actor.ActorRef +import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types._ @@ -11,6 +13,9 @@ import org.specs2.mutable.Specification class CertTerminalTest extends Specification { "Cert_Terminal" should { val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val terminal = Terminal(GlobalDefinitions.cert_terminal) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.cert_terminal) @@ -18,27 +23,23 @@ class CertTerminalTest extends Specification { } "player can learn a certification ('medium_assault')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.LearnCertification(CertificationType.MediumAssault, 2) } "player can not learn a fake certification ('juggling')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() } "player can forget a certification ('medium_assault')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.SellCertification(CertificationType.MediumAssault, 2) } "player can not forget a fake certification ('juggling')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "juggling", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala index d7850c4e..d498e3e0 100644 --- a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala @@ -2,8 +2,10 @@ 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.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} import org.specs2.mutable.Specification @@ -11,6 +13,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 terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) @@ -18,8 +23,8 @@ class DropshipVehicleTerminalTest extends Specification { } "player can buy a galaxy ('dropship')" in { - val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "dropship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] @@ -41,7 +46,6 @@ class DropshipVehicleTerminalTest extends Specification { } "player can not buy a fake vehicle ('galaxy')" in { - val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "galaxy", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala index c2207562..831bb0b1 100644 --- a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala @@ -2,8 +2,10 @@ 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.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} import org.specs2.mutable.Specification @@ -11,6 +13,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 terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) @@ -18,8 +23,8 @@ class GroundVehicleTerminalTest extends Specification { } "player can buy a harasser ('two_man_assault_buggy')" in { - val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] @@ -35,7 +40,6 @@ class GroundVehicleTerminalTest extends Specification { } "player can not buy a fake vehicle ('harasser')" in { - val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala b/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala index c5893ea0..d846ab94 100644 --- a/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala +++ b/common/src/test/scala/objects/terminal/ImplantTerminalInterfaceTest.scala @@ -2,8 +2,10 @@ 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.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} import org.specs2.mutable.Specification @@ -11,6 +13,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 terminal = Terminal(GlobalDefinitions.implant_terminal_interface) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.implant_terminal_interface) @@ -18,8 +23,8 @@ class ImplantTerminalInterfaceTest extends Specification { } "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] @@ -27,15 +32,14 @@ class ImplantTerminalInterfaceTest extends Specification { } "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] diff --git a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala index 9677a09a..a58f0cf0 100644 --- a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala +++ b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala @@ -1,13 +1,13 @@ // Copyright (c) 2017 PSForever package objects.terminal -import akka.actor.{ActorRef, Props} +import akka.actor.{ActorRef, ActorSystem, Props} import net.psforever.objects.definition.SeatDefinition -import net.psforever.objects.mount.Mountable +import net.psforever.objects.serverobject.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 net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} import objects.ActorTest import org.specs2.mutable.Specification @@ -68,43 +68,101 @@ class ImplantTerminalMechControl1Test extends ActorTest() { 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 (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) val msg = Mountable.TryMount(player, 0) - obj.Actor ! msg - val reply = receiveOne(Duration.create(100, "ms")) + mech.Actor ! msg + val reply = receiveOne(Duration.create(200, "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.obj == mech) assert(reply3.seat_num == 0) } } } class ImplantTerminalMechControl3Test extends ActorTest() { + import net.psforever.types.CharacterGender "ImplantTerminalMechControl" should { "block a player from mounting" in { - val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) 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) + + mech.Actor ! Mountable.TryMount(player1, 0) receiveOne(Duration.create(100, "ms")) //consume reply - obj.Actor ! Mountable.TryMount(player2, 0) + mech.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.obj == mech) assert(reply3.seat_num == 0) } } } + +class ImplantTerminalMechControl4Test extends ActorTest() { + "ImplantTerminalMechControl" should { + "dismount player after mounting" in { + val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) + mech.Actor ! Mountable.TryMount(player, 0) + receiveOne(Duration.create(100, "ms")) //consume reply + assert(mech.Seat(0).get.isOccupied) + + mech.Actor ! Mountable.TryDismount(player, 0) + 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.CanDismount]) + val reply3 = reply2.response.asInstanceOf[Mountable.CanDismount] + assert(reply3.obj == mech) + assert(reply3.seat_num == 0) + assert(!mech.Seat(0).get.isOccupied) + } + } +} + +class ImplantTerminalMechControl5Test extends ActorTest() { + "ImplantTerminalMechControl" should { + "block a player from dismounting" in { + val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) + mech.Actor ! Mountable.TryMount(player, 0) + receiveOne(Duration.create(100, "ms")) //consume reply + assert(mech.Seat(0).get.isOccupied) + + mech.Velocity = Vector3(1,0,0) //makes no sense, but it works as the "seat" is not bailable + mech.Actor ! Mountable.TryDismount(player, 0) + 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.CanNotDismount]) + val reply3 = reply2.response.asInstanceOf[Mountable.CanNotDismount] + assert(reply3.obj == mech) + assert(reply3.seat_num == 0) + assert(mech.Seat(0).get.isOccupied) + } + } +} + +object ImplantTerminalMechTest { + def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ImplantTerminalMech) = { + import net.psforever.objects.serverobject.structures.Building + import net.psforever.objects.zones.Zone + import net.psforever.packet.game.PlanetSideGUID + + 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.Faction = faction + terminal.GUID = PlanetSideGUID(1) + (Player("test", faction, CharacterGender.Male, 0, 0), terminal) + } +} diff --git a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala index c5e41100..1998d7d7 100644 --- a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala @@ -2,7 +2,9 @@ package objects.terminal import akka.actor.ActorRef +import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.objects.{AmmoBox, GlobalDefinitions, Player, Tool} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types._ @@ -11,6 +13,9 @@ import org.specs2.mutable.Specification class OrderTerminalTest extends Specification { "Order_Terminal" should { val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val terminal = Terminal(GlobalDefinitions.order_terminal) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.order_terminal) @@ -18,7 +23,6 @@ class OrderTerminalTest extends Specification { } "player can buy a box of ammunition ('9mmbullet_AP')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "9mmbullet_AP", 0, PlanetSideGUID(0)) val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true @@ -29,7 +33,6 @@ class OrderTerminalTest extends Specification { } "player can buy a weapon ('suppressor')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "suppressor", 0, PlanetSideGUID(0)) val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true @@ -39,7 +42,6 @@ class OrderTerminalTest extends Specification { } "player can buy a box of vehicle ammunition ('105mmbullet')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "105mmbullet", 0, PlanetSideGUID(0)) val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true @@ -50,7 +52,6 @@ class OrderTerminalTest extends Specification { } "player can buy a support tool ('bank')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 2, "bank", 0, PlanetSideGUID(0)) val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true @@ -60,14 +61,12 @@ class OrderTerminalTest extends Specification { } "player can buy different armor ('lite_armor')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile) } "player can not buy fake equipment ('sabot')" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "sabot", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() @@ -76,7 +75,6 @@ class OrderTerminalTest extends Specification { //TODO loudout tests "player can not buy equipment from the wrong page ('9mmbullet_AP', page 1)" in { - val terminal = Terminal(GlobalDefinitions.order_terminal) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "9mmbullet_AP", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala index b5106259..f3a461f9 100644 --- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala +++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala @@ -1,8 +1,10 @@ // Copyright (c) 2017 PSForever package objects.terminal -import akka.actor.Props -import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl} +import akka.actor.{ActorSystem, Props} +import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition} +import net.psforever.objects.zones.Zone import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types._ @@ -21,8 +23,7 @@ class TerminalControl1Test extends ActorTest() { class TerminalControl2Test extends ActorTest() { "TerminalControl can not process wrong messages" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") + val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) terminal.Actor !"hello" val reply = receiveOne(Duration.create(500, "ms")) @@ -34,9 +35,7 @@ class TerminalControl2Test extends ActorTest() { //test for Cert_Terminal messages (see CertTerminalTest) class CertTerminalControl1Test extends ActorTest() { "TerminalControl can be used to learn a certification ('medium_assault')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0)) terminal.Actor ! Terminal.Request(player, msg) @@ -51,9 +50,7 @@ class CertTerminalControl1Test extends ActorTest() { class CertTerminalControl2Test extends ActorTest() { "TerminalControl can be used to warn about not learning a fake certification ('juggling')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0)) terminal.Actor ! Terminal.Request(player, msg) @@ -68,9 +65,7 @@ class CertTerminalControl2Test extends ActorTest() { class CertTerminalControl3Test extends ActorTest() { "TerminalControl can be used to forget a certification ('medium_assault')" in { - val terminal = Terminal(GlobalDefinitions.cert_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0)) terminal.Actor ! Terminal.Request(player, msg) @@ -85,9 +80,7 @@ class CertTerminalControl3Test extends ActorTest() { class VehicleTerminalControl1Test extends ActorTest() { "TerminalControl can be used to buy a vehicle ('two_man_assault_buggy')" in { - val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) terminal.Actor ! Terminal.Request(player, msg) @@ -112,9 +105,7 @@ class VehicleTerminalControl1Test extends ActorTest() { class VehicleTerminalControl2Test extends ActorTest() { "TerminalControl can be used to warn about not buy a vehicle ('harasser')" in { - val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) - terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") - val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) terminal.Actor ! Terminal.Request(player, msg) @@ -126,3 +117,13 @@ class VehicleTerminalControl2Test extends ActorTest() { assert(reply2.response == Terminal.NoDeal()) } } + +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.Faction = faction + (Player("test", faction, CharacterGender.Male, 0, 0), terminal) + } +} diff --git a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala index 0bcb24d1..6a390432 100644 --- a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala +++ b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala @@ -2,8 +2,10 @@ 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.terminals.Terminal +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} import org.specs2.mutable.Specification @@ -11,6 +13,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 terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) + terminal.Owner = new Building(0, Zone.Nowhere) + terminal.Owner.Faction = PlanetSideEmpire.TR "construct" in { val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) @@ -18,8 +23,8 @@ class VehicleTerminalCombinedTest extends Specification { } "player can buy a ground vehicle, the harasser ('two_man_assault_buggy')" in { - val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] @@ -35,8 +40,8 @@ class VehicleTerminalCombinedTest extends Specification { } "player can buy a flying vehicle, the reaver ('lightgunship')" in { - val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] @@ -52,7 +57,6 @@ class VehicleTerminalCombinedTest extends Specification { } "player can not buy a fake vehicle ('harasser')" in { - val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala index 03e55a6c..71c81084 100644 --- a/pslogin/src/main/scala/Maps.scala +++ b/pslogin/src/main/scala/Maps.scala @@ -7,6 +7,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.pad.VehicleSpawnPad +import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder} import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.types.Vector3 @@ -103,30 +104,46 @@ object Maps { VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f)) )) //TODO guid not correct - LocalBases = 30 + LocalBuilding(2, FoundationBuilder(Building.Structure)) + 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(853, 2) //TODO check building_id + ObjectToBuilding(855, 2) //TODO check building_id + ObjectToBuilding(860, 2) //TODO check building_id + ObjectToBuilding(1063, 2) //TODO unowned courtyard terminal? + ObjectToBuilding(500, 2) //TODO unowned courtyard spawnpad? + ObjectToBuilding(304, 2) //TODO unowned courtyard terminal? + ObjectToBuilding(501, 2) //TODO unowned courtyard spawnpad? - ObjectToBase(330, 29) - ObjectToBase(331, 29) - ObjectToBase(332, 29) - ObjectToBase(333, 29) - //ObjectToBase(520, 29) - ObjectToBase(522, 2) - ObjectToBase(523, 2) - ObjectToBase(524, 2) - ObjectToBase(525, 2) - ObjectToBase(526, 2) - ObjectToBase(527, 2) - ObjectToBase(528, 2) - ObjectToBase(529, 2) - ObjectToBase(556, 29) - ObjectToBase(557, 29) - ObjectToBase(558, 29) - ObjectToBase(559, 29) - ObjectToBase(1081, 2) - ObjectToBase(1063, 2) //TODO unowned courtyard terminal? - ObjectToBase(500, 2) //TODO unowned courtyard spawnpad? - ObjectToBase(304, 2) //TODO unowned courtyard terminal? - ObjectToBase(501, 2) //TODO unowned courtyard spawnpad? + LocalBuilding(29, FoundationBuilder(Building.Structure)) + ObjectToBuilding(330, 29) + ObjectToBuilding(332, 29) + ObjectToBuilding(556, 29) + ObjectToBuilding(558, 29) + + //ObjectToBuilding(1081, ?) + //ObjectToBuilding(520, ?) DoorToLock(330, 558) DoorToLock(331, 559) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index cbe972f4..46b6ef0e 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -14,7 +14,8 @@ import net.psforever.objects._ import net.psforever.objects.equipment._ import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} -import net.psforever.objects.mount.Mountable +import net.psforever.objects.serverobject.mount.Mountable +import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech @@ -424,7 +425,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val player_guid : PlanetSideGUID = tplayer.GUID val obj_guid : PlanetSideGUID = obj.GUID log.info(s"MountVehicleMsg: $player_guid mounts $obj @ $seat_num") - tplayer.VehicleSeated = Some(obj_guid) + //tplayer.VehicleSeated = Some(obj_guid) sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(obj_guid, 0, 1000L))) //health of mech sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(obj_guid, player_guid, seat_num))) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num)) @@ -434,7 +435,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val player_guid : PlanetSideGUID = tplayer.GUID log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num") vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(obj_guid) //clear all deconstruction timers - tplayer.VehicleSeated = Some(obj_guid) + //tplayer.VehicleSeated = Some(obj_guid) if(seat_num == 0) { //simplistic vehicle ownership management obj.Owner match { case Some(owner_guid) => @@ -466,8 +467,37 @@ class WorldSessionActor extends Actor with MDCContextAware { case Mountable.CanMount(obj : Mountable, _) => log.warn(s"MountVehicleMsg: $obj is some generic mountable object and nothing will happen") + case Mountable.CanDismount(obj : ImplantTerminalMech, seat_num) => + val obj_guid : PlanetSideGUID = obj.GUID + val player_guid : PlanetSideGUID = tplayer.GUID + log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num") + sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, seat_num, false))) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, seat_num, false)) + + case Mountable.CanDismount(obj : Vehicle, seat_num) => + val player_guid : PlanetSideGUID = tplayer.GUID + if(player_guid == player.GUID) { + //disembarking self + log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num") + sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, seat_num, false))) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, seat_num, false)) + UnAccessContents(obj) + } + else { + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, seat_num, true, obj.GUID)) + } + if(obj.Seats.values.count(seat => seat.isOccupied) == 0) { + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m) + } + + case Mountable.CanDismount(obj : Mountable, _) => + log.warn(s"DismountVehicleMsg: $obj is some generic mountable object and nothing will happen") + case Mountable.CanNotMount(obj, seat_num) => log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed") + + case Mountable.CanNotDismount(obj, seat_num) => + log.warn(s"DismountVehicleMsg: $tplayer attempted to dismount $obj's seat $seat_num, but was not allowed") } case Terminal.TerminalMessage(tplayer, msg, order) => @@ -961,11 +991,10 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info("Load the now-registered player") //load the now-registered player tplayer.Spawn - sendResponse(PacketCoding.CreateGamePacket(0, - ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, tplayer.Definition.Packet.DetailedConstructorData(tplayer).get) - )) + val dcdata = tplayer.Definition.Packet.DetailedConstructorData(tplayer).get + sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, dcdata))) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.LoadPlayer(tplayer.GUID, tplayer.Definition.Packet.ConstructorData(tplayer).get)) - log.debug(s"ObjectCreateDetailedMessage: ${tplayer.Definition.Packet.DetailedConstructorData(tplayer).get}") + log.debug(s"ObjectCreateDetailedMessage: $dcdata") case SetCurrentAvatar(tplayer) => val guid = tplayer.GUID @@ -1823,52 +1852,49 @@ class WorldSessionActor extends Actor with MDCContextAware { // TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg) continent.GUID(object_guid) match { case Some(door : Door) => - continent.Map.DoorToLock.get(object_guid.guid) match { //check for IFF Lock - case Some(lock_guid) => - val lock_hacked = continent.GUID(lock_guid).get.asInstanceOf[IFFLock].HackedBy match { - case Some(_) => - true - case None => - Vector3.ScalarProjection(door.Outwards, player.Position - door.Position) < 0f - } - continent.Map.ObjectToBase.get(lock_guid) match { //check for associated base - case Some(base_id) => - if(continent.Base(base_id).get.Faction == player.Faction || lock_hacked) { //either base allegiance aligns or locks is hacked - door.Actor ! Door.Use(player, msg) - } - case None => - if(lock_hacked) { //is lock hacked? this may be a weird case - door.Actor ! Door.Use(player, msg) - } - } - case None => - door.Actor ! Door.Use(player, msg) //let door open freely + if(player.Faction == door.Faction || ((continent.Map.DoorToLock.get(object_guid.guid) match { + case Some(lock_guid) => continent.GUID(lock_guid).get.asInstanceOf[IFFLock].HackedBy.isDefined + case None => !door.isOpen + }) || Vector3.ScalarProjection(door.Outwards, player.Position - door.Position) < 0f)) { + door.Actor ! Door.Use(player, msg) + } + else if(door.isOpen) { + //the door is open globally ... except on our screen + sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16))) } case Some(panel : IFFLock) => - player.Slot(player.DrawnSlot).Equipment match { - case Some(tool : SimpleItem) => - if(tool.Definition == GlobalDefinitions.remote_electronics_kit) { - //TODO get player hack level (for now, presume 15s in intervals of 4/s) - progressBarValue = Some(-2.66f) - self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, FinishHackingDoor(panel, 1114636288L)) - log.info("Hacking a door~") - } - case _ => ; + if(panel.Faction != player.Faction && panel.HackedBy.isEmpty) { + player.Slot(player.DrawnSlot).Equipment match { + case Some(tool : SimpleItem) => + if(tool.Definition == GlobalDefinitions.remote_electronics_kit) { + //TODO get player hack level (for now, presume 15s in intervals of 4/s) + progressBarValue = Some(-2.66f) + self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, FinishHackingDoor(panel, 1114636288L)) + log.info("Hacking a door~") + } + case _ => ; + } } case Some(obj : Locker) => - val container = player.Locker - accessedContainer = Some(container) - sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456))) + if(player.Faction == obj.Faction) { + log.info(s"UseItem: $player accessing a locker") + val container = player.Locker + accessedContainer = Some(container) + sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456))) + } + else { + log.info(s"UseItem: not $player's locker") + } case Some(obj : Vehicle) => - if(obj.Faction == player.Faction) { - val equipment = player.Slot(player.DrawnSlot).Equipment + val equipment = player.Slot(player.DrawnSlot).Equipment + if(player.Faction == obj.Faction) { if(equipment match { case Some(tool : Tool) => tool.Definition match { - case GlobalDefinitions.nano_dispenser | GlobalDefinitions.remote_electronics_kit => false + case GlobalDefinitions.nano_dispenser => false case _ => true } case _ => true @@ -1889,13 +1915,19 @@ class WorldSessionActor extends Actor with MDCContextAware { case GlobalDefinitions.nano_dispenser => //TODO repairing behavior - case GlobalDefinitions.remote_electronics_kit => - //TODO hacking behavior - case _ => ; } } } + //enemy player interactions + else if(equipment.isDefined) { + equipment.get.Definition match { + case GlobalDefinitions.remote_electronics_kit => + //TODO hacking behavior + + case _ => ; + } + } case Some(obj : PlanetSideGameObject) => if(itemType != 121) { @@ -1933,7 +1965,9 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info("ItemTransaction: " + msg) continent.GUID(terminal_guid) match { case Some(term : Terminal) => - term.Actor ! Terminal.Request(player, msg) + if(player.Faction == term.Faction) { + term.Actor ! Terminal.Request(player, msg) + } case Some(obj : PlanetSideGameObject) => ; case None => ; } @@ -2015,36 +2049,19 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ DismountVehicleMsg(player_guid, unk1, unk2) => //TODO optimize this later log.info(s"DismountVehicleMsg: $msg") + //common warning for this section + def dismountWarning(msg : String) : Unit = { + log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it") + } if(player.GUID == player_guid) { //normally disembarking from a seat - val previouslySeated = player.VehicleSeated - player.VehicleSeated = None - sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2))) - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2)) - //common warning for this section - def dismountWarning(msg : String) : Unit = { - log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it") - } - //find vehicle seat and disembark it - previouslySeated match { + player.VehicleSeated match { case Some(obj_guid) => continent.GUID(obj_guid) match { case Some(obj : Mountable) => - val seats = obj.Seats.values - seats.find(seat => seat.Occupant.contains(player)) match { - case Some(seat) => - if(seat.Bailable || obj.Velocity.isEmpty || Vector3.MagnitudeSquared(obj.Velocity.get).toInt == 0) { //ugh, float comparison - seat.Occupant = None - //special actions - obj match { - case (veh : Vehicle) => - if(seats.count(seat => seat.isOccupied) == 0) { - vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(veh, continent, 600L) //start vehicle decay (10m) - UnAccessContents(veh) - } - case _ => ; - } - } + obj.PassengerInSeat(player) match { + case Some(seat_num : Int) => + obj.Actor ! Mountable.TryDismount(player, seat_num) case None => dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable $obj_guid") } @@ -2056,35 +2073,23 @@ class WorldSessionActor extends Actor with MDCContextAware { } } else { - //kicking someone else out of a seat; need to own that seat + //kicking someone else out of a seat; need to own that seat/mountable player.VehicleOwned match { - case Some(vehicle_guid) => - continent.GUID(player_guid) match { - case Some(tplayer : Player) => - if(tplayer.VehicleSeated.contains(vehicle_guid)) { - continent.GUID(vehicle_guid) match { - case Some(obj : Vehicle) => - val seats = obj.Seats.values - seats.find(seat => seat.Occupant.contains(tplayer)) match { - case Some(seat) => - seat.Occupant = None - tplayer.VehicleSeated = None - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid)) - if(seats.count(seat => seat.isOccupied) == 0) { - vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m) - } - case None => - log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid") - } - case _ => - log.warn(s"DismountVehicleMsg: can not find vehicle $vehicle_guid") - } + case Some(obj_guid) => + (continent.GUID(obj_guid), continent.GUID(player_guid)) match { + case (Some(obj : Mountable), Some(tplayer : Player)) => + obj.PassengerInSeat(tplayer) match { + case Some(seat_num : Int) => + obj.Actor ! Mountable.TryDismount(tplayer, seat_num) + case None => + dismountWarning(s"DismountVehicleMsg: can not find where other player $player_guid is seated in mountable $obj_guid") } - else { - log.warn(s"DismountVehicleMsg: non-owner player $player trying to kick player $tplayer out of his seat") - } - case _ => + case (None, _) => ; + log.warn(s"DismountVehicleMsg: $player can not find his vehicle") + case (_, None) => ; log.warn(s"DismountVehicleMsg: player $player_guid could not be found to kick") + case _ => + log.warn(s"DismountVehicleMsg: object is either not a Mountable or not a Player") } case None => log.warn(s"DismountVehicleMsg: $player does not own a vehicle") diff --git a/pslogin/src/main/scala/Zones.scala b/pslogin/src/main/scala/Zones.scala index 786d3384..f856f487 100644 --- a/pslogin/src/main/scala/Zones.scala +++ b/pslogin/src/main/scala/Zones.scala @@ -32,8 +32,8 @@ object Zones { super.Init(context) import net.psforever.types.PlanetSideEmpire - Base(2).get.Faction = PlanetSideEmpire.VS //HART building C - Base(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower + Building(2).get.Faction = PlanetSideEmpire.VS //HART building C + Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower } } diff --git a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala index 760cbf20..adc9668a 100644 --- a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala +++ b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala @@ -53,7 +53,7 @@ class DeconstructionActor extends Actor { case DeconstructionActor.RequestDeleteVehicle(vehicle, zone, time) => vehicles = vehicles :+ DeconstructionActor.VehicleEntry(vehicle, zone, time) vehicle.Actor ! Vehicle.PrepareForDeletion - //kick everyone out + //kick everyone out; this is a no-blocking manual form of MountableBehavior ! Mountable.TryDismount vehicle.Definition.MountPoints.values.foreach(seat_num => { val zone_id : String = zone.Id val seat : Seat = vehicle.Seat(seat_num).get