diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala
index 77ad80d36..08e9e27d6 100644
--- a/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala
@@ -1,24 +1,48 @@
// Copyright (c) 2016 PSForever.net to present
package net.psforever.packet.game
-import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec
import scodec.codecs._
/**
- * Report the state of the "grenade throw" animation for this player.
+ * An `Enumeration` of the kinds of states applicable to the grenade animation.
+ */
+object GrenadeState extends Enumeration {
+ type Type = Value
+ val UNK0,
+ PRIMED, //avatars and other depicted player characters
+ THROWN //avatars only
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
+}
+
+/**
+ * Report the state of the grenade throw animation for this player.
+ * The default state is "held at side," though the client's avatar never has to announce this.
+ *
+ * The throwing animation has a minor timing glitch.
+ * Causing another player to raise his arm will always result in that arm being lowered a few seconds later.
+ * This is as opposed to the client's avatar, who can seem to hold a grenade in the "prepare to throw" state indefinitely.
+ * If the avatar looks away from a player whose grenade arm is up ("prepare to throw"), however, when they look back at the player
+ * his grenade arm will occasionally have been lowered ("held at side") again before it would normally be lowered.
+ *
+ * A client will dispatch state '1' and state '2' for the avatar's actions.
+ * A client will only react temporarily for another character other than the avatar when the given a state '1'.
+ * If that internal state is not changed, however, that other character will not respond to any subsequent '1' state.
+ * (This may also be a glitch.)
*
* States:
- * 1 - prepare for throwing (grenade held back over shoulder)
- * 2 - throwing (grenade released overhand)
- *
- * Exploration:
- * How many grenade states are possible?
+ * `
+ * 1 - prepare to throw (grenade held back over shoulder)
+ * 2 - throwing (grenade released overhand and then reset) (avatar only)
+ * `
* @param player_guid the player
* @param state the animation state
*/
final case class AvatarGrenadeStateMessage(player_guid : PlanetSideGUID,
- state : Int)
+ state : GrenadeState.Value)
extends PlanetSideGamePacket {
type Packet = AvatarGrenadeStateMessage
def opcode = GamePacketOpcode.AvatarGrenadeStateMessage
@@ -28,6 +52,6 @@ final case class AvatarGrenadeStateMessage(player_guid : PlanetSideGUID,
object AvatarGrenadeStateMessage extends Marshallable[AvatarGrenadeStateMessage] {
implicit val codec : Codec[AvatarGrenadeStateMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
- ("state" | uint8L)
+ ("state" | GrenadeState.codec)
).as[AvatarGrenadeStateMessage]
}
diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala
index 319434268..00810ce40 100644
--- a/common/src/test/scala/GamePacketTest.scala
+++ b/common/src/test/scala/GamePacketTest.scala
@@ -688,14 +688,14 @@ class GamePacketTest extends Specification {
PacketCoding.DecodePacket(string).require match {
case AvatarGrenadeStateMessage(player_guid, state) =>
player_guid mustEqual PlanetSideGUID(4570)
- state mustEqual 1
+ state mustEqual GrenadeState.PRIMED
case default =>
ko
}
}
"encode" in {
- val msg = AvatarGrenadeStateMessage(PlanetSideGUID(4570), 1)
+ val msg = AvatarGrenadeStateMessage(PlanetSideGUID(4570), GrenadeState.PRIMED)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string