recovery from original angles branch, mostly concerning changes with ChangeFireStateMessage_Stop and WeaponFireMessage field info

This commit is contained in:
Jason_DiDonato@yahoo.com 2021-06-25 22:57:55 -04:00
parent 2216958d72
commit 42db02f576
4 changed files with 177 additions and 138 deletions

View file

@ -4120,46 +4120,24 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case msg @ ChangeFireStateMessage_Stop(item_guid) => case msg @ ChangeFireStateMessage_Stop(item_guid) =>
prefire = None prefire = None
shootingStop = System.currentTimeMillis() shootingStop = System.currentTimeMillis()
val weapon: Option[Equipment] = if (shooting.contains(item_guid)) { if (shooting.contains(item_guid)) {
shooting = None shooting = None
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Stop(player.GUID, item_guid)
)
FindEquipment
} else {
FindEquipment match {
case Some(tool: Tool) => //special cases
//the decimator does not send a ChangeFireState_Start on the last shot
if (
tool.Definition == GlobalDefinitions.phoenix &&
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
) {
//suppress the decimator's alternate fire mode, however
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
)
shootingStart = System.currentTimeMillis() - 1L
}
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Stop(player.GUID, item_guid)
)
Some(tool)
case Some(tool) => //permissible, for now
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Stop(player.GUID, item_guid)
)
Some(tool)
case _ =>
//log.warn(s"ChangeFireState_Stop: ${player.Name} never started firing item ${item_guid.guid} in the first place?")
None
}
} }
weapon match { val pguid = player.GUID
FindEquipment match {
case Some(tool: Tool) => case Some(tool: Tool) =>
//the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why
if (
tool.Definition == GlobalDefinitions.phoenix &&
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
) {
//suppress the decimator's alternate fire mode, however
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Start(pguid, item_guid)
)
shootingStart = System.currentTimeMillis() - 1L
}
tool.FireMode match { tool.FireMode match {
case mode: ChargeFireModeDefinition => case mode: ChargeFireModeDefinition =>
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine)) sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
@ -4168,18 +4146,24 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (tool.Magazine == 0) { if (tool.Magazine == 0) {
FireCycleCleanup(tool) FireCycleCleanup(tool)
} }
case Some(trigger: BoomerTrigger) =>
val playerGUID = player.GUID
continent.AvatarEvents ! AvatarServiceMessage( continent.AvatarEvents ! AvatarServiceMessage(
continent.id, continent.id,
AvatarAction.ChangeFireState_Start(playerGUID, item_guid) AvatarAction.ChangeFireState_Stop(pguid, item_guid)
)
case Some(trigger: BoomerTrigger) =>
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Start(pguid, item_guid)
) )
continent.GUID(trigger.Companion) match { continent.GUID(trigger.Companion) match {
case Some(boomer: BoomerDeployable) => case Some(boomer: BoomerDeployable) =>
boomer.Actor ! CommonMessages.Use(player, Some(trigger)) boomer.Actor ! CommonMessages.Use(player, Some(trigger))
case Some(_) | None => ; case Some(_) | None => ;
} }
case _ => ; case _ => ;
//log.warn(s"ChangeFireState_Stop: ${player.Name} never started firing item ${item_guid.guid} in the first place?")
} }
progressBarUpdate.cancel() progressBarUpdate.cancel()
progressBarValue = None progressBarValue = None
@ -5128,19 +5112,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
} }
case msg @ WeaponFireMessage( case msg @ WeaponFireMessage(
seq_time, _,
weapon_guid, weapon_guid,
projectile_guid, projectile_guid,
shot_origin, shot_origin,
unk1, _,
unk2, _,
unk3, _,
unk4, _, //max_distance,
unk5, _,
unk6, _, //projectile_type,
unk7 thrown_projectile_vel
) => ) =>
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin) HandleWeaponFire(weapon_guid, projectile_guid, shot_origin, thrown_projectile_vel.flatten)
case msg @ WeaponLazeTargetPositionMessage(_, _, pos2) => case msg @ WeaponLazeTargetPositionMessage(_, _, pos2) =>
log.info(s"${player.Name} is lazing the position ${continent.id}@(${pos2.x},${pos2.y},${pos2.z})") log.info(s"${player.Name} is lazing the position ${continent.id}@(${pos2.x},${pos2.y},${pos2.z})")
@ -9009,7 +8993,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
middlewareActor ! MiddlewareActor.Teardown() middlewareActor ! MiddlewareActor.Teardown()
} }
def HandleWeaponFire(weaponGUID: PlanetSideGUID, projectileGUID: PlanetSideGUID, shotOrigin: Vector3): Unit = { def HandleWeaponFire(
weaponGUID: PlanetSideGUID,
projectileGUID: PlanetSideGUID,
shotOrigin: Vector3,
shotVelocity: Option[Vector3]
): Unit = {
HandleWeaponFireAccountability(weaponGUID, projectileGUID) match { HandleWeaponFireAccountability(weaponGUID, projectileGUID) match {
case (Some(obj), Some(tool)) => case (Some(obj), Some(tool)) =>
val projectileIndex = projectileGUID.guid - Projectile.baseUID val projectileIndex = projectileGUID.guid - Projectile.baseUID
@ -9054,10 +9043,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
projectile_info, projectile_info,
tool.Definition, tool.Definition,
tool.FireMode, tool.FireMode,
player, PlayerSource(player),
attribution, attribution,
shotOrigin, shotOrigin,
angle angle,
shotVelocity
) )
val initialQuality = tool.FireMode match { val initialQuality = tool.FireMode match {
case mode: ChargeFireModeDefinition => case mode: ChargeFireModeDefinition =>
@ -9071,18 +9061,14 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile" s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile"
) )
continent.tasks ! (if (projectile.HasGUID) { continent.tasks ! (if (projectile.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage( continent.AvatarEvents ! AvatarServiceMessage(
continent.id, continent.id,
AvatarAction.ProjectileExplodes( AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)
player.GUID, )
projectile.GUID, ReregisterProjectile(projectile)
projectile } else {
) RegisterProjectile(projectile)
) })
ReregisterProjectile(projectile)
} else {
RegisterProjectile(projectile)
})
} }
projectilesToCleanUp(projectileIndex) = false projectilesToCleanUp(projectileIndex) = false

