diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 44b7a782..9da9c70c 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -556,7 +556,7 @@ object GamePacketOpcode extends Enumeration {
case 0xc6 => game.ZoneInfoMessage.decode
case 0xc7 => noDecoder(LongRangeProjectileInfoMessage)
// 0xc8
- case 0xc8 => noDecoder(WeaponLazeTargetPositionMessage)
+ case 0xc8 => game.WeaponLazeTargetPositionMessage.decode
case 0xc9 => noDecoder(ModuleLimitsMessage)
case 0xca => noDecoder(OutfitBenefitMessage)
case 0xcb => noDecoder(EmpireChangeTimeMessage)
diff --git a/common/src/main/scala/net/psforever/packet/game/WeaponLazeTargetPositionMessage.scala b/common/src/main/scala/net/psforever/packet/game/WeaponLazeTargetPositionMessage.scala
new file mode 100644
index 00000000..37430e50
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/WeaponLazeTargetPositionMessage.scala
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 PSForever.net to present
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import net.psforever.types.Vector3
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * Dispatched to the server when a position is being marked by a laze pointer tool.
+ *
+ * When the laze tool is used, a progress bar window is displayed, along with the text "Acquiring Target Position."
+ * The player using the tool constantly sends packets to the server for as long as the progress bar is filling.
+ * In all, about fifty packets are sent.
+ * (Measured during low-load testing.
+ * The actual number of packets may be related to network load.)
+ *
+ * While firing, the player's movement is locked for the duration.
+ * The weapon fire can be aborted at any time, returning control.
+ * @param weapon_uid the laze pointer tool
+ * @param player_pos the position of (the player holding the) laze pointer
+ * @param lazed_pos position of the tip of the laze pointer's beam, or where it intersects something
+ */
+final case class WeaponLazeTargetPositionMessage(weapon_uid : PlanetSideGUID,
+ player_pos : Vector3,
+ lazed_pos : Vector3)
+ extends PlanetSideGamePacket {
+ type Packet = WeaponLazeTargetPositionMessage
+ def opcode = GamePacketOpcode.WeaponLazeTargetPositionMessage
+ def encode = WeaponLazeTargetPositionMessage.encode(this)
+}
+
+object WeaponLazeTargetPositionMessage extends Marshallable[WeaponLazeTargetPositionMessage] {
+ implicit val codec : Codec[WeaponLazeTargetPositionMessage] = (
+ ("weapon_uid" | PlanetSideGUID.codec) ::
+ ("player_pos" | Vector3.codec_pos) ::
+ ("lazed_pos" | Vector3.codec_pos)
+ ).as[WeaponLazeTargetPositionMessage]
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index 5d695ffb..333704b4 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -271,6 +271,7 @@ object ObjectClass {
final val bank = 132
final val nano_dispenser = 577
final val command_detonater = 213
+ final val laze_pointer = 297
//unknown
final val locker_container = 456 //strange item found in inventory slot #5, between holsters and grid
@@ -547,7 +548,7 @@ object ObjectClass {
case ObjectClass.bank => WeaponData.genericCodec
case ObjectClass.nano_dispenser => WeaponData.genericCodec
case ObjectClass.command_detonater => WeaponData.genericCodec
-
+ case ObjectClass.laze_pointer => WeaponData.genericCodec
diff --git a/common/src/test/scala/game/WeaponLazeTargetPositionMessageTest.scala b/common/src/test/scala/game/WeaponLazeTargetPositionMessageTest.scala
new file mode 100644
index 00000000..c21ce137
--- /dev/null
+++ b/common/src/test/scala/game/WeaponLazeTargetPositionMessageTest.scala
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 PSForever.net to present
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.types.Vector3
+import scodec.bits._
+
+class WeaponLazeTargetPositionMessageTest extends Specification {
+ val string = hex"C8 4C00 6C2D7 65535 CA16 982D7 4A535 CA16"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case WeaponLazeTargetPositionMessage(weapon_uid, pos1, pos2) =>
+ weapon_uid mustEqual PlanetSideGUID(76)
+ pos1.x mustEqual 3674.8438f
+ pos1.y mustEqual 2726.789f
+ pos1.z mustEqual 91.15625f
+ pos2.x mustEqual 3675.1875f
+ pos2.y mustEqual 2726.5781f
+ pos2.z mustEqual 91.15625f
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = WeaponLazeTargetPositionMessage(
+ PlanetSideGUID(76),
+ Vector3(3674.8438f, 2726.789f, 91.15625f),
+ Vector3(3675.1875f, 2726.5781f, 91.15625f)
+ )
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 64a3be52..5dc6479e 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -349,6 +349,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
log.info("WeaponFire: " + msg)
+ case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
+ log.info("Lazing position: " + pos2.toString)
+
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
log.info("Hit: " + msg)