diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 5e1eaeeb..934c1ca2 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2160,6 +2160,7 @@ object GlobalDefinitions { aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated aphelion_starfire_projectile.ExistsOnRemoteClients = true aphelion_starfire_projectile.RemoteClientData = (39577, 249) //starfire_projectile data + aphelion_starfire_projectile.AutoLock = true aphelion_starfire_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile) @@ -3022,6 +3023,7 @@ object GlobalDefinitions { peregrine_sparrow_projectile.Lifespan = 7.5f peregrine_sparrow_projectile.ExistsOnRemoteClients = true peregrine_sparrow_projectile.RemoteClientData = (13107, 187) //sparrow_projectile data + peregrine_sparrow_projectile.AutoLock = true peregrine_sparrow_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile) @@ -3355,6 +3357,7 @@ object GlobalDefinitions { sparrow_projectile.Lifespan = 5.85f sparrow_projectile.ExistsOnRemoteClients = true sparrow_projectile.RemoteClientData = (13107, 187) + sparrow_projectile.AutoLock = true sparrow_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(sparrow_projectile) @@ -3372,6 +3375,7 @@ object GlobalDefinitions { sparrow_secondary_projectile.Lifespan = 5.85f sparrow_secondary_projectile.ExistsOnRemoteClients = true sparrow_secondary_projectile.RemoteClientData = (13107, 187) + sparrow_secondary_projectile.AutoLock = true sparrow_secondary_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile) @@ -3425,6 +3429,7 @@ object GlobalDefinitions { starfire_projectile.Lifespan = 7.8f starfire_projectile.ExistsOnRemoteClients = true starfire_projectile.RemoteClientData = (39577, 249) + starfire_projectile.AutoLock = true starfire_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(starfire_projectile) @@ -3459,6 +3464,7 @@ object GlobalDefinitions { striker_missile_targeting_projectile.Lifespan = 4.2f striker_missile_targeting_projectile.ExistsOnRemoteClients = true striker_missile_targeting_projectile.RemoteClientData = (26214, 134) + striker_missile_targeting_projectile.AutoLock = true striker_missile_targeting_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile) @@ -3549,6 +3555,7 @@ object GlobalDefinitions { wasp_rocket_projectile.Lifespan = 6.5f wasp_rocket_projectile.ExistsOnRemoteClients = true wasp_rocket_projectile.RemoteClientData = (0, 208) + wasp_rocket_projectile.AutoLock = true wasp_rocket_projectile.Packet = projectileConverter ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile) 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 9d221d63..e1069612 100644 --- a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala @@ -25,6 +25,7 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) 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) + private var autoLock : Boolean = false //derived calculations private var distanceMax : Float = 0f private var distanceFromAcceleration : Float = 0f @@ -125,6 +126,13 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) RemoteClientData } + def AutoLock : Boolean = autoLock + + def AutoLock_=(lockState : Boolean) : Boolean = { + autoLock = lockState + AutoLock + } + def DistanceMax : Float = distanceMax //accessor only def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only diff --git a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala index 8c1ffd30..10a097b1 100644 --- a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala @@ -22,17 +22,15 @@ import shapeless.{::, HNil} * This packet will continue to be dispatched by the client for as long as the projectile being tracked is in the air. * All projectiles have a maximum lifespan before they will lose control and either despawn and/or explode. * This number is tracked in the packet for simplicity. - * If the projectile strikes a valid target, the count will jump to a significantly enormous value beyond its normal lifespan. - * This ensures that the projectile - locally and the shared model - will despawn. *
- * This control can not be exerted until that projectile is physically constructed on the other clients + * This control can not be demonstrated until that projectile is physically constructed on the other clients * in the same way that a player or a vehicle is constructed. * A projectile that exhibits intentional construction behavior is flagged using the property `exists_on_remote_client`. * The model comes with a number of caveats, * some that originate from the object construction process itself, * but also some from this packet. * For example, - * as indicated by the static `shot_orient` values reported by this packet. + * as indicated by the static `shot_original_orient` values reported by this packet. * a discharged controlled projectile will not normally rotate. * A minor loss of lifespan may be levied. * @see `ProjectileDefinition` @@ -41,18 +39,19 @@ import shapeless.{::, HNil} * when dispatched by the server, the global unique identifier for the synchronized projectile object * @param shot_pos the position of the projectile * @param shot_vel the velocity of the projectile - * @param shot_orient the orientation of the projectile - * @param unk na; - * usually `false` - * @param progress a measure of how long the projectile has been in the air; - * often expressed in multiples of 2 + * @param shot_original_orient the orientation of the projectile when it was discharged + * @param sequence_num an incrementing index of the packet in this projectile's lifetime; + * suggests the "time alive" and indicates a place in packet ordering + * @param explode indicates the projectile should explode + * @param unk na */ final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, - shot_orient : Vector3, - unk : Boolean, - progress : Int) + shot_original_orient : Vector3, + sequence_num : Int, + explode : Boolean, + unk : Int) extends PlanetSideGamePacket { type Packet = ProjectileStateMessage def opcode = GamePacketOpcode.ProjectileStateMessage @@ -67,16 +66,17 @@ object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] { ("roll" | Angular.codec_roll) :: ("pitch" | Angular.codec_pitch) :: ("yaw" | Angular.codec_yaw()) :: - ("unk" | bool) :: - ("progress" | uint16L) + ("sequence_num" | uint8) :: + ("explode" | bool) :: + ("unk" | uint16L) ).xmap[ProjectileStateMessage] ( { - case guid :: pos :: vel :: roll :: pitch :: yaw :: unk :: progress :: HNil => - ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), unk, progress) + case guid :: pos :: vel :: roll :: pitch :: yaw :: sequence_num :: explode :: unk :: HNil => + ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), sequence_num , explode, unk) }, { - case ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), unk, progress) => - guid :: pos :: vel :: roll :: pitch :: yaw :: unk :: progress :: HNil + case ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), sequence_num, explode, unk) => + guid :: pos :: vel :: roll :: pitch :: yaw :: sequence_num :: explode :: unk :: HNil } ) } diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala index d47627b6..98c9705d 100644 --- a/common/src/main/scala/services/avatar/AvatarService.scala +++ b/common/src/main/scala/services/avatar/AvatarService.scala @@ -2,7 +2,7 @@ package services.avatar import akka.actor.{Actor, ActorRef, Props} -import net.psforever.packet.game.ObjectCreateMessage +import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID} import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData} import services.avatar.support.{CorpseRemovalActor, DroppedItemRemover} import services.{GenericEventBus, RemoverActor, Service} @@ -161,13 +161,21 @@ class AvatarService extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand)) ) + case AvatarAction.ProjectileAutoLockAwareness(mode) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", PlanetSideGUID(0), AvatarResponse.ProjectileAutoLockAwareness(mode)) + ) + case AvatarAction.ProjectileTrackingAwareness(guid) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", PlanetSideGUID(0), AvatarResponse.ProjectileTrackingAwareness(guid)) + ) case AvatarAction.ProjectileExplodes(player_guid, projectile_guid, projectile) => AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileExplodes(projectile_guid, projectile)) ) - case AvatarAction.ProjectileState(player_guid, projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive) => + case AvatarAction.ProjectileState(player_guid, projectile_guid, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3) => AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive)) + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3)) ) case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) => janitor forward RemoverActor.ClearSpecific(List(item), zone) diff --git a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala index 40d2eeb5..ade58d68 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala @@ -49,8 +49,10 @@ object AvatarAction { final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action + final case class ProjectileAutoLockAwareness(mode : Int) extends Action + final case class ProjectileTrackingAwareness(projectile_guid : PlanetSideGUID) extends Action final case class ProjectileExplodes(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, projectile : Projectile) extends Action - final case class ProjectileState(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, unk : Boolean, time_alive : Int) extends Action + final case class ProjectileState(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, unk1 : Int, unk2 : Boolean, unk3 : Int) extends Action final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action final case class Revive(target_guid: PlanetSideGUID) extends Action diff --git a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala index b4450865..2f60e87a 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala @@ -41,8 +41,10 @@ object AvatarResponse { final case class PlanetsideAttributeToAll(attribute_type : Int, attribute_value : Long) extends Response final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response final case class PlayerState(pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Response + final case class ProjectileAutoLockAwareness(mode : Int) extends Response + final case class ProjectileTrackingAwareness(projectile_guid : PlanetSideGUID) extends Response final case class ProjectileExplodes(projectile_guid : PlanetSideGUID, projectile : Projectile) extends Response - final case class ProjectileState(projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, unk : Boolean, time_alive : Int) extends Response + final case class ProjectileState(projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, unk1 : Int, unk2 : Boolean, unk3 : Int) extends Response final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response final case class Release(player : Player) extends Response final case class Reload(weapon_guid : PlanetSideGUID) extends Response diff --git a/common/src/test/scala/game/ProjectileStateMessageTest.scala b/common/src/test/scala/game/ProjectileStateMessageTest.scala index 80891f88..6c0afc7d 100644 --- a/common/src/test/scala/game/ProjectileStateMessageTest.scala +++ b/common/src/test/scala/game/ProjectileStateMessageTest.scala @@ -8,17 +8,18 @@ import net.psforever.types.Vector3 import scodec.bits._ class ProjectileStateMessageTest extends Specification { - val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 020000" + val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 02000000" "decode" in { PacketCoding.DecodePacket(string).require match { - case ProjectileStateMessage(projectile, pos, vel, orient, unk, time_alive) => + case ProjectileStateMessage(projectile, pos, vel, orient, sequence, explode, unk) => projectile mustEqual PlanetSideGUID(40229) pos mustEqual Vector3(4611.539f, 5576.375f, 82.328125f) vel mustEqual Vector3(18.64686f, -33.43247f, 11.599553f) - orient mustEqual Vector3(0, 22.5f, 56.25f) - unk mustEqual false - time_alive mustEqual 4 + orient mustEqual Vector3(0, 22.5f, 146.25f) + sequence mustEqual 2 + explode mustEqual false + unk mustEqual 0 case _ => ko } @@ -29,9 +30,10 @@ class ProjectileStateMessageTest extends Specification { PlanetSideGUID(40229), Vector3(4611.539f, 5576.375f, 82.328125f), Vector3(18.64686f, -33.43247f, 11.599553f), - Vector3(0, 22.5f, 56.25f), + Vector3(0, 22.5f, 146.25f), + 2, false, - 4 + 0 ) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 86f6de1d..9be7c388 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -153,6 +153,9 @@ class WorldSessionActor extends Actor with MDCContextAware { lazy val unsignedIntMaxValue : Long = Int.MaxValue.toLong * 2L + 1L var serverTime : Long = 0 + var projectileAutoLockTargets : List[PlanetSideGUID] = Nil + var projectileAutoLocksActive : Set[PlanetSideGUID] = Set.empty + var amsSpawnPoints : List[SpawnPoint] = Nil var clientKeepAlive : Cancellable = DefaultCancellable.obj var progressBarUpdate : Cancellable = DefaultCancellable.obj @@ -1473,31 +1476,42 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + case AvatarResponse.ProjectileTrackingAwareness(projectile_guid) => + continent.GUID(projectile_guid) match { + case Some(projectile : Projectile) => + val mode = 7 + (projectile.profile == GlobalDefinitions.wasp_rocket_projectile) + sendResponse(GenericActionMessage(mode)) + case _ => ; + } + case AvatarResponse.ProjectileExplodes(projectile_guid, projectile) => - //turn the projectile into a boomer - sendResponse( - ObjectCreateMessage( - GlobalDefinitions.boomer.ObjectId, - projectile_guid, - CommonFieldDataWithPlacement( - PlacementData(projectile.Position, projectile.Orientation), - CommonFieldData(projectile.owner.Faction,false,false,false,None,false,Some(false),None, PlanetSideGUID(0)) - ) - ) - ) - //detonate the boomer, then clean it up - sendResponse(TriggerEffectMessage(projectile_guid, "detonate_boomer")) +// //turn the projectile into a boomer +// sendResponse( +// ObjectCreateMessage( +// GlobalDefinitions.boomer.ObjectId, +// projectile_guid, +// CommonFieldDataWithPlacement( +// PlacementData(projectile.Position, projectile.Orientation), +// CommonFieldData(projectile.owner.Faction,false,false,false,None,false,Some(false),None, PlanetSideGUID(0)) +// ) +// ) +// ) +// //detonate the boomer, then clean it up +// sendResponse(TriggerEffectMessage(projectile_guid, "detonate_boomer")) sendResponse(ObjectDeleteMessage(projectile_guid, 2)) - case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive) => + case AvatarResponse.ProjectileAutoLockAwareness(mode) => + sendResponse(GenericActionMessage(mode)) + + case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3) => if(tplayer_guid != guid) { - //sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, unk, time_alive)) - continent.GUID(projectile_guid) match { - case Some(obj) => - val definition = obj.Definition - sendResponse(ObjectCreateMessage(definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(obj).get)) - case _ => ; - } + sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3)) +// continent.GUID(projectile_guid) match { +// case Some(obj) => +// val definition = obj.Definition +// sendResponse(ObjectCreateMessage(definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(obj).get)) +// case _ => ; +// } } case AvatarResponse.PutDownFDU(target) => @@ -3481,7 +3495,10 @@ class WorldSessionActor extends Actor with MDCContextAware { import net.psforever.objects.GlobalDefinitions._ import net.psforever.types.CertificationType._ - val faction = PlanetSideEmpire.VS + val faction = + if(sessionId % 5 == 0) PlanetSideEmpire.TR + else if(sessionId % 3 == 0) PlanetSideEmpire.NC + else PlanetSideEmpire.VS val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1) avatar.Certifications += StandardAssault avatar.Certifications += MediumAssault @@ -3539,7 +3556,7 @@ class WorldSessionActor extends Actor with MDCContextAware { //player.Slot(12).Equipment = AmmoBox(bullet_9mm) //player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP) //player.Slot(36).Equipment = AmmoBox(GlobalDefinitions.StandardPistolAmmo(player.Faction)) - //player.Slot(39).Equipment = SimpleItem(remote_electronics_kit) + player.Slot(39).Equipment = SimpleItem(remote_electronics_kit) player.Locker.Inventory += 0 -> SimpleItem(remote_electronics_kit) player.Inventory.Items.foreach { _.obj.Faction = faction } //TODO end temp player character auto-loading @@ -4031,15 +4048,15 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) => //log.info(s"VehicleSubState: $vehicle_guid, $player_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2") - case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, unk, progress) => - //log.info(s"ProjectileState: $msg") + case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3) => + log.info(s"ProjectileState: $msg") projectiles(projectile_guid.guid - Projectile.BaseUID) match { case Some(projectile) if projectile.HasGUID => val projectileGlobalUID = projectile.GUID projectile.Position = shot_pos projectile.Orientation = shot_orient projectile.Velocity = shot_vel - avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileState(player.GUID, projectileGlobalUID, shot_pos, shot_vel, shot_orient, unk, progress)) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileState(player.GUID, projectileGlobalUID, shot_pos, shot_vel, shot_orient, unk1, unk2, unk3)) case _ => log.error(s"ProjectileState: the projectile@${projectile_guid.guid} can not be found") } @@ -5496,6 +5513,32 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) => log.info("Lazing position: " + pos2.toString) + case msg @ ObjectDetectedMessage(guid1, guid2, unk, targets) => + FindWeapon match { + case Some(weapon) if weapon.Projectile.AutoLock => + //projectile with auto-lock instigates a warning on the target + val mode = 7 + (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) + projectileAutoLockTargets = targets + targets + .map { continent.GUID } + .collect { + case Some(obj : Vehicle) if !obj.Cloaked => + //TODO vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode)) + obj.Seats.values + .collect { case seat if seat.isOccupied => + avatarService ! AvatarServiceMessage(s"${seat.Occupant.get.Name}", AvatarAction.ProjectileAutoLockAwareness(mode)) + } + case Some(obj : Mountable) => + obj.Seats.values + .collect { case seat if seat.isOccupied => + avatarService ! AvatarServiceMessage(s"${seat.Occupant.get.Name}", AvatarAction.ProjectileAutoLockAwareness(mode)) + } + case Some(obj : Player) if obj.ExoSuit == ExoSuitType.MAX => + avatarService ! AvatarServiceMessage(s"${obj.Name}", AvatarAction.ProjectileAutoLockAwareness(mode)) + } + case _ => ; + } + case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) => log.info(s"Hit: $msg") (hit_info match { @@ -5544,7 +5587,7 @@ class WorldSessionActor extends Actor with MDCContextAware { }) if(projectile.profile.ExistsOnRemoteClients) { //cleanup - avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileExplodes(player.GUID, projectile_guid, projectile)) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)) taskResolver ! UnregisterProjectile(projectile) } case None => ;