View file

@ -12,7 +12,7 @@ import net.psforever.objects.vital.base.DamageResolution
import net.psforever.types.Vector3 import net.psforever.types.Vector3
/** /**
* A summation of weapon (`Tool`) discharge. * A summation of weapon discharge.
* @see `ProjectileDefinition` * @see `ProjectileDefinition`
* @see `ToolDefinition` * @see `ToolDefinition`
* @see `FireModeDefinition` * @see `FireModeDefinition`
@ -28,9 +28,10 @@ import net.psforever.types.Vector3
* if not, then it is a type of vehicle (and owner should have a positive `seated` field) * if not, then it is a type of vehicle (and owner should have a positive `seated` field)
* @param shot_origin where the projectile started * @param shot_origin where the projectile started
* @param shot_angle in which direction the projectile was aimed when it was discharged * @param shot_angle in which direction the projectile was aimed when it was discharged
* @param shot_velocity the initial velocity coordinates of the projectile according to its world directions
* @param quality na * @param quality na
* @param id an exclusive identifier for this projectile; * @param id an exclusive identifier for this projectile;
* normally generated internally, but can be manually set * normally generated internally, but can be manually set (for modifying a continuous projectile reference)
* @param fire_time when the weapon discharged was recorded; * @param fire_time when the weapon discharged was recorded;
* defaults to `System.currentTimeMillis()` * defaults to `System.currentTimeMillis()`
*/ */
@ -42,18 +43,20 @@ final case class Projectile(
attribute_to: Int, attribute_to: Int,
shot_origin: Vector3, shot_origin: Vector3,
shot_angle: Vector3, shot_angle: Vector3,
shot_velocity: Option[Vector3],
quality: ProjectileQuality = ProjectileQuality.Normal, quality: ProjectileQuality = ProjectileQuality.Normal,
id: Long = Projectile.idGenerator.getAndIncrement(), id: Long = Projectile.idGenerator.getAndIncrement(),
fire_time: Long = System.currentTimeMillis() fire_time: Long = System.currentTimeMillis()
) extends PlanetSideGameObject { ) extends PlanetSideGameObject {
Position = shot_origin Position = shot_origin
Orientation = shot_angle Orientation = shot_angle
Velocity = { Velocity = shot_velocity.getOrElse {
val initVel: Int = profile.InitialVelocity //initial velocity val radz = math.toRadians(shot_angle.z)
val radAngle: Double = math.toRadians(shot_angle.y) //angle of elevation Vector3.Unit(Vector3(
val rise: Float = initVel * math.sin(radAngle).toFloat //z math.sin(radz).toFloat,
val ground: Float = initVel * math.cos(radAngle).toFloat //base math.cos(radz).toFloat,
Vector3.Rz(Vector3(0, -ground, 0), shot_angle.z) + Vector3.z(rise) math.sin(math.toRadians(shot_angle.y)).toFloat
)) * profile.InitialVelocity.toFloat
} }
/** Information about the current world coordinates and orientation of the projectile */ /** Information about the current world coordinates and orientation of the projectile */
@ -77,6 +80,7 @@ final case class Projectile(
attribute_to, attribute_to,
shot_origin, shot_origin,
shot_angle, shot_angle,
shot_velocity,
value, value,
id, id,
fire_time fire_time
@ -127,14 +131,14 @@ object Projectile {
* @return the `Projectile` object * @return the `Projectile` object
*/ */
def apply( def apply(
profile: ProjectileDefinition, profile: ProjectileDefinition,
tool_def: ToolDefinition, tool_def: ToolDefinition,
fire_mode: FireModeDefinition, fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity, owner: PlanetSideGameObject with FactionAffinity,
shot_origin: Vector3, shot_origin: Vector3,
shot_angle: Vector3 shot_angle: Vector3
): Projectile = { ): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle) Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle, None)
} }
/** /**
@ -149,14 +153,26 @@ object Projectile {
* @return the `Projectile` object * @return the `Projectile` object
*/ */
def apply( def apply(
profile: ProjectileDefinition, profile: ProjectileDefinition,
tool_def: ToolDefinition, tool_def: ToolDefinition,
fire_mode: FireModeDefinition, fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity, owner: PlanetSideGameObject with FactionAffinity,
attribute_to: Int, attribute_to: Int,
shot_origin: Vector3, shot_origin: Vector3,
shot_angle: Vector3 shot_angle: Vector3
): Projectile = { ): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle) Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle, None)
}
def apply(
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: SourceEntry,
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
Projectile(profile, tool_def, fire_mode, owner, attribute_to, shot_origin, shot_angle, None)
} }
} }

