diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 2146e5d7..8c1d8076 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -537,7 +537,7 @@ object GamePacketOpcode extends Enumeration {
case 0xb6 => game.ZonePopulationUpdateMessage.decode
case 0xb7 => game.DisconnectMessage.decode
// 0xb8
- case 0xb8 => noDecoder(ExperienceAddedMessage)
+ case 0xb8 => game.ExperienceAddedMessage.decode
case 0xb9 => game.OrbitalStrikeWaypointMessage.decode
case 0xba => game.KeepAliveMessage.decode
case 0xbb => noDecoder(MapObjectStateBlockMessage)
diff --git a/common/src/main/scala/net/psforever/packet/game/ExperienceAddedMessage.scala b/common/src/main/scala/net/psforever/packet/game/ExperienceAddedMessage.scala
new file mode 100644
index 00000000..b38808f8
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/ExperienceAddedMessage.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._
+
+/**
+ * Displays a message about being awarded experience points in the events chat.
+ *
+ * This packet does not actually award any experience points.
+ * It merely generates the message:
+ * `"You have been awarded x experience points."`
+ * ... where `x` is the number of experience points that have been promised.
+ * If the `Boolean` parameter is `true`, `x` will be equal to the number provided followed by the word "Command."
+ * If the `Boolean` parameter is `false`, `x` will be represented as an obvious blank space character.
+ * (Yes, it prints to the events chat like that.)
+ * @param exp the number of (Command) experience points earned
+ * @param unk defaults to `true` for effect;
+ * if `false`, the number of experience points in the message will be blanked
+ */
+final case class ExperienceAddedMessage(exp : Int,
+ unk : Boolean = true)
+ extends PlanetSideGamePacket {
+ type Packet = ExperienceAddedMessage
+ def opcode = GamePacketOpcode.ExperienceAddedMessage
+ def encode = ExperienceAddedMessage.encode(this)
+}
+
+object ExperienceAddedMessage extends Marshallable[ExperienceAddedMessage] {
+ implicit val codec : Codec[ExperienceAddedMessage] = (
+ ("cep" | uintL(15)) ::
+ ("unk" | bool)
+ ).as[ExperienceAddedMessage]
+}
diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala
index 776380ae..0efcd7ad 100644
--- a/common/src/test/scala/GamePacketTest.scala
+++ b/common/src/test/scala/GamePacketTest.scala
@@ -1555,6 +1555,27 @@ class GamePacketTest extends Specification {
}
}
+ "ExperienceAddedMessage" should {
+ val string = hex"B8 04 03"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ExperienceAddedMessage(exp, unk) =>
+ exp mustEqual 260 //0x104
+ unk mustEqual true
+ case default =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = ExperienceAddedMessage(260)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+ }
+
"WeaponFireMessage" should {
val string = hex"34 44130029272F0B5DFD4D4EC5C00009BEF78172003FC0"