From c57999d676723ebdf5d603cc36888ccc169b0dd6 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 13 Jun 2018 07:34:31 -0400 Subject: [PATCH] basic projectiles tracked in WSA - generated (WeaponFire), queued, and marked as resolved through any of four packets (RequestDestroy, Hit, Splash, and Lash) --- .../psforever/objects/GlobalDefinitions.scala | 1 + .../net/psforever/objects/Projectile.scala | 42 +++++++++++++ .../scala/net/psforever/objects/Tool.scala | 1 + .../DamageProfile.scala | 2 +- .../DamageType.scala | 2 +- .../ballistics/ProjectileResolution.scala | 13 ++++ .../Projectiles.scala | 2 +- .../definition/ProjectileDefinition.scala | 2 +- .../objects/entity/SimpleWorldEntity.scala | 4 +- .../equipment/FireModeDefinition.scala | 1 + .../src/main/scala/WorldSessionActor.scala | 60 ++++++++++++++++--- 11 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/Projectile.scala rename common/src/main/scala/net/psforever/objects/{equipment => ballistics}/DamageProfile.scala (89%) rename common/src/main/scala/net/psforever/objects/{equipment => ballistics}/DamageType.scala (85%) create mode 100644 common/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala rename common/src/main/scala/net/psforever/objects/{equipment => ballistics}/Projectiles.scala (99%) diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index d09d152e..c95aba14 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -14,6 +14,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.tube.SpawnTubeDefinition import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition +import net.psforever.objects.ballistics.{DamageType, Projectiles} import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType} import net.psforever.types.PlanetSideEmpire diff --git a/common/src/main/scala/net/psforever/objects/Projectile.scala b/common/src/main/scala/net/psforever/objects/Projectile.scala new file mode 100644 index 00000000..0ee77c7c --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/Projectile.scala @@ -0,0 +1,42 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects + +import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition} +import net.psforever.objects.entity.SimpleWorldEntity +import net.psforever.objects.ballistics.ProjectileResolution +import net.psforever.types.Vector3 + +final case class Projectile(profile : ProjectileDefinition, + tool_def : ToolDefinition, + pos : Vector3, + orient : Vector3, + resolution : ProjectileResolution.Value, + fire_time : Long = System.nanoTime, + hit_time : Long = 0) { + val current : SimpleWorldEntity = new SimpleWorldEntity() + + def Resolve(hitPos : Vector3, hitAng : Vector3, hitVel : Vector3, resolution : ProjectileResolution.Value) : Projectile = { + val obj = Resolve(resolution) + obj.current.Position = hitPos + obj.current.Orientation = hitAng + obj.current.Velocity = hitVel + obj + } + + def Resolve(resolution : ProjectileResolution.Value) : Projectile = { + resolution match { + case ProjectileResolution.Unresolved => + this + case _ => + Projectile(profile, tool_def, pos, orient, resolution, fire_time, System.nanoTime) + } + } +} + +object Projectile { + final val BaseUID : Int = 40100 + + def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, pos : Vector3, orient : Vector3) : Projectile = { + Projectile(profile, tool_def, pos, orient, ProjectileResolution.Unresolved) + } +} diff --git a/common/src/main/scala/net/psforever/objects/Tool.scala b/common/src/main/scala/net/psforever/objects/Tool.scala index 16110334..3aa92530 100644 --- a/common/src/main/scala/net/psforever/objects/Tool.scala +++ b/common/src/main/scala/net/psforever/objects/Tool.scala @@ -3,6 +3,7 @@ package net.psforever.objects import net.psforever.objects.definition.{AmmoBoxDefinition, ProjectileDefinition, ToolDefinition} import net.psforever.objects.equipment._ +import net.psforever.objects.ballistics.Projectiles import scala.annotation.tailrec diff --git a/common/src/main/scala/net/psforever/objects/equipment/DamageProfile.scala b/common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala similarity index 89% rename from common/src/main/scala/net/psforever/objects/equipment/DamageProfile.scala rename to common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala index de7da147..4f4b2b46 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/DamageProfile.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects.equipment +package net.psforever.objects.ballistics trait DamageProfile { def Damage0 : Int diff --git a/common/src/main/scala/net/psforever/objects/equipment/DamageType.scala b/common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala similarity index 85% rename from common/src/main/scala/net/psforever/objects/equipment/DamageType.scala rename to common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala index af088e45..dbbf59a4 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/DamageType.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects.equipment +package net.psforever.objects.ballistics /** * An `Enumeration` of the damage type. diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala new file mode 100644 index 00000000..85fc958b --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala @@ -0,0 +1,13 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.ballistics + +object ProjectileResolution extends Enumeration { + type Type = Value + + val + Unresolved, + MissedShot, + Target, + Obstacle + = Value +} diff --git a/common/src/main/scala/net/psforever/objects/equipment/Projectiles.scala b/common/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala similarity index 99% rename from common/src/main/scala/net/psforever/objects/equipment/Projectiles.scala rename to common/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala index 11e73fd7..45ed97d4 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/Projectiles.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects.equipment +package net.psforever.objects.ballistics /** * An `Enumeration` of all the projectile types in the game, paired with their object id as the `Value`. diff --git a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala index f6246153..def0b20b 100644 --- a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.definition -import net.psforever.objects.equipment.{DamageProfile, DamageType, Projectiles} +import net.psforever.objects.ballistics.{DamageProfile, DamageType, Projectiles} class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) with DamageProfile { private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException diff --git a/common/src/main/scala/net/psforever/objects/entity/SimpleWorldEntity.scala b/common/src/main/scala/net/psforever/objects/entity/SimpleWorldEntity.scala index 704dab21..de2c96c0 100644 --- a/common/src/main/scala/net/psforever/objects/entity/SimpleWorldEntity.scala +++ b/common/src/main/scala/net/psforever/objects/entity/SimpleWorldEntity.scala @@ -4,8 +4,8 @@ package net.psforever.objects.entity import net.psforever.types.Vector3 class SimpleWorldEntity extends WorldEntity { - private var coords : Vector3 = Vector3(0f, 0f, 0f) - private var orient : Vector3 = Vector3(0f, 0f, 0f) + private var coords : Vector3 = Vector3.Zero + private var orient : Vector3 = Vector3.Zero private var vel : Option[Vector3] = None def Position : Vector3 = coords diff --git a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala index e6715fc0..50ae85ba 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala @@ -2,6 +2,7 @@ package net.psforever.objects.equipment import net.psforever.objects.Tool +import net.psforever.objects.ballistics.DamageProfile import scala.collection.mutable diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 3897d94e..16c70c62 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -53,6 +53,7 @@ import scala.concurrent.Future import scala.concurrent.duration._ import scala.util.Success import akka.pattern.ask +import net.psforever.objects.ballistics.ProjectileResolution class WorldSessionActor extends Actor with MDCContextAware { import WorldSessionActor._ @@ -84,6 +85,9 @@ class WorldSessionActor extends Actor with MDCContextAware { var traveler : Traveler = null var deadState : DeadState.Value = DeadState.Dead var whenUsedLastKit : Long = 0 + /** the client rotates between 40100 -- 40124 for projectiles normally, skipping only for long-lived projectiles + * 40125 -- 40149 is being reserved as a (potential) guard against overflow */ + val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](50)(None) var amsSpawnPoint : Option[SpawnTube] = None @@ -2586,7 +2590,6 @@ class WorldSessionActor extends Actor with MDCContextAware { else if((player.DrawnSlot = held_holsters) != before) { avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot)) - // Ignore non-equipment holsters //todo: check current suit holster slots? if(held_holsters >= 0 && held_holsters < 5) { @@ -2598,7 +2601,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } case None => ; } - } // Stop using proximity terminals if player unholsters a weapon (which should re-trigger the proximity effect and re-holster the weapon) @@ -2688,7 +2690,9 @@ class WorldSessionActor extends Actor with MDCContextAware { log.warn(s"RequestDestroy: not allowed to delete object $thing") case None => - log.warn(s"RequestDestroy: object $object_guid not found") + if(ResolveProjectileEntry(object_guid.guid - Projectile.BaseUID, ProjectileResolution.MissedShot).isEmpty) { + log.warn(s"RequestDestroy: object $object_guid not found") + } } case msg @ ObjectDeleteMessage(object_guid, unk1) => @@ -3137,8 +3141,8 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) => log.info("WeaponFire: " + msg) - FindWeapon match { - case Some(tool : Tool) => + FindContainedWeapon match { + case (Some(obj), Some(tool : Tool)) => if(tool.Magazine <= 0) { //safety: enforce ammunition depletion tool.Magazine = 0 sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, weapon_guid, 0)) @@ -3149,7 +3153,17 @@ class WorldSessionActor extends Actor with MDCContextAware { } else { //shooting tool.Discharge - //TODO other stuff? + val projectileIndex = projectile_guid.guid - Projectile.BaseUID + val ang = obj match { + case _ : Player => + obj.Orientation //TODO upper body facing + case _ : Vehicle => + tool.Orientation //TODO this is too simplistic + case _ => + Vector3.Zero + } + projectiles(projectileIndex) = + Some(Projectile(tool.Projectile, tool.Definition, shot_origin, ang)) } case _ => ; } @@ -3158,10 +3172,16 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info("Lazing position: " + pos2.toString) case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) => - log.info("Hit: " + msg) + log.info(s"Hit: $msg") + ResolveProjectileEntry(projectile_guid.guid - Projectile.BaseUID, ProjectileResolution.Target) - case msg @ SplashHitMessage(unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8) => - log.info("SplashHitMessage: " + msg) + case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) => + log.info(s"Splash: $msg") + ResolveProjectileEntry(projectile_guid.guid - Projectile.BaseUID, ProjectileResolution.Target) + + case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) => + log.info(s"Lash: $msg") + ResolveProjectileEntry(projectile_guid.guid - Projectile.BaseUID, ProjectileResolution.Target) case msg @ AvatarFirstTimeEventMessage(avatar_guid, object_guid, unk1, event_name) => log.info("AvatarFirstTimeEvent: " + msg) @@ -5229,6 +5249,28 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + def ResolveProjectileEntry(index : Int, resolution : ProjectileResolution.Value) : Option[Projectile] = { + if(0 <= index && index < projectiles.length) { + val entry = projectiles(index) + entry match { + case None => + log.warn(s"ResolveProjectile: expected projectile, but ${Projectile.BaseUID + index} not found") + None + case Some(projectile) => + projectile.resolution match { + case ProjectileResolution.Unresolved => ; + case _ => + log.warn(s"ResolveProjectileEntry: ${projectile.profile.ProjectileType} projectile found but reports to already be resolved") + } + projectiles(index) = Some(projectile.Resolve(resolution)) + projectiles(index) + } + } + else { + None + } + } + def failWithError(error : String) = { log.error(error) sendResponse(ConnectionClose())