View file

@ -1,42 +1,79 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import enumeratum.values.{IntEnum, IntEnumEntry}
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.{PlanetSideGUID, Vector3} import net.psforever.types.{PlanetSideGUID, Vector3}
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
sealed abstract class ProjectileCharacteristics(val value: Int) extends IntEnumEntry
/** /**
* WeaponFireMessage seems to be sent each time a weapon actually shoots. * Characteristics about the projectile being produced by a `WeaponFireMessage` packet.
* Not really useful outside of `WeaponFireMessage`?
*/
object ProjectileCharacteristics extends IntEnum[ProjectileCharacteristics] {
val values = findValues
/** most common characteristic;
* utilized for both straight-fire and various arcing projectiles
*/
case object Standard extends ProjectileCharacteristics(value = 0)
/** some arcing explosive projectiles such as the Thumper's alternate fire and the Leviathan's fluxpod launcher;
* exceptions include: Punisher grenade cartridges, Grenade alternate fire (see `Thrown`), Pounder alternate fire
*/
case object DelayedExplosion extends ProjectileCharacteristics(value = 1)
/** remote client projectiles (those constructed through packets) */
case object Guided extends ProjectileCharacteristics(value = 2)
/** grenades, and only grenades */
case object Thrown extends ProjectileCharacteristics(value = 3)
/** unused? */
case object u4 extends ProjectileCharacteristics(value = 4)
/** unused? */
case object u5 extends ProjectileCharacteristics(value = 5)
/** unused? */
case object u6 extends ProjectileCharacteristics(value = 6)
/** unused? */
case object u7 extends ProjectileCharacteristics(value = 7)
}
/**
* Dispatched form the client each time a weapon discharges.
* *
* @param seq_time See [[PlayerStateMessageUpstream]] for explanation of seq_time. * @param seq_time see [[PlayerStateMessageUpstream]] for explanation of seq_time
* @param weapon_guid * @param weapon_guid the weapon of discharge;
* @param projectile_guid * when dispatched to a client, an unreferenced entity results in the projectile not being rendered
* @param shot_origin * @param projectile_guid the (client-local) projectile unique identifier;
* @param unk1 Always zero from testing so far * when dispatched to a client, can be unreferenced (or blanked)
* @param unk2 Seems semi-random * @param shot_origin the position where the projectile is first spawned
* @param unk3 Seems semi-random * @param unk1 na;
* @param unk4 Maximum travel distance in meters - seems to be zero for decimator rockets * always 0?
* @param unk5 Possibly always 255 from testing * @param spread_a related to the spread of the discharge;
* @param unk6 0 for bullet * works with `spread_b` field in unknown way;
* 1 for possibly delayed explosion (thumper alt fire) or thresher/leviathan flux cannon * the unmodified value is high (65535) when accurate, low (0) when not
* 2 for vs starfire (lockon type?) * @param spread_b related to the spread of the discharge;
* 3 for thrown (e.g. grenades) * works with `spread_a` field in unknown way
* @param unk7 Seems to be thrown weapon velocity/direction * @param max_distance maximum travel distance (m), with exceptions, e.g., decimator rockets are always 0
* @param unk5 na;
* always 255?
* @param projectile_type the sort of projectile produced
* @param thrown_projectile_vel if a thrown projectile, its velocity
*/ */
final case class WeaponFireMessage( final case class WeaponFireMessage(
seq_time: Int, seq_time: Int,
weapon_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID,
projectile_guid: PlanetSideGUID, projectile_guid: PlanetSideGUID,
shot_origin: Vector3, shot_origin: Vector3,
unk1: Int, unk1: Int,
unk2: Int, spread_a: Int,
unk3: Int, spread_b: Int,
unk4: Int, max_distance: Int,
unk5: Int, unk5: Int,
unk6: Int, projectile_type: ProjectileCharacteristics,
unk7: Option[Option[Vector3]] thrown_projectile_vel: Option[Option[Vector3]]
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = WeaponFireMessage type Packet = WeaponFireMessage
def opcode = GamePacketOpcode.WeaponFireMessage def opcode = GamePacketOpcode.WeaponFireMessage
def encode = WeaponFireMessage.encode(this) def encode = WeaponFireMessage.encode(this)
@ -44,17 +81,17 @@ final case class WeaponFireMessage(
object WeaponFireMessage extends Marshallable[WeaponFireMessage] { object WeaponFireMessage extends Marshallable[WeaponFireMessage] {
implicit val codec: Codec[WeaponFireMessage] = ( implicit val codec: Codec[WeaponFireMessage] = (
("seq_time" | uintL(10)) :: ("seq_time" | uintL(bits = 10)) ::
("weapon_guid" | PlanetSideGUID.codec) :: ("weapon_guid" | PlanetSideGUID.codec) ::
("projectile_guid" | PlanetSideGUID.codec) :: ("projectile_guid" | PlanetSideGUID.codec) ::
("shot_origin" | Vector3.codec_pos) :: ("shot_origin" | Vector3.codec_pos) ::
("unk1" | uint16L) :: ("unk1" | uint16L) ::
("unk2" | uint16L) :: ("spread_a" | uint16L.xmap[Int]( { x => 65535 - x }, { x => 65535 - x })) :: //low when accurate, high when not
("unk3" | uint16L) :: ("spread_b" | uint16L) ::
("unk4" | uint16L) :: ("max_distance" | uint16L) ::
("unk5" | uint8L) :: ("unk5" | uint8L) ::
(("unk6" | uintL(3)) >>:~ { unk6_value => ("projectile_type" | PacketHelpers.createIntEnumCodec(ProjectileCharacteristics, uint(bits = 3)) >>:~ { ptype =>
conditional(unk6_value == 3, "unk7" | optional(bool, Vector3.codec_vel)).hlist ("thrown_projectile_vel" | conditional(ptype == ProjectileCharacteristics.Thrown, optional(bool, Vector3.codec_vel))).hlist
}) })
).as[WeaponFireMessage] ).as[WeaponFireMessage]
} }

View file

@ -30,12 +30,12 @@ class WeaponFireMessageTest extends Specification {
projectile_guid mustEqual PlanetSideGUID(40100) projectile_guid mustEqual PlanetSideGUID(40100)
shot_origin mustEqual Vector3(3675.4688f, 2726.9922f, 92.921875f) shot_origin mustEqual Vector3(3675.4688f, 2726.9922f, 92.921875f)
unk1 mustEqual 0 unk1 mustEqual 0
unk2 mustEqual 64294 unk2 mustEqual 1241
unk3 mustEqual 1502 unk3 mustEqual 1502
unk4 mustEqual 200 unk4 mustEqual 200
unk5 mustEqual 255 unk5 mustEqual 255
unk6 mustEqual 0 unk6 mustEqual ProjectileCharacteristics.Standard
unk7 mustEqual None unk7.isEmpty mustEqual true
case _ => case _ =>
ko ko
} }
@ -48,11 +48,11 @@ class WeaponFireMessageTest extends Specification {
PlanetSideGUID(40100), PlanetSideGUID(40100),
Vector3(3675.4688f, 2726.9922f, 92.921875f), Vector3(3675.4688f, 2726.9922f, 92.921875f),
0, 0,
64294, 1241,
1502, 1502,
200, 200,
255, 255,
0, ProjectileCharacteristics.Standard,
None None
) )
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector