diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 45b50dfb2..1ee7ca6fb 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -367,7 +367,7 @@ object GamePacketOpcode extends Enumeration {
case 0x27 => noDecoder(ObjectDetachMessage)
// 0x28
case 0x28 => game.CreateShortcutMessage.decode
- case 0x29 => noDecoder(ChangeShortcutBankMessage)
+ case 0x29 => game.ChangeShortcutBankMessage.decode
case 0x2a => noDecoder(ObjectAttachMessage)
case 0x2b => noDecoder(UnknownMessage43)
case 0x2c => noDecoder(PlanetsideAttributeMessage)
diff --git a/common/src/main/scala/net/psforever/packet/game/ChangeShortcutBankMessage.scala b/common/src/main/scala/net/psforever/packet/game/ChangeShortcutBankMessage.scala
new file mode 100644
index 000000000..c2a097bba
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/ChangeShortcutBankMessage.scala
@@ -0,0 +1,35 @@
+// Copyright (c) 2016 PSForever.net to present
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * Switch the set of shortcuts displayed on the HUD's hotbar.
+ *
+ * The hotbar contains eight slots for user shortcuts - medkits, implants, and text macros.
+ * Next to the first slot are up and down arrow buttons with a number.
+ * By progressing through the options available from the arrows, eight sets of eight shortcut slots are revealed.
+ * Which set is visible determines the effect of the respective binding keys (the Function keys) for the hotbar.
+ * Each set is called a "bank," obviously.
+ *
+ * This packet coordinates the bank number both as an upstream and as a downstream packet.
+ * @param player_guid the player
+ * @param bank the shortcut bank (zero-indexed);
+ * 0-7 are the valid banks
+ */
+final case class ChangeShortcutBankMessage(player_guid : PlanetSideGUID,
+ bank : Int)
+ extends PlanetSideGamePacket {
+ type Packet = ChangeShortcutBankMessage
+ def opcode = GamePacketOpcode.ChangeShortcutBankMessage
+ def encode = ChangeShortcutBankMessage.encode(this)
+}
+
+object ChangeShortcutBankMessage extends Marshallable[ChangeShortcutBankMessage] {
+ implicit val codec : Codec[ChangeShortcutBankMessage] = (
+ ("player_guid" | PlanetSideGUID.codec) ::
+ ("bank" | uint4L)
+ ).as[ChangeShortcutBankMessage]
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala b/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
index cfed14927..0232394b3 100644
--- a/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
@@ -57,7 +57,13 @@ final case class Shortcut(purpose : Int,
*
* When `addShortcut` is `true`, the provided `Shortcut` will be defined and attached to the respective hotbar slot indicated by `slot`.
* If it is `false`, the given `slot` will be unbound.
- * Nothing happens if the `slot` selection is invalid.
+ * Nothing happens if the `slot` selection is invalid.
+ *
+ * This packet coordinates the shortcut both as an upstream and as a downstream packet, leaning heavily towards the latter.
+ * An interesting application is that, if the user does not already have a medkit or a medkit shortcut;
+ * but, if he places a medkit in his inventory, the shortcut will be automatically added to his hotbar.
+ * This, in turn, dispatches a packet informing the server.
+ * The prior functionality will rarely be appreciated, however, as players rarely never have their medkit shortcut unbound.
* @param player_guid the player
* @param slot the hotbar slot number (one-indexed)
* @param unk na; always zero?
diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala
index 6fd87305c..48d669c6b 100644
--- a/common/src/test/scala/GamePacketTest.scala
+++ b/common/src/test/scala/GamePacketTest.scala
@@ -334,7 +334,7 @@ class GamePacketTest extends Specification {
shortcut.get.tile mustEqual "shortcut_macro"
shortcut.get.effect1 mustEqual "NTU"
shortcut.get.effect2 mustEqual "/platoon Incoming NTU spam!"
- case default =>
+ case _ =>
ko
}
}
@@ -407,6 +407,27 @@ class GamePacketTest extends Specification {
}
}
+ "ChangeShortcutBankMessage" should {
+ val string = hex"29 4B00 20"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ChangeShortcutBankMessage(player_guid, bank) =>
+ player_guid mustEqual PlanetSideGUID(75)
+ bank mustEqual 2
+ case default =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = ChangeShortcutBankMessage(PlanetSideGUID(75), 2)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+ }
+
"DropItemMessage" should {
val string = hex"37 4C00"