From 7673c8941bccf6449968d469cd38ab877edbb100 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 13 May 2018 21:57:14 -0400 Subject: [PATCH 1/7] begun work on repair and rearm silos by dividing up proximity terminal behavior; terminals at Anguta wired --- .../psforever/objects/GlobalDefinitions.scala | 2 + .../terminals/ProximityTerminal.scala | 18 +----- .../terminals/ProximityTerminalControl.scala | 26 +++------ .../terminals/ProximityUnit.scala | 55 +++++++++++++++++++ .../terminals/RepairRearmControl.scala | 22 ++++++++ .../terminals/RepairRearmSilo.scala | 38 +++++++++++++ .../terminals/RepairRearmSiloDefinition.scala | 27 +++++++++ .../serverobject/terminals/Terminal.scala | 6 +- .../terminals/TerminalControl.scala | 3 +- pslogin/src/main/scala/Maps.scala | 6 +- .../src/main/scala/WorldSessionActor.scala | 36 ++++++++---- 11 files changed, 186 insertions(+), 53 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index fc511e8d..d39790ef 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -524,6 +524,8 @@ object GlobalDefinitions { val medical_terminal = new MedicalTerminalDefinition(529) + val repair_silo = new RepairRearmSiloDefinition(729) + val spawn_pad = new VehicleSpawnPadDefinition val mb_locker = new LockerDefinition diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala index c98e7da5..4eafb142 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala @@ -1,8 +1,6 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.terminals -import net.psforever.packet.game.PlanetSideGUID - /** * A server object that is a "terminal" that can be accessed for amenities and services, * triggered when a certain distance from the unit itself (proximity-based).
@@ -11,21 +9,7 @@ import net.psforever.packet.game.PlanetSideGUID * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class ProximityTerminal(tdef : MedicalTerminalDefinition) extends Terminal(tdef) { - private var users : Set[PlanetSideGUID] = Set.empty - - def NumberUsers : Int = users.size - - def AddUser(player_guid : PlanetSideGUID) : Int = { - users += player_guid - NumberUsers - } - - def RemoveUser(player_guid : PlanetSideGUID) : Int = { - users -= player_guid - NumberUsers - } -} +class ProximityTerminal(tdef : MedicalTerminalDefinition) extends Terminal(tdef) with ProximityUnit object ProximityTerminal { /** diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 407fd0cb..753cdb65 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -2,36 +2,24 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor -import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} -import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage /** - * * An `Actor` that handles messages being dispatched to a specific `ProximityTerminal`. * Although this "terminal" itself does not accept the same messages as a normal `Terminal` object, * it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`. * @param term the proximity unit (terminal) */ -class ProximityTerminalControl(term : ProximityTerminal) extends Actor with FactionAffinityBehavior.Check { +class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor with FactionAffinityBehavior.Check with ProximityUnit.Use { def FactionObject : FactionAffinity = term - def receive : Receive = checkBehavior.orElse { - case CommonMessages.Use(player) => - val hadNoUsers = term.NumberUsers == 0 - if(term.AddUser(player.GUID) == 1 && hadNoUsers) { - sender ! TerminalMessage(player, null, Terminal.StartProximityEffect(term)) - } + def TerminalObject : Terminal with ProximityUnit = term - case CommonMessages.Unuse(player) => - val hadUsers = term.NumberUsers > 0 - if(term.RemoveUser(player.GUID) == 0 && hadUsers) { - sender ! TerminalMessage(player, null, Terminal.StopProximityEffect(term)) - } - - case _ => - sender ! Terminal.NoDeal() - } + def receive : Receive = checkBehavior + .orElse(proximityBehavior) + .orElse { + case _ => ; + } override def toString : String = term.Definition.Name } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala new file mode 100644 index 00000000..8339e51e --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala @@ -0,0 +1,55 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.serverobject.CommonMessages +import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage +import net.psforever.packet.game.PlanetSideGUID + +/** + * A server object that is a "terminal" that can be accessed for amenities and services, + * triggered when a certain distance from the unit itself (proximity-based).
+ *
+ * Unlike conventional terminals, this structure is not necessarily structure-owned. + * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. + */ +trait ProximityUnit { + this : Terminal => + + private var users : Set[PlanetSideGUID] = Set.empty + + def NumberUsers : Int = users.size + + def AddUser(player_guid : PlanetSideGUID) : Int = { + users += player_guid + NumberUsers + } + + def RemoveUser(player_guid : PlanetSideGUID) : Int = { + users -= player_guid + NumberUsers + } +} + +object ProximityUnit { + import akka.actor.Actor + + trait Use { + this : Actor => + + def TerminalObject : Terminal with ProximityUnit + + val proximityBehavior : Receive = { + case CommonMessages.Use(player) => + val hadNoUsers = TerminalObject.NumberUsers == 0 + if(TerminalObject.AddUser(player.GUID) == 1 && hadNoUsers) { + sender ! TerminalMessage(player, null, Terminal.StartProximityEffect(TerminalObject)) + } + + case CommonMessages.Unuse(player) => + val hadUsers = TerminalObject.NumberUsers > 0 + if(TerminalObject.RemoveUser(player.GUID) == 0 && hadUsers) { + sender ! TerminalMessage(player, null, Terminal.StopProximityEffect(TerminalObject)) + } + } + } +} \ No newline at end of file diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala new file mode 100644 index 00000000..b5514305 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala @@ -0,0 +1,22 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} + +class RepairRearmControl(term : RepairRearmSilo) extends Actor with FactionAffinityBehavior.Check with ProximityUnit.Use { + def FactionObject : FactionAffinity = term + + def TerminalObject : Terminal with ProximityUnit = term + + def receive : Receive = checkBehavior + .orElse(proximityBehavior) + .orElse { + case Terminal.Request(player, msg) => + sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) + + case _ => ; + } + + override def toString : String = term.Definition.Name +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala new file mode 100644 index 00000000..45a01302 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala @@ -0,0 +1,38 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +/** + * A server object that is a "terminal" that can be accessed for amenities and services, + * triggered when a certain distance from the unit itself (proximity-based).
+ *
+ * Unlike conventional terminals, this structure is not necessarily structure-owned. + * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. + * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields + */ +class RepairRearmSilo(tdef : RepairRearmSiloDefinition) extends Terminal(tdef) with ProximityUnit + +object RepairRearmSilo { + /** + * Overloaded constructor. + * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields + */ + def apply(tdef : RepairRearmSiloDefinition) : RepairRearmSilo = { + new RepairRearmSilo(tdef) + } + + import akka.actor.ActorContext + + /** + * Instantiate an configure a `Terminal` object + * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields + * @param id the unique id that will be assigned to this entity + * @param context a context to allow the object to properly set up `ActorSystem` functionality + * @return the `Terminal` object + */ + def Constructor(tdef : RepairRearmSiloDefinition)(id : Int, context : ActorContext) : RepairRearmSilo = { + import akka.actor.Props + val obj = RepairRearmSilo(tdef) + obj.Actor = context.actorOf(Props(classOf[RepairRearmControl], obj), s"${tdef.Name}_$id") + obj + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala new file mode 100644 index 00000000..b0daa854 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala @@ -0,0 +1,27 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) { + Name = "repair_silo" + + private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange = EquipmentTerminalDefinition.Buy(Map.empty, Map.empty, Map.empty) + + override def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg) + + override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + if(msg.item_page == 4) { //Favorites tab + player.LoadLoadout(msg.unk1) match { + case Some(loadout) => + Terminal.VehicleLoadout(Nil, Nil) + case None => + Terminal.NoDeal() + } + } + else { + Terminal.NoDeal() + } + } +} 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 b031a990..8ee17814 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 @@ -190,17 +190,19 @@ object Terminal { */ final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange + final case class VehicleLoadout(weapons : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange + /** * Start the special effects caused by a proximity-base service. * @param terminal the proximity-based unit */ - final case class StartProximityEffect(terminal : ProximityTerminal) extends Exchange + final case class StartProximityEffect(terminal : Terminal with ProximityUnit) extends Exchange /** * Stop the special effects caused by a proximity-base service. * @param terminal the proximity-based unit */ - final case class StopProximityEffect(terminal : ProximityTerminal) extends Exchange + final case class StopProximityEffect(terminal : Terminal with ProximityUnit) extends Exchange /** * Overloaded constructor. 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 ad63babf..4efa324e 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 @@ -15,8 +15,7 @@ class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavio case Terminal.Request(player, msg) => sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) - case _ => - sender ! Terminal.NoDeal() + case _ => ; } override def toString : String = term.Definition.Name diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala index 4051208c..7d0494dd 100644 --- a/pslogin/src/main/scala/Maps.scala +++ b/pslogin/src/main/scala/Maps.scala @@ -7,7 +7,7 @@ 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, StructureType, WarpGate} -import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} +import net.psforever.objects.serverobject.terminals.{ProximityTerminal, RepairRearmSilo, Terminal} import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.types.Vector3 @@ -113,6 +113,8 @@ object Maps { LocalObject(2145, SpawnTube.Constructor(Vector3(3980.4062f, 4252.7656f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2146, SpawnTube.Constructor(Vector3(3980.4062f, 4259.992f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2147, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))) + LocalObject(2050, RepairRearmSilo.Constructor(repair_silo)) + LocalObject(2062, RepairRearmSilo.Constructor(repair_silo)) LocalObject(2239, Terminal.Constructor(spawn_terminal)) LocalObject(2244, Terminal.Constructor(spawn_terminal)) LocalObject(2245, Terminal.Constructor(spawn_terminal)) @@ -214,6 +216,8 @@ object Maps { ObjectToBuilding(1576, 2) ObjectToBuilding(1577, 2) ObjectToBuilding(1578, 2) + ObjectToBuilding(2050, 2) + ObjectToBuilding(2062, 2) ObjectToBuilding(2145, 2) ObjectToBuilding(2146, 2) ObjectToBuilding(2147, 2) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index bfbfe5cf..33514522 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -28,8 +28,7 @@ import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided} import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate} -import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, ProximityTerminal, Terminal} -import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState} @@ -895,6 +894,9 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false)) } + case Terminal.VehicleLoadout(weapons, inventory) => + log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") + case Terminal.SellCertification(cert, cost) => if(tplayer.Certifications.contains(cert)) { log.info(s"$tplayer is forgetting the $cert certification for $cost points") @@ -2267,6 +2269,16 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + case Some(obj : RepairRearmSilo) => + player.VehicleSeated match { + case Some(vehicle_guid) => + val vehicle = continent.GUID(vehicle_guid).get.asInstanceOf[Vehicle] + sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, unk1, vehicle_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId)) + case None => + log.error("UseItem: expected seated vehicle, but found none") + } + case Some(obj : Terminal) => if(obj.Definition.isInstanceOf[MatrixTerminalDefinition]) { //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) @@ -2300,7 +2312,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ ProximityTerminalUseMessage(player_guid, object_guid, _) => log.info(s"ProximityTerminal: $msg") continent.GUID(object_guid) match { - case Some(obj : ProximityTerminal) => + case Some(obj : Terminal with ProximityUnit) => if(usingProximityTerminal.contains(object_guid)) { SelectProximityUnit(obj) } @@ -2308,9 +2320,9 @@ class WorldSessionActor extends Actor with MDCContextAware { StartUsingProximityUnit(obj) } case Some(obj) => ; - log.warn(s"ProximityTerminal: object is not a terminal - $obj") + log.warn(s"ProximityTerminalUse: object is not a proximity terminal - $obj") case None => - log.warn(s"ProximityTerminal: no object with guid $object_guid found") + log.warn(s"ProximityTerminalUse: no object with guid $object_guid found") } case msg @ UnuseItemMessage(player_guid, object_guid) => @@ -3985,7 +3997,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * Special note is warranted in the case of a medical terminal or an advanced medical terminal. * @param terminal the proximity-based unit */ - def StartUsingProximityUnit(terminal : ProximityTerminal) : Unit = { + def StartUsingProximityUnit(terminal : Terminal with ProximityUnit) : Unit = { val term_guid = terminal.GUID if(!usingProximityTerminal.contains(term_guid)) { usingProximityTerminal += term_guid @@ -4006,7 +4018,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * Other sorts of proximity-based units are put on a timer. * @param terminal the proximity-based unit */ - def StopUsingProximityUnit(terminal : ProximityTerminal) : Unit = { + def StopUsingProximityUnit(terminal : Terminal with ProximityUnit) : Unit = { val term_guid = terminal.GUID if(usingProximityTerminal.contains(term_guid)) { usingProximityTerminal -= term_guid @@ -4025,7 +4037,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * If this timer completes, a message will be sent that will attempt to disassociate from the target proximity unit. * @param terminal the proximity-based unit */ - def SetDelayedProximityUnitReset(terminal : ProximityTerminal) : Unit = { + def SetDelayedProximityUnitReset(terminal : Terminal with ProximityUnit) : Unit = { val terminal_guid = terminal.GUID ClearDelayedProximityUnitReset(terminal_guid) import scala.concurrent.duration._ @@ -4070,7 +4082,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * and determinig which kind of unit is being utilized. * @param terminal the proximity-based unit */ - def SelectProximityUnit(terminal : ProximityTerminal) : Unit = { + def SelectProximityUnit(terminal : Terminal with ProximityUnit) : Unit = { terminal.Definition match { case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal => ProximityMedicalTerminal(terminal) @@ -4089,7 +4101,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * If the player is both fully healed and fully repaired, stop using the terminal. * @param unit the medical terminal */ - def ProximityMedicalTerminal(unit : ProximityTerminal) : Unit = { + def ProximityMedicalTerminal(unit : Terminal with ProximityUnit) : Unit = { val healthFull : Boolean = if(player.Health < player.MaxHealth) { HealAction(player) } @@ -4113,7 +4125,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * If the player is fully healed, stop using the crystal. * @param unit the healing crystal */ - def ProximityHealCrystal(unit : ProximityTerminal) : Unit = { + def ProximityHealCrystal(unit : Terminal with ProximityUnit) : Unit = { val healthFull : Boolean = if(player.Health < player.MaxHealth) { HealAction(player) } @@ -4253,7 +4265,7 @@ object WorldSessionActor { private final case class ListAccountCharacters() private final case class SetCurrentAvatar(tplayer : Player) private final case class VehicleLoaded(vehicle : Vehicle) - private final case class DelayedProximityUnitStop(unit : ProximityTerminal) + private final case class DelayedProximityUnitStop(unit : Terminal with ProximityUnit) private final case class UnregisterCorpseOnVehicleDisembark(corpse : Player) /** From 9d7d1b0456de02c57632a85963a47379533f6ed4 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 13 May 2018 23:06:01 -0400 Subject: [PATCH 2/7] split previous Loadout into separate InfantryLoadout and VehicleLoadout classes; moved new Loadout code into its own package and corrected import statements; corrected Terminal code and tests --- .../scala/net/psforever/objects/Avatar.scala | 1 + .../scala/net/psforever/objects/Player.scala | 1 + .../objects/loadouts/InfantryLoadout.scala | 28 ++++ .../objects/{ => loadouts}/Loadout.scala | 54 +++---- .../objects/loadouts/VehicleLoadout.scala | 11 ++ .../EquipmentTerminalDefinition.scala | 3 +- .../terminals/OrderTerminalABDefinition.scala | 5 +- .../terminals/OrderTerminalDefinition.scala | 5 +- .../terminals/VehicleTerminalDefinition.scala | 144 +++++++++++------- .../src/test/scala/objects/AvatarTest.scala | 13 +- .../src/test/scala/objects/LoadoutTest.scala | 11 +- .../src/main/scala/WorldSessionActor.scala | 3 +- 12 files changed, 166 insertions(+), 113 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala rename common/src/main/scala/net/psforever/objects/{ => loadouts}/Loadout.scala (86%) create mode 100644 common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala diff --git a/common/src/main/scala/net/psforever/objects/Avatar.scala b/common/src/main/scala/net/psforever/objects/Avatar.scala index 067dafec..8b704a6d 100644 --- a/common/src/main/scala/net/psforever/objects/Avatar.scala +++ b/common/src/main/scala/net/psforever/objects/Avatar.scala @@ -3,6 +3,7 @@ package net.psforever.objects import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition} import net.psforever.objects.equipment.EquipmentSize +import net.psforever.objects.loadouts.Loadout import net.psforever.types.{CertificationType, CharacterGender, ImplantType, PlanetSideEmpire} import scala.annotation.tailrec diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala index f5642611..72246fb4 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 import net.psforever.objects.equipment.{Equipment, EquipmentSize} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} +import net.psforever.objects.loadouts.Loadout import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.packet.game.PlanetSideGUID import net.psforever.types._ diff --git a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala new file mode 100644 index 00000000..72100a61 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.loadouts + +import net.psforever.types.ExoSuitType + +final case class InfantryLoadout(label : String, + visible_slots : List[Loadout.SimplifiedEntry], + inventory : List[Loadout.SimplifiedEntry], + exosuit : ExoSuitType.Value, + subtype : Int) extends Loadout(label, visible_slots, inventory) { + /** + * The exo-suit in which the avatar will be dressed. + * Might be restricted and, thus, restrict the rest of the `Equipment` from being constructed and given. + * @return the exo-suit + */ + def ExoSuit : ExoSuitType.Value = exosuit + + /** + * The mechanized assault exo-suit specialization number that indicates whether the MAX performs: + * anti-infantry (1), + * anti-vehicular (2), + * or anti-air work (3). + * The major distinction is the type of arm weapons that MAX is equipped. + * When the blueprint doesn't call for a MAX, the number will be 0. + * @return the specialization number + */ + def Subtype : Int = subtype +} diff --git a/common/src/main/scala/net/psforever/objects/Loadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala similarity index 86% rename from common/src/main/scala/net/psforever/objects/Loadout.scala rename to common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala index ec997d0a..89d2261c 100644 --- a/common/src/main/scala/net/psforever/objects/Loadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects +package net.psforever.objects.loadouts +import net.psforever.objects._ import net.psforever.objects.definition._ import net.psforever.objects.equipment.Equipment import net.psforever.objects.inventory.InventoryItem @@ -34,38 +35,16 @@ import scala.annotation.tailrec * @param label the name by which this inventory will be known when displayed in a Favorites list * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk - * @param exosuit na - * @param subtype na */ -final case class Loadout(private val label : String, - private val visible_slots : List[Loadout.SimplifiedEntry], - private val inventory : List[Loadout.SimplifiedEntry], - private val exosuit : ExoSuitType.Value, - private val subtype : Int) { +abstract class Loadout(label : String, + visible_slots : List[Loadout.SimplifiedEntry], + inventory : List[Loadout.SimplifiedEntry]) { /** * The label by which this `Loadout` is called. * @return the label */ def Label : String = label - /** - * The exo-suit in which the avatar will be dressed. - * Might be restricted and, thus, restrict the rest of the `Equipment` from being constructed and given. - * @return the exo-suit - */ - def ExoSuit : ExoSuitType.Value = exosuit - - /** - * The mechanized assault exo-suit specialization number that indicates whether the MAX performs: - * anti-infantry (1), - * anti-vehicular (2), - * or anti-air work (3). - * The major distinction is the type of arm weapons that MAX is equipped. - * When the blueprint doesn't call for a MAX, the number will be 0. - * @return the specialization number - */ - def Subtype : Int = subtype - /** * The `Equipment` in the `Player`'s holster slots when this `Loadout` is created. * @return a `List` of the holster item blueprints @@ -80,12 +59,8 @@ final case class Loadout(private val label : String, } object Loadout { - def apply(label : String, visible : List[SimplifiedEntry], inventory : List[SimplifiedEntry]) : Loadout = { - new Loadout(label, visible, inventory, ExoSuitType.Standard, 0) - } - def Create(player : Player, label : String) : Loadout = { - new Loadout( + InfantryLoadout( label, packageSimplifications(player.Holsters()), packageSimplifications(player.Inventory.Items.values.toList), @@ -95,10 +70,11 @@ object Loadout { } def Create(vehicle : Vehicle, label : String) : Loadout = { - Loadout( + VehicleLoadout( label, packageSimplifications(vehicle.Weapons.map({ case ((index, weapon)) => InventoryItem(weapon.Equipment.get, index) }).toList), - packageSimplifications(vehicle.Trunk.Items.values.toList) + packageSimplifications(vehicle.Trunk.Items.values.toList), + vehicle.Definition ) } @@ -153,8 +129,12 @@ object Loadout { final case class ShorthandKit(definition : KitDefinition) extends Simplification def DetermineSubtype(player : Player) : Int = { - if(player.ExoSuit == ExoSuitType.MAX) { - player.Slot(0).Equipment match { + DetermineSubtype(player.ExoSuit, player.Slot(0).Equipment) + } + + def DetermineSubtype(suit : ExoSuitType.Value, weapon : Option[Equipment]) : Int = { + if(suit == ExoSuitType.MAX) { + weapon match { case Some(item) => item.Definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar => @@ -180,7 +160,7 @@ object Loadout { * @param equipment the holster slots * @return a `List` of simplified `Equipment` */ - private def packageSimplifications(equipment : Array[EquipmentSlot]) : List[SimplifiedEntry] = { + protected def packageSimplifications(equipment : Array[EquipmentSlot]) : List[SimplifiedEntry] = { recursiveHolsterSimplifications(equipment.iterator) } @@ -189,7 +169,7 @@ object Loadout { * @param equipment the enumerated contents of the inventory * @return a `List` of simplified `Equipment` */ - private def packageSimplifications(equipment : List[InventoryItem]) : List[SimplifiedEntry] = { + protected def packageSimplifications(equipment : List[InventoryItem]) : List[SimplifiedEntry] = { equipment.map(entry => { SimplifiedEntry(buildSimplification(entry.obj), entry.start) }) } diff --git a/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala new file mode 100644 index 00000000..b4713d64 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala @@ -0,0 +1,11 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.loadouts + +import net.psforever.objects.definition._ + +final case class VehicleLoadout(label : String, + visible_slots : List[Loadout.SimplifiedEntry], + inventory : List[Loadout.SimplifiedEntry], + vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory) { + def Definition : VehicleDefinition = vehicle_definition +} \ No newline at end of file diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala index 2c062abb..9067ae34 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala @@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects._ import net.psforever.objects.definition._ import net.psforever.objects.equipment.Equipment +import net.psforever.objects.loadouts.Loadout import net.psforever.packet.game.ItemTransactionMessage import net.psforever.types.ExoSuitType @@ -336,7 +337,7 @@ object EquipmentTerminalDefinition { * `TerminalDefinition.MakeKit` */ def BuildSimplifiedPattern(entry : Loadout.Simplification) : Equipment = { - import net.psforever.objects.Loadout._ + import net.psforever.objects.loadouts.Loadout._ entry match { case obj : ShorthandTool => val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.definition }) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala index 192d87dc..8060db3c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals import akka.actor.ActorContext import net.psforever.objects.Player +import net.psforever.objects.loadouts.InfantryLoadout import net.psforever.objects.inventory.InventoryItem import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.ItemTransactionMessage @@ -52,7 +53,7 @@ class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefini override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { - case Some(loadout) => + case Some(loadout : InfantryLoadout) => if(loadout.ExoSuit != ExoSuitType.MAX) { val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) @@ -61,7 +62,7 @@ class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefini else { Terminal.NoDeal() } - case None => + case Some(_) | None => Terminal.NoDeal() } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala index a1bfca86..c5ab86aa 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player +import net.psforever.objects.loadouts.InfantryLoadout import net.psforever.objects.inventory.InventoryItem import net.psforever.packet.game.ItemTransactionMessage import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._ @@ -37,11 +38,11 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) { override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { - case Some(loadout) => + case Some(loadout : InfantryLoadout) => val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory) - case None => + case Some(_) | None => Terminal.NoDeal() } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala index a64fad15..bff5fe26 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.{Player, Vehicle} +import net.psforever.objects.loadouts.VehicleLoadout import net.psforever.objects.inventory.InventoryItem import net.psforever.packet.game.ItemTransactionMessage @@ -83,7 +84,7 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition // "aphelion_flight" -> (()=>Unit) ) - import net.psforever.objects.{Loadout => _Loadout} //distinguish from Terminal.Loadout message + import net.psforever.objects.loadouts.{Loadout => _Loadout} //distinguish from Terminal.Loadout message import _Loadout._ /** * A `Map` of the default contents of a `Vehicle` inventory, called the trunk. @@ -101,29 +102,31 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition val ammo_flux = ShorthandAmmoBox(flux_cannon_thresher_battery, flux_cannon_thresher_battery.Capacity) val ammo_bomb = ShorthandAmmoBox(liberator_bomb, liberator_bomb.Capacity) Map( - //"quadstealth" -> _Loadout("default_quadstealth", List(), List()), - "quadassault" -> _Loadout("default_quadassault", List(), + //"quadstealth" -> VehicleLoadout("default_quadstealth", List(), List(), quadstealth), + "quadassault" -> VehicleLoadout("default_quadassault", List(), List( SimplifiedEntry(ammo_12mm, 30), SimplifiedEntry(ammo_12mm, 34), SimplifiedEntry(ammo_12mm, 74), SimplifiedEntry(ammo_12mm, 78) - ) + ), + quadassault ), { val ammo = ShorthandAmmoBox(hellfire_ammo, hellfire_ammo.Capacity) - "fury" -> _Loadout("default_fury", List(), + "fury" -> VehicleLoadout("default_fury", List(), List( SimplifiedEntry(ammo, 30), SimplifiedEntry(ammo, 34), SimplifiedEntry(ammo, 74), SimplifiedEntry(ammo, 78) - ) + ), + fury ) }, - //"ant" -> _Loadout("default_ant", List(), List()), - //"ams" -> _Loadout("default_ams", List(), List()), - "two_man_assault_buggy" -> _Loadout("default_two_man_assault_buggy", List(), + //"ant" -> VehicleLoadout("default_ant", List(), List(), ant), + //"ams" -> VehicleLoadout("default_ams", List(), List(), ams), + "two_man_assault_buggy" -> VehicleLoadout("default_two_man_assault_buggy", List(), List( SimplifiedEntry(ammo_12mm, 30), SimplifiedEntry(ammo_12mm, 34), @@ -131,11 +134,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_12mm, 90), SimplifiedEntry(ammo_12mm, 94), SimplifiedEntry(ammo_12mm, 98) - ) + ), + two_man_assault_buggy ), { val ammo = ShorthandAmmoBox(skyguard_flak_cannon_ammo, skyguard_flak_cannon_ammo.Capacity) - "skyguard" -> _Loadout("default_skyguard", List(), + "skyguard" -> VehicleLoadout("default_skyguard", List(), List( SimplifiedEntry(ammo_12mm, 30), SimplifiedEntry(ammo_12mm, 34), @@ -143,10 +147,11 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 90), SimplifiedEntry(ammo, 94), SimplifiedEntry(ammo, 98) - ) + ), + skyguard ) }, - "threemanheavybuggy" -> _Loadout("default_threemanheavybuggy", List(), + "threemanheavybuggy" -> VehicleLoadout("default_threemanheavybuggy", List(), List( SimplifiedEntry(ammo_12mm, 30), SimplifiedEntry(ammo_12mm, 34), @@ -154,11 +159,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_mortar, 90), SimplifiedEntry(ammo_mortar, 94), SimplifiedEntry(ammo_mortar, 98) - ) + ), + threemanheavybuggy ), { val ammo = ShorthandAmmoBox(firebird_missile, firebird_missile.Capacity) - "twomanheavybuggy" -> _Loadout("default_twomanheavybuggy", List(), + "twomanheavybuggy" -> VehicleLoadout("default_twomanheavybuggy", List(), List( SimplifiedEntry(ammo, 30), SimplifiedEntry(ammo, 34), @@ -166,10 +172,11 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 90), SimplifiedEntry(ammo, 94), SimplifiedEntry(ammo, 98) - ) + ), + twomanheavybuggy ) }, - "twomanhoverbuggy" -> _Loadout("default_twomanhoverbuggy", List(), + "twomanhoverbuggy" -> VehicleLoadout("default_twomanhoverbuggy", List(), List( SimplifiedEntry(ammo_flux, 30), SimplifiedEntry(ammo_flux, 34), @@ -177,9 +184,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_flux, 90), SimplifiedEntry(ammo_flux, 94), SimplifiedEntry(ammo_flux, 98) - ) + ), + twomanhoverbuggy ), - "mediumtransport" -> _Loadout("default_mediumtransport", List(), + "mediumtransport" -> VehicleLoadout("default_mediumtransport", List(), List( SimplifiedEntry(ammo_20mm, 30), SimplifiedEntry(ammo_20mm, 34), @@ -190,9 +198,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_20mm, 150), SimplifiedEntry(ammo_20mm, 154), SimplifiedEntry(ammo_20mm, 158) - ) + ), + mediumtransport ), - "battlewagon" -> _Loadout("default_battlewagon", List(), + "battlewagon" -> VehicleLoadout("default_battlewagon", List(), List( SimplifiedEntry(ammo_15mm, 30), SimplifiedEntry(ammo_15mm, 34), @@ -203,11 +212,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_15mm, 150), SimplifiedEntry(ammo_15mm, 154), SimplifiedEntry(ammo_15mm, 158) - ) + ), + battlewagon ), { val ammo = ShorthandAmmoBox(gauss_cannon_ammo, gauss_cannon_ammo.Capacity) - "thunderer" -> _Loadout("default_thunderer", List(), + "thunderer" -> VehicleLoadout("default_thunderer", List(), List( SimplifiedEntry(ammo, 30), SimplifiedEntry(ammo, 34), @@ -218,12 +228,13 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 150), SimplifiedEntry(ammo, 154), SimplifiedEntry(ammo, 158) - ) + ), + thunderer ) }, { val ammo = ShorthandAmmoBox(fluxpod_ammo, fluxpod_ammo.Capacity) - "aurora" -> _Loadout("default_aurora", List(), + "aurora" -> VehicleLoadout("default_aurora", List(), List( SimplifiedEntry(ammo, 30), SimplifiedEntry(ammo, 34), @@ -234,10 +245,11 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 150), SimplifiedEntry(ammo, 154), SimplifiedEntry(ammo, 158) - ) + ), + aurora ) }, - "apc_tr" -> _Loadout("default_apc_tr", List(), + "apc_tr" -> VehicleLoadout("default_apc_tr", List(), List( SimplifiedEntry(ammo_75mm, 30), SimplifiedEntry(ammo_75mm, 34), @@ -259,9 +271,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_15mm, 278), SimplifiedEntry(ammo_15mm, 282), SimplifiedEntry(ammo_15mm, 286) - ) + ), + apc_tr ), - "apc_nc" -> _Loadout("default_apc_nc", List(), + "apc_nc" -> VehicleLoadout("default_apc_nc", List(), List( SimplifiedEntry(ammo_75mm, 30), SimplifiedEntry(ammo_75mm, 34), @@ -283,9 +296,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_20mm, 278), SimplifiedEntry(ammo_20mm, 282), SimplifiedEntry(ammo_20mm, 286) - ) + ), + apc_nc ), - "apc_vs" -> _Loadout("default_apc_vs", List(), + "apc_vs" -> VehicleLoadout("default_apc_vs", List(), List( SimplifiedEntry(ammo_75mm, 30), SimplifiedEntry(ammo_75mm, 34), @@ -307,9 +321,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_flux, 278), SimplifiedEntry(ammo_flux, 282), SimplifiedEntry(ammo_flux, 286) - ) + ), + apc_vs ), - "lightning" -> _Loadout("default_lightning", List(), + "lightning" -> VehicleLoadout("default_lightning", List(), List( SimplifiedEntry(ammo_25mm, 30), SimplifiedEntry(ammo_25mm, 34), @@ -317,11 +332,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_75mm, 90), SimplifiedEntry(ammo_75mm, 94), SimplifiedEntry(ammo_75mm, 98) - ) + ), + lightning ), { val ammo = ShorthandAmmoBox(bullet_105mm, bullet_105mm.Capacity) - "prowler" -> _Loadout("default_prowler", List(), + "prowler" -> VehicleLoadout("default_prowler", List(), List( SimplifiedEntry(ammo_15mm, 30), SimplifiedEntry(ammo_15mm, 34), @@ -329,12 +345,13 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 90), SimplifiedEntry(ammo, 94), SimplifiedEntry(ammo, 98) - ) + ), + prowler ) }, { val ammo = ShorthandAmmoBox(bullet_150mm, bullet_150mm.Capacity) - "vanguard" -> _Loadout("default_vanguard", List(), + "vanguard" -> VehicleLoadout("default_vanguard", List(), List( SimplifiedEntry(ammo_20mm, 30), SimplifiedEntry(ammo_20mm, 34), @@ -342,13 +359,14 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 90), SimplifiedEntry(ammo, 94), SimplifiedEntry(ammo, 98) - ) + ), + vanguard ) }, { val ammo1 = ShorthandAmmoBox(pulse_battery, pulse_battery.Capacity) val ammo2 = ShorthandAmmoBox(heavy_rail_beam_battery, heavy_rail_beam_battery.Capacity) - "magrider" -> _Loadout("default_magrider", List(), + "magrider" -> VehicleLoadout("default_magrider", List(), List( SimplifiedEntry(ammo1, 30), SimplifiedEntry(ammo1, 34), @@ -356,23 +374,25 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo2, 90), SimplifiedEntry(ammo2, 94), SimplifiedEntry(ammo2, 98) - ) + ), + magrider ) }, - //"flail" -> _Loadout("default_flail", List(), List()), - //"switchblade" -> _Loadout("default_switchblade", List(), List()), - //"router" -> _Loadout("default_router", List(), List()), - "mosquito" -> _Loadout("default_mosquito", List(), + //"flail" -> VehicleLoadout("default_flail", List(), List(), flail), + //"switchblade" -> VehicleLoadout("default_switchblade", List(), List(), switchblade), + //"router" -> VehicleLoadout("default_router", List(), List(), router), + "mosquito" -> VehicleLoadout("default_mosquito", List(), List( SimplifiedEntry(ammo_12mm, 30), SimplifiedEntry(ammo_12mm, 34), SimplifiedEntry(ammo_12mm, 74), SimplifiedEntry(ammo_12mm, 78) - ) + ), + mosquito ), { val ammo = ShorthandAmmoBox(reaver_rocket, reaver_rocket.Capacity) - "lightgunship" -> _Loadout("default_lightgunship", List(), + "lightgunship" -> VehicleLoadout("default_lightgunship", List(), List( SimplifiedEntry(ammo, 30), SimplifiedEntry(ammo, 34), @@ -380,22 +400,24 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo, 90), SimplifiedEntry(ammo_20mm, 94), SimplifiedEntry(ammo_20mm, 98) - ) + ), + lightgunship ) }, { val ammo1 = ShorthandAmmoBox(wasp_rocket_ammo, wasp_rocket_ammo.Capacity) val ammo2 = ShorthandAmmoBox(wasp_gun_ammo, wasp_gun_ammo.Capacity) - "wasp" -> _Loadout("default_wasp", List(), + "wasp" -> VehicleLoadout("default_wasp", List(), List( SimplifiedEntry(ammo1, 30), SimplifiedEntry(ammo1, 34), SimplifiedEntry(ammo2, 74), SimplifiedEntry(ammo2, 78) - ) + ), + wasp ) }, - "liberator" -> _Loadout("default_liberator", List(), + "liberator" -> VehicleLoadout("default_liberator", List(), List( SimplifiedEntry(ammo_35mm, 30), SimplifiedEntry(ammo_35mm, 34), @@ -406,9 +428,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_bomb, 150), SimplifiedEntry(ammo_bomb, 154), SimplifiedEntry(ammo_bomb, 158) - ) + ), + liberator ), - "vulture" -> _Loadout("default_vulture", List(), + "vulture" -> VehicleLoadout("default_vulture", List(), List( SimplifiedEntry(ammo_35mm, 30), SimplifiedEntry(ammo_35mm, 34), @@ -418,9 +441,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_bomb, 98), SimplifiedEntry(ammo_bomb, 102), SimplifiedEntry(ammo_bomb, 106) - ) //TODO confirm + ), //TODO confirm + vulture ), - "dropship" -> _Loadout("default_dropship", List(), + "dropship" -> VehicleLoadout("default_dropship", List(), List( SimplifiedEntry(ammo_20mm, 30), SimplifiedEntry(ammo_20mm, 34), @@ -434,9 +458,10 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_20mm, 162), SimplifiedEntry(ammo_20mm, 166), SimplifiedEntry(ammo_20mm, 170) - ) + ), + dropship ), - "galaxy_gunship" -> _Loadout("galaxy_gunship", List(), + "galaxy_gunship" -> VehicleLoadout("galaxy_gunship", List(), List( SimplifiedEntry(ammo_35mm, 30), SimplifiedEntry(ammo_35mm, 34), @@ -450,10 +475,11 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition SimplifiedEntry(ammo_mortar, 178), SimplifiedEntry(ammo_mortar, 182), SimplifiedEntry(ammo_mortar, 186) - ) + ), + galaxy_gunship ) - //"phantasm" -> _Loadout("default_phantasm", List(), List()), - //"lodestar" -> _Loadout("default_lodestar", List(), List()), + //"phantasm" -> VehicleLoadout("default_phantasm", List(), List(), phantasm), + //"lodestar" -> VehicleLoadout("default_lodestar", List(), List(), lodestar), ) } diff --git a/common/src/test/scala/objects/AvatarTest.scala b/common/src/test/scala/objects/AvatarTest.scala index 95fb698f..6a45225d 100644 --- a/common/src/test/scala/objects/AvatarTest.scala +++ b/common/src/test/scala/objects/AvatarTest.scala @@ -3,6 +3,7 @@ package objects import net.psforever.objects.GlobalDefinitions._ import net.psforever.objects._ +import net.psforever.objects.loadouts._ import net.psforever.objects.definition.ImplantDefinition import net.psforever.types.{CharacterGender, ImplantType, PlanetSideEmpire} import org.specs2.mutable._ @@ -291,7 +292,7 @@ class AvatarTest extends Specification { avatar.SaveLoadout(obj, "test", 0) avatar.LoadLoadout(0) match { - case Some(items) => + case Some(items : InfantryLoadout) => items.Label mustEqual "test" items.ExoSuit mustEqual obj.ExoSuit items.Subtype mustEqual 0 @@ -321,7 +322,7 @@ class AvatarTest extends Specification { inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual energy_cell inventory(5).index mustEqual 39 inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit - case None => + case _ => ko } } @@ -347,13 +348,13 @@ class AvatarTest extends Specification { avatar.SaveLoadout(obj, "test", 0) avatar.LoadLoadout(0) match { - case Some(items) => + case Some(items : InfantryLoadout) => items.Label mustEqual "test" items.ExoSuit mustEqual obj.ExoSuit items.Subtype mustEqual 0 items.VisibleSlots.length mustEqual 3 items.Inventory.length mustEqual 0 //empty - case None => + case _ => ko } } @@ -366,13 +367,13 @@ class AvatarTest extends Specification { avatar.SaveLoadout(obj, "test", 0) avatar.LoadLoadout(0) match { - case Some(items) => + case Some(items : InfantryLoadout) => items.Label mustEqual "test" items.ExoSuit mustEqual obj.ExoSuit items.Subtype mustEqual 0 items.VisibleSlots.length mustEqual 0 //empty items.Inventory.length mustEqual 6 - case None => + case _ => ko } } diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala index fb01d696..208f5f6d 100644 --- a/common/src/test/scala/objects/LoadoutTest.scala +++ b/common/src/test/scala/objects/LoadoutTest.scala @@ -2,6 +2,7 @@ package objects import net.psforever.objects._ +import net.psforever.objects.loadouts._ import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire} import net.psforever.objects.GlobalDefinitions._ import org.specs2.mutable._ @@ -36,7 +37,7 @@ class LoadoutTest extends Specification { "create a loadout that contains a player's inventory" in { val player = CreatePlayer() - val obj = Loadout.Create(player, "test") + val obj = Loadout.Create(player, "test").asInstanceOf[InfantryLoadout] obj.Label mustEqual "test" obj.ExoSuit mustEqual obj.ExoSuit @@ -71,16 +72,16 @@ class LoadoutTest extends Specification { slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max) Player.SuitSetup(player, ExoSuitType.MAX) - val ldout1 = Loadout.Create(player, "weaponless") + val ldout1 = Loadout.Create(player, "weaponless").asInstanceOf[InfantryLoadout] slot.Equipment = None slot.Equipment = Tool(trhev_dualcycler) - val ldout2 = Loadout.Create(player, "cycler") + val ldout2 = Loadout.Create(player, "cycler").asInstanceOf[InfantryLoadout] slot.Equipment = None slot.Equipment = Tool(trhev_pounder) - val ldout3 = Loadout.Create(player, "pounder") + val ldout3 = Loadout.Create(player, "pounder").asInstanceOf[InfantryLoadout] slot.Equipment = None slot.Equipment = Tool(trhev_burster) - val ldout4 = Loadout.Create(player, "burster") + val ldout4 = Loadout.Create(player, "burster").asInstanceOf[InfantryLoadout] ldout1.Subtype mustEqual 0 ldout2.Subtype mustEqual 1 diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 33514522..fd9bd7d1 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -15,6 +15,7 @@ import net.psforever.objects._ import net.psforever.objects.definition.ToolDefinition import net.psforever.objects.definition.converter.CorpseConverter import net.psforever.objects.equipment._ +import net.psforever.objects.loadouts._ import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} import net.psforever.objects.serverobject.mount.Mountable @@ -2360,6 +2361,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } case msg @ FavoritesRequest(player_guid, unk, action, line, label) => + log.info(s"FavoritesRequest: $msg") if(player.GUID == player_guid) { val name = label.getOrElse("missing_loadout_name") action match { @@ -2372,7 +2374,6 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(FavoritesMessage(0, player_guid, line, "")) } } - log.info("FavoritesRequest: " + msg) case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) => log.info("WeaponDelayFire: " + msg) From 2a4fe4865e7090741d8cf33a30dbbfa49e0877b3 Mon Sep 17 00:00:00 2001 From: FateJH Date: Tue, 15 May 2018 08:05:33 -0400 Subject: [PATCH 3/7] partially-working vehicle favorites system; modifications to existing favorites system, identification of packet parameters --- .../scala/net/psforever/objects/Avatar.scala | 17 +- .../scala/net/psforever/objects/Vehicle.scala | 33 +++ .../terminals/OrderTerminalABDefinition.scala | 2 +- .../terminals/OrderTerminalDefinition.scala | 2 +- .../terminals/RepairRearmSiloDefinition.scala | 13 +- .../serverobject/terminals/Terminal.scala | 5 +- .../terminals/TerminalDefinition.scala | 2 +- .../packet/game/FavoritesMessage.scala | 29 +-- .../packet/game/FavoritesRequest.scala | 22 +- .../net/psforever/types/LoadoutType.scala | 16 ++ .../net/psforever/types/TransactionType.scala | 2 +- .../scala/game/FavoritesMessageTest.scala | 9 +- .../scala/game/FavoritesRequestTest.scala | 7 +- .../terminal/OrderTerminalABTest.scala | 4 +- .../src/main/scala/WorldSessionActor.scala | 220 ++++++++++++++---- 15 files changed, 295 insertions(+), 88 deletions(-) create mode 100644 common/src/main/scala/net/psforever/types/LoadoutType.scala diff --git a/common/src/main/scala/net/psforever/objects/Avatar.scala b/common/src/main/scala/net/psforever/objects/Avatar.scala index 8b704a6d..05caf2be 100644 --- a/common/src/main/scala/net/psforever/objects/Avatar.scala +++ b/common/src/main/scala/net/psforever/objects/Avatar.scala @@ -17,7 +17,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : /** Certifications */ private val certs : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]() /** Implants
- * Unlike other objects, the maximum number of `ImplantSlots` are built into the `Avatar`. + * Unlike other objects, all `ImplantSlot` objects are already built into the `Avatar`. * Additionally, implants do not have tightly-coupled "`Definition` objects" that explain a formal implant object. * The `ImplantDefinition` objects themselves are moved around as if they were the implants. * The terms externally used for the states of process is "installed" and "uninstalled." @@ -25,9 +25,12 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : * @see `DetailedCharacterData.implants` */ private val implants : Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot) - /** Loadouts */ - private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None) - /** Locker (inventory slot number five) */ + /** Loadouts
+ * 0-9 are Infantry loadouts + * 10-14 are Vehicle loadouts + */ + private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](15)(None) + /** Locker */ private val locker : LockerContainer = new LockerContainer() { override def toString : String = { s"$name's ${Definition.Name}" @@ -154,6 +157,12 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : } } + def SaveLoadout(owner : Vehicle, label : String, line : Int) : Unit = { + if(line > 9 && line < loadouts.length) { + loadouts(line) = Some(Loadout.Create(owner, label)) + } + } + def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).getOrElse(None) def DeleteLoadout(line : Int) : Unit = { diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala index 3aaa32eb..6dabd99a 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala @@ -349,6 +349,39 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ def VisibleSlots : Set[Int] = weapons.keySet + override def Slot(slotNum : Int) : EquipmentSlot = { + weapons.get(slotNum) +// .orElse(utilities.get(slotNum) match { +// case Some(_) => +// //TODO what do now? +// None +// case None => ; +// None +// }) + .orElse(Some(Inventory.Slot(slotNum))).get + } + + override def Find(guid : PlanetSideGUID) : Option[Int] = { + weapons.find({ case (_, obj) => + obj.Equipment match { + case Some(item) => + if(item.HasGUID && item.GUID == guid) { + true + } + else { + false + } + case None => + false + } + }) match { + case Some((index, _)) => + Some(index) + case None => + Inventory.Find(guid) + } + } + /** * A reference to the `Vehicle` `Trunk` space. * @return this `Vehicle` `Trunk` diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala index 8060db3c..3aaac12a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala @@ -42,7 +42,7 @@ class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefini override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg) /** - * Process a `TransactionType.InfantryLoadout` action by the user. + * Process a `TransactionType.Loadout` action by the user. * `Loadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings. * If a valid loadout is found, its data is transformed back into actual `Equipment` for return to the user. * Loadouts that would suit the player into a mechanized assault exo-suit are not permitted. diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala index c5ab86aa..8a270b34 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala @@ -28,7 +28,7 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) { override def Buy(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = buyFunc(player, msg) /** - * Process a `TransactionType.InfantryLoadout` action by the user. + * Process a `TransactionType.Loadout` action by the user. * `Loadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings. * If a valid loadout is found, its data is transformed back into actual `Equipment` for return to the user. * @param player the player diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala index b0daa854..baf37bc4 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala @@ -2,6 +2,9 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player +import net.psforever.objects.inventory.InventoryItem +import net.psforever.objects.loadouts.VehicleLoadout +import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition.BuildSimplifiedPattern import net.psforever.packet.game.ItemTransactionMessage class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) { @@ -13,10 +16,12 @@ class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinit override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { if(msg.item_page == 4) { //Favorites tab - player.LoadLoadout(msg.unk1) match { - case Some(loadout) => - Terminal.VehicleLoadout(Nil, Nil) - case None => + player.LoadLoadout(msg.unk1 + 10) match { + case Some(loadout : VehicleLoadout) => + val weapons = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + Terminal.VehicleLoadout(loadout.vehicle_definition, weapons, inventory) + case _ => Terminal.NoDeal() } } 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 8ee17814..7a8676bb 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,6 +2,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player +import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{TransactionType, Vector3} @@ -73,7 +74,7 @@ class Terminal(tdef : TerminalDefinition) extends Amenity { case TransactionType.Sell => tdef.Sell(player, msg) - case TransactionType.InfantryLoadout => + case TransactionType.Loadout => tdef.Loadout(player, msg) case _ => @@ -190,7 +191,7 @@ object Terminal { */ final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange - final case class VehicleLoadout(weapons : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange + final case class VehicleLoadout(vehicle_definition : VehicleDefinition, weapons : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange /** * Start the special effects caused by a proximity-base service. diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala index be739f4c..562c6c91 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala @@ -24,7 +24,7 @@ abstract class TerminalDefinition(objectId : Int) extends net.psforever.objects. def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() /** - * The unimplemented functionality for this `Terminal`'s `TransactionType.InfantryLoadout` activity. + * The unimplemented functionality for this `Terminal`'s `TransactionType.Loadout` activity. */ def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() } diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala index 7fa3d32d..314986c4 100644 --- a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala @@ -2,6 +2,7 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import net.psforever.types.LoadoutType import scodec.Codec import scodec.codecs._ import shapeless.{::, HNil} @@ -18,12 +19,6 @@ import shapeless.{::, HNil} * Infantry equipment favorites are appended with a code for the type of exo-suit that they will load on a player. * This does not match the same two field numbering system as in `ArmorChangedMessage` packets.
*
- * Lists:
- * ` - * 0 - Equipment Terminal (infantry)
- * 1 - Repair/Rearm Silo (standard vehicles)
- * ` - *
* Armors:
* ` * 1 - Agile
@@ -33,13 +28,7 @@ import shapeless.{::, HNil} * 6 - AV MAX
* ` *
- * Exploration 1:
- * The identifier for the list is two bits so four separated lists of `Favorites` are supportable. - * Two of the lists are common enough and we can assume one of the others is related to Battleframe Robotics. - * These lists also do not include `Squad Defintion...` presets. - * What are the unknown lists?
- *
- * Exploration 2:
+ * Exploration:
* There are three unaccounted exo-suit indices - 0, 3, and 7; * and, there are two specific kinds of exo-suit that are not defined - Infiltration and Standard. * It is possible that one of the indices also defines the generic MAX (see `ArmorChangedMessage`). @@ -50,11 +39,11 @@ import shapeless.{::, HNil} * @param label the identifier for this entry * @param armor the type of exo-suit, if an Infantry loadout */ -final case class FavoritesMessage(list : Int, +final case class FavoritesMessage(list : LoadoutType.Value, player_guid : PlanetSideGUID, line : Int, label : String, - armor : Option[Int] = None) + armor : Option[Int]) extends PlanetSideGamePacket { type Packet = FavoritesMessage def opcode = GamePacketOpcode.FavoritesMessage @@ -62,12 +51,16 @@ final case class FavoritesMessage(list : Int, } object FavoritesMessage extends Marshallable[FavoritesMessage] { + def apply(list : LoadoutType.Value, player_guid : PlanetSideGUID, line : Int, label : String) : FavoritesMessage = { + FavoritesMessage(list, player_guid, line, label, None) + } + implicit val codec : Codec[FavoritesMessage] = ( - ("list" | uint2L) >>:~ { value => + ("list" | LoadoutType.codec) >>:~ { value => ("player_guid" | PlanetSideGUID.codec) :: ("line" | uint4L) :: ("label" | PacketHelpers.encodedWideStringAligned(2)) :: - conditional(value == 0, "armor" | uintL(3)) + conditional(value == LoadoutType.Infantry, "armor" | uintL(3)) }).xmap[FavoritesMessage] ( { case lst :: guid :: ln :: str :: arm :: HNil => @@ -75,7 +68,7 @@ object FavoritesMessage extends Marshallable[FavoritesMessage] { }, { case FavoritesMessage(lst, guid, ln, str, arm) => - val armset : Option[Int] = if(lst == 0 && arm.isEmpty) { Some(0) } else { arm } + val armset : Option[Int] = if(lst == LoadoutType.Infantry && arm.isEmpty) { Some(0) } else { arm } lst :: guid :: ln :: str :: armset :: HNil } ) diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesRequest.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesRequest.scala index 3d9af568..ac3ccb10 100644 --- a/common/src/main/scala/net/psforever/packet/game/FavoritesRequest.scala +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesRequest.scala @@ -2,21 +2,33 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import net.psforever.types.LoadoutType import scodec.Codec import scodec.codecs._ object FavoritesAction extends Enumeration { type Type = Value - val Unknown, - Save, - Delete = Value + val + Unknown, + Save, + Delete + = Value implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L) } +/** + * na + * @param player_guid the player + * @param list na + * @param action the behavior of this packet + * @param line what line of the applicable loadout ("Saved Favorites") list is modified + * @param label applicable when a load out is being saved; + * this is the string that will be displayed in the list of loadouts on that line + */ final case class FavoritesRequest(player_guid : PlanetSideGUID, - unk : Int, + list : LoadoutType.Value, action : FavoritesAction.Value, line : Int, label : Option[String]) @@ -29,7 +41,7 @@ final case class FavoritesRequest(player_guid : PlanetSideGUID, object FavoritesRequest extends Marshallable[FavoritesRequest] { implicit val codec : Codec[FavoritesRequest] = ( ("player_guid" | PlanetSideGUID.codec) :: - ("unk" | uint2L) :: + ("list" | LoadoutType.codec) :: (("action" | FavoritesAction.codec) >>:~ { action => ("line" | uint4L) :: conditional(action == FavoritesAction.Save, "label" | PacketHelpers.encodedWideString) diff --git a/common/src/main/scala/net/psforever/types/LoadoutType.scala b/common/src/main/scala/net/psforever/types/LoadoutType.scala new file mode 100644 index 00000000..ad180e77 --- /dev/null +++ b/common/src/main/scala/net/psforever/types/LoadoutType.scala @@ -0,0 +1,16 @@ +// Copyright (c) 2017 PSForever +package net.psforever.types + +import net.psforever.packet.PacketHelpers +import scodec.codecs.uint2L + +object LoadoutType extends Enumeration { + type Type = Value + + val + Infantry, + Vehicle + = Value + + implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L) +} diff --git a/common/src/main/scala/net/psforever/types/TransactionType.scala b/common/src/main/scala/net/psforever/types/TransactionType.scala index bf76e025..4fee2ec1 100644 --- a/common/src/main/scala/net/psforever/types/TransactionType.scala +++ b/common/src/main/scala/net/psforever/types/TransactionType.scala @@ -12,7 +12,7 @@ object TransactionType extends Enumeration { Sell, // or forget on certif term Unk4, Unk5, - InfantryLoadout, + Loadout, Unk7 = Value diff --git a/common/src/test/scala/game/FavoritesMessageTest.scala b/common/src/test/scala/game/FavoritesMessageTest.scala index 4033eb82..74e25392 100644 --- a/common/src/test/scala/game/FavoritesMessageTest.scala +++ b/common/src/test/scala/game/FavoritesMessageTest.scala @@ -4,6 +4,7 @@ package game import org.specs2.mutable._ import net.psforever.packet._ import net.psforever.packet.game._ +import net.psforever.types.LoadoutType import scodec.bits._ class FavoritesMessageTest extends Specification { @@ -13,7 +14,7 @@ class FavoritesMessageTest extends Specification { "decode (for infantry)" in { PacketCoding.DecodePacket(stringInfantry).require match { case FavoritesMessage(list, player_guid, line, label, armor) => - list mustEqual 0 + list mustEqual LoadoutType.Infantry player_guid mustEqual PlanetSideGUID(3760) line mustEqual 0 label mustEqual "Agile (basic)" @@ -25,7 +26,7 @@ class FavoritesMessageTest extends Specification { } "encode (for infantry)" in { - val msg = FavoritesMessage(0, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1)) + val msg = FavoritesMessage(LoadoutType.Infantry, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual stringInfantry @@ -34,7 +35,7 @@ class FavoritesMessageTest extends Specification { "decode (for vehicles)" in { PacketCoding.DecodePacket(stringVehicles).require match { case FavoritesMessage(list, player_guid, line, label, armor) => - list mustEqual 1 + list mustEqual LoadoutType.Vehicle player_guid mustEqual PlanetSideGUID(4210) line mustEqual 0 label mustEqual "Skyguard" @@ -45,7 +46,7 @@ class FavoritesMessageTest extends Specification { } "encode (for vehicles)" in { - val msg = FavoritesMessage(1, PlanetSideGUID(4210), 0, "Skyguard") + val msg = FavoritesMessage(LoadoutType.Vehicle, PlanetSideGUID(4210), 0, "Skyguard") val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual stringVehicles diff --git a/common/src/test/scala/game/FavoritesRequestTest.scala b/common/src/test/scala/game/FavoritesRequestTest.scala index 23adfccc..ca66f047 100644 --- a/common/src/test/scala/game/FavoritesRequestTest.scala +++ b/common/src/test/scala/game/FavoritesRequestTest.scala @@ -4,6 +4,7 @@ package game import org.specs2.mutable._ import net.psforever.packet._ import net.psforever.packet.game._ +import net.psforever.types.LoadoutType import scodec.bits._ class FavoritesRequestTest extends Specification { @@ -11,9 +12,9 @@ class FavoritesRequestTest extends Specification { "decode (for infantry)" in { PacketCoding.DecodePacket(stringInfantry).require match { - case FavoritesRequest(player_guid, unk, action, line, label) => + case FavoritesRequest(player_guid, list, action, line, label) => player_guid mustEqual PlanetSideGUID(75) - unk mustEqual 0 + list mustEqual LoadoutType.Infantry action mustEqual FavoritesAction.Save line mustEqual 1 label.isDefined mustEqual true @@ -24,7 +25,7 @@ class FavoritesRequestTest extends Specification { } "encode (for infantry)" in { - val msg = FavoritesRequest(PlanetSideGUID(75), 0, FavoritesAction.Save, 1, Some("Example")) + val msg = FavoritesRequest(PlanetSideGUID(75), LoadoutType.Infantry, FavoritesAction.Save, 1, Some("Example")) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual stringInfantry diff --git a/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala index c4d44e14..ee4b4a47 100644 --- a/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala +++ b/common/src/test/scala/objects/terminal/OrderTerminalABTest.scala @@ -68,10 +68,10 @@ class OrderTerminalABTest extends Specification { player.ExoSuit = ExoSuitType.MAX avatar.SaveLoadout(player, "test2", 1) - val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 0, PlanetSideGUID(0)) + val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Loadout, 4, "", 0, PlanetSideGUID(0)) terminal.Request(player, msg1) mustEqual Terminal.InfantryLoadout(ExoSuitType.Standard, 0, Nil, Nil) - val msg2 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 1, PlanetSideGUID(0)) + val msg2 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Loadout, 4, "", 1, PlanetSideGUID(0)) terminal.Request(player, msg2) mustEqual Terminal.NoDeal() } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index fd9bd7d1..9b05f120 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -39,6 +39,7 @@ import net.psforever.types._ import services._ import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} +import services.vehicle.VehicleAction.UnstowEquipment import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse} import scala.annotation.tailrec @@ -818,7 +819,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) => //TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}") - sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true)) + sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) val dropPred = DropPredicate(tplayer) val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred) val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred) @@ -881,7 +882,59 @@ class WorldSessionActor extends Actor with MDCContextAware { val objDef = obj.Definition avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get)) }) - sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true)) + sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) + + case Terminal.VehicleLoadout(definition, weapons, inventory) => + log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") + log.info(s"Vehicle: $definition") + log.info(s"Weapons (${weapons.size}): $weapons") + log.info(s"Inventory (${inventory.size}): $inventory") + LocalVehicle match { + case Some(vehicle) => + sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) + val (_, afterInventory) = inventory.partition( DropPredicate(tplayer) ) //dropped items are lost + //common action - remove old inventory + val deleteEquipment : (Int,Equipment)=>Unit = DeleteEquipmentFromVehicle(vehicle) + vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) => + deleteEquipment(index, obj) + taskResolver ! GUIDTask.UnregisterEquipment(obj)(continent.GUID) + }) + val stowEquipment : (Int,Equipment)=>TaskResolver.GiveTask = StowNewEquipmentInVehicle(vehicle) + (if(vehicle.Definition == definition) { + //vehicles are the same type; transfer over weapons + vehicle.Weapons + .filter({ case (_, slot) => slot.Equipment.nonEmpty }) + .foreach({ case (_, slot) => + val equipment = slot.Equipment.get + slot.Equipment = None + val equipment_guid = equipment.GUID + sendResponse(ObjectDeleteMessage(equipment_guid, 0)) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, equipment_guid)) + taskResolver ! GUIDTask.UnregisterEquipment(equipment)(continent.GUID) + }) + weapons.foreach({ case InventoryItem(obj, index) => + //create weapons and share with everyone + taskResolver ! PutNewWeaponInVehicleSlot(vehicle, obj.asInstanceOf[Tool], index) + }) + afterInventory + } + else { + //do not transfer over weapons + if(vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset) { + afterInventory + } + else { + //accommodate as much of inventory as possible + //TODO map x,y -> x,y rather than reorganize items + val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory) //dropped items can be forgotten + stow + } + }).foreach({ case InventoryItem(obj, index) => + taskResolver ! stowEquipment(index, obj) + }) + case None => + log.error(s"can not apply the loadout - can not find a vehicle") + } case Terminal.LearnCertification(cert, cost) => if(!tplayer.Certifications.contains(cert)) { @@ -895,9 +948,6 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false)) } - case Terminal.VehicleLoadout(weapons, inventory) => - log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") - case Terminal.SellCertification(cert, cost) => if(tplayer.Certifications.contains(cert)) { log.info(s"$tplayer is forgetting the $cert certification for $cost points") @@ -1424,9 +1474,10 @@ class WorldSessionActor extends Actor with MDCContextAware { AwardBattleExperiencePoints(avatar, 1000000L) player = new Player(avatar) //player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) //home3, HART C - //player.Orientation = Vector3(0f, 0f, 90f) - player.Position = Vector3(4262.211f ,4067.0625f ,262.35938f) //z6, Akna.tower - player.Orientation = Vector3(0f, 0f, 132.1875f) + player.Position = Vector3(3940.3984f, 4343.625f, 266.45312f) + player.Orientation = Vector3(0f, 0f, 90f) + //player.Position = Vector3(4262.211f ,4067.0625f ,262.35938f) //z6, Akna.tower + //player.Orientation = Vector3(0f, 0f, 132.1875f) // player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting player.Slot(0).Equipment = SimpleItem(remote_electronics_kit) //Tool(GlobalDefinitions.StandardPistol(player.Faction)) player.Slot(2).Equipment = Tool(punisher) //suppressor @@ -1757,15 +1808,15 @@ class WorldSessionActor extends Actor with MDCContextAware { case x :: xs => val (deleteFunc, modifyFunc) : ((Int, AmmoBox)=>Unit, (AmmoBox, Int)=>Unit) = obj match { case (veh : Vehicle) => - (DeleteAmmunitionInVehicle(veh), ModifyAmmunitionInVehicle(veh)) + (DeleteEquipmentFromVehicle(veh), ModifyAmmunitionInVehicle(veh)) case _ => - (DeleteAmmunition(obj), ModifyAmmunition(obj)) + (DeleteEquipment(obj), ModifyAmmunition(obj)) } val (stowFuncTask, stowFunc) : ((Int, AmmoBox)=>TaskResolver.GiveTask, (Int, AmmoBox)=>Unit) = obj match { case (veh : Vehicle) => - (StowNewAmmunitionInVehicles(veh), StowAmmunitionInVehicles(veh)) + (StowNewEquipmentInVehicle(veh), StowEquipmentInVehicles(veh)) case _ => - (StowNewAmmunition(obj), StowAmmunition(obj)) + (StowNewEquipment(obj), StowEquipment(obj)) } xs.foreach(item => { obj.Inventory -= x.start @@ -1977,9 +2028,9 @@ class WorldSessionActor extends Actor with MDCContextAware { case x :: xs => val (deleteFunc, modifyFunc) : ((Int, AmmoBox)=>Unit, (AmmoBox, Int)=>Unit) = obj match { case (veh : Vehicle) => - (DeleteAmmunitionInVehicle(veh), ModifyAmmunitionInVehicle(veh)) + (DeleteEquipmentFromVehicle(veh), ModifyAmmunitionInVehicle(veh)) case _ => - (DeleteAmmunition(obj), ModifyAmmunition(obj)) + (DeleteEquipment(obj), ModifyAmmunition(obj)) } xs.foreach(item => { deleteFunc(item.start, item.obj.asInstanceOf[AmmoBox]) @@ -2071,7 +2122,14 @@ class WorldSessionActor extends Actor with MDCContextAware { findFunc(parent) case None => None - }) match { + }) + .orElse(LocalVehicle match { + case Some(parent) => + findFunc(parent) + case None => + None + }) + match { case Some((parent, Some(slot))) => taskResolver ! RemoveEquipmentFromSlot(parent, obj, slot) log.info(s"RequestDestroy: equipment $object_guid") @@ -2360,18 +2418,42 @@ class WorldSessionActor extends Actor with MDCContextAware { log.error(s"ItemTransaction: $terminal_guid does not exist") } - case msg @ FavoritesRequest(player_guid, unk, action, line, label) => + case msg @ FavoritesRequest(player_guid, list, action, line, label) => log.info(s"FavoritesRequest: $msg") if(player.GUID == player_guid) { - val name = label.getOrElse("missing_loadout_name") + val lineno = if(list == LoadoutType.Vehicle) { line + 10 } else { line } + val name = label.getOrElse(s"missing_loadout_${line+1}") action match { - case FavoritesAction.Unknown => ; case FavoritesAction.Save => - avatar.SaveLoadout(player, name, line) - sendResponse(FavoritesMessage(0, player_guid, line, name)) + (if(list == LoadoutType.Infantry) { + Some(player) + } + else if(list == LoadoutType.Vehicle) { + player.VehicleSeated match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) + case None => + None + } + } + else { + None + }) match { + case Some(owner : Player) => + avatar.SaveLoadout(owner, name, lineno) + case Some(owner : Vehicle) => + avatar.SaveLoadout(owner, name, lineno) + case Some(_) | None => + log.error("FavoritesRequest: unexpected owner for favorites") + } + sendResponse(FavoritesMessage(list, player_guid, line, name)) + case FavoritesAction.Delete => - avatar.DeleteLoadout(line) - sendResponse(FavoritesMessage(0, player_guid, line, "")) + avatar.DeleteLoadout(lineno) + sendResponse(FavoritesMessage(list, player_guid, line, "")) + + case FavoritesAction.Unknown => + log.warn("FavoritesRequest: unknown favorites action") } } @@ -2653,6 +2735,46 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + def PutNewWeaponInVehicleSlot(target : Vehicle, obj : Tool, index : Int) : TaskResolver.GiveTask = { + TaskResolver.GiveTask( + new Task() { + private val localTarget = target + private val localIndex = index + private val localObject = obj + private val localAnnounce = self + private val localAvatarService = avatarService + private val localVehicleService = vehicleService + + override def isComplete : Task.Resolution.Value = { + if(localTarget.Slot(localIndex).Equipment.contains(localObject)) { + Task.Resolution.Success + } + else { + Task.Resolution.Incomplete + } + } + + def Execute(resolver : ActorRef) : Unit = { + localTarget.Slot(localIndex).Equipment = localObject + resolver ! scala.util.Success(this) + } + + override def onSuccess() : Unit = { + val definition = localObject.Definition + if(localTarget.VisibleSlots.contains(localIndex)) { + localAvatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentInHand(localTarget.GUID, localTarget.GUID, localIndex, localObject)) + } + val channel = s"${localTarget.Actor}" + (0 until localObject.MaxAmmoSlot).foreach({ index => + val box = localObject.AmmoSlots(index).Box + val boxDef = box.Definition + val boxdata = boxDef.Packet.DetailedConstructorData(box).get + localVehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState(PlanetSideGUID(0), box, localObject.GUID, index, boxdata)) + }) + } + }, List(GUIDTask.RegisterTool(obj)(continent.GUID))) + } + /** * Construct tasking that coordinates the following:
* 1) Remove a new piece of `Equipment` from where it is currently stored.
@@ -3297,14 +3419,14 @@ class WorldSessionActor extends Actor with MDCContextAware { } /** - * Given an object that contains a box of amunition in its `Inventory` at a certain location, + * Given an object that contains an item (`Equipment`) in its `Inventory` at a certain location, * remove it permanently. * @param obj the `Container` - * @param start where the ammunition can be found - * @param item an object to unregister (should have been the ammunition that was removed); + * @param start where the item can be found + * @param item an object to unregister; * not explicitly checked */ - private def DeleteAmmunition(obj : PlanetSideGameObject with Container)(start : Int, item : AmmoBox) : Unit = { + private def DeleteEquipment(obj : PlanetSideGameObject with Container)(start : Int, item : Equipment) : Unit = { val item_guid = item.GUID obj.Inventory -= start taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID) @@ -3312,17 +3434,17 @@ class WorldSessionActor extends Actor with MDCContextAware { } /** - * Given a vehicle that contains a box of amunition in its `Trunk` at a certain location, + * Given a vehicle that contains an item (`Equipment`) in its `Trunk` at a certain location, * remove it permanently. - * @see `DeleteAmmunition` + * @see `DeleteEquipment` * @param obj the `Vehicle` - * @param start where the ammunition can be found - * @param item an object to unregister (should have been the ammunition that was removed); + * @param start where the item can be found + * @param item an object to unregister; * not explicitly checked */ - private def DeleteAmmunitionInVehicle(obj : Vehicle)(start : Int, item : AmmoBox) : Unit = { + private def DeleteEquipmentFromVehicle(obj : Vehicle)(start : Int, item : Equipment) : Unit = { val item_guid = item.GUID - DeleteAmmunition(obj)(start, item) + DeleteEquipment(obj)(start, item) vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player.GUID, item_guid)) } @@ -3355,27 +3477,27 @@ class WorldSessionActor extends Actor with MDCContextAware { /** * Announce that an already-registered `AmmoBox` object exists in a given position in some `Container` object's inventory. - * @see `StowAmmunitionInVehicles` + * @see `StowEquipmentInVehicles` * @see `ChangeAmmoMessage` * @param obj the `Container` object * @param index an index in `obj`'s inventory * @param item an `AmmoBox` */ - def StowAmmunition(obj : PlanetSideGameObject with Container)(index : Int, item : AmmoBox) : Unit = { + def StowEquipment(obj : PlanetSideGameObject with Container)(index : Int, item : AmmoBox) : Unit = { obj.Inventory += index -> item sendResponse(ObjectAttachMessage(obj.GUID, item.GUID, index)) } /** * Announce that an already-registered `AmmoBox` object exists in a given position in some vehicle's inventory. - * @see `StowAmmunition` + * @see `StowEquipment` * @see `ChangeAmmoMessage` * @param obj the `Vehicle` object * @param index an index in `obj`'s inventory * @param item an `AmmoBox` */ - def StowAmmunitionInVehicles(obj : Vehicle)(index : Int, item : AmmoBox) : Unit = { - StowAmmunition(obj)(index, item) + def StowEquipmentInVehicles(obj : Vehicle)(index : Int, item : AmmoBox) : Unit = { + StowEquipment(obj)(index, item) vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player.GUID, obj.GUID, index, item)) } @@ -3383,14 +3505,14 @@ class WorldSessionActor extends Actor with MDCContextAware { * Prepare tasking that registers an `AmmoBox` object * and announces that it exists in a given position in some `Container` object's inventory. * `PutEquipmentInSlot` is the fastest way to achieve these goals. - * @see `StowNewAmmunitionInVehicles` + * @see `StowNewEquipmentInVehicle` * @see `ChangeAmmoMessage` * @param obj the `Container` object * @param index an index in `obj`'s inventory * @param item an `AmmoBox` * @return a `TaskResolver.GiveTask` chain that executes the action */ - def StowNewAmmunition(obj : PlanetSideGameObject with Container)(index : Int, item : AmmoBox) : TaskResolver.GiveTask = { + def StowNewEquipment(obj : PlanetSideGameObject with Container)(index : Int, item : Equipment) : TaskResolver.GiveTask = { PutEquipmentInSlot(obj, item, index) } @@ -3398,14 +3520,14 @@ class WorldSessionActor extends Actor with MDCContextAware { * Prepare tasking that registers an `AmmoBox` object * and announces that it exists in a given position in some vehicle's inventory. * `PutEquipmentInSlot` is the fastest way to achieve these goals. - * @see `StowNewAmmunition` + * @see `StowNewEquipment` * @see `ChangeAmmoMessage` * @param obj the `Container` object * @param index an index in `obj`'s inventory * @param item an `AmmoBox` * @return a `TaskResolver.GiveTask` chain that executes the action */ - def StowNewAmmunitionInVehicles(obj : Vehicle)(index : Int, item : AmmoBox) : TaskResolver.GiveTask = { + def StowNewEquipmentInVehicle(obj : Vehicle)(index : Int, item : Equipment) : TaskResolver.GiveTask = { TaskResolver.GiveTask( new Task() { private val localService = vehicleService @@ -3424,7 +3546,7 @@ class WorldSessionActor extends Actor with MDCContextAware { resolver ! scala.util.Success(this) } }, - List(StowNewAmmunition(obj)(index, item)) + List(StowNewEquipment(obj)(index, item)) ) } @@ -3640,6 +3762,20 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + def LocalVehicle : Option[Vehicle] = { + player.VehicleSeated match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) match { + case Some(obj : Vehicle) => + Some(obj) + case _ => + None + } + case None => + None + } + } + /** * Perform specific operations depending on the target of deployment. * @param obj the object that has deployed From a513f4996ec37f2305797dfd6488591cb8c7b92a Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 16 May 2018 00:27:25 -0400 Subject: [PATCH 4/7] working vehicle loadouts from repair/rearm silos; code documentation and expanded tests --- .../objects/loadouts/InfantryLoadout.scala | 41 +++-- .../psforever/objects/loadouts/Loadout.scala | 80 +++++---- .../objects/loadouts/VehicleLoadout.scala | 22 ++- .../terminals/OrderTerminalABDefinition.scala | 8 +- .../terminals/OrderTerminalDefinition.scala | 6 +- .../terminals/ProximityUnit.scala | 25 +-- .../terminals/RepairRearmControl.scala | 5 + .../terminals/RepairRearmSilo.scala | 3 + .../terminals/RepairRearmSiloDefinition.scala | 8 +- .../terminals/VehicleTerminalDefinition.scala | 8 +- .../src/test/scala/objects/AvatarTest.scala | 34 ++-- .../src/test/scala/objects/LoadoutTest.scala | 46 +++-- .../objects/ServerObjectBuilderTest.scala | 19 ++ .../src/test/scala/objects/VehicleTest.scala | 32 ++++ .../objects/terminal/OrderTerminalTest.scala | 46 ++++- .../ProximityTerminalControlTest.scala | 3 +- .../objects/terminal/ProximityTest.scala | 170 ++++++++++++++++++ .../terminal/RepairRearmSiloTest.scala | 91 ++++++++++ .../terminal/TerminalControlTest.scala | 3 +- .../src/main/scala/WorldSessionActor.scala | 129 +++++-------- .../services/vehicle/VehicleAction.scala | 1 + .../services/vehicle/VehicleResponse.scala | 1 + .../services/vehicle/VehicleService.scala | 4 + .../src/test/scala/VehicleServiceTest.scala | 16 ++ 24 files changed, 601 insertions(+), 200 deletions(-) create mode 100644 common/src/test/scala/objects/terminal/ProximityTest.scala create mode 100644 common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala diff --git a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala index 72100a61..e7470546 100644 --- a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala @@ -3,26 +3,29 @@ package net.psforever.objects.loadouts import net.psforever.types.ExoSuitType +/** + * A blueprint of a player's uniform, their holster items, and their inventory items, saved in a specific state. + * This previous state can be restored on any given player template + * by reconstructing the items (if permitted) and re-assigning the uniform (if available).
+ *
+ * The fifth tab on an `order_terminal` window is occupied by the list of "Favorite" `Loadout` blueprints. + * The ten-long list is initialized with `FavoritesMessage` packets assigned to the "Infantry" list. + * Specific entries are added or removed using `FavoritesRequest` packets, + * re-established using other conventional game packets. + * @param label the name by which this inventory will be known when displayed in a Favorites list; + * field gets inherited + * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target; + * field gets inherited + * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk; + * field gets inherited + * @param exosuit the exo-suit in which the avatar will be dressed; + * may be restricted + * @param subtype the mechanized assault exo-suit specialization number that indicates whether the MAX performs: + * anti-infantry (1), anti-vehicular (2), or anti-air work (3); + * the default value is 0 + */ final case class InfantryLoadout(label : String, visible_slots : List[Loadout.SimplifiedEntry], inventory : List[Loadout.SimplifiedEntry], exosuit : ExoSuitType.Value, - subtype : Int) extends Loadout(label, visible_slots, inventory) { - /** - * The exo-suit in which the avatar will be dressed. - * Might be restricted and, thus, restrict the rest of the `Equipment` from being constructed and given. - * @return the exo-suit - */ - def ExoSuit : ExoSuitType.Value = exosuit - - /** - * The mechanized assault exo-suit specialization number that indicates whether the MAX performs: - * anti-infantry (1), - * anti-vehicular (2), - * or anti-air work (3). - * The major distinction is the type of arm weapons that MAX is equipped. - * When the blueprint doesn't call for a MAX, the number will be 0. - * @return the specialization number - */ - def Subtype : Int = subtype -} + subtype : Int) extends Loadout(label, visible_slots, inventory) diff --git a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala index 89d2261c..7035c89d 100644 --- a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala @@ -10,27 +10,16 @@ import net.psforever.types.ExoSuitType import scala.annotation.tailrec /** - * From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.
+ * The base of all specific kinds of blueprint containers. + * This previous state can be restored on any appropriate template from which the loadout was copied + * by reconstructing the items (if permitted). + * The three fields are the name assigned to the loadout, + * the visible items that are created (which obey different rules depending on the source), + * and the concealed items that are created and added to the source's `Inventory`.
+ * For example, the `visible_slots` on a `Player`-borne loadout will transform into the form `Array[EquipmentSlot]`; + * `Vehicle`-originating loadouts transform into the form `Map[Int, Equipment]`. *
- * `Loadout` objects are composed of the following information, as if a blueprint:
- * - the avatar's current exo-suit
- * - the type of specialization, called a "subtype" (mechanized assault exo-suits only)
- * - the contents of the avatar's occupied holster slots
- * - the contents of the avatar's occupied inventory
- * `Equipment` contents of the holsters and of the formal inventory region will be condensed into a simplified form. - * These are also "blueprints." - * At its most basic, this simplification will merely comprise the former object's `EquipmentDefinition`. - * For items that are already simple - `Kit` objects and `SimpleItem` objects - this form will not be too far removed. - * For more complicated affairs like `Tool` objects and `AmmoBox` objects, only essential information will be retained.
- *
- * The deconstructed blueprint can be applied to any avatar. - * They are, however, typically tied to unique users and unique characters. - * For reasons of certifications, however, permissions on that avatar may affect what `Equipment` can be distributed. - * Even a whole blueprint can be denied if the user lacks the necessary exo-suit certification. - * A completely new piece of `Equipment` is constructed when the `Loadout` is regurgitated.
- *
- * The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `Loadout` entries. - * The ten-long list is initialized with `FavoritesMessage` packets. + * The lists of user-specific loadouts are initialized with `FavoritesMessage` packets. * Specific entries are loaded or removed using `FavoritesRequest` packets. * @param label the name by which this inventory will be known when displayed in a Favorites list * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target @@ -38,27 +27,15 @@ import scala.annotation.tailrec */ abstract class Loadout(label : String, visible_slots : List[Loadout.SimplifiedEntry], - inventory : List[Loadout.SimplifiedEntry]) { - /** - * The label by which this `Loadout` is called. - * @return the label - */ - def Label : String = label - - /** - * The `Equipment` in the `Player`'s holster slots when this `Loadout` is created. - * @return a `List` of the holster item blueprints - */ - def VisibleSlots : List[Loadout.SimplifiedEntry] = visible_slots - - /** - * The `Equipment` in the `Player`'s inventory region when this `Loadout` is created. - * @return a `List` of the inventory item blueprints - */ - def Inventory : List[Loadout.SimplifiedEntry] = inventory -} + inventory : List[Loadout.SimplifiedEntry]) object Loadout { + /** + * Produce the blueprint on a player. + * @param player the player + * @param label the name of this loadout + * @return an `InfantryLoadout` object populated with appropriate information about the current state of the player + */ def Create(player : Player, label : String) : Loadout = { InfantryLoadout( label, @@ -69,6 +46,12 @@ object Loadout { ) } + /** + * Produce the blueprint of a vehicle. + * @param vehicle the vehicle + * @param label the name of this loadout + * @return a `VehicleLoadout` object populated with appropriate information about the current state of the vehicle + */ def Create(vehicle : Vehicle, label : String) : Loadout = { VehicleLoadout( label, @@ -128,10 +111,29 @@ object Loadout { */ final case class ShorthandKit(definition : KitDefinition) extends Simplification + /** + * The sub-type of the player's uniform. + * Applicable to mechanized assault units, mainly. + * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit + * as indicated by the arm weapon(s). + * @param player the player + * @return the numeric subtype + */ def DetermineSubtype(player : Player) : Int = { DetermineSubtype(player.ExoSuit, player.Slot(0).Equipment) } + /** + * The sub-type of the player's uniform. + * Applicable to mechanized assault units, mainly. + * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit + * as indicated by the arm weapon(s). + * @param suit the player's uniform; + * the target is for MAX armors + * @param weapon any weapon the player may have it his "first pistol slot;" + * to a MAX, that is its "primary weapon slot" + * @return the numeric subtype + */ def DetermineSubtype(suit : ExoSuitType.Value, weapon : Option[Equipment]) : Int = { if(suit == ExoSuitType.MAX) { weapon match { diff --git a/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala index b4713d64..5f7e0b63 100644 --- a/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala @@ -3,9 +3,25 @@ package net.psforever.objects.loadouts import net.psforever.objects.definition._ +/** + * A blueprint of a vehicle's mounted weapons and its inventory items, saved in a specific state. + * This previous state can be restored on an apporpriate vehicle template + * by reconstructing the items (if permitted). + * Mismatched vehicles may produce no loadout or an imperfect loadout depending on specifications.
+ *
+ * The second tab on an `repair_silo` window is occupied by the list of "Favorite" `Loadout` blueprints. + * The five-long list is initialized with `FavoritesMessage` packets assigned to the "Vehicle" list. + * Specific entries are added or removed using `FavoritesRequest` packets, + * re-established using other conventional game packets. + * @param label the name by which this inventory will be known when displayed in a Favorites list; + * field gets inherited + * @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target; + * field gets inherited + * @param inventory simplified representation of the `Equipment` in the target's inventory or trunk; + * field gets inherited + * @param vehicle_definition the original type of vehicle whose state is being populated + */ final case class VehicleLoadout(label : String, visible_slots : List[Loadout.SimplifiedEntry], inventory : List[Loadout.SimplifiedEntry], - vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory) { - def Definition : VehicleDefinition = vehicle_definition -} \ No newline at end of file + vehicle_definition : VehicleDefinition) extends Loadout(label, visible_slots, inventory) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala index 3aaac12a..c90aca31 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalABDefinition.scala @@ -54,10 +54,10 @@ class OrderTerminalABDefinition(object_id : Int) extends EquipmentTerminalDefini if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { case Some(loadout : InfantryLoadout) => - if(loadout.ExoSuit != ExoSuitType.MAX) { - val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) - val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) - Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory) + if(loadout.exosuit != ExoSuitType.MAX) { + val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory) } else { Terminal.NoDeal() diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala index 8a270b34..d9730bd6 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala @@ -39,9 +39,9 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) { if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { case Some(loadout : InfantryLoadout) => - val holsters = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) - val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) - Terminal.InfantryLoadout(loadout.ExoSuit, loadout.Subtype, holsters, inventory) + val holsters = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + Terminal.InfantryLoadout(loadout.exosuit, loadout.subtype, holsters, inventory) case Some(_) | None => Terminal.NoDeal() } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala index 8339e51e..25843fa1 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala @@ -6,26 +6,27 @@ import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage import net.psforever.packet.game.PlanetSideGUID /** - * A server object that is a "terminal" that can be accessed for amenities and services, - * triggered when a certain distance from the unit itself (proximity-based).
- *
- * Unlike conventional terminals, this structure is not necessarily structure-owned. + * A server object that provides a service, triggered when a certain distance from the unit itself (proximity-based). + * Unlike conventional terminals, this one is not necessarily structure-owned. * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. */ trait ProximityUnit { this : Terminal => - private var users : Set[PlanetSideGUID] = Set.empty + /** + * A list of targets that are currently affected by this proximity unit. + */ + private var targets : Set[PlanetSideGUID] = Set.empty - def NumberUsers : Int = users.size + def NumberUsers : Int = targets.size def AddUser(player_guid : PlanetSideGUID) : Int = { - users += player_guid + targets += player_guid NumberUsers } def RemoveUser(player_guid : PlanetSideGUID) : Int = { - users -= player_guid + targets -= player_guid NumberUsers } } @@ -33,11 +34,15 @@ trait ProximityUnit { object ProximityUnit { import akka.actor.Actor + /** + * A mixin `trait` for an `Actor`'s `PartialFunction` that handles messages, + * in this case handling messages that controls the telegraphed state of the `ProximityUnit` object as the number of users changes. + */ trait Use { this : Actor => def TerminalObject : Terminal with ProximityUnit - + val proximityBehavior : Receive = { case CommonMessages.Use(player) => val hadNoUsers = TerminalObject.NumberUsers == 0 @@ -52,4 +57,4 @@ object ProximityUnit { } } } -} \ No newline at end of file +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala index b5514305..2451fbb3 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala @@ -4,6 +4,11 @@ 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 `IFFLock`. + * @param term the `RepairRearmSilo` object being governed + * @see `CommonMessages` + */ class RepairRearmControl(term : RepairRearmSilo) extends Actor with FactionAffinityBehavior.Check with ProximityUnit.Use { def FactionObject : FactionAffinity = term diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala index 45a01302..b01cfbae 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala @@ -2,6 +2,9 @@ package net.psforever.objects.serverobject.terminals /** + * A structure-owned server object for preserving vehicle loadouts, + * obtaining vehicle weapon ammunition, + * and, with proper perks, automatically repairing damage doen to allied vehicles. * A server object that is a "terminal" that can be accessed for amenities and services, * triggered when a certain distance from the unit itself (proximity-based).
*
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala index baf37bc4..2fd82802 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala @@ -7,6 +7,10 @@ import net.psforever.objects.loadouts.VehicleLoadout import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition.BuildSimplifiedPattern import net.psforever.packet.game.ItemTransactionMessage +/** + * The `Definition` for any `Terminal` that is of a type "repair_silo." + * Has both proximity-based operation and direct access purchasing power. + */ class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) { Name = "repair_silo" @@ -18,8 +22,8 @@ class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinit if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1 + 10) match { case Some(loadout : VehicleLoadout) => - val weapons = loadout.VisibleSlots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) - val inventory = loadout.Inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + val weapons = loadout.visible_slots.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) + val inventory = loadout.inventory.map(entry => { InventoryItem(BuildSimplifiedPattern(entry.item), entry.index) }) Terminal.VehicleLoadout(loadout.vehicle_definition, weapons, inventory) case _ => Terminal.NoDeal() diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala index bff5fe26..8785ae7a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalDefinition.scala @@ -487,12 +487,12 @@ abstract class VehicleTerminalDefinition(objId : Int) extends TerminalDefinition vehicles.get(msg.item_name) match { case Some(vehicle) => val (weapons, inventory) = trunk.get(msg.item_name) match { - case Some(loadout) => + case Some(loadout : VehicleLoadout) => ( - loadout.VisibleSlots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }), - loadout.Inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }) + loadout.visible_slots.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }), + loadout.inventory.map(entry => { InventoryItem(EquipmentTerminalDefinition.BuildSimplifiedPattern(entry.item), entry.index) }) ) - case None => + case _ => (List.empty, List.empty) } Terminal.BuyVehicle(vehicle(), weapons, inventory) diff --git a/common/src/test/scala/objects/AvatarTest.scala b/common/src/test/scala/objects/AvatarTest.scala index 6a45225d..6c331798 100644 --- a/common/src/test/scala/objects/AvatarTest.scala +++ b/common/src/test/scala/objects/AvatarTest.scala @@ -293,12 +293,12 @@ class AvatarTest extends Specification { avatar.LoadLoadout(0) match { case Some(items : InfantryLoadout) => - items.Label mustEqual "test" - items.ExoSuit mustEqual obj.ExoSuit - items.Subtype mustEqual 0 + items.label mustEqual "test" + items.exosuit mustEqual obj.ExoSuit + items.subtype mustEqual 0 - items.VisibleSlots.length mustEqual 3 - val holsters = items.VisibleSlots.sortBy(_.index) + items.visible_slots.length mustEqual 3 + val holsters = items.visible_slots.sortBy(_.index) holsters.head.index mustEqual 0 holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1 //we changed this @@ -308,8 +308,8 @@ class AvatarTest extends Specification { holsters(2).index mustEqual 4 holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade - items.Inventory.length mustEqual 6 - val inventory = items.Inventory.sortBy(_.index) + items.inventory.length mustEqual 6 + val inventory = items.inventory.sortBy(_.index) inventory.head.index mustEqual 6 inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm inventory(1).index mustEqual 9 @@ -349,11 +349,11 @@ class AvatarTest extends Specification { avatar.LoadLoadout(0) match { case Some(items : InfantryLoadout) => - items.Label mustEqual "test" - items.ExoSuit mustEqual obj.ExoSuit - items.Subtype mustEqual 0 - items.VisibleSlots.length mustEqual 3 - items.Inventory.length mustEqual 0 //empty + items.label mustEqual "test" + items.exosuit mustEqual obj.ExoSuit + items.subtype mustEqual 0 + items.visible_slots.length mustEqual 3 + items.inventory.length mustEqual 0 //empty case _ => ko } @@ -368,11 +368,11 @@ class AvatarTest extends Specification { avatar.LoadLoadout(0) match { case Some(items : InfantryLoadout) => - items.Label mustEqual "test" - items.ExoSuit mustEqual obj.ExoSuit - items.Subtype mustEqual 0 - items.VisibleSlots.length mustEqual 0 //empty - items.Inventory.length mustEqual 6 + items.label mustEqual "test" + items.exosuit mustEqual obj.ExoSuit + items.subtype mustEqual 0 + items.visible_slots.length mustEqual 0 //empty + items.inventory.length mustEqual 6 case _ => ko } diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala index 208f5f6d..1f32386e 100644 --- a/common/src/test/scala/objects/LoadoutTest.scala +++ b/common/src/test/scala/objects/LoadoutTest.scala @@ -39,12 +39,12 @@ class LoadoutTest extends Specification { val player = CreatePlayer() val obj = Loadout.Create(player, "test").asInstanceOf[InfantryLoadout] - obj.Label mustEqual "test" - obj.ExoSuit mustEqual obj.ExoSuit - obj.Subtype mustEqual 0 + obj.label mustEqual "test" + obj.exosuit mustEqual ExoSuitType.Standard + obj.subtype mustEqual 0 - obj.VisibleSlots.length mustEqual 3 - val holsters = obj.VisibleSlots.sortBy(_.index) + obj.visible_slots.length mustEqual 3 + val holsters = obj.visible_slots.sortBy(_.index) holsters.head.index mustEqual 0 holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer holsters(1).index mustEqual 2 @@ -52,8 +52,8 @@ class LoadoutTest extends Specification { holsters(2).index mustEqual 4 holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade - obj.Inventory.length mustEqual 5 - val inventory = obj.Inventory.sortBy(_.index) + obj.inventory.length mustEqual 5 + val inventory = obj.inventory.sortBy(_.index) inventory.head.index mustEqual 6 inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].definition mustEqual ace inventory(1).index mustEqual 9 @@ -66,6 +66,30 @@ class LoadoutTest extends Specification { inventory(4).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit } + "create a loadout that contains a vehicle's inventory" in { + val vehicle = Vehicle(mediumtransport) + vehicle.Inventory += 30 -> AmmoBox(bullet_9mm) + vehicle.Inventory += 33 -> AmmoBox(bullet_9mm_AP) + val obj = Loadout.Create(vehicle, "test").asInstanceOf[VehicleLoadout] + + obj.label mustEqual "test" + obj.vehicle_definition mustEqual mediumtransport + + obj.visible_slots.length mustEqual 2 + val holsters = obj.visible_slots.sortBy(_.index) + holsters.head.index mustEqual 5 + holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual mediumtransport_weapon_systemA + holsters(1).index mustEqual 6 + holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual mediumtransport_weapon_systemB + + obj.inventory.length mustEqual 2 + val inventory = obj.inventory.sortBy(_.index) + inventory.head.index mustEqual 30 + inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm + inventory(1).index mustEqual 33 + inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm_AP + } + "distinguish MAX subtype information" in { val player = CreatePlayer() val slot = player.Slot(0) @@ -83,9 +107,9 @@ class LoadoutTest extends Specification { slot.Equipment = Tool(trhev_burster) val ldout4 = Loadout.Create(player, "burster").asInstanceOf[InfantryLoadout] - ldout1.Subtype mustEqual 0 - ldout2.Subtype mustEqual 1 - ldout3.Subtype mustEqual 2 - ldout4.Subtype mustEqual 3 + ldout1.subtype mustEqual 0 + ldout2.subtype mustEqual 1 + ldout3.subtype mustEqual 2 + ldout4.subtype mustEqual 3 } } diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala index 49678d5f..1f5b6dcf 100644 --- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala +++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala @@ -208,6 +208,25 @@ class SpawnTubeObjectBuilderTest extends ActorTest { } } +class RepairRearmSiloObjectBuilderTest extends ActorTest { + import net.psforever.objects.GlobalDefinitions.repair_silo + import net.psforever.objects.serverobject.terminals.RepairRearmSilo + "LockerObjectBuilder" should { + "build" in { + val hub = ServerObjectBuilderTest.NumberPoolHub + val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, + RepairRearmSilo.Constructor(repair_silo)), hub), "silo") + actor ! "!" + + val reply = receiveOne(Duration.create(1000, "ms")) + assert(reply.isInstanceOf[RepairRearmSilo]) + assert(reply.asInstanceOf[RepairRearmSilo].HasGUID) + assert(reply.asInstanceOf[RepairRearmSilo].GUID == PlanetSideGUID(1)) + assert(reply == hub(1).get) + } + } +} + object ServerObjectBuilderTest { import net.psforever.objects.guid.source.LimitedNumberSource def NumberPoolHub : NumberPoolHub = { diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala index 0124917d..e0495c4a 100644 --- a/common/src/test/scala/objects/VehicleTest.scala +++ b/common/src/test/scala/objects/VehicleTest.scala @@ -277,6 +277,38 @@ class VehicleTest extends Specification { filteredMap(0).UtilType mustEqual UtilityType.order_terminalb filteredMap(2).UtilType mustEqual UtilityType.order_terminalb } + + "access its mounted weapons by Slot" in { + val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + harasser_vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(10) + + harasser_vehicle.Slot(2).Equipment.get.GUID mustEqual PlanetSideGUID(10) + } + + "access its trunk by Slot" in { + val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + val ammobox = AmmoBox(GlobalDefinitions.armor_canister) + ammobox.GUID = PlanetSideGUID(10) + harasser_vehicle.Inventory += 30 -> ammobox + + harasser_vehicle.Slot(30).Equipment.get.GUID mustEqual PlanetSideGUID(10) + } + + "find its mounted weapons by GUID" in { + val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + harasser_vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(10) + + harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(2) + } + + "find items in its trunk by GUID" in { + val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + val ammobox = AmmoBox(GlobalDefinitions.armor_canister) + ammobox.GUID = PlanetSideGUID(10) + harasser_vehicle.Inventory += 30 -> ammobox + + harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(30) + } } } diff --git a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala index 024c8794..76e30ba1 100644 --- a/common/src/test/scala/objects/terminal/OrderTerminalTest.scala +++ b/common/src/test/scala/objects/terminal/OrderTerminalTest.scala @@ -72,12 +72,54 @@ class OrderTerminalTest extends Specification { terminal.Request(player, msg) mustEqual Terminal.NoDeal() } - //TODO loudout tests - "player can not buy equipment from the wrong page ('9mmbullet_AP', page 1)" in { val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "9mmbullet_AP", 0, PlanetSideGUID(0)) terminal.Request(player, msg) mustEqual Terminal.NoDeal() } + + "player can retrieve an infantry loadout" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + player2.ExoSuit = ExoSuitType.Agile + player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer) + player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer) + avatar.SaveLoadout(player2, "test", 0) + + val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 0, PlanetSideGUID(0))) + msg.isInstanceOf[Terminal.InfantryLoadout] mustEqual true + val loadout = msg.asInstanceOf[Terminal.InfantryLoadout] + loadout.exosuit mustEqual ExoSuitType.Agile + loadout.subtype mustEqual 0 + loadout.holsters.size mustEqual 1 + loadout.holsters.head.obj.Definition mustEqual GlobalDefinitions.beamer + loadout.holsters.head.start mustEqual 0 + loadout.inventory.head.obj.Definition mustEqual GlobalDefinitions.beamer + loadout.inventory.head.start mustEqual 6 + } + + "player can not retrieve an infantry loadout from the wrong page" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + player2.ExoSuit = ExoSuitType.Agile + player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer) + player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer) + avatar.SaveLoadout(player2, "test", 0) + + val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 3, "", 0, PlanetSideGUID(0))) //page 3 + msg.isInstanceOf[Terminal.NoDeal] mustEqual true + } + + "player can not retrieve an infantry loadout from the wrong line" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + player2.ExoSuit = ExoSuitType.Agile + player2.Slot(0).Equipment = Tool(GlobalDefinitions.beamer) + player2.Slot(6).Equipment = Tool(GlobalDefinitions.beamer) + avatar.SaveLoadout(player2, "test", 0) + + val msg = terminal.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 1, PlanetSideGUID(0))) + msg.isInstanceOf[Terminal.NoDeal] mustEqual true + } } } diff --git a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala index f817be6a..a28235fe 100644 --- a/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala +++ b/common/src/test/scala/objects/terminal/ProximityTerminalControlTest.scala @@ -25,8 +25,7 @@ class ProximityTerminalControl2Test extends ActorTest() { val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR) terminal.Actor !"hello" - val reply = receiveOne(Duration.create(500, "ms")) - assert(reply.isInstanceOf[Terminal.NoDeal]) + expectNoMsg(Duration.create(500, "ms")) } } diff --git a/common/src/test/scala/objects/terminal/ProximityTest.scala b/common/src/test/scala/objects/terminal/ProximityTest.scala new file mode 100644 index 00000000..7b799d1d --- /dev/null +++ b/common/src/test/scala/objects/terminal/ProximityTest.scala @@ -0,0 +1,170 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.Props +import net.psforever.objects.serverobject.CommonMessages +import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage +import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal} +import net.psforever.objects.{Avatar, GlobalDefinitions, Player} +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types.{CharacterGender, PlanetSideEmpire} +import objects.ActorTest +import org.specs2.mutable.Specification + +import scala.concurrent.duration._ + +class ProximityTest extends Specification { + "ProximityUnit" should { + "construct (with a Terminal object)" in { + val obj = new ProximityTest.SampleTerminal() + obj.NumberUsers mustEqual 0 + } + + "keep track of users (add)" in { + val obj = new ProximityTest.SampleTerminal() + obj.NumberUsers mustEqual 0 + obj.AddUser(PlanetSideGUID(10)) mustEqual obj.NumberUsers + obj.NumberUsers mustEqual 1 + obj.AddUser(PlanetSideGUID(20)) mustEqual obj.NumberUsers + obj.NumberUsers mustEqual 2 + } + + "keep track of users (remove)" in { + val obj = new ProximityTest.SampleTerminal() + obj.AddUser(PlanetSideGUID(10)) + obj.AddUser(PlanetSideGUID(20)) + obj.NumberUsers mustEqual 2 + obj.RemoveUser(PlanetSideGUID(10)) mustEqual obj.NumberUsers + obj.NumberUsers mustEqual 1 + obj.RemoveUser(PlanetSideGUID(20)) mustEqual obj.NumberUsers + obj.NumberUsers mustEqual 0 + } + + "can not add a user twice" in { + val obj = new ProximityTest.SampleTerminal() + obj.AddUser(PlanetSideGUID(10)) + obj.NumberUsers mustEqual 1 + obj.AddUser(PlanetSideGUID(10)) + obj.NumberUsers mustEqual 1 + } + + "can not remove a user that was not added" in { + val obj = new ProximityTest.SampleTerminal() + obj.AddUser(PlanetSideGUID(10)) + obj.NumberUsers mustEqual 1 + obj.RemoveUser(PlanetSideGUID(20)) + obj.NumberUsers mustEqual 1 + } + } + + "ProximityTerminal" should { + "construct" in { + ProximityTerminal(GlobalDefinitions.medical_terminal) + ok + } + } +} + +class ProximityTerminalControl1bTest extends ActorTest { + "ProximityTerminalControl" should { + "send out a start message" in { + val obj = ProximityTerminal(GlobalDefinitions.medical_terminal) + obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl") + val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player.GUID = PlanetSideGUID(10) + + assert(obj.NumberUsers == 0) + obj.Actor ! CommonMessages.Use(player) + val msg = receiveOne(200 milliseconds) + assert(obj.NumberUsers == 1) + assert(msg.isInstanceOf[TerminalMessage]) + val msgout = msg.asInstanceOf[TerminalMessage] + assert(msgout.player == player) + assert(msgout.msg == null) + assert(msgout.response.isInstanceOf[Terminal.StartProximityEffect]) + } + } +} + +class ProximityTerminalControl2bTest extends ActorTest { + "ProximityTerminalControl" should { + "will not send out one start message unless first user" in { + val obj = ProximityTerminal(GlobalDefinitions.medical_terminal) + obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl") + val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player1.GUID = PlanetSideGUID(10) + val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player2.GUID = PlanetSideGUID(11) + assert(obj.NumberUsers == 0) + + obj.Actor ! CommonMessages.Use(player1) + val msg = receiveOne(200 milliseconds) + assert(obj.NumberUsers == 1) + assert(msg.isInstanceOf[TerminalMessage]) + assert(msg.asInstanceOf[TerminalMessage].response.isInstanceOf[Terminal.StartProximityEffect]) + obj.Actor ! CommonMessages.Use(player2) + expectNoMsg(500 milliseconds) + assert(obj.NumberUsers == 2) + } + } +} + +class ProximityTerminalControl3bTest extends ActorTest { + "ProximityTerminalControl" should { + "send out a stop message" in { + val obj = ProximityTerminal(GlobalDefinitions.medical_terminal) + obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl") + val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player.GUID = PlanetSideGUID(10) + + assert(obj.NumberUsers == 0) + obj.Actor ! CommonMessages.Use(player) + receiveOne(200 milliseconds) + assert(obj.NumberUsers == 1) + obj.Actor ! CommonMessages.Unuse(player) + val msg = receiveOne(200 milliseconds) + assert(obj.NumberUsers == 0) + assert(msg.isInstanceOf[TerminalMessage]) + val msgout = msg.asInstanceOf[TerminalMessage] + assert(msgout.player == player) + assert(msgout.msg == null) + assert(msgout.response.isInstanceOf[Terminal.StopProximityEffect]) + } + } +} + +class ProximityTerminalControl4bTest extends ActorTest { + "ProximityTerminalControl" should { + "will not send out one stop message until last user" in { + val obj = ProximityTerminal(GlobalDefinitions.medical_terminal) + obj.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], obj), "prox-ctrl") + val player1 = Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player1.GUID = PlanetSideGUID(10) + val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + player2.GUID = PlanetSideGUID(11) + assert(obj.NumberUsers == 0) + + obj.Actor ! CommonMessages.Use(player1) + receiveOne(200 milliseconds) //StartProximityEffect + assert(obj.NumberUsers == 1) + obj.Actor ! CommonMessages.Use(player2) + expectNoMsg(500 milliseconds) + assert(obj.NumberUsers == 2) + obj.Actor ! CommonMessages.Unuse(player1) + expectNoMsg(500 milliseconds) + assert(obj.NumberUsers == 1) + obj.Actor ! CommonMessages.Unuse(player2) + val msg = receiveOne(200 milliseconds) + assert(obj.NumberUsers == 0) + assert(msg.isInstanceOf[TerminalMessage]) + val msgout = msg.asInstanceOf[TerminalMessage] + assert(msgout.player == player2) + assert(msgout.msg == null) + assert(msgout.response.isInstanceOf[Terminal.StopProximityEffect]) + } + } +} + +object ProximityTest { + class SampleTerminal extends Terminal(GlobalDefinitions.dropship_vehicle_terminal) with ProximityUnit +} diff --git a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala new file mode 100644 index 00000000..eeb6fa33 --- /dev/null +++ b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala @@ -0,0 +1,91 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.ActorRef +import net.psforever.objects.serverobject.structures.{Building, StructureType} +import net.psforever.objects._ +import net.psforever.objects.serverobject.terminals.{RepairRearmSilo, 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 + +class RepairRearmSiloTest extends Specification { + "RepairRearmSilo" should { + val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) + val silo = RepairRearmSilo(GlobalDefinitions.repair_silo) + silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building) + silo.Owner.Faction = PlanetSideEmpire.TR + + "define" in { + GlobalDefinitions.repair_silo.ObjectId mustEqual 729 + } + + "construct" in { + val obj = RepairRearmSilo(GlobalDefinitions.repair_silo) + obj.Actor mustEqual ActorRef.noSender + } + + "player can buy a box of ammunition ('bullet_35mm')" in { + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "35mmbullet", 0, PlanetSideGUID(0)) + val reply = silo.Request(player, msg) + reply.isInstanceOf[Terminal.BuyEquipment] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyEquipment] + reply2.item.isInstanceOf[AmmoBox] mustEqual true + reply2.item.asInstanceOf[AmmoBox].Definition mustEqual GlobalDefinitions.bullet_35mm + reply2.item.asInstanceOf[AmmoBox].Capacity mustEqual 100 + } + + "player can not buy fake equipment ('sabot')" in { + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 3, "sabot", 0, PlanetSideGUID(0)) + + silo.Request(player, msg) mustEqual Terminal.NoDeal() + } + + "player can not buy equipment from the wrong page ('35mmbullet', page 1)" in { + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "35mmbullet", 0, PlanetSideGUID(0)) + + silo.Request(player, msg) mustEqual Terminal.NoDeal() + } + + "player can retrieve a vehicle loadout" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + val vehicle = Vehicle(GlobalDefinitions.fury) + vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm) + avatar.SaveLoadout(vehicle, "test", 10) + + val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 0, PlanetSideGUID(0))) + msg.isInstanceOf[Terminal.VehicleLoadout] mustEqual true + val loadout = msg.asInstanceOf[Terminal.VehicleLoadout] + loadout.vehicle_definition mustEqual GlobalDefinitions.fury + loadout.weapons.size mustEqual 1 + loadout.weapons.head.obj.Definition mustEqual GlobalDefinitions.fury_weapon_systema + loadout.weapons.head.start mustEqual 1 + loadout.inventory.head.obj.Definition mustEqual GlobalDefinitions.bullet_9mm + loadout.inventory.head.start mustEqual 30 + } + + "player can not retrieve a vehicle loadout from the wrong line" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + val vehicle = Vehicle(GlobalDefinitions.fury) + vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm) + avatar.SaveLoadout(vehicle, "test", 10) + + val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 3, "", 0, PlanetSideGUID(0))) //page 3 + msg.isInstanceOf[Terminal.NoDeal] mustEqual true + } + + "player can not retrieve a vehicle loadout from the wrong line" in { + val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val player2 = Player(avatar) + val vehicle = Vehicle(GlobalDefinitions.fury) + vehicle.Slot(30).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm) + avatar.SaveLoadout(vehicle, "test", 10) + + val msg = silo.Request(player2, ItemTransactionMessage(PlanetSideGUID(10), TransactionType.Loadout, 4, "", 1, PlanetSideGUID(0))) //line 11 + msg.isInstanceOf[Terminal.NoDeal] mustEqual true + } + } +} diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala index 428d8d87..e8432d3d 100644 --- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala +++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala @@ -26,8 +26,7 @@ class TerminalControl2Test extends ActorTest() { val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) terminal.Actor !"hello" - val reply = receiveOne(Duration.create(500, "ms")) - assert(reply.isInstanceOf[Terminal.NoDeal]) + expectNoMsg(Duration.create(500, "ms")) } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9b05f120..ca2fd7ae 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -468,6 +468,11 @@ class WorldSessionActor extends Actor with MDCContextAware { ) } + case VehicleResponse.InventoryState2(obj_guid, parent_guid, value) => + if(tplayer_guid != guid) { + sendResponse(InventoryStateMessage(obj_guid, 0, parent_guid, value)) + } + case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) => sendResponse(DismountVehicleMsg(guid, unk1, unk2)) if(tplayer_guid == guid) { @@ -819,7 +824,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) => //TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}") - sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) + sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true)) val dropPred = DropPredicate(tplayer) val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred) val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred) @@ -838,7 +843,7 @@ class WorldSessionActor extends Actor with MDCContextAware { taskResolver ! GUIDTask.UnregisterEquipment(elem.obj)(continent.GUID) }) //report change - sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, 0)) + sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, subtype)) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype)) sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 4, tplayer.Armor)) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor)) @@ -882,44 +887,37 @@ class WorldSessionActor extends Actor with MDCContextAware { val objDef = obj.Definition avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get)) }) - sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) case Terminal.VehicleLoadout(definition, weapons, inventory) => log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") - log.info(s"Vehicle: $definition") - log.info(s"Weapons (${weapons.size}): $weapons") - log.info(s"Inventory (${inventory.size}): $inventory") - LocalVehicle match { + FindLocalVehicle match { case Some(vehicle) => sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true)) val (_, afterInventory) = inventory.partition( DropPredicate(tplayer) ) //dropped items are lost - //common action - remove old inventory + //remove old inventory val deleteEquipment : (Int,Equipment)=>Unit = DeleteEquipmentFromVehicle(vehicle) - vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) => - deleteEquipment(index, obj) - taskResolver ! GUIDTask.UnregisterEquipment(obj)(continent.GUID) - }) + vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) => deleteEquipment(index, obj) }) val stowEquipment : (Int,Equipment)=>TaskResolver.GiveTask = StowNewEquipmentInVehicle(vehicle) (if(vehicle.Definition == definition) { - //vehicles are the same type; transfer over weapons - vehicle.Weapons - .filter({ case (_, slot) => slot.Equipment.nonEmpty }) - .foreach({ case (_, slot) => - val equipment = slot.Equipment.get - slot.Equipment = None - val equipment_guid = equipment.GUID - sendResponse(ObjectDeleteMessage(equipment_guid, 0)) - avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, equipment_guid)) - taskResolver ! GUIDTask.UnregisterEquipment(equipment)(continent.GUID) - }) + //vehicles are the same type; transfer over weapon ammo + //TODO ammo switching? no vehicle weapon does that currently but ... + //TODO want to completely swap weapons, but holster icon vanishes temporarily after swap + //TODO BFR arms must be swapped properly + val channel = s"${vehicle.Actor}" weapons.foreach({ case InventoryItem(obj, index) => - //create weapons and share with everyone - taskResolver ! PutNewWeaponInVehicleSlot(vehicle, obj.asInstanceOf[Tool], index) + val savedWeapon = obj.asInstanceOf[Tool] + val existingWeapon = vehicle.Weapons(index).Equipment.get.asInstanceOf[Tool] + (0 until existingWeapon.MaxAmmoSlot).foreach({ index => + val existingBox = existingWeapon.AmmoSlots(index).Box + existingBox.Capacity = savedWeapon.AmmoSlots(index).Box.Capacity + //use VehicleAction.InventoryState2; VehicleAction.InventoryState temporarily glitches ammo count in ui + vehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState2(PlanetSideGUID(0), existingBox.GUID, existingWeapon.GUID, existingBox.Capacity)) + }) }) afterInventory } else { - //do not transfer over weapons + //do not transfer over weapon ammo if(vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset) { afterInventory } @@ -934,6 +932,7 @@ class WorldSessionActor extends Actor with MDCContextAware { }) case None => log.error(s"can not apply the loadout - can not find a vehicle") + sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, false)) } case Terminal.LearnCertification(cert, cost) => @@ -2123,7 +2122,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => None }) - .orElse(LocalVehicle match { + .orElse(FindLocalVehicle match { case Some(parent) => findFunc(parent) case None => @@ -2735,46 +2734,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - def PutNewWeaponInVehicleSlot(target : Vehicle, obj : Tool, index : Int) : TaskResolver.GiveTask = { - TaskResolver.GiveTask( - new Task() { - private val localTarget = target - private val localIndex = index - private val localObject = obj - private val localAnnounce = self - private val localAvatarService = avatarService - private val localVehicleService = vehicleService - - override def isComplete : Task.Resolution.Value = { - if(localTarget.Slot(localIndex).Equipment.contains(localObject)) { - Task.Resolution.Success - } - else { - Task.Resolution.Incomplete - } - } - - def Execute(resolver : ActorRef) : Unit = { - localTarget.Slot(localIndex).Equipment = localObject - resolver ! scala.util.Success(this) - } - - override def onSuccess() : Unit = { - val definition = localObject.Definition - if(localTarget.VisibleSlots.contains(localIndex)) { - localAvatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentInHand(localTarget.GUID, localTarget.GUID, localIndex, localObject)) - } - val channel = s"${localTarget.Actor}" - (0 until localObject.MaxAmmoSlot).foreach({ index => - val box = localObject.AmmoSlots(index).Box - val boxDef = box.Definition - val boxdata = boxDef.Packet.DetailedConstructorData(box).get - localVehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState(PlanetSideGUID(0), box, localObject.GUID, index, boxdata)) - }) - } - }, List(GUIDTask.RegisterTool(obj)(continent.GUID))) - } - /** * Construct tasking that coordinates the following:
* 1) Remove a new piece of `Equipment` from where it is currently stored.
@@ -3418,6 +3377,25 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + /** + * Get the current `Vehicle` object that the player is riding/driving. + * The vehicle must be found solely through use of `player.VehicleSeated`. + * @return the vehicle + */ + def FindLocalVehicle : Option[Vehicle] = { + player.VehicleSeated match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) match { + case Some(obj : Vehicle) => + Some(obj) + case _ => + None + } + case None => + None + } + } + /** * Given an object that contains an item (`Equipment`) in its `Inventory` at a certain location, * remove it permanently. @@ -3428,7 +3406,8 @@ class WorldSessionActor extends Actor with MDCContextAware { */ private def DeleteEquipment(obj : PlanetSideGameObject with Container)(start : Int, item : Equipment) : Unit = { val item_guid = item.GUID - obj.Inventory -= start + obj.Slot(start).Equipment = None + //obj.Inventory -= start taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID) sendResponse(ObjectDeleteMessage(item_guid, 0)) } @@ -3762,20 +3741,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - def LocalVehicle : Option[Vehicle] = { - player.VehicleSeated match { - case Some(vehicle_guid) => - continent.GUID(vehicle_guid) match { - case Some(obj : Vehicle) => - Some(obj) - case _ => - None - } - case None => - None - } - } - /** * Perform specific operations depending on the target of deployment. * @param obj the object that has deployed diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala index 66afa834..06d3c768 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala @@ -16,6 +16,7 @@ object VehicleAction { final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action + final case class InventoryState2(player_guid : PlanetSideGUID, obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Action final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala index 5c2fb5b2..74280a63 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala @@ -17,6 +17,7 @@ object VehicleResponse { final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response + final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala index e820db05..2061fdc0 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala @@ -60,6 +60,10 @@ class VehicleService extends Actor { VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data)) ) + case VehicleAction.InventoryState2(player_guid, obj_guid, parent_guid, value) => + VehicleEvents.publish( + VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState2(obj_guid, parent_guid, value)) + ) case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) => VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid)) diff --git a/pslogin/src/test/scala/VehicleServiceTest.scala b/pslogin/src/test/scala/VehicleServiceTest.scala index d8cd0d4f..d1467ab8 100644 --- a/pslogin/src/test/scala/VehicleServiceTest.scala +++ b/pslogin/src/test/scala/VehicleServiceTest.scala @@ -136,6 +136,22 @@ class InventoryStateTest extends ActorTest { } } +class InventoryState2Test extends ActorTest { + ServiceManager.boot(system) + val tool = Tool(GlobalDefinitions.beamer) + tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13) + val cdata = tool.Definition.Packet.ConstructorData(tool).get + + "VehicleService" should { + "pass InventoryState2" in { + val service = system.actorOf(Props[VehicleService], "v-service") + service ! Service.Join("test") + service ! VehicleServiceMessage("test", VehicleAction.InventoryState2(PlanetSideGUID(10), PlanetSideGUID(11), PlanetSideGUID(12), 13)) + expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.InventoryState2(PlanetSideGUID(11), PlanetSideGUID(12), 13))) + } + } +} + class KickPassengerTest extends ActorTest { ServiceManager.boot(system) From e9aac5514a36856047fefc8a62bd93121a537fdf Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 16 May 2018 19:24:50 -0400 Subject: [PATCH 5/7] more tests for the proximity amenities; added the other silo in Anguta, Ceryshen and the terminal-facing silo in home3; initial work on FacilityBenefitShieldCharge packet and tests --- .../objects/loadouts/InfantryLoadout.scala | 71 +++++++++++++++++++ .../psforever/objects/loadouts/Loadout.scala | 39 ++-------- .../terminals/MedicalTerminalDefinition.scala | 14 +--- .../terminals/ProximityDefinition.scala | 15 ++++ .../terminals/ProximityTerminal.scala | 6 +- .../terminals/RepairRearmControl.scala | 27 ------- .../terminals/RepairRearmSilo.scala | 41 ----------- .../terminals/RepairRearmSiloDefinition.scala | 2 +- .../psforever/packet/GamePacketOpcode.scala | 6 +- ...ityBenefitShieldChargeRequestMessage.scala | 24 +++++++ .../packet/game/FavoritesMessage.scala | 24 ++++++- ...enefitShieldChargeRequestMessageTest.scala | 28 ++++++++ .../scala/game/FavoritesMessageTest.scala | 2 +- .../src/test/scala/objects/LoadoutTest.scala | 38 +++++++++- .../objects/ServerObjectBuilderTest.scala | 19 ----- .../terminal/RepairRearmSiloTest.scala | 6 +- pslogin/src/main/scala/Maps.scala | 14 +++- .../src/main/scala/WorldSessionActor.scala | 40 ++++++----- 18 files changed, 252 insertions(+), 164 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala delete mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala delete mode 100644 common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala create mode 100644 common/src/test/scala/game/FacilityBenefitShieldChargeRequestMessageTest.scala diff --git a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala index e7470546..5277f4ba 100644 --- a/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala @@ -29,3 +29,74 @@ final case class InfantryLoadout(label : String, inventory : List[Loadout.SimplifiedEntry], exosuit : ExoSuitType.Value, subtype : Int) extends Loadout(label, visible_slots, inventory) + +object InfantryLoadout { + import net.psforever.objects.Player + import net.psforever.objects.GlobalDefinitions + import net.psforever.objects.equipment.Equipment + + /** + * The sub-type of the player's uniform. + * Applicable to mechanized assault units, mainly. + * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit + * as indicated by the arm weapon(s). + * @param player the player + * @return the numeric subtype + */ + def DetermineSubtype(player : Player) : Int = { + DetermineSubtypeA(player.ExoSuit, player.Slot(0).Equipment) + } + + /** + * The sub-type of the player's uniform. + * Applicable to mechanized assault units, mainly. + * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit + * as indicated by the arm weapon(s). + * @param suit the player's uniform; + * the target is for MAX armors + * @param weapon any weapon the player may have it his "first pistol slot;" + * to a MAX, that is its "primary weapon slot" + * @return the numeric subtype + */ + def DetermineSubtypeA(suit : ExoSuitType.Value, weapon : Option[Equipment]) : Int = { + if(suit == ExoSuitType.MAX) { + weapon match { + case Some(item) => + item.Definition match { + case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar => + 1 + case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet => + 2 + case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire => + 3 + case _ => + 0 + } + case None => + 0 + } + } + else { + 0 + } + } + + /** + * The sub-type of the player's uniform, as used in `FavoritesMessage`.
+ *
+ * The values for `Standard`, `Infiltration`, and the generic `MAX` are not perfectly known. + * The latter-most exo-suit option is presumed. + * @param suit the player's uniform + * @param subtype the mechanized assault exo-suit subtype as determined by their arm weapons + * @return the numeric subtype + */ + def DetermineSubtypeB(suit : ExoSuitType.Value, subtype : Int) : Int = { + suit match { + case ExoSuitType.Standard => 0 + case ExoSuitType.Agile => 1 + case ExoSuitType.Reinforced => 2 + case ExoSuitType.MAX => 3 + subtype //4, 5, 6 + case ExoSuitType.Infiltration => 7 + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala index 7035c89d..5ff4f92d 100644 --- a/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala +++ b/common/src/main/scala/net/psforever/objects/loadouts/Loadout.scala @@ -5,7 +5,6 @@ import net.psforever.objects._ import net.psforever.objects.definition._ import net.psforever.objects.equipment.Equipment import net.psforever.objects.inventory.InventoryItem -import net.psforever.types.ExoSuitType import scala.annotation.tailrec @@ -120,42 +119,16 @@ object Loadout { * @return the numeric subtype */ def DetermineSubtype(player : Player) : Int = { - DetermineSubtype(player.ExoSuit, player.Slot(0).Equipment) + InfantryLoadout.DetermineSubtype(player) } /** - * The sub-type of the player's uniform. - * Applicable to mechanized assault units, mainly. - * The subtype is reported as a number but indicates the specialization - anti-infantry, ani-vehicular, anti-air - of the suit - * as indicated by the arm weapon(s). - * @param suit the player's uniform; - * the target is for MAX armors - * @param weapon any weapon the player may have it his "first pistol slot;" - * to a MAX, that is its "primary weapon slot" - * @return the numeric subtype + * The sub-type of the vehicle. + * Vehicle's have no subtype. + * @param vehicle the vehicle + * @return the numeric subtype, always 0 */ - def DetermineSubtype(suit : ExoSuitType.Value, weapon : Option[Equipment]) : Int = { - if(suit == ExoSuitType.MAX) { - weapon match { - case Some(item) => - item.Definition match { - case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar => - 1 - case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet => - 2 - case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire => - 3 - case _ => - 0 - } - case None => - 0 - } - } - else { - 0 - } - } + def DetermineSubtype(vehicle : Vehicle) : Int = 0 /** * Overloaded entry point for constructing simplified blueprints from holster slot equipment. diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala index fbd7f012..66581aac 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala @@ -1,18 +1,12 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.terminals -import net.psforever.objects.Player -import net.psforever.packet.game.ItemTransactionMessage - /** * The definition for any `Terminal` that is of a type "medical_terminal". - * This includes the limited proximity-based functionality of the formal medical terminals - * and the actual proximity-based functionality of the cavern crystals.
- *
- * Do not confuse the "medical_terminal" category and the actual `medical_terminal` object (529). - * Objects created by this definition being linked by their use of `ProximityTerminalUseMessage` is more accurate. + * This includes the functionality of the formal medical terminals and some of the cavern crystals. + * Do not confuse the game's internal "medical_terminal" object category and the actual `medical_terminal` object (529). */ -class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) { +class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) with ProximityDefinition { Name = if(objectId == 38) { "adv_med_terminal" } @@ -31,6 +25,4 @@ class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objec else { throw new IllegalArgumentException("medical terminal must be either object id 38, 225, 226, 529, or 689") } - - def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala new file mode 100644 index 00000000..f1ccfcc4 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala @@ -0,0 +1,15 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +/** + * The definition for any `Terminal` that possesses a proximity-based effect. + * This includes the limited proximity-based functionality of the formal medical terminals + * and the actual proximity-based functionality of the cavern crystals. + * Objects created by this definition being linked by their use of `ProximityTerminalUseMessage`. + */ +trait ProximityDefinition { + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala index 4eafb142..8b9b7e67 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala @@ -9,14 +9,14 @@ package net.psforever.objects.serverobject.terminals * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class ProximityTerminal(tdef : MedicalTerminalDefinition) extends Terminal(tdef) with ProximityUnit +class ProximityTerminal(tdef : TerminalDefinition with ProximityDefinition) extends Terminal(tdef) with ProximityUnit object ProximityTerminal { /** * Overloaded constructor. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ - def apply(tdef : MedicalTerminalDefinition) : ProximityTerminal = { + def apply(tdef : TerminalDefinition with ProximityDefinition) : ProximityTerminal = { new ProximityTerminal(tdef) } @@ -29,7 +29,7 @@ object ProximityTerminal { * @param context a context to allow the object to properly set up `ActorSystem` functionality * @return the `Terminal` object */ - def Constructor(tdef : MedicalTerminalDefinition)(id : Int, context : ActorContext) : Terminal = { + def Constructor(tdef : TerminalDefinition with ProximityDefinition)(id : Int, context : ActorContext) : Terminal = { import akka.actor.Props val obj = ProximityTerminal(tdef) obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), s"${tdef.Name}_$id") diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala deleted file mode 100644 index 2451fbb3..00000000 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmControl.scala +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2017 PSForever -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 `IFFLock`. - * @param term the `RepairRearmSilo` object being governed - * @see `CommonMessages` - */ -class RepairRearmControl(term : RepairRearmSilo) extends Actor with FactionAffinityBehavior.Check with ProximityUnit.Use { - def FactionObject : FactionAffinity = term - - def TerminalObject : Terminal with ProximityUnit = term - - def receive : Receive = checkBehavior - .orElse(proximityBehavior) - .orElse { - case Terminal.Request(player, msg) => - sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) - - case _ => ; - } - - override def toString : String = term.Definition.Name -} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala deleted file mode 100644 index b01cfbae..00000000 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSilo.scala +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.objects.serverobject.terminals - -/** - * A structure-owned server object for preserving vehicle loadouts, - * obtaining vehicle weapon ammunition, - * and, with proper perks, automatically repairing damage doen to allied vehicles. - * A server object that is a "terminal" that can be accessed for amenities and services, - * triggered when a certain distance from the unit itself (proximity-based).
- *
- * Unlike conventional terminals, this structure is not necessarily structure-owned. - * For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object. - * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields - */ -class RepairRearmSilo(tdef : RepairRearmSiloDefinition) extends Terminal(tdef) with ProximityUnit - -object RepairRearmSilo { - /** - * Overloaded constructor. - * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields - */ - def apply(tdef : RepairRearmSiloDefinition) : RepairRearmSilo = { - new RepairRearmSilo(tdef) - } - - import akka.actor.ActorContext - - /** - * Instantiate an configure a `Terminal` object - * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields - * @param id the unique id that will be assigned to this entity - * @param context a context to allow the object to properly set up `ActorSystem` functionality - * @return the `Terminal` object - */ - def Constructor(tdef : RepairRearmSiloDefinition)(id : Int, context : ActorContext) : RepairRearmSilo = { - import akka.actor.Props - val obj = RepairRearmSilo(tdef) - obj.Actor = context.actorOf(Props(classOf[RepairRearmControl], obj), s"${tdef.Name}_$id") - obj - } -} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala index 2fd82802..9cfc6fa7 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala @@ -11,7 +11,7 @@ import net.psforever.packet.game.ItemTransactionMessage * The `Definition` for any `Terminal` that is of a type "repair_silo." * Has both proximity-based operation and direct access purchasing power. */ -class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) { +class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) with ProximityDefinition { Name = "repair_silo" private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange = EquipmentTerminalDefinition.Buy(Map.empty, Map.empty, Map.empty) diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index e7ebd353..9d586476 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -312,7 +312,7 @@ object GamePacketOpcode extends Enumeration { = Value private def noDecoder(opcode : GamePacketOpcode.Type) = (a : BitVector) => - Attempt.failure(Err(s"Could not find a marshaller for game packet ${opcode}")) + Attempt.failure(Err(s"Could not find a marshaller for game packet $opcode")) /// Mapping of packet IDs to decoders. Notice that we are using the @switch annotation which ensures that the Scala /// compiler will be able to optimize this as a lookup table (switch statement). Microbenchmarks show a nearly 400x @@ -549,7 +549,7 @@ object GamePacketOpcode extends Enumeration { // OPCODES 0xc0-cf case 0xc0 => noDecoder(CaptureFlagUpdateMessage) case 0xc1 => noDecoder(VanuModuleUpdateMessage) - case 0xc2 => noDecoder(FacilityBenefitShieldChargeRequestMessage) + case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode case 0xc3 => game.ProximityTerminalUseMessage.decode case 0xc4 => game.QuantityDeltaUpdateMessage.decode case 0xc5 => noDecoder(ChainLashMessage) @@ -608,7 +608,7 @@ object GamePacketOpcode extends Enumeration { case 0xf1 => game.MailMessage.decode case 0xf2 => noDecoder(GameVarUpdate) case 0xf3 => noDecoder(ClientCheatedMessage) - case default => noDecoder(opcode) + case _ => noDecoder(opcode) } implicit val codec: Codec[this.Value] = PacketHelpers.createEnumerationCodec(this, uint8L) diff --git a/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala b/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala new file mode 100644 index 00000000..15389532 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala @@ -0,0 +1,24 @@ +// Copyright (c) 2017 PSForever +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +/** + * Dispatched by the client when driving a vehicle in the sphere of influence of an allied base + * that is a dropship center or that possesses the lattice-connected benefit of a dropship center. + * The vehicle that is being driven will not have perfect fully-charged shields at the time. + * @param vehicle_guid the vehicle whose shield is being charged + */ +final case class FacilityBenefitShieldChargeRequestMessage(vehicle_guid : PlanetSideGUID) + extends PlanetSideGamePacket { + type Packet = FacilityBenefitShieldChargeRequestMessage + def opcode = GamePacketOpcode.FacilityBenefitShieldChargeRequestMessage + def encode = FacilityBenefitShieldChargeRequestMessage.encode(this) +} + +object FacilityBenefitShieldChargeRequestMessage extends Marshallable[FacilityBenefitShieldChargeRequestMessage] { + implicit val codec : Codec[FacilityBenefitShieldChargeRequestMessage] = + ("vehicle_guid" | PlanetSideGUID.codec).as[FacilityBenefitShieldChargeRequestMessage] +} diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala index 314986c4..3e28c027 100644 --- a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala @@ -51,11 +51,31 @@ final case class FavoritesMessage(list : LoadoutType.Value, } object FavoritesMessage extends Marshallable[FavoritesMessage] { + /** + * Overloaded constructor, for infantry loadouts specifically. + * @param list the destination list + * @param player_guid the player + * @param line the zero-indexed line number of this entry in its list + * @param label the identifier for this entry + * @param armor the type of exo-suit, if an Infantry loadout + * @return a `FavoritesMessage` object + */ + def apply(list : LoadoutType.Value, player_guid : PlanetSideGUID, line : Int, label : String, armor : Int) : FavoritesMessage = { + FavoritesMessage(list, player_guid, line, label, Some(armor)) + } + + /** + * Overloaded constructor, for vehicle loadouts specifically. + * @param list the destination list + * @param player_guid the player + * @param line the zero-indexed line number of this entry in its list + * @param label the identifier for this entry + * @return a `FavoritesMessage` object + */ def apply(list : LoadoutType.Value, player_guid : PlanetSideGUID, line : Int, label : String) : FavoritesMessage = { FavoritesMessage(list, player_guid, line, label, None) } - - implicit val codec : Codec[FavoritesMessage] = ( +implicit val codec : Codec[FavoritesMessage] = ( ("list" | LoadoutType.codec) >>:~ { value => ("player_guid" | PlanetSideGUID.codec) :: ("line" | uint4L) :: diff --git a/common/src/test/scala/game/FacilityBenefitShieldChargeRequestMessageTest.scala b/common/src/test/scala/game/FacilityBenefitShieldChargeRequestMessageTest.scala new file mode 100644 index 00000000..71b885c0 --- /dev/null +++ b/common/src/test/scala/game/FacilityBenefitShieldChargeRequestMessageTest.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2017 PSForever +package game + +import org.specs2.mutable._ +import net.psforever.packet._ +import net.psforever.packet.game._ +import scodec.bits._ + +class FacilityBenefitShieldChargeRequestMessageTest extends Specification { + val string = hex"C2 4C00" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case FacilityBenefitShieldChargeRequestMessage(guid) => + guid mustEqual PlanetSideGUID(76) + case _ => + ko + } + } + + "encode" in { + val msg = FacilityBenefitShieldChargeRequestMessage(PlanetSideGUID(76)) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } +} + diff --git a/common/src/test/scala/game/FavoritesMessageTest.scala b/common/src/test/scala/game/FavoritesMessageTest.scala index 74e25392..660d088d 100644 --- a/common/src/test/scala/game/FavoritesMessageTest.scala +++ b/common/src/test/scala/game/FavoritesMessageTest.scala @@ -26,7 +26,7 @@ class FavoritesMessageTest extends Specification { } "encode (for infantry)" in { - val msg = FavoritesMessage(LoadoutType.Infantry, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1)) + val msg = FavoritesMessage(LoadoutType.Infantry, PlanetSideGUID(3760), 0, "Agile (basic)", 1) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual stringInfantry diff --git a/common/src/test/scala/objects/LoadoutTest.scala b/common/src/test/scala/objects/LoadoutTest.scala index 1f32386e..67d0f310 100644 --- a/common/src/test/scala/objects/LoadoutTest.scala +++ b/common/src/test/scala/objects/LoadoutTest.scala @@ -110,6 +110,42 @@ class LoadoutTest extends Specification { ldout1.subtype mustEqual 0 ldout2.subtype mustEqual 1 ldout3.subtype mustEqual 2 - ldout4.subtype mustEqual 3 + ldout4.subtype mustEqual InfantryLoadout.DetermineSubtype(player) //example + } + + "players have additional uniform subtype" in { + val player = CreatePlayer() + val slot = player.Slot(0) + slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max) + + player.ExoSuit = ExoSuitType.Standard + val ldout0 = Loadout.Create(player, "standard").asInstanceOf[InfantryLoadout] + player.ExoSuit = ExoSuitType.Agile + val ldout1 = Loadout.Create(player, "agile").asInstanceOf[InfantryLoadout] + player.ExoSuit = ExoSuitType.Reinforced + val ldout2 = Loadout.Create(player, "rein").asInstanceOf[InfantryLoadout] + player.ExoSuit = ExoSuitType.Infiltration + val ldout7 = Loadout.Create(player, "inf").asInstanceOf[InfantryLoadout] + + Player.SuitSetup(player, ExoSuitType.MAX) + val ldout3 = Loadout.Create(player, "weaponless").asInstanceOf[InfantryLoadout] + slot.Equipment = None + slot.Equipment = Tool(trhev_dualcycler) + val ldout4 = Loadout.Create(player, "cycler").asInstanceOf[InfantryLoadout] + slot.Equipment = None + slot.Equipment = Tool(trhev_pounder) + val ldout5 = Loadout.Create(player, "pounder").asInstanceOf[InfantryLoadout] + slot.Equipment = None + slot.Equipment = Tool(trhev_burster) + val ldout6 = Loadout.Create(player, "burster").asInstanceOf[InfantryLoadout] + + InfantryLoadout.DetermineSubtypeB(ldout0.exosuit, ldout0.subtype) mustEqual 0 + InfantryLoadout.DetermineSubtypeB(ldout1.exosuit, ldout1.subtype) mustEqual 1 + InfantryLoadout.DetermineSubtypeB(ldout2.exosuit, ldout2.subtype) mustEqual 2 + InfantryLoadout.DetermineSubtypeB(ldout3.exosuit, ldout3.subtype) mustEqual 3 + InfantryLoadout.DetermineSubtypeB(ldout4.exosuit, ldout4.subtype) mustEqual 4 + InfantryLoadout.DetermineSubtypeB(ldout5.exosuit, ldout5.subtype) mustEqual 5 + InfantryLoadout.DetermineSubtypeB(ldout6.exosuit, ldout6.subtype) mustEqual 6 + InfantryLoadout.DetermineSubtypeB(ldout7.exosuit, ldout7.subtype) mustEqual 7 } } diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala index 1f5b6dcf..49678d5f 100644 --- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala +++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala @@ -208,25 +208,6 @@ class SpawnTubeObjectBuilderTest extends ActorTest { } } -class RepairRearmSiloObjectBuilderTest extends ActorTest { - import net.psforever.objects.GlobalDefinitions.repair_silo - import net.psforever.objects.serverobject.terminals.RepairRearmSilo - "LockerObjectBuilder" should { - "build" in { - val hub = ServerObjectBuilderTest.NumberPoolHub - val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, - RepairRearmSilo.Constructor(repair_silo)), hub), "silo") - actor ! "!" - - val reply = receiveOne(Duration.create(1000, "ms")) - assert(reply.isInstanceOf[RepairRearmSilo]) - assert(reply.asInstanceOf[RepairRearmSilo].HasGUID) - assert(reply.asInstanceOf[RepairRearmSilo].GUID == PlanetSideGUID(1)) - assert(reply == hub(1).get) - } - } -} - object ServerObjectBuilderTest { import net.psforever.objects.guid.source.LimitedNumberSource def NumberPoolHub : NumberPoolHub = { diff --git a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala index eeb6fa33..516e834c 100644 --- a/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala +++ b/common/src/test/scala/objects/terminal/RepairRearmSiloTest.scala @@ -4,7 +4,7 @@ package objects.terminal import akka.actor.ActorRef import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects._ -import net.psforever.objects.serverobject.terminals.{RepairRearmSilo, Terminal} +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} @@ -13,7 +13,7 @@ import org.specs2.mutable.Specification class RepairRearmSiloTest extends Specification { "RepairRearmSilo" should { val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)) - val silo = RepairRearmSilo(GlobalDefinitions.repair_silo) + val silo = Terminal(GlobalDefinitions.repair_silo) silo.Owner = new Building(0, Zone.Nowhere, StructureType.Building) silo.Owner.Faction = PlanetSideEmpire.TR @@ -22,7 +22,7 @@ class RepairRearmSiloTest extends Specification { } "construct" in { - val obj = RepairRearmSilo(GlobalDefinitions.repair_silo) + val obj = Terminal(GlobalDefinitions.repair_silo) obj.Actor mustEqual ActorRef.noSender } diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala index 7d0494dd..c1b62dfa 100644 --- a/pslogin/src/main/scala/Maps.scala +++ b/pslogin/src/main/scala/Maps.scala @@ -7,7 +7,7 @@ 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, StructureType, WarpGate} -import net.psforever.objects.serverobject.terminals.{ProximityTerminal, RepairRearmSilo, Terminal} +import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.types.Vector3 @@ -113,8 +113,10 @@ object Maps { LocalObject(2145, SpawnTube.Constructor(Vector3(3980.4062f, 4252.7656f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2146, SpawnTube.Constructor(Vector3(3980.4062f, 4259.992f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2147, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))) - LocalObject(2050, RepairRearmSilo.Constructor(repair_silo)) - LocalObject(2062, RepairRearmSilo.Constructor(repair_silo)) + LocalObject(2049, ProximityTerminal.Constructor(repair_silo)) //repair terminal A + LocalObject(2050, Terminal.Constructor(repair_silo)) //rearm terminal A + LocalObject(2061, ProximityTerminal.Constructor(repair_silo)) //repair terminal B + LocalObject(2062, Terminal.Constructor(repair_silo)) //rearm terminal B LocalObject(2239, Terminal.Constructor(spawn_terminal)) LocalObject(2244, Terminal.Constructor(spawn_terminal)) LocalObject(2245, Terminal.Constructor(spawn_terminal)) @@ -216,7 +218,9 @@ object Maps { ObjectToBuilding(1576, 2) ObjectToBuilding(1577, 2) ObjectToBuilding(1578, 2) + ObjectToBuilding(2049, 2) ObjectToBuilding(2050, 2) + ObjectToBuilding(2061, 2) ObjectToBuilding(2062, 2) ObjectToBuilding(2145, 2) ObjectToBuilding(2146, 2) @@ -432,6 +436,8 @@ object Maps { def Building2() : Unit = { //HART building C LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building))) + LocalObject(12, ProximityTerminal.Constructor(repair_silo)) //repair terminal A + LocalObject(13, Terminal.Constructor(repair_silo)) //rearm terminal A //ItemTransaction: ItemTransactionMessage(PlanetSideGUID(2050),Buy,3,25mmbullet,0,PlanetSideGUID(0)) LocalObject(186, Terminal.Constructor(cert_terminal)) LocalObject(187, Terminal.Constructor(cert_terminal)) LocalObject(188, Terminal.Constructor(cert_terminal)) @@ -479,6 +485,8 @@ object Maps { LocalObject(1087, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct LocalObject(1088, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct LocalObject(1089, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct + ObjectToBuilding(12, 2) + ObjectToBuilding(13, 2) ObjectToBuilding(186, 2) ObjectToBuilding(187, 2) ObjectToBuilding(188, 2) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index ca2fd7ae..2461e431 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1104,7 +1104,7 @@ class WorldSessionActor extends Actor with MDCContextAware { vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) } sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on? - //sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, vehicle.Definition.MaxHealth))) + sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth)) sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //??? sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //??? ReloadVehicleAccessPermissions(vehicle) @@ -2327,21 +2327,20 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - case Some(obj : RepairRearmSilo) => - player.VehicleSeated match { - case Some(vehicle_guid) => - val vehicle = continent.GUID(vehicle_guid).get.asInstanceOf[Vehicle] - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) - sendResponse(UseItemMessage(avatar_guid, unk1, vehicle_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId)) - case None => - log.error("UseItem: expected seated vehicle, but found none") - } - case Some(obj : Terminal) => if(obj.Definition.isInstanceOf[MatrixTerminalDefinition]) { //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, obj.Position)) } + else if(obj.Definition == GlobalDefinitions.repair_silo) { + FindLocalVehicle match { + case Some(vehicle) => + sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, unk1, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId)) + case None => + log.error("UseItem: expected seated vehicle, but found none") + } + } else { sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) } @@ -2368,7 +2367,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } case msg @ ProximityTerminalUseMessage(player_guid, object_guid, _) => - log.info(s"ProximityTerminal: $msg") + log.info(s"ProximityTerminalUse: $msg") continent.GUID(object_guid) match { case Some(obj : Terminal with ProximityUnit) => if(usingProximityTerminal.contains(object_guid)) { @@ -2378,7 +2377,7 @@ class WorldSessionActor extends Actor with MDCContextAware { StartUsingProximityUnit(obj) } case Some(obj) => ; - log.warn(s"ProximityTerminalUse: object is not a proximity terminal - $obj") + log.warn(s"ProximityTerminalUse: object does not have proximity effects - $obj") case None => log.warn(s"ProximityTerminalUse: no object with guid $object_guid found") } @@ -2438,14 +2437,16 @@ class WorldSessionActor extends Actor with MDCContextAware { else { None }) match { - case Some(owner : Player) => + case Some(owner : Player) => //InfantryLoadout avatar.SaveLoadout(owner, name, lineno) - case Some(owner : Vehicle) => + import InfantryLoadout._ + sendResponse(FavoritesMessage(list, player_guid, line, name, DetermineSubtypeB(player.ExoSuit, DetermineSubtype(player)))) + case Some(owner : Vehicle) => //VehicleLoadout avatar.SaveLoadout(owner, name, lineno) + sendResponse(FavoritesMessage(list, player_guid, line, name)) case Some(_) | None => log.error("FavoritesRequest: unexpected owner for favorites") } - sendResponse(FavoritesMessage(list, player_guid, line, name)) case FavoritesAction.Delete => avatar.DeleteLoadout(lineno) @@ -2637,6 +2638,9 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value)) } + case msg @ FacilityBenefitShieldChargeRequestMessage(guid) => + //log.info(s"ShieldChargeRequest: $msg") + case msg @ BattleplanMessage(char_id, player_name, zonr_id, diagrams) => log.info("Battleplan: "+msg) @@ -4193,6 +4197,10 @@ class WorldSessionActor extends Actor with MDCContextAware { SetDelayedProximityUnitReset(terminal) ProximityHealCrystal(terminal) + case GlobalDefinitions.repair_silo => + SetDelayedProximityUnitReset(terminal) + //TODO insert vehicle repair here; see ProximityMedicalTerminal for example + case _ => ; } } From e368c8b5be1a43762fa57eddb9f61cc9bf28ace7 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 16 May 2018 20:35:33 -0400 Subject: [PATCH 6/7] added silos and air pads on z6 and on home3; undid temp lattice benefits and temp vehicle damage --- .../psforever/objects/GlobalDefinitions.scala | 2 ++ .../terminals/RepairRearmSiloDefinition.scala | 10 +++++++- pslogin/src/main/scala/Maps.scala | 24 +++++++++++++++++++ .../src/main/scala/WorldSessionActor.scala | 7 +++--- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index d39790ef..91f6c77e 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -524,6 +524,8 @@ object GlobalDefinitions { val medical_terminal = new MedicalTerminalDefinition(529) + val pad_landing = new RepairRearmSiloDefinition(719) + val repair_silo = new RepairRearmSiloDefinition(729) val spawn_pad = new VehicleSpawnPadDefinition diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala index 9cfc6fa7..f7463bf0 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/RepairRearmSiloDefinition.scala @@ -12,7 +12,15 @@ import net.psforever.packet.game.ItemTransactionMessage * Has both proximity-based operation and direct access purchasing power. */ class RepairRearmSiloDefinition(objectId : Int) extends EquipmentTerminalDefinition(objectId) with ProximityDefinition { - Name = "repair_silo" + Name = if(objectId == 719) { + "pad_landing" + } + else if(objectId == 729) { + "repair_silo" + } + else { + throw new IllegalArgumentException("repair re-arm terminal must be either object id 719 or 729") + } private val buyFunc : (Player, ItemTransactionMessage)=>Terminal.Exchange = EquipmentTerminalDefinition.Buy(Map.empty, Map.empty, Map.empty) diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala index c1b62dfa..4c750b62 100644 --- a/pslogin/src/main/scala/Maps.scala +++ b/pslogin/src/main/scala/Maps.scala @@ -110,6 +110,14 @@ object Maps { LocalObject(1576, Terminal.Constructor(order_terminal)) LocalObject(1577, Terminal.Constructor(order_terminal)) LocalObject(1578, Terminal.Constructor(order_terminal)) + LocalObject(1744, ProximityTerminal.Constructor(pad_landing)) //air pad A + LocalObject(1745, Terminal.Constructor(pad_landing)) //air pad A + LocalObject(1747, ProximityTerminal.Constructor(pad_landing)) //air pad B + LocalObject(1748, Terminal.Constructor(pad_landing)) //air pad B + LocalObject(1756, ProximityTerminal.Constructor(pad_landing)) //air pad C + LocalObject(1757, Terminal.Constructor(pad_landing)) //air pad C + LocalObject(1765, ProximityTerminal.Constructor(pad_landing)) //air pad D + LocalObject(1766, Terminal.Constructor(pad_landing)) //air pad D LocalObject(2145, SpawnTube.Constructor(Vector3(3980.4062f, 4252.7656f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2146, SpawnTube.Constructor(Vector3(3980.4062f, 4259.992f, 257.5625f), Vector3(0, 0, 90))) LocalObject(2147, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))) @@ -218,6 +226,14 @@ object Maps { ObjectToBuilding(1576, 2) ObjectToBuilding(1577, 2) ObjectToBuilding(1578, 2) + ObjectToBuilding(1744, 2) + ObjectToBuilding(1745, 2) + ObjectToBuilding(1747, 2) + ObjectToBuilding(1748, 2) + ObjectToBuilding(1756, 2) + ObjectToBuilding(1757, 2) + ObjectToBuilding(1765, 2) + ObjectToBuilding(1766, 2) ObjectToBuilding(2049, 2) ObjectToBuilding(2050, 2) ObjectToBuilding(2061, 2) @@ -359,6 +375,10 @@ object Maps { LocalObject(1591, Terminal.Constructor(order_terminal)) LocalObject(1592, Terminal.Constructor(order_terminal)) LocalObject(1593, Terminal.Constructor(order_terminal)) + LocalObject(1846, ProximityTerminal.Constructor(pad_landing)) //air pad S + LocalObject(1847, Terminal.Constructor(pad_landing)) //air pad S + LocalObject(1849, ProximityTerminal.Constructor(pad_landing)) //air pad N + LocalObject(1850, Terminal.Constructor(pad_landing)) //air pad N LocalObject(2156, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3994.125f, 228.1875f), Vector3(0, 0, 90))) LocalObject(2157, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3977.7266f, 228.1875f), Vector3(0, 0, 90))) LocalObject(2333, Door.Constructor) //spawn tube door @@ -382,6 +402,10 @@ object Maps { ObjectToBuilding(1591, 49) ObjectToBuilding(1592, 49) ObjectToBuilding(1593, 49) + ObjectToBuilding(1846, 49) + ObjectToBuilding(1847, 49) + ObjectToBuilding(1849, 49) + ObjectToBuilding(1850, 49) ObjectToBuilding(2156, 49) ObjectToBuilding(2157, 49) ObjectToBuilding(2333, 49) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 2461e431..3ef631db 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1104,7 +1104,7 @@ class WorldSessionActor extends Actor with MDCContextAware { vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) } sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on? - sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth)) + //sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth)) sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //??? sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //??? ReloadVehicleAccessPermissions(vehicle) @@ -2332,7 +2332,7 @@ class WorldSessionActor extends Actor with MDCContextAware { //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, obj.Position)) } - else if(obj.Definition == GlobalDefinitions.repair_silo) { + else if(obj.Definition.isInstanceOf[RepairRearmSiloDefinition]) { FindLocalVehicle match { case Some(vehicle) => sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) @@ -2363,7 +2363,8 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(ObjectDeleteMessage(PlanetSideGUID(unk1), 2)) } - case None => ; + case None => + log.error(s"UseItem: can not find object $object_guid") } case msg @ ProximityTerminalUseMessage(player_guid, object_guid, _) => From 26d42e9f37e174e337544e466727175307d627c7 Mon Sep 17 00:00:00 2001 From: FateJH Date: Mon, 21 May 2018 07:29:09 -0400 Subject: [PATCH 7/7] fixing incorrect documentation --- .../packet/game/FacilityBenefitShieldChargeRequestMessage.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala b/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala index 15389532..6b1d6110 100644 --- a/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/FacilityBenefitShieldChargeRequestMessage.scala @@ -7,7 +7,7 @@ import scodec.codecs._ /** * Dispatched by the client when driving a vehicle in the sphere of influence of an allied base - * that is a dropship center or that possesses the lattice-connected benefit of a dropship center. + * that is an amp station facility or that possesses the lattice-connected benefit of an amp station. * The vehicle that is being driven will not have perfect fully-charged shields at the time. * @param vehicle_guid the vehicle whose shield is being charged */