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) =>
prefire = None
shootingStop = System.currentTimeMillis()
val weapon: Option[Equipment] = if (shooting.contains(item_guid)) {
if (shooting.contains(item_guid)) {
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) =>
//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 {
case mode: ChargeFireModeDefinition =>
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
@ -4168,18 +4146,24 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (tool.Magazine == 0) {
FireCycleCleanup(tool)
}
case Some(trigger: BoomerTrigger) =>
val playerGUID = player.GUID
continent.AvatarEvents ! AvatarServiceMessage(
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 {
case Some(boomer: BoomerDeployable) =>
boomer.Actor ! CommonMessages.Use(player, Some(trigger))
case Some(_) | None => ;
}
case _ => ;
//log.warn(s"ChangeFireState_Stop: ${player.Name} never started firing item ${item_guid.guid} in the first place?")
}
progressBarUpdate.cancel()
progressBarValue = None
@ -5128,19 +5112,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
case msg @ WeaponFireMessage(
seq_time,
weapon_guid,
projectile_guid,
shot_origin,
unk1,
unk2,
unk3,
unk4,
unk5,
unk6,
unk7
) =>
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin)
_,
weapon_guid,
projectile_guid,
shot_origin,
_,
_,
_,
_, //max_distance,
_,
_, //projectile_type,
thrown_projectile_vel
) =>
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin, thrown_projectile_vel.flatten)
case msg @ WeaponLazeTargetPositionMessage(_, _, pos2) =>
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()
}
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 {
case (Some(obj), Some(tool)) =>
val projectileIndex = projectileGUID.guid - Projectile.baseUID
@ -9054,10 +9043,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
projectile_info,
tool.Definition,
tool.FireMode,
player,
PlayerSource(player),
attribution,
shotOrigin,
angle
angle,
shotVelocity
)
val initialQuality = tool.FireMode match {
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"
)
continent.tasks ! (if (projectile.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(
player.GUID,
projectile.GUID,
projectile
)
)
ReregisterProjectile(projectile)
} else {
RegisterProjectile(projectile)
})
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)
)
ReregisterProjectile(projectile)
} else {
RegisterProjectile(projectile)
})
}
projectilesToCleanUp(projectileIndex) = false

View file

@ -12,7 +12,7 @@ import net.psforever.objects.vital.base.DamageResolution
import net.psforever.types.Vector3
/**
* A summation of weapon (`Tool`) discharge.
* A summation of weapon discharge.
* @see `ProjectileDefinition`
* @see `ToolDefinition`
* @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)
* @param shot_origin where the projectile started
* @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 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;
* defaults to `System.currentTimeMillis()`
*/
@ -42,18 +43,20 @@ final case class Projectile(
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3,
shot_velocity: Option[Vector3],
quality: ProjectileQuality = ProjectileQuality.Normal,
id: Long = Projectile.idGenerator.getAndIncrement(),
fire_time: Long = System.currentTimeMillis()
) extends PlanetSideGameObject {
Position = shot_origin
Orientation = shot_angle
Velocity = {
val initVel: Int = profile.InitialVelocity //initial velocity
val radAngle: Double = math.toRadians(shot_angle.y) //angle of elevation
val rise: Float = initVel * math.sin(radAngle).toFloat //z
val ground: Float = initVel * math.cos(radAngle).toFloat //base
Vector3.Rz(Vector3(0, -ground, 0), shot_angle.z) + Vector3.z(rise)
Velocity = shot_velocity.getOrElse {
val radz = math.toRadians(shot_angle.z)
Vector3.Unit(Vector3(
math.sin(radz).toFloat,
math.cos(radz).toFloat,
math.sin(math.toRadians(shot_angle.y)).toFloat
)) * profile.InitialVelocity.toFloat
}
/** Information about the current world coordinates and orientation of the projectile */
@ -77,6 +80,7 @@ final case class Projectile(
attribute_to,
shot_origin,
shot_angle,
shot_velocity,
value,
id,
fire_time
@ -127,14 +131,14 @@ object Projectile {
* @return the `Projectile` object
*/
def apply(
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle)
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
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
*/
def apply(
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle)
profile: ProjectileDefinition,
tool_def: ToolDefinition,
fire_mode: FireModeDefinition,
owner: PlanetSideGameObject with FactionAffinity,
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3
): Projectile = {
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
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 scodec.Codec
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 weapon_guid
* @param projectile_guid
* @param shot_origin
* @param unk1 Always zero from testing so far
* @param unk2 Seems semi-random
* @param unk3 Seems semi-random
* @param unk4 Maximum travel distance in meters - seems to be zero for decimator rockets
* @param unk5 Possibly always 255 from testing
* @param unk6 0 for bullet
* 1 for possibly delayed explosion (thumper alt fire) or thresher/leviathan flux cannon
* 2 for vs starfire (lockon type?)
* 3 for thrown (e.g. grenades)
* @param unk7 Seems to be thrown weapon velocity/direction
* @param seq_time see [[PlayerStateMessageUpstream]] for explanation of seq_time
* @param weapon_guid the weapon of discharge;
* when dispatched to a client, an unreferenced entity results in the projectile not being rendered
* @param projectile_guid the (client-local) projectile unique identifier;
* when dispatched to a client, can be unreferenced (or blanked)
* @param shot_origin the position where the projectile is first spawned
* @param unk1 na;
* always 0?
* @param spread_a related to the spread of the discharge;
* works with `spread_b` field in unknown way;
* the unmodified value is high (65535) when accurate, low (0) when not
* @param spread_b related to the spread of the discharge;
* works with `spread_a` field in unknown way
* @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(
seq_time: Int,
weapon_guid: PlanetSideGUID,
projectile_guid: PlanetSideGUID,
shot_origin: Vector3,
unk1: Int,
unk2: Int,
unk3: Int,
unk4: Int,
unk5: Int,
unk6: Int,
unk7: Option[Option[Vector3]]
) extends PlanetSideGamePacket {
seq_time: Int,
weapon_guid: PlanetSideGUID,
projectile_guid: PlanetSideGUID,
shot_origin: Vector3,
unk1: Int,
spread_a: Int,
spread_b: Int,
max_distance: Int,
unk5: Int,
projectile_type: ProjectileCharacteristics,
thrown_projectile_vel: Option[Option[Vector3]]
) extends PlanetSideGamePacket {
type Packet = WeaponFireMessage
def opcode = GamePacketOpcode.WeaponFireMessage
def encode = WeaponFireMessage.encode(this)
@ -44,17 +81,17 @@ final case class WeaponFireMessage(
object WeaponFireMessage extends Marshallable[WeaponFireMessage] {
implicit val codec: Codec[WeaponFireMessage] = (
("seq_time" | uintL(10)) ::
("weapon_guid" | PlanetSideGUID.codec) ::
("projectile_guid" | PlanetSideGUID.codec) ::
("shot_origin" | Vector3.codec_pos) ::
("unk1" | uint16L) ::
("unk2" | uint16L) ::
("unk3" | uint16L) ::
("unk4" | uint16L) ::
("unk5" | uint8L) ::
(("unk6" | uintL(3)) >>:~ { unk6_value =>
conditional(unk6_value == 3, "unk7" | optional(bool, Vector3.codec_vel)).hlist
("seq_time" | uintL(bits = 10)) ::
("weapon_guid" | PlanetSideGUID.codec) ::
("projectile_guid" | PlanetSideGUID.codec) ::
("shot_origin" | Vector3.codec_pos) ::
("unk1" | uint16L) ::
("spread_a" | uint16L.xmap[Int]( { x => 65535 - x }, { x => 65535 - x })) :: //low when accurate, high when not
("spread_b" | uint16L) ::
("max_distance" | uint16L) ::
("unk5" | uint8L) ::
("projectile_type" | PacketHelpers.createIntEnumCodec(ProjectileCharacteristics, uint(bits = 3)) >>:~ { ptype =>
("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)
shot_origin mustEqual Vector3(3675.4688f, 2726.9922f, 92.921875f)
unk1 mustEqual 0
unk2 mustEqual 64294
unk2 mustEqual 1241
unk3 mustEqual 1502
unk4 mustEqual 200
unk5 mustEqual 255
unk6 mustEqual 0
unk7 mustEqual None
unk6 mustEqual ProjectileCharacteristics.Standard
unk7.isEmpty mustEqual true
case _ =>
ko
}
@ -48,11 +48,11 @@ class WeaponFireMessageTest extends Specification {
PlanetSideGUID(40100),
Vector3(3675.4688f, 2726.9922f, 92.921875f),
0,
64294,
1241,
1502,
200,
255,
0,
ProjectileCharacteristics.Standard,
None
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector