mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
commit
76f283c617
|
|
@ -4121,46 +4121,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))
|
||||
|
|
@ -4169,18 +4147,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
|
||||
|
|
@ -5135,19 +5119,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})")
|
||||
|
|
@ -9016,7 +9000,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
|
||||
|
|
@ -9061,10 +9050,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 =>
|
||||
|
|
@ -9078,18 +9068,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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue