diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 986a2082..5c29f2b0 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -477,7 +477,7 @@ object GamePacketOpcode extends Enumeration {
case 0x83 => noDecoder(SquadWaypointRequest)
case 0x84 => noDecoder(SquadWaypointEvent)
case 0x85 => noDecoder(OffshoreVehicleMessage)
- case 0x86 => noDecoder(ObjectDeployedMessage)
+ case 0x86 => game.ObjectDeployedMessage.decode
case 0x87 => noDecoder(ObjectDeployedCountMessage)
// 0x88
case 0x88 => game.WeaponDelayFireMessage.decode
diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectDeployedMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectDeployedMessage.scala
new file mode 100644
index 00000000..9b6e237a
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/ObjectDeployedMessage.scala
@@ -0,0 +1,89 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.Codec
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * An `Enumeration` for the forms of the event chat message produced by this packet.
+ */
+object DeploymentOutcome extends Enumeration(1) {
+ type Type = Value
+
+ val Failure = Value(2)
+ //3 produces a Success message, but 4 is common
+ val Success = Value(4)
+
+ val codec = PacketHelpers.createLongEnumerationCodec(this, uint32L)
+}
+
+/**
+ * Dispatched by the server to generate a message in the events chat when placing deployables.
+ *
+ * This packet does not actually modify anything in regards to deployables.
+ * The most common form of the generated message is:
+ * `"You have placed x of a possible y thing s."`
+ * ... where `x` is the current count of objects of this type that have been deployed;
+ * `y` is the (reported) maximum amount of objects of this type that can be deployed;
+ * and, `thing` is the token for objects of this type.
+ * If the `thing` is a valid string token, it will be replaced by language-appropriate descriptive text in the message.
+ * Otherwise, that text is placed directly into the message, with an obvious space between the text and the "s".
+ * "boomer," for example, is replaced by "Boomer Heavy Explosives" in the message for English language.
+ * "bullet_9mm_AP," however, is just "bullet_9mm_AP s."
+ *
+ * When the `action` is `Success`, the message in the chat will be shown as above.
+ * When the `action` is `Failure`, the message will be:
+ * `"thing failed to deploy and was destroyed."`
+ * ... where, again, `thing` is a valid string token.
+ * @param unk na;
+ * usually 0?
+ * @param desc descriptive text of what kind of object is being deployed;
+ * string token of the object, at best
+ * @param action the form the message will take
+ * @param count the current number of this type of object deployed
+ * @param max the maximum number of this type of object that can be deployed
+ */
+final case class ObjectDeployedMessage(unk : Int,
+ desc : String,
+ action : DeploymentOutcome.Value,
+ count : Long,
+ max : Long)
+ extends PlanetSideGamePacket {
+ type Packet = ObjectDeployedMessage
+ def opcode = GamePacketOpcode.ObjectDeployedMessage
+ def encode = ObjectDeployedMessage.encode(this)
+}
+
+object ObjectDeployedMessage extends Marshallable[ObjectDeployedMessage] {
+ /**
+ * Overloaded constructor for when the guid is not required.
+ * @param desc descriptive text of what kind of object is being deployed
+ * @param action na
+ * @param count the number of this type of object deployed
+ * @param max the maximum number of this type of object that can be deployed
+ * @return an `ObjectDeployedMessage` object
+ */
+ def apply(desc : String, action : DeploymentOutcome.Value, count : Long, max : Long) : ObjectDeployedMessage =
+ new ObjectDeployedMessage(0, desc, action, count, max)
+
+ implicit val codec : Codec[ObjectDeployedMessage] = (
+ ("unk" | uint16L) ::
+ ("desc" | PacketHelpers.encodedString) ::
+ ("action" | DeploymentOutcome.codec) ::
+ ("count" | uint32L) ::
+ ("max" | uint32L)
+ ).xmap[ObjectDeployedMessage] (
+ {
+ case guid :: str :: unk :: cnt ::mx :: HNil =>
+ ObjectDeployedMessage(guid, str, unk, cnt, mx)
+ },
+ {
+ case ObjectDeployedMessage(guid, str, unk, cnt, mx) =>
+ //truncate string length to 100 characters; raise no warnings
+ val limitedStr : String = if(str.length() > 100) { str.substring(0,100) } else { str }
+ guid :: limitedStr :: unk :: cnt :: mx :: HNil
+ }
+ )
+}
diff --git a/common/src/test/scala/game/ObjectDeployedMessageTest.scala b/common/src/test/scala/game/ObjectDeployedMessageTest.scala
new file mode 100644
index 00000000..88c9c0b7
--- /dev/null
+++ b/common/src/test/scala/game/ObjectDeployedMessageTest.scala
@@ -0,0 +1,31 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class ObjectDeployedMessageTest extends Specification {
+ val string_boomer = hex"86 000086626F6F6D6572040000000100000019000000"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string_boomer).require match {
+ case ObjectDeployedMessage(unk : Int, desc : String, act : DeploymentOutcome.Value, count : Long, max : Long) =>
+ unk mustEqual 0
+ desc mustEqual "boomer"
+ act mustEqual DeploymentOutcome.Success
+ count mustEqual 1
+ max mustEqual 25
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = ObjectDeployedMessage("boomer", DeploymentOutcome.Success, 1, 25)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_boomer
+ }
+}