mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-13 19:53:38 +00:00
partially-working vehicle favorites system; modifications to existing favorites system, identification of packet parameters
This commit is contained in:
parent
9d7d1b0456
commit
2a4fe4865e
15 changed files with 295 additions and 88 deletions
|
|
@ -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<br>
|
||||
* 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<br>
|
||||
* 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 = {
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.<br>
|
||||
* <br>
|
||||
* Lists:<br>
|
||||
* `
|
||||
* 0 - Equipment Terminal (infantry)<br>
|
||||
* 1 - Repair/Rearm Silo (standard vehicles)<br>
|
||||
* `
|
||||
* <br>
|
||||
* Armors:<br>
|
||||
* `
|
||||
* 1 - Agile<br>
|
||||
|
|
@ -33,13 +28,7 @@ import shapeless.{::, HNil}
|
|||
* 6 - AV MAX<br>
|
||||
* `
|
||||
* <br>
|
||||
* Exploration 1:<br>
|
||||
* 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?<br>
|
||||
* <br>
|
||||
* Exploration 2:<br>
|
||||
* Exploration:<br>
|
||||
* 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
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
16
common/src/main/scala/net/psforever/types/LoadoutType.scala
Normal file
16
common/src/main/scala/net/psforever/types/LoadoutType.scala
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ object TransactionType extends Enumeration {
|
|||
Sell, // or forget on certif term
|
||||
Unk4,
|
||||
Unk5,
|
||||
InfantryLoadout,
|
||||
Loadout,
|
||||
Unk7
|
||||
= Value
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue