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:
FateJH 2019-05-17 01:54:27 -04:00
parent f1c73688f7
commit 8952ab86f6
10 changed files with 258 additions and 65 deletions

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
)
)
}

View file

@ -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)
}
)
}

View file

@ -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)
))
)

View file

@ -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

View file

@ -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

View file

@ -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)
}
}