diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 8855bb51..cd45fa0e 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -519,7 +519,7 @@ object GamePacketOpcode extends Enumeration { case 0xa7 => noDecoder(GenericActionMessage) // 0xa8 case 0xa8 => game.ContinentalLockUpdateMessage.decode - case 0xa9 => noDecoder(AvatarGrenadeStateMessage) + case 0xa9 => game.AvatarGrenadeStateMessage.decode case 0xaa => noDecoder(UnknownMessage170) case 0xab => noDecoder(UnknownMessage171) case 0xac => noDecoder(ReleaseAvatarRequestMessage) diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala new file mode 100644 index 00000000..08e9e27d --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/AvatarGrenadeStateMessage.scala @@ -0,0 +1,57 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +/** + * 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 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 : GrenadeState.Value) + extends PlanetSideGamePacket { + type Packet = AvatarGrenadeStateMessage + def opcode = GamePacketOpcode.AvatarGrenadeStateMessage + def encode = AvatarGrenadeStateMessage.encode(this) +} + +object AvatarGrenadeStateMessage extends Marshallable[AvatarGrenadeStateMessage] { + implicit val codec : Codec[AvatarGrenadeStateMessage] = ( + ("player_guid" | PlanetSideGUID.codec) :: + ("state" | GrenadeState.codec) + ).as[AvatarGrenadeStateMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index c5d847da..ad5c5017 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -1398,6 +1398,27 @@ class GamePacketTest extends Specification { } } + "AvatarGrenadeStateMessage" should { + val string = hex"A9 DA11 01" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case AvatarGrenadeStateMessage(player_guid, state) => + player_guid mustEqual PlanetSideGUID(4570) + state mustEqual GrenadeState.PRIMED + case default => + ko + } + } + + "encode" in { + val msg = AvatarGrenadeStateMessage(PlanetSideGUID(4570), GrenadeState.PRIMED) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } + } + "BroadcastWarpgateUpdateMessage" should { val string = hex"D9 0D 00 01 00 20" diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index fbce7b3e..23f5f6d9 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -329,6 +329,9 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ MountVehicleMsg(player_guid, vehicle_guid, unk) => log.info("MounVehicleMsg: "+msg) + case msg @ AvatarGrenadeStateMessage(player_guid, state) => + log.info("AvatarGrenadeStateMessage: " + msg) + case default => log.debug(s"Unhandled GamePacket ${pkt}") }