mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
events for future expansion; added custom projectile remote client data for OCM packet creation; initial projectile velocity calculation; (too many) details when it comes to Enumerations for projectiles; hooked up theoretical projectile creatiuon, management, and deletion
This commit is contained in:
parent
f1c73688f7
commit
8952ab86f6
|
|
@ -630,9 +630,7 @@ object GlobalDefinitions {
|
|||
|
||||
val maelstrom = ToolDefinition(ObjectClass.maelstrom)
|
||||
|
||||
val phoenix = new ToolDefinition(ObjectClass.phoenix) {
|
||||
override def NextFireModeIndex(index : Int) : Int = index
|
||||
} //decimator
|
||||
val phoenix = ToolDefinition(ObjectClass.phoenix) //decimator
|
||||
|
||||
val striker = new ToolDefinition(ObjectClass.striker) {
|
||||
override def NextFireModeIndex(index : Int) : Int = index
|
||||
|
|
@ -640,8 +638,8 @@ object GlobalDefinitions {
|
|||
}
|
||||
|
||||
val hunterseeker = new ToolDefinition(ObjectClass.hunterseeker) {
|
||||
// override def NextFireModeIndex(index : Int) : Int = index
|
||||
// DefaultFireModeIndex = 1
|
||||
override def NextFireModeIndex(index : Int) : Int = index
|
||||
DefaultFireModeIndex = 1
|
||||
} //phoenix
|
||||
|
||||
val lancer = ToolDefinition(ObjectClass.lancer)
|
||||
|
|
@ -2221,6 +2219,7 @@ object GlobalDefinitions {
|
|||
aphelion_starfire_projectile.Lifespan = 7f
|
||||
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||
aphelion_starfire_projectile.ExistsOnRemoteClients = true
|
||||
aphelion_starfire_projectile.RemoteClientData = (39577, 249) //starfire_projectile data
|
||||
aphelion_starfire_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile)
|
||||
|
||||
|
|
@ -2699,6 +2698,7 @@ object GlobalDefinitions {
|
|||
hunter_seeker_missile_projectile.InitialVelocity = 40
|
||||
hunter_seeker_missile_projectile.Lifespan = 6.3f
|
||||
hunter_seeker_missile_projectile.ExistsOnRemoteClients = true
|
||||
hunter_seeker_missile_projectile.RemoteClientData = (39577, 201)
|
||||
hunter_seeker_missile_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile)
|
||||
|
||||
|
|
@ -2986,6 +2986,7 @@ object GlobalDefinitions {
|
|||
oicw_projectile.InitialVelocity = 5
|
||||
oicw_projectile.Lifespan = 6.1f
|
||||
oicw_projectile.ExistsOnRemoteClients = true
|
||||
oicw_projectile.RemoteClientData = (13107, 195)
|
||||
oicw_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
|
||||
|
||||
|
|
@ -2997,7 +2998,7 @@ object GlobalDefinitions {
|
|||
oicw_little_buddy.ProjectileDamageType = DamageType.Splash
|
||||
oicw_little_buddy.InitialVelocity = 40
|
||||
oicw_little_buddy.Lifespan = 0.5f
|
||||
oicw_little_buddy.ExistsOnRemoteClients = true
|
||||
oicw_little_buddy.ExistsOnRemoteClients = false //TODO true
|
||||
oicw_little_buddy.Packet = projectileConverter
|
||||
//add_property oicw_little_buddy multi_stage_spawn_server_side true ...
|
||||
ProjectileDefinition.CalculateDerivedFields(oicw_little_buddy)
|
||||
|
|
@ -3080,6 +3081,7 @@ object GlobalDefinitions {
|
|||
peregrine_sparrow_projectile.InitialVelocity = 45
|
||||
peregrine_sparrow_projectile.Lifespan = 7.5f
|
||||
peregrine_sparrow_projectile.ExistsOnRemoteClients = true
|
||||
peregrine_sparrow_projectile.RemoteClientData = (13107, 187) //sparrow_projectile data
|
||||
peregrine_sparrow_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
|
||||
|
||||
|
|
@ -3133,6 +3135,7 @@ object GlobalDefinitions {
|
|||
phoenix_missile_guided_projectile.InitialVelocity = 0
|
||||
phoenix_missile_guided_projectile.Lifespan = 3f
|
||||
phoenix_missile_guided_projectile.ExistsOnRemoteClients = true
|
||||
phoenix_missile_guided_projectile.RemoteClientData = (39577, 201) //hunter_seeker_missile_projectile data
|
||||
phoenix_missile_guided_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
|
||||
|
||||
|
|
@ -3411,6 +3414,7 @@ object GlobalDefinitions {
|
|||
sparrow_projectile.InitialVelocity = 60
|
||||
sparrow_projectile.Lifespan = 5.85f
|
||||
sparrow_projectile.ExistsOnRemoteClients = true
|
||||
sparrow_projectile.RemoteClientData = (13107, 187)
|
||||
sparrow_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
|
||||
|
||||
|
|
@ -3427,6 +3431,7 @@ object GlobalDefinitions {
|
|||
sparrow_secondary_projectile.InitialVelocity = 60
|
||||
sparrow_secondary_projectile.Lifespan = 5.85f
|
||||
sparrow_secondary_projectile.ExistsOnRemoteClients = true
|
||||
sparrow_secondary_projectile.RemoteClientData = (13107, 187)
|
||||
sparrow_secondary_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
|
||||
|
||||
|
|
@ -3479,6 +3484,7 @@ object GlobalDefinitions {
|
|||
starfire_projectile.InitialVelocity = 45
|
||||
starfire_projectile.Lifespan = 7.8f
|
||||
starfire_projectile.ExistsOnRemoteClients = true
|
||||
starfire_projectile.RemoteClientData = (39577, 249)
|
||||
starfire_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
|
||||
|
||||
|
|
@ -3512,6 +3518,7 @@ object GlobalDefinitions {
|
|||
striker_missile_targeting_projectile.InitialVelocity = 30
|
||||
striker_missile_targeting_projectile.Lifespan = 4.2f
|
||||
striker_missile_targeting_projectile.ExistsOnRemoteClients = true
|
||||
striker_missile_targeting_projectile.RemoteClientData = (26214, 134)
|
||||
striker_missile_targeting_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile)
|
||||
|
||||
|
|
@ -3601,6 +3608,7 @@ object GlobalDefinitions {
|
|||
wasp_rocket_projectile.InitialVelocity = 60
|
||||
wasp_rocket_projectile.Lifespan = 6.5f
|
||||
wasp_rocket_projectile.ExistsOnRemoteClients = true
|
||||
wasp_rocket_projectile.RemoteClientData = (0, 208)
|
||||
wasp_rocket_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ final case class Projectile(profile : ProjectileDefinition,
|
|||
shot_origin : Vector3,
|
||||
shot_angle : Vector3,
|
||||
fire_time: Long = System.nanoTime) 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)
|
||||
}
|
||||
/** Information about the current world coordinates and orientation of the projectile */
|
||||
val current : SimpleWorldEntity = new SimpleWorldEntity()
|
||||
private var resolved : ProjectileResolution.Value = ProjectileResolution.Unresolved
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
private var damageRadius : Float = 1f
|
||||
private var useDamage1Subtract : Boolean = false
|
||||
private var existsOnRemoteClients : Boolean = false //`true` spawns a server-managed object
|
||||
private var remoteClientData : (Int, Int) = (0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
|
||||
//derived calculations
|
||||
private var distanceMax : Float = 0f
|
||||
private var distanceFromAcceleration : Float = 0f
|
||||
|
|
@ -117,6 +118,13 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
ExistsOnRemoteClients
|
||||
}
|
||||
|
||||
def RemoteClientData : (Int, Int) = remoteClientData
|
||||
|
||||
def RemoteClientData_=(remoteClientData : (Int, Int)) : (Int, Int) = {
|
||||
this.remoteClientData = remoteClientData
|
||||
RemoteClientData
|
||||
}
|
||||
|
||||
def DistanceMax : Float = distanceMax //accessor only
|
||||
|
||||
def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class ProjectileConverter extends ObjectCreateConverter[Projectile]() {
|
|||
PlacementData(
|
||||
obj.Position,
|
||||
obj.Orientation,
|
||||
obj.Velocity
|
||||
None
|
||||
),
|
||||
CommonFieldData(
|
||||
obj.owner.Faction,
|
||||
|
|
@ -29,11 +29,11 @@ class ProjectileConverter extends ObjectCreateConverter[Projectile]() {
|
|||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
obj.profile.RemoteClientData._1,
|
||||
obj.profile.RemoteClientData._2,
|
||||
FlightPhysics.State4,
|
||||
0,
|
||||
0,
|
||||
FlightPhysics.State3,
|
||||
7,
|
||||
2
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,56 @@
|
|||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
object TrackedProjectile extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val OICWLittleBuddy = Value(-1) //?, ?
|
||||
val Meteor = Value(32) //0, 32
|
||||
val Wasp = Value(208) //0, 208
|
||||
val Sparrow = Value(3355579) //13107, 187
|
||||
val OICW = Value(3355587) //13107, 195
|
||||
val Striker = Value(6710918) //26214, 134
|
||||
val HunterSeeker = Value(10131913) //39577, 201
|
||||
val Starfire = Value(10131961) //39577, 249
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint24)
|
||||
}
|
||||
|
||||
object TrackedProjectiles {
|
||||
abstract class Data(val is : TrackedProjectile.Value, val a : Int, val b : Int)
|
||||
|
||||
final case object Meteor extends Data(TrackedProjectile.Meteor, 0, 32)
|
||||
final case object Wasp extends Data(TrackedProjectile.Wasp, 0, 208)
|
||||
final case object Sparrow extends Data(TrackedProjectile.Sparrow, 13107, 187)
|
||||
final case object OICW extends Data(TrackedProjectile.OICW, 13107, 195)
|
||||
final case object Striker extends Data(TrackedProjectile.Striker, 26214, 134)
|
||||
final case object HunterSeeker extends Data(TrackedProjectile.HunterSeeker, 39577, 201)
|
||||
final case object Starfire extends Data(TrackedProjectile.Starfire, 39577, 249)
|
||||
class OICWLittleBuddy(x : Int, y : Int) extends Data(TrackedProjectile.OICWLittleBuddy, x, y)
|
||||
|
||||
val values: Seq[TrackedProjectiles.Data] = Seq(Meteor, Wasp, Sparrow, OICW, Striker, HunterSeeker, Starfire)
|
||||
|
||||
def apply(x : Int, y : Int) : TrackedProjectiles.Data = {
|
||||
values.find(p => p.a == x && p.b == y) match {
|
||||
case Some(projectileData) => projectileData
|
||||
case None =>
|
||||
throw new IllegalArgumentException("no combination of projectile data equates to a defined projectile type")
|
||||
}
|
||||
}
|
||||
|
||||
def apply(is : TrackedProjectile.Value) : TrackedProjectiles.Data = {
|
||||
values.find(p => p.is == is) match {
|
||||
case Some(projectileData) => projectileData
|
||||
case None =>
|
||||
throw new IllegalArgumentException("unknown projectile type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object FlightPhysics extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
|
|
@ -27,41 +73,38 @@ object FlightPhysics extends Enumeration {
|
|||
|
||||
/**
|
||||
* A representation of a projectile that the server must intentionally convey to players other than the shooter.
|
||||
* @param data common game object information
|
||||
* @param unk2 na
|
||||
* @param unit_distance_limit how quickly the projectile travels before naturally being destroyed
|
||||
* `FlightPhysics` needs to be
|
||||
* @param common_data common game object information
|
||||
* @param unk3 na
|
||||
*/
|
||||
final case class TrackedProjectileData(data : CommonFieldDataWithPlacement,
|
||||
unk2 : Int,
|
||||
unit_distance_limit : Int,
|
||||
final case class TrackedProjectileData(common_data : CommonFieldDataWithPlacement,
|
||||
u1 : Int,
|
||||
u2 : Int,
|
||||
unk3 : FlightPhysics.Value,
|
||||
unk4 : Int,
|
||||
unk5 : Int
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = 33L + data.bitsize
|
||||
override def bitsize : Long = 33L + common_data.bitsize
|
||||
}
|
||||
|
||||
object TrackedProjectileData extends Marshallable[TrackedProjectileData] {
|
||||
implicit val codec : Codec[TrackedProjectileData] = (
|
||||
("data" | CommonFieldDataWithPlacement.codec) ::
|
||||
("unk2" | uint16) ::
|
||||
("unit_distance_limit" | uint8) ::
|
||||
("u1" | uint16) ::
|
||||
("u2" | uint8) ::
|
||||
("unk3" | FlightPhysics.codec) ::
|
||||
("unk4" | uint(3)) ::
|
||||
("unk5" | uint2)
|
||||
).exmap[TrackedProjectileData] (
|
||||
{
|
||||
case data :: unk2 :: lim :: unk3 :: unk4 :: unk5 :: HNil =>
|
||||
Attempt.successful(TrackedProjectileData(data, unk2, lim, unk3, unk4, unk5))
|
||||
case data :: u1 :: u2 :: unk3 :: unk4 :: unk5 :: HNil =>
|
||||
Attempt.successful(TrackedProjectileData(data, u1, u2, unk3, unk4, unk5))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid projectile data format - $data"))
|
||||
// case data =>
|
||||
// Attempt.failure(Err(s"invalid projectile data format - $data"))
|
||||
},
|
||||
{
|
||||
case TrackedProjectileData(data, unk2, lim, unk3, unk4, unk5) =>
|
||||
Attempt.successful(data :: unk2 :: lim :: unk3 :: unk4 :: unk5 :: HNil)
|
||||
case TrackedProjectileData(data, u1, u2, unk3, unk4, unk5) =>
|
||||
Attempt.successful(data :: u1 :: u2 :: unk3 :: unk4 :: unk5 :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ class AvatarService extends Actor {
|
|||
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
|
||||
)
|
||||
)
|
||||
case AvatarAction.GenericObjectAction(player_guid, object_guid, action_code) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.GenericObjectAction(object_guid, action_code))
|
||||
)
|
||||
case AvatarAction.HitHint(source_guid, player_guid) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
|
||||
|
|
@ -129,7 +133,7 @@ class AvatarService extends Actor {
|
|||
)
|
||||
case AvatarAction.LoadProjectile(player_guid, object_id, obj, cdata) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadProjectile(
|
||||
ObjectCreateMessage(object_id, obj.GUID, cdata)
|
||||
))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ object AvatarAction {
|
|||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||
final case class GenericObjectAction(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, action_code : Int) extends Action
|
||||
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
||||
final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
|
||||
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ object AvatarResponse {
|
|||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
|
||||
final case class GenericObjectAction(object_guid : PlanetSideGUID, action_code : Int) extends Response
|
||||
final case class HitHint(source_guid : PlanetSideGUID) extends Response
|
||||
final case class KilledWhileInVehicle() extends Response
|
||||
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import scodec.bits._
|
|||
|
||||
class TrackedProjectileDataTest extends Specification {
|
||||
val string_striker_projectile = hex"17 C5000000 A4B 009D 4C129 0CB0A 9814 00 F5 E3 040000666686400"
|
||||
val string_hunter_seeker_missile_projectile = hex"17 c5000000 ca9 ab9e af127 ec465 3723 00 15 c4 2400009a99c9400"
|
||||
|
||||
"TrackedProjectileData" should {
|
||||
"decode" in {
|
||||
"decode (striker_missile_targeting_projectile)" in {
|
||||
PacketCoding.DecodePacket(string_striker_projectile).require match {
|
||||
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||
len mustEqual 197
|
||||
|
|
@ -46,7 +47,41 @@ class TrackedProjectileDataTest extends Specification {
|
|||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
"decode (hunter_seeker_missile_projectile)" in {
|
||||
PacketCoding.DecodePacket(string_hunter_seeker_missile_projectile).require match {
|
||||
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||
len mustEqual 197
|
||||
cls mustEqual ObjectClass.hunter_seeker_missile_projectile
|
||||
guid mustEqual PlanetSideGUID(40619)
|
||||
parent.isDefined mustEqual false
|
||||
data match {
|
||||
case TrackedProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, lim, unk3, unk4, unk5) =>
|
||||
pos.coord mustEqual Vector3(3621.3672f, 2701.8438f, 140.85938f)
|
||||
pos.orient mustEqual Vector3(0, 300.9375f, 258.75f)
|
||||
deploy.faction mustEqual PlanetSideEmpire.NC
|
||||
deploy.bops mustEqual false
|
||||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.v4.isEmpty mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(0)
|
||||
|
||||
unk2 mustEqual 39577
|
||||
lim mustEqual 201
|
||||
unk3 mustEqual FlightPhysics.State4
|
||||
unk4 mustEqual 0
|
||||
unk5 mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (striker_missile_targeting_projectile)" in {
|
||||
val obj = TrackedProjectileData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f),
|
||||
|
|
@ -63,4 +98,21 @@ class TrackedProjectileDataTest extends Specification {
|
|||
pkt.toBitVector.drop(141) mustEqual string_striker_projectile.toBitVector.drop(141)
|
||||
}
|
||||
}
|
||||
|
||||
"encode (hunter_seeker_missile_projectile)" in {
|
||||
val obj = TrackedProjectileData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(3621.3672f, 2701.8438f, 140.85938f, 0, 300.9375f, 258.75f),
|
||||
CommonFieldData(PlanetSideEmpire.NC, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||
),
|
||||
39577, 201, FlightPhysics.State4, 0, 0
|
||||
)
|
||||
val msg = ObjectCreateMessage(ObjectClass.hunter_seeker_missile_projectile, PlanetSideGUID(40619), obj)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
//pkt mustEqual string_hunter_seeker_missile_projectile
|
||||
|
||||
pkt.toBitVector.take(132) mustEqual string_hunter_seeker_missile_projectile.toBitVector.take(132)
|
||||
pkt.toBitVector.drop(133).take(7) mustEqual string_hunter_seeker_missile_projectile.toBitVector.drop(133).take(7)
|
||||
pkt.toBitVector.drop(141) mustEqual string_hunter_seeker_missile_projectile.toBitVector.drop(141)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1364,6 +1364,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(pkt)
|
||||
}
|
||||
|
||||
case AvatarResponse.GenericObjectAction(object_guid, action_code) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(GenericObjectActionMessage(object_guid, action_code))
|
||||
}
|
||||
|
||||
case AvatarResponse.HitHint(source_guid) =>
|
||||
if(player.isAlive) {
|
||||
sendResponse(HitHint(source_guid, guid))
|
||||
|
|
@ -3453,6 +3458,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
//all head features, faction, and sex also match that test character
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
import net.psforever.types.CertificationType._
|
||||
|
||||
val faction = PlanetSideEmpire.VS
|
||||
val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1)
|
||||
avatar.Certifications += StandardAssault
|
||||
|
|
@ -3893,6 +3899,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
||||
}
|
||||
|
||||
if(!player.Crouching && is_crouching) {
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
405,
|
||||
PlanetSideGUID(40288),
|
||||
TrackedProjectileData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(Vector3(3561.0f, 2854.0f, 92.859375f), Vector3(0f, 348.75f, 267.1875f), None),
|
||||
CommonFieldData(PlanetSideEmpire.NC, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||
),
|
||||
39577, 201, FlightPhysics.State4, 0, 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
else if(player.Crouching && !is_crouching) {
|
||||
sendResponse(
|
||||
ObjectDeleteMessage(PlanetSideGUID(40288), 2)
|
||||
//ProjectileStateMessage(PlanetSideGUID(40288),Vector3(3561.0f, 2854.0f, 92.859375f),Vector3(-38.814934f,-2.578959f,-9.313708f),Vector3(0,348.75f,267.1875f),false,0)
|
||||
)
|
||||
}
|
||||
player.Position = pos
|
||||
player.Velocity = vel
|
||||
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
||||
|
|
@ -4001,19 +4028,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive) =>
|
||||
log.info(s"ProjectileState: $msg")
|
||||
projectiles
|
||||
.collect {
|
||||
case Some(projectile) if projectile.HasGUID =>
|
||||
projectile
|
||||
}
|
||||
.find(_.GUID == projectile_guid) match {
|
||||
case Some(projectile) =>
|
||||
projectiles(projectile_guid.guid - Projectile.BaseUID) match {
|
||||
case Some(projectile) if projectile.HasGUID =>
|
||||
val projectileGlobalUID = projectile.GUID
|
||||
log.info(s"ProjectileState: mapped local uid ${projectile_guid.guid} to global uid ${projectileGlobalUID.guid}; updating object ...")
|
||||
projectile.Position = shot_pos
|
||||
projectile.Orientation = shot_orient
|
||||
projectile.Velocity = shot_vel
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileState(player.GUID, projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive))
|
||||
case None =>
|
||||
log.info(s"ProjectileState: the projectile GUID#${projectile_guid.guid} can not be found")
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileState(player.GUID, projectile.GUID, shot_pos, shot_vel, shot_orient, unk, time_alive))
|
||||
case _ =>
|
||||
log.error(s"ProjectileState: the projectile@${projectile_guid.guid} can not be found")
|
||||
}
|
||||
|
||||
case msg @ ReleaseAvatarRequestMessage() =>
|
||||
|
|
@ -5454,6 +5478,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val projectile = Projectile(projectile_info, tool.Definition, tool.FireMode, player, attribution, shot_origin, angle)
|
||||
projectiles(projectileIndex) = Some(projectile)
|
||||
if(projectile_info.ExistsOnRemoteClients) {
|
||||
log.trace(s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile")
|
||||
taskResolver ! ReregisterProjectile(projectile)
|
||||
}
|
||||
}
|
||||
|
|
@ -5491,26 +5516,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) =>
|
||||
log.info(s"Splash: $msg")
|
||||
continent.GUID(direct_victim_uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash, target, target.Position) match {
|
||||
case Some(projectile) =>
|
||||
HandleDealingDamage(target, projectile)
|
||||
case None => ;
|
||||
FindProjectileEntry(projectile_guid) match {
|
||||
case Some(projectile) =>
|
||||
continent.GUID(direct_victim_uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
|
||||
case Some(projectile) =>
|
||||
HandleDealingDamage(target, projectile)
|
||||
case None => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
targets.foreach(elem => {
|
||||
continent.GUID(elem.uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash, target, explosion_pos) match {
|
||||
case Some(projectile) =>
|
||||
HandleDealingDamage(target, projectile)
|
||||
case None => ;
|
||||
targets.foreach(elem => {
|
||||
continent.GUID(elem.uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match {
|
||||
case Some(projectile) =>
|
||||
HandleDealingDamage(target, projectile)
|
||||
case None => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
})
|
||||
})
|
||||
if(projectile.profile.ExistsOnRemoteClients) {
|
||||
//cleanup
|
||||
taskResolver ! UnregisterProjectile(projectile)
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
||||
log.info(s"Lash: $msg")
|
||||
|
|
@ -6077,6 +6110,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}, List(GUIDTask.RegisterAvatar(driver)(continent.GUID), GUIDTask.RegisterVehicle(obj)(continent.GUID)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that adds a completed but unregistered projectile into the scene.
|
||||
* After the projectile is registered to the curent zone's global unique identifier system,
|
||||
* all connected clients save for the one that registered it will be informed about the projectile's "creation."
|
||||
* @param obj the projectile to be registered
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def RegisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||
val definition = obj.Definition
|
||||
TaskResolver.GiveTask(
|
||||
|
|
@ -6126,12 +6166,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}, List(GUIDTask.UnregisterAvatar(driver)(continent.GUID), GUIDTask.UnregisterVehicle(obj)(continent.GUID)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that removes a formerly complete and currently registered projectile from the scene.
|
||||
* After the projectile is unregistered from the curent zone's global unique identifier system,
|
||||
* all connected clients save for the one that registered it will be informed about the projectile's "destruction."
|
||||
* @param obj the projectile to be unregistered
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def UnregisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||
TaskResolver.GiveTask(
|
||||
new Task() {
|
||||
private val globalProjectile = obj
|
||||
private val localAnnounce = avatarService
|
||||
private val localMsg = AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, obj.GUID))
|
||||
private val localMsg = AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, obj.GUID, 2))
|
||||
|
||||
override def isComplete : Task.Resolution.Value = {
|
||||
if(!globalProjectile.HasGUID) {
|
||||
|
|
@ -6192,6 +6239,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the projectile object is unregistered, register it.
|
||||
* If the projectile object is already registered, unregister it and then register it again.
|
||||
* @see `RegisterProjectile(Projectile)`
|
||||
* @see `UnregisterProjectile(Projectile)`
|
||||
* @param obj the projectile to be registered (a second time?)
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def ReregisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||
val reg = RegisterProjectile(obj)
|
||||
if(obj.HasGUID) {
|
||||
|
|
@ -8406,9 +8461,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
/**
|
||||
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
||||
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
|
||||
* The internal copy of the projectile is retained as merely `Resolved`
|
||||
* while the observed projectile is promoted to the suggested resolution status.
|
||||
* @param projectile the projectile object
|
||||
* @param index where the projectile was found
|
||||
* @param resolution the resolution status to promote the projectile
|
||||
|
|
@ -8419,8 +8471,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.error(s"expected projectile could not be found at $index; can not resolve")
|
||||
None
|
||||
}
|
||||
else if(projectile.isMiss) {
|
||||
log.error(s"expected projectile at $index was already counted as a missed shot; can not resolve any further")
|
||||
else {
|
||||
ResolveProjectileEntry(projectile, resolution, target, pos)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
||||
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
|
||||
* The internal copy of the projectile is retained as merely `Resolved`
|
||||
* while the observed projectile is promoted to the suggested resolution status.
|
||||
* @param projectile the projectile object
|
||||
* @param resolution the resolution status to promote the projectile
|
||||
* @return a copy of the projectile
|
||||
*/
|
||||
def ResolveProjectileEntry(projectile : Projectile, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
||||
if(projectile.isMiss) {
|
||||
log.error("expected projectile was already counted as a missed shot; can not resolve any further")
|
||||
None
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue