mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Merge pull request #137 from Fate-JH/targeting-info
Packet: TargetingInfoMessage
This commit is contained in:
commit
461a4f9507
|
|
@ -414,7 +414,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x4f => game.LashMessage.decode
|
||||
|
||||
// OPCODES 0x50-5f
|
||||
case 0x50 => noDecoder(TargetingInfoMessage)
|
||||
case 0x50 => game.TargetingInfoMessage.decode
|
||||
case 0x51 => noDecoder(TriggerEffectMessage)
|
||||
case 0x52 => game.WeaponDryFireMessage.decode
|
||||
case 0x53 => noDecoder(DroppodLaunchRequestMessage)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* An entry regarding a target's health and, if applicable, any secondary defensive option they possess, hitherto, "armor."
|
||||
* @param target_guid the target
|
||||
* @param health the amount of health the target has, as a percentage of a filled bar scaled between 0f and 1f inclusive
|
||||
* @param armor the amount of armor the target has, as a percentage of a filled bar scaled between 0f and 1f inclusive;
|
||||
* defaults to 0f
|
||||
*/
|
||||
final case class TargetInfo(target_guid : PlanetSideGUID,
|
||||
health : Float,
|
||||
armor : Float = 0f)
|
||||
|
||||
/**
|
||||
* Dispatched by the server to update status information regarding the listed targets.<br>
|
||||
* <br>
|
||||
* This packet is often in response to a client-sent `TargetingImplantRequest` packet, when related to the implant's operation.
|
||||
* It can also arrive independent of a formal request and will operate even without the implant.
|
||||
* The enumerated targets report their status as two "progress bars" that can be empty (0f) or filled (1f).
|
||||
* When this packet is received, the client will actually update the current fields associated with those values for the target.
|
||||
* For example, for `0x17` player characters, the values are assigned to their health points and armor points respectively.
|
||||
* Allied player characters will have their "progress bar" visuals updated immediately;
|
||||
* the implant is still necessary to view enemy target progress bars, if they will be visible.<br>
|
||||
* <br>
|
||||
* This function can be used to update fields properly.
|
||||
* The value between 0 and 255 (0f to 1f) can be inserted directly into `ObjectCreateMessage` creations as it matches the scale.
|
||||
* The target will be killed or destroyed as expected when health is set to zero.
|
||||
* @param target_list a list of targets
|
||||
*/
|
||||
final case class TargetingInfoMessage(target_list : List[TargetInfo])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = TargetingInfoMessage
|
||||
def opcode = GamePacketOpcode.TargetingInfoMessage
|
||||
def encode = TargetingInfoMessage.encode(this)
|
||||
}
|
||||
|
||||
object TargetInfo {
|
||||
/**
|
||||
* Overloaded constructor that takes `Integer` values rather than `Float` values.
|
||||
* @param target_guid the target
|
||||
* @param health the amount of health the target has
|
||||
* @param armor the amount of armor the target has
|
||||
* @return a `TargetInfo` object
|
||||
*/
|
||||
def apply(target_guid : PlanetSideGUID, health : Int, armor : Int) : TargetInfo = {
|
||||
val health2 : Float = TargetingInfoMessage.rangedFloat(health)
|
||||
val armor2 : Float = TargetingInfoMessage.rangedFloat(armor)
|
||||
TargetInfo(target_guid, health2, armor2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded constructor that takes `Integer` values rather than `Float` values and only expects the first field.
|
||||
* @param target_guid the target
|
||||
* @param health the amount of health the target has
|
||||
* @return a `TargetInfo` object
|
||||
*/
|
||||
def apply(target_guid : PlanetSideGUID, health : Int) : TargetInfo = {
|
||||
val health2 : Float = TargetingInfoMessage.rangedFloat(health)
|
||||
TargetInfo(target_guid, health2)
|
||||
}
|
||||
}
|
||||
|
||||
object TargetingInfoMessage extends Marshallable[TargetingInfoMessage] {
|
||||
private final val unit : Double = 0.0039215689 //common constant for 1/255
|
||||
|
||||
/**
|
||||
* Transform an unsigned `Integer` number into a scaled `Float`.
|
||||
* @param n an unsigned `Integer` number inclusive 0 and below 256
|
||||
* @return a scaled `Float` number inclusive to 0f to 1f
|
||||
*/
|
||||
def rangedFloat(n : Int) : Float = {
|
||||
(
|
||||
(if(n <= 0) {
|
||||
0
|
||||
}
|
||||
else if(n >= 255) {
|
||||
255
|
||||
}
|
||||
else {
|
||||
n
|
||||
}).toDouble * unit
|
||||
).toFloat
|
||||
}
|
||||
/**
|
||||
* Transform a scaled `Float` number into an unsigned `Integer`.
|
||||
* @param n `Float` number inclusive to 0f to 1f
|
||||
* @return a scaled unsigned `Integer` number inclusive 0 and below 256
|
||||
*/
|
||||
def rangedInt(n : Float) : Int = {
|
||||
(
|
||||
(if(n <= 0f) {
|
||||
0f
|
||||
}
|
||||
else if(n >= 1.0f) {
|
||||
1.0f
|
||||
}
|
||||
else {
|
||||
n
|
||||
}).toDouble * 255
|
||||
).toInt
|
||||
}
|
||||
|
||||
private val info_codec : Codec[TargetInfo] = (
|
||||
("target_guid" | PlanetSideGUID.codec) ::
|
||||
("unk1" | uint8L) ::
|
||||
("unk2" | uint8L)
|
||||
).xmap[TargetInfo] (
|
||||
{
|
||||
case a :: b :: c :: HNil =>
|
||||
val b2 : Float = rangedFloat(b)
|
||||
val c2 : Float = rangedFloat(c)
|
||||
TargetInfo(a, b2, c2)
|
||||
},
|
||||
{
|
||||
case TargetInfo(a, b, c) =>
|
||||
val b2 : Int = rangedInt(b)
|
||||
val c2 : Int = rangedInt(c)
|
||||
a :: b2 :: c2 :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[TargetingInfoMessage] = ("target_list" | listOfN(uint8L, info_codec)).as[TargetingInfoMessage]
|
||||
}
|
||||
54
common/src/test/scala/game/TargetingInfoMessageTest.scala
Normal file
54
common/src/test/scala/game/TargetingInfoMessageTest.scala
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class TargetingInfoMessageTest extends Specification {
|
||||
val string = hex"50 05 3D10C200 570EFF3C 2406EC00 2B068C00 2A069400"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case TargetingInfoMessage(target_list) =>
|
||||
target_list.size mustEqual 5
|
||||
//0
|
||||
target_list.head.target_guid mustEqual PlanetSideGUID(4157)
|
||||
target_list.head.health mustEqual 0.7607844f
|
||||
target_list.head.armor mustEqual 0f
|
||||
//1
|
||||
target_list(1).target_guid mustEqual PlanetSideGUID(3671)
|
||||
target_list(1).health mustEqual 1.0000001f
|
||||
target_list(1).armor mustEqual 0.23529413f
|
||||
//2
|
||||
target_list(2).target_guid mustEqual PlanetSideGUID(1572)
|
||||
target_list(2).health mustEqual 0.92549026f
|
||||
target_list(2).armor mustEqual 0f
|
||||
//3
|
||||
target_list(3).target_guid mustEqual PlanetSideGUID(1579)
|
||||
target_list(3).health mustEqual 0.54901963f
|
||||
target_list(3).armor mustEqual 0f
|
||||
//4
|
||||
target_list(4).target_guid mustEqual PlanetSideGUID(1578)
|
||||
target_list(4).health mustEqual 0.5803922f
|
||||
target_list(4).armor mustEqual 0f
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = TargetingInfoMessage(
|
||||
TargetInfo(PlanetSideGUID(4157), 0.7607844f) ::
|
||||
TargetInfo(PlanetSideGUID(3671), 1.0000001f, 0.23529413f) ::
|
||||
TargetInfo(PlanetSideGUID(1572), 0.92549026f) ::
|
||||
TargetInfo(PlanetSideGUID(1579), 0.54901963f) ::
|
||||
TargetInfo(PlanetSideGUID(1578), 0.5803922f) ::
|
||||
Nil
|
||||
)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue