diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 09be1073..acaf71e6 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -1950,6 +1950,8 @@ object GlobalDefinitions { * Initialize `ProjectileDefinition` globals. */ private def init_projectile() : Unit = { + val projectileConverter : ProjectileConverter = new ProjectileConverter + bullet_105mm_projectile.Name = "105mmbullet_projectile" bullet_105mm_projectile.Damage0 = 150 bullet_105mm_projectile.Damage1 = 300 @@ -2220,6 +2222,7 @@ object GlobalDefinitions { aphelion_starfire_projectile.Lifespan = 7f aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated aphelion_starfire_projectile.ExistsOnRemoteClients = true + aphelion_starfire_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile) bolt_projectile.Name = "bolt_projectile" @@ -2697,6 +2700,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.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile) jammer_cartridge_projectile.Name = "jammer_cartridge_projectile" @@ -2983,6 +2987,7 @@ object GlobalDefinitions { oicw_projectile.InitialVelocity = 5 oicw_projectile.Lifespan = 6.1f oicw_projectile.ExistsOnRemoteClients = true + oicw_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(oicw_projectile) oicw_little_buddy.Name = "oicw_projectile" @@ -2994,6 +2999,7 @@ object GlobalDefinitions { oicw_little_buddy.InitialVelocity = 40 oicw_little_buddy.Lifespan = 0.5f oicw_little_buddy.ExistsOnRemoteClients = true + oicw_little_buddy.Packet = projectileConverter //add_property oicw_little_buddy multi_stage_spawn_server_side true ... ProjectileDefinition.CalculateDerivedFields(oicw_little_buddy) @@ -3075,6 +3081,7 @@ object GlobalDefinitions { peregrine_sparrow_projectile.InitialVelocity = 45 peregrine_sparrow_projectile.Lifespan = 7.5f peregrine_sparrow_projectile.ExistsOnRemoteClients = true + peregrine_sparrow_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile) phalanx_av_projectile.Name = "phalanx_av_projectile" @@ -3126,6 +3133,8 @@ object GlobalDefinitions { phoenix_missile_guided_projectile.ProjectileDamageType = DamageType.Splash phoenix_missile_guided_projectile.InitialVelocity = 0 phoenix_missile_guided_projectile.Lifespan = 3f + phoenix_missile_guided_projectile.ExistsOnRemoteClients = true + phoenix_missile_guided_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile) phoenix_missile_projectile.Name = "phoenix_missile_projectile" @@ -3403,6 +3412,7 @@ object GlobalDefinitions { sparrow_projectile.InitialVelocity = 60 sparrow_projectile.Lifespan = 5.85f sparrow_projectile.ExistsOnRemoteClients = true + sparrow_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(sparrow_projectile) sparrow_secondary_projectile.Name = "sparrow_secondary_projectile" @@ -3418,6 +3428,7 @@ object GlobalDefinitions { sparrow_secondary_projectile.InitialVelocity = 60 sparrow_secondary_projectile.Lifespan = 5.85f sparrow_secondary_projectile.ExistsOnRemoteClients = true + sparrow_secondary_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile) spiker_projectile.Name = "spiker_projectile" @@ -3469,6 +3480,7 @@ object GlobalDefinitions { starfire_projectile.InitialVelocity = 45 starfire_projectile.Lifespan = 7.8f starfire_projectile.ExistsOnRemoteClients = true + starfire_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(starfire_projectile) striker_missile_projectile.Name = "striker_missile_projectile" @@ -3501,6 +3513,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.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile) trek_projectile.Name = "trek_projectile" @@ -3589,6 +3602,7 @@ object GlobalDefinitions { wasp_rocket_projectile.InitialVelocity = 60 wasp_rocket_projectile.Lifespan = 6.5f wasp_rocket_projectile.ExistsOnRemoteClients = true + wasp_rocket_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile) winchester_projectile.Name = "winchester_projectile" diff --git a/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala index fdc9ad8e..136c20bd 100644 --- a/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala @@ -35,7 +35,7 @@ final case class Projectile(profile : ProjectileDefinition, attribute_to : Int, shot_origin : Vector3, shot_angle : Vector3, - fire_time: Long = System.nanoTime) { + fire_time: Long = System.nanoTime) extends PlanetSideGameObject { /** Information about the current world coordinates and orientation of the projectile */ val current : SimpleWorldEntity = new SimpleWorldEntity() private var resolved : ProjectileResolution.Value = ProjectileResolution.Unresolved @@ -54,6 +54,8 @@ final case class Projectile(profile : ProjectileDefinition, def isResolved : Boolean = resolved == ProjectileResolution.Resolved || resolved == ProjectileResolution.MissedShot def isMiss : Boolean = resolved == ProjectileResolution.MissedShot + + def Definition = profile } object Projectile { diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/ProjectileConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/ProjectileConverter.scala new file mode 100644 index 00000000..a1534461 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/definition/converter/ProjectileConverter.scala @@ -0,0 +1,43 @@ +// Copyright (c) 2019 PSForever +package net.psforever.objects.definition.converter + +import net.psforever.objects.ballistics.Projectile +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.packet.game.objectcreate.{CommonFieldData, CommonFieldDataWithPlacement, FlightPhysics, PlacementData, TrackedProjectileData} + +import scala.util.{Failure, Success, Try} + +class ProjectileConverter extends ObjectCreateConverter[Projectile]() { + override def ConstructorData(obj : Projectile) : Try[TrackedProjectileData] = { + Success( + TrackedProjectileData( + CommonFieldDataWithPlacement( + PlacementData( + obj.Position, + obj.Orientation, + obj.Velocity + ), + CommonFieldData( + obj.owner.Faction, + false, + false, + true, + None, + false, + None, + None, + PlanetSideGUID(0) + ) + ), + 0, + 0, + FlightPhysics.State3, + 7, + 2 + ) + ) + } + + override def DetailedConstructorData(obj : Projectile) : Try[TrackedProjectileData] = + Failure(new Exception("ProjectileConverter should not be used to generate detailed projectile data (nothing should)")) +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala index 7308f84e..e3318427 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala @@ -295,7 +295,7 @@ object ObjectClass { final val portable_manned_turret_tr = 687 final val portable_manned_turret_vs = 688 //projectiles - final val hunter_seeker_missile_projectile = 405 + final val hunter_seeker_missile_projectile = 405 //phoenix projectile final val meteor_common = 543 final val meteor_projectile_b_large = 544 final val meteor_projectile_b_medium = 545 @@ -303,13 +303,14 @@ object ObjectClass { final val meteor_projectile_large = 547 final val meteor_projectile_medium = 548 final val meteor_projectile_small = 549 - final val oicw_little_buddy = 601 - final val oicw_projectile = 602 + final val phoenix_missile_guided_projectile = 675 //decimator projectile + final val oicw_little_buddy = 601 //scorpion projectile's projectiles + final val oicw_projectile = 602 //scorpion projectile final val radiator_cload = 717 - final val sparrow_projectile = 792 - final val starfire_projectile = 831 - final val striker_missile_targeting_projectile = 841 - final val wasp_rocket_projectile = 1001 + final val sparrow_projectile = 792 //nc aa max projectile + final val starfire_projectile = 831 //vs aa max projectile + final val striker_missile_targeting_projectile = 841 //striker projectile + final val wasp_rocket_projectile = 1001 //wasp projectile //vehicles final val apc_destroyed = 65 final val apc_tr = 67 //juggernaut @@ -1230,6 +1231,7 @@ object ObjectClass { case ObjectClass.meteor_projectile_large => ConstructorData(TrackedProjectileData.codec, "meteor") case ObjectClass.meteor_projectile_medium => ConstructorData(TrackedProjectileData.codec, "meteor") case ObjectClass.meteor_projectile_small => ConstructorData(TrackedProjectileData.codec, "meteor") + case ObjectClass.phoenix_missile_guided_projectile => ConstructorData(TrackedProjectileData.codec, "projectile") case ObjectClass.oicw_little_buddy => ConstructorData(TrackedProjectileData.codec, "projectile") case ObjectClass.oicw_projectile => ConstructorData(TrackedProjectileData.codec, "projectile") case ObjectClass.sparrow_projectile => ConstructorData(TrackedProjectileData.codec, "projectile") diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/TrackedProjectileData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/TrackedProjectileData.scala index 89daa598..a70e2845 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/TrackedProjectileData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/TrackedProjectileData.scala @@ -1,20 +1,44 @@ // Copyright (c) 2017 PSForever package net.psforever.packet.game.objectcreate -import net.psforever.packet.Marshallable +import net.psforever.packet.{Marshallable, PacketHelpers} import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} +object FlightPhysics extends Enumeration { + type Type = Value + + //valid (extremely small distance) (requires non-zero unk4, unk5) + val State3 = Value(3) + //valid (infinite) (if unk4 == 0 unk5 == 0, minimum distance + time) + val State4 = Value(4) + //valid(infinite) + val State5 = Value(5) + //valid (uses velocity) (infinite) + val State6 = Value(6) + //valid (uses velocity) (infinite) + val State7 = Value(7) + //valid (uses velocity) (time > 0 is infinite) (unk5 == 2) + val State15 = Value(15) + + implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) +} + /** * A representation of a projectile that the server must intentionally convey to players other than the shooter. - * @param data na + * @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 unk3 na */ final case class TrackedProjectileData(data : CommonFieldDataWithPlacement, unk2 : Int, - unk3 : Int = 0 + unit_distance_limit : Int, + unk3 : FlightPhysics.Value, + unk4 : Int, + unk5 : Int ) extends ConstructorData { override def bitsize : Long = 33L + data.bitsize } @@ -22,21 +46,22 @@ final case class TrackedProjectileData(data : CommonFieldDataWithPlacement, object TrackedProjectileData extends Marshallable[TrackedProjectileData] { implicit val codec : Codec[TrackedProjectileData] = ( ("data" | CommonFieldDataWithPlacement.codec) :: - ("unk2" | uint24) :: - uint4 :: - uint(3) :: - uint2 + ("unk2" | uint16) :: + ("unit_distance_limit" | uint8) :: + ("unk3" | FlightPhysics.codec) :: + ("unk4" | uint(3)) :: + ("unk5" | uint2) ).exmap[TrackedProjectileData] ( { - case data :: unk2 :: 4 :: unk3 :: 0 :: HNil => - Attempt.successful(TrackedProjectileData(data, unk2, unk3)) + case data :: unk2 :: lim :: unk3 :: unk4 :: unk5 :: HNil => + Attempt.successful(TrackedProjectileData(data, unk2, lim, unk3, unk4, unk5)) case data => Attempt.failure(Err(s"invalid projectile data format - $data")) }, { - case TrackedProjectileData(data, unk2, unk3) => - Attempt.successful(data :: unk2 :: 4 :: unk3 :: 0 :: HNil) + case TrackedProjectileData(data, unk2, lim, unk3, unk4, unk5) => + Attempt.successful(data :: unk2 :: lim :: unk3 :: unk4 :: unk5 :: HNil) } ) } diff --git a/common/src/test/scala/game/objectcreate/TrackedProjectileDataTest.scala b/common/src/test/scala/game/objectcreate/TrackedProjectileDataTest.scala index def883bd..586345bd 100644 --- a/common/src/test/scala/game/objectcreate/TrackedProjectileDataTest.scala +++ b/common/src/test/scala/game/objectcreate/TrackedProjectileDataTest.scala @@ -20,7 +20,7 @@ class TrackedProjectileDataTest extends Specification { guid mustEqual PlanetSideGUID(40192) parent.isDefined mustEqual false data match { - case TrackedProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, unk3) => + case TrackedProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, lim, unk3, unk4, unk5) => pos.coord mustEqual Vector3(4644.5938f, 5472.0938f, 82.375f) pos.orient mustEqual Vector3(0, 30.9375f, 171.5625f) deploy.faction mustEqual PlanetSideEmpire.TR @@ -33,9 +33,11 @@ class TrackedProjectileDataTest extends Specification { deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(0) - unk2 mustEqual 6710918 - - unk3 mustEqual 0 + unk2 mustEqual 26214 + lim mustEqual 134 + unk3 mustEqual FlightPhysics.State4 + unk4 mustEqual 0 + unk5 mustEqual 0 case _ => ko } @@ -50,11 +52,11 @@ class TrackedProjectileDataTest extends Specification { PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f), CommonFieldData(PlanetSideEmpire.TR, false, false, true, None, false, None, None, PlanetSideGUID(0)) ), - 6710918, - 0 + 26214, 134, FlightPhysics.State4, 0, 0 ) val msg = ObjectCreateMessage(ObjectClass.striker_missile_targeting_projectile, PlanetSideGUID(40192), obj) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + //pkt mustEqual string_striker_projectile pkt.toBitVector.take(132) mustEqual string_striker_projectile.toBitVector.take(132) pkt.toBitVector.drop(133).take(7) mustEqual string_striker_projectile.toBitVector.drop(133).take(7)