From bb26c5d56efe34eb5f6bd3218631c589ec0ca112 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 18 Dec 2019 00:14:39 -0500 Subject: [PATCH] proper jammering behavior for both infantry and vehicles; moved certain vehicle operations onto the VehicleControl actor --- .../objects/equipment/JammingUnit.scala | 31 ++- .../turret/FacilityTurretControl.scala | 4 +- .../objects/vehicles/VehicleControl.scala | 209 +++++++++++++++++- .../psforever/objects/vital/Vitality.scala | 2 +- .../scala/services/local/LocalService.scala | 4 +- .../services/vehicle/VehicleService.scala | 37 +++- .../src/main/scala/WorldSessionActor.scala | 189 ++-------------- 7 files changed, 290 insertions(+), 186 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala index 1bd54684..9199f097 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala @@ -1,10 +1,25 @@ // Copyright (c) 2019 PSForever package net.psforever.objects.equipment +import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.serverobject.terminals.TargetValidation import scala.collection.mutable -import scala.concurrent.duration.Duration + +trait JammableUnit { + private var jammed : Boolean = false + + def Jammed : Boolean = jammed + + def Jammed_=(state : Boolean) : Boolean = { + jammed = state + Jammed + } +} + +object JammableUnit { + final case class Jammer() +} trait JammingUnit { private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer() @@ -13,3 +28,17 @@ trait JammingUnit { def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = jammedEffectDuration } + +object JammingUnit { + def FindJammerDuration(jammer : JammingUnit, target : PlanetSideGameObject) : Option[Int] = { + jammer.JammedEffectDuration + .collect { case (TargetValidation(_, test), duration) if test(target) => duration } + .toList + .sortWith(_ > _) + .headOption + } + + def FindJammerDuration(jammer : JammingUnit, targets : Seq[PlanetSideGameObject]) : Seq[Option[Int]] = { + targets.map { target => FindJammerDuration(jammer, target) } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala index 0e70e0f6..906e49b5 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala @@ -42,13 +42,13 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor case Vitality.Damage(damage_func) => if(turret.Health > 0) { val originalHealth = turret.Health - damage_func(turret) + val cause = damage_func(turret) val health = turret.Health val damageToHealth = originalHealth - health val name = turret.Actor.toString val slashPoint = name.lastIndexOf("/") org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth") - sender ! Vitality.DamageResolution(turret) + sender ! Vitality.DamageResolution(turret, cause) } case _ => ; diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala index deafa3ae..765ff003 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -1,14 +1,23 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.vehicles -import akka.actor.{Actor, ActorRef} -import net.psforever.objects.Vehicle -import net.psforever.objects.ballistics.VehicleSource +import akka.actor.{Actor, ActorRef, Cancellable} +import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, Tool, Vehicle} +import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource} +import net.psforever.objects.equipment.JammingUnit import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} -import net.psforever.objects.serverobject.deploy.DeploymentBehavior +import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior} import net.psforever.objects.vital.{VehicleShieldCharge, Vitality} -import net.psforever.types.ExoSuitType +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types.{DriveState, ExoSuitType, Vector3} +import services.{RemoverActor, Service} +import services.avatar.{AvatarAction, AvatarServiceMessage} +import services.local.{LocalAction, LocalServiceMessage} +import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage} + +import scala.concurrent.duration._ /** * An `Actor` that handles messages being dispatched to a specific `Vehicle`.
@@ -22,6 +31,9 @@ class VehicleControl(vehicle : Vehicle) extends Actor with DeploymentBehavior with MountableBehavior.Mount with MountableBehavior.Dismount { + var jammeredSoundTimer : Cancellable = DefaultCancellable.obj + var jammeredStatusTimer : Cancellable = DefaultCancellable.obj + //make control actors belonging to utilities when making control actor belonging to vehicle vehicle.Utilities.foreach({case (_, util) => util.Setup }) @@ -73,15 +85,17 @@ class VehicleControl(vehicle : Vehicle) extends Actor if(vehicle.Health > 0) { val originalHealth = vehicle.Health val originalShields = vehicle.Shields - damage_func(vehicle) + val cause = damage_func(vehicle) val health = vehicle.Health val shields = vehicle.Shields val damageToHealth = originalHealth - health val damageToShields = originalShields - shields - val name = vehicle.Actor.toString - val slashPoint = name.lastIndexOf("/") - org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields") - sender ! Vitality.DamageResolution(vehicle) + VehicleControl.HandleVehicleDamageResolution(vehicle, cause, damageToHealth + damageToShields) + if(damageToHealth > 0 || damageToShields > 0) { + val name = vehicle.Actor.toString + val slashPoint = name.lastIndexOf("/") + org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint + 1, name.length - 1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields") + } } case Vehicle.ChargeShields(amount) => @@ -91,7 +105,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor !vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))) { vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount)) vehicle.Shields = vehicle.Shields + amount - sender ! Vehicle.UpdateShieldsCharge(vehicle) + vehicle.Zone.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Actor}", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields)) } case FactionAffinity.ConvertFactionAffinity(faction) => @@ -101,6 +115,15 @@ class VehicleControl(vehicle : Vehicle) extends Actor } sender ! FactionAffinity.AssertFactionAffinity(vehicle, faction) + case VehicleControl.Jammered(cause) => + TryJammerVehicleWithProjectile(vehicle, cause) + + case VehicleControl.ClearJammeredSound() => + CancelJammeredSound(vehicle) + + case VehicleControl.ClearJammeredStatus() => + StopJammeredStatus(vehicle) + case Vehicle.PrepareForDeletion => context.become(Disabled) @@ -110,14 +133,61 @@ class VehicleControl(vehicle : Vehicle) extends Actor def Disabled : Receive = checkBehavior .orElse(dismountBehavior) .orElse { + case VehicleControl.ClearJammeredSound() => + CancelJammeredSound(vehicle) + + case VehicleControl.ClearJammeredStatus() => + StopJammeredStatus(vehicle) + case Vehicle.Reactivate => context.become(Enabled) case _ => ; } + + def TryJammerVehicleWithProjectile(target : Vehicle, cause : ResolvedProjectile) : Unit = { + val radius = cause.projectile.profile.DamageRadius + JammingUnit.FindJammerDuration(cause.projectile.profile, target) match { + case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius => + //jammered sound + target.Zone.VehicleEvents ! VehicleServiceMessage(target.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 54, 1)) + import scala.concurrent.ExecutionContext.Implicits.global + jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, VehicleControl.ClearJammeredSound()) + //jammered status + StartJammeredStatus(target, dur) + case _ => ; + } + } + + def StartJammeredStatus(target : Vehicle, dur : Int) : Boolean = { + if(jammeredStatusTimer.isCancelled) { + VehicleControl.JammeredStatus(target, 1) + import scala.concurrent.ExecutionContext.Implicits.global + jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, VehicleControl.ClearJammeredStatus()) + true + } + else { + false + } + } + + def StopJammeredStatus(target : Vehicle) : Boolean = { + VehicleControl.JammeredStatus(target, 0) + jammeredStatusTimer.cancel + } + + def CancelJammeredSound(target : Vehicle) : Unit = { + jammeredSoundTimer.cancel + target.Zone.VehicleEvents ! VehicleServiceMessage(target.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 54, 0)) + } } object VehicleControl { + private final case class Jammered(cause : ResolvedProjectile) + + private final case class ClearJammeredSound() + + private final case class ClearJammeredStatus() import net.psforever.objects.vital.{DamageFromProjectile, VehicleShieldCharge, VitalsActivity} import scala.concurrent.duration._ @@ -136,4 +206,121 @@ object VehicleControl { case _ => false } } + + /** + * na + * @param target na + */ + def HandleVehicleDamageResolution(target : Vehicle, cause : ResolvedProjectile, damage : Int) : Unit = { + val targetGUID = target.GUID + val playerGUID = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match { + case Some(player) => player.GUID + case _ => PlanetSideGUID(0) + } + if(target.Health > 0) { + //activity on map + if(damage > 0) { + target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos) + //alert occupants to damage source + HandleVehicleDamageAwareness(target, playerGUID, cause) + } + if(cause.projectile.profile.JammerProjectile) { + target.Actor ! VehicleControl.Jammered(cause) + } + } + else { + //alert to vehicle death (hence, occupants' deaths) + HandleVehicleDestructionAwareness(target, playerGUID, cause) + } + target.Zone.VehicleEvents ! VehicleServiceMessage(target.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health)) + target.Zone.VehicleEvents ! VehicleServiceMessage(s"${target.Actor}", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, target.Shields)) + } + + /** + * na + * @param target na + * @param attribution na + * @param lastShot na + */ + def HandleVehicleDamageAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = { + //alert occupants to damage source + target.Seats.values.filter(seat => { + seat.isOccupied && seat.Occupant.get.isAlive + }).foreach(seat => { + val tplayer = seat.Occupant.get + target.Zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(attribution, tplayer.GUID)) + }) + //alert cargo occupants to damage source + target.CargoHolds.values.foreach(hold => { + hold.Occupant match { + case Some(cargo) => + cargo.Health = 0 + cargo.Shields = 0 + cargo.History(lastShot) + HandleVehicleDamageAwareness(cargo, attribution, lastShot) + case None => ; + } + }) + } + + /** + * na + * @param target na + * @param attribution na + * @param lastShot na + */ + def HandleVehicleDestructionAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = { + val continentId = target.Zone.Id + //alert to vehicle death (hence, occupants' deaths) + target.Seats.values.filter(seat => { + seat.isOccupied && seat.Occupant.get.isAlive + }).foreach(seat => { + val tplayer = seat.Occupant.get + val tplayerGUID = tplayer.GUID + target.Zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID)) + target.Zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self + }) + //vehicle wreckage has no weapons + target.Weapons.values + .filter { + _.Equipment.nonEmpty + } + .foreach(slot => { + val wep = slot.Equipment.get + target.Zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID)) + }) + target.CargoHolds.values.foreach(hold => { + hold.Occupant match { + case Some(cargo) => + cargo.Health = 0 + cargo.Shields = 0 + cargo.Position += Vector3.z(1) + cargo.History(lastShot) //necessary to kill cargo vehicle occupants //TODO: collision damage + HandleVehicleDestructionAwareness(cargo, attribution, lastShot) //might cause redundant packets + case None => ; + } + }) + target.Definition match { + case GlobalDefinitions.ams => + target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying) + case GlobalDefinitions.router => + target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying) + VehicleService.BeforeUnloadVehicle(target, target.Zone) + target.Zone.LocalEvents ! LocalServiceMessage(target.Zone.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None)) + case _ => ; + } + target.Zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position)) + target.Zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), target.Zone)) + target.Zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, target.Zone, Some(1 minute))) + } + + def JammeredStatus(target : Vehicle, statusCode : Int) : Unit = { + target.Zone.VehicleEvents ! VehicleServiceMessage(target.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 27, statusCode)) + target.Weapons.values + .map { _.Equipment } + .collect { + case Some(item : Tool) => + target.Zone.VehicleEvents ! VehicleServiceMessage(target.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, item.GUID, 27, statusCode)) + } + } } diff --git a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala index 1c7628ac..17a862bc 100644 --- a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala +++ b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala @@ -111,5 +111,5 @@ object Vitality { * Report that a vitals object must be updated due to damage. * @param obj the vital object */ - final case class DamageResolution(obj : Vitality) + final case class DamageResolution(obj : Vitality, cause : ResolvedProjectile) } diff --git a/common/src/main/scala/services/local/LocalService.scala b/common/src/main/scala/services/local/LocalService.scala index 69002e53..1c5818ed 100644 --- a/common/src/main/scala/services/local/LocalService.scala +++ b/common/src/main/scala/services/local/LocalService.scala @@ -283,8 +283,8 @@ class LocalService(zone : Zone) extends Actor { //synchronized damage calculations case Vitality.DamageOn(target : Deployable, func) => - func(target) - sender ! Vitality.DamageResolution(target) + val cause = func(target) + sender ! Vitality.DamageResolution(target, cause) case msg => log.warn(s"Unhandled message $msg from $sender") diff --git a/common/src/main/scala/services/vehicle/VehicleService.scala b/common/src/main/scala/services/vehicle/VehicleService.scala index a12ab503..418c7f15 100644 --- a/common/src/main/scala/services/vehicle/VehicleService.scala +++ b/common/src/main/scala/services/vehicle/VehicleService.scala @@ -2,16 +2,18 @@ package services.vehicle import akka.actor.{Actor, ActorRef, Props} -import net.psforever.objects.Vehicle +import net.psforever.objects.{GlobalDefinitions, TelepadDeployable, Vehicle} import net.psforever.objects.ballistics.VehicleSource import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit} +import net.psforever.objects.vehicles.{Utility, UtilityType} import net.psforever.objects.vital.RepairFromTerm import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID} import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import services.vehicle.support.{TurretUpgrader, VehicleRemover} import net.psforever.types.DriveState +import services.local.LocalServiceMessage import services.{GenericEventBus, RemoverActor, Service} import scala.concurrent.duration._ @@ -253,3 +255,36 @@ class VehicleService(zone : Zone) extends Actor { .map(util => util().asInstanceOf[SpawnTube]) } } + +object VehicleService { + /** + * Before a vehicle is removed from the game world, the following actions must be performed. + * @param vehicle the vehicle + */ + def BeforeUnloadVehicle(vehicle : Vehicle, zone : Zone) : Unit = { + vehicle.Definition match { + case GlobalDefinitions.ams => + zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone) + case GlobalDefinitions.router => + RemoveTelepads(vehicle, zone) + case _ => ; + } + } + + def RemoveTelepads(vehicle: Vehicle, zone : Zone) : Unit = { + (vehicle.Utility(UtilityType.internal_router_telepad_deployable) match { + case Some(util : Utility.InternalTelepad) => + val telepad = util.Telepad + util.Telepad = None + zone.GUID(telepad) + case _ => + None + }) match { + case Some(telepad : TelepadDeployable) => + telepad.Active = false + zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone)) + zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds))) + case _ => ; + } + } +} diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 6033140a..c551b170 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1104,10 +1104,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) => HandleHackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) - case Vitality.DamageResolution(target : Vehicle) => - HandleVehicleDamageResolution(target) - - case Vitality.DamageResolution(target : TrapDeployable) => + case Vitality.DamageResolution(target : TrapDeployable, _) => //tank_traps val guid = target.GUID val health = target.Health @@ -1116,7 +1113,7 @@ class WorldSessionActor extends Actor with MDCContextAware { AnnounceDestroyDeployable(target, None) } - case Vitality.DamageResolution(target : SensorDeployable) => + case Vitality.DamageResolution(target : SensorDeployable, _) => //sensors val guid = target.GUID val health = target.Health @@ -1125,7 +1122,7 @@ class WorldSessionActor extends Actor with MDCContextAware { AnnounceDestroyDeployable(target, Some(0 seconds)) } - case Vitality.DamageResolution(target : SimpleDeployable) => + case Vitality.DamageResolution(target : SimpleDeployable, _) => //boomers, mines if(target.Health <= 0) { //update if destroyed @@ -1134,10 +1131,10 @@ class WorldSessionActor extends Actor with MDCContextAware { AnnounceDestroyDeployable(target, Some(0 seconds)) } - case Vitality.DamageResolution(target : TurretDeployable) => + case Vitality.DamageResolution(target : TurretDeployable, _) => HandleTurretDeployableDamageResolution(target) - case Vitality.DamageResolution(target : ComplexDeployable) => + case Vitality.DamageResolution(target : ComplexDeployable, _) => //shield_generators val health = target.Health val guid = target.GUID @@ -1146,15 +1143,12 @@ class WorldSessionActor extends Actor with MDCContextAware { AnnounceDestroyDeployable(target, None) } - case Vitality.DamageResolution(target : FacilityTurret) => + case Vitality.DamageResolution(target : FacilityTurret, _) => HandleFacilityTurretDamageResolution(target) - case Vitality.DamageResolution(target : PlanetSideGameObject) => + case Vitality.DamageResolution(target : PlanetSideGameObject, _) => log.warn(s"Vital target ${target.Definition.Name} damage resolution not supported using this method") - case Vehicle.UpdateShieldsCharge(vehicle) => - continent.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Actor}", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields)) - case ResponseToSelf(pkt) => log.info(s"Received a direct message: $pkt") sendResponse(pkt) @@ -1331,9 +1325,9 @@ class WorldSessionActor extends Actor with MDCContextAware { } } } - if(cause.projectile.profile.JammerProjectile) { + if(target.isAlive && cause.projectile.profile.JammerProjectile) { val radius = cause.projectile.profile.DamageRadius - FindJammerDuration(cause.projectile.profile, target) match { + JammingUnit.FindJammerDuration(cause.projectile.profile, target) match { case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius => //implants DeactivateImplants() @@ -1344,12 +1338,11 @@ class WorldSessionActor extends Actor with MDCContextAware { jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, ClearJammeredSound()) //jammered status skipStaminaRegenForTurns = 5 - sendResponse(PlanetsideAttributeMessage(player.GUID, 2, 0)) jammeredEquipment = player.Holsters() .map { _.Equipment } .collect { case Some(item) if item.Size != EquipmentSize.Melee => - sendResponse(GenericObjectActionMessage(item.GUID, 156)) + sendResponse(PlanetsideAttributeMessage(item.GUID, 24, 1)) item.GUID } jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, ClearJammeredStatus()) @@ -2493,7 +2486,6 @@ class WorldSessionActor extends Actor with MDCContextAware { case VehicleResponse.ConcealPlayer(player_guid) => sendResponse(GenericObjectActionMessage(player_guid, 9)) - //sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1)) case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) => if(tplayer_guid != guid) { @@ -2969,113 +2961,6 @@ class WorldSessionActor extends Actor with MDCContextAware { msgs } - /** - * na - * @param target na - */ - def HandleVehicleDamageResolution(target : Vehicle) : Unit = { - val targetGUID = target.GUID - val playerGUID = player.GUID - val players = target.Seats.values.filter(seat => { - seat.isOccupied && seat.Occupant.get.isAlive - }) - target.LastShot match { //TODO: collision damage from/in history - case Some(shot) => - if(target.Health > 0) { - //activity on map - continent.Activity ! Zone.HotSpot.Activity(shot.target, shot.projectile.owner, shot.hit_pos) - //alert occupants to damage source - HandleVehicleDamageAwareness(target, playerGUID, shot) - } - else { - //alert to vehicle death (hence, occupants' deaths) - HandleVehicleDestructionAwareness(target, shot) - } - continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health)) - continent.VehicleEvents ! VehicleServiceMessage(s"${target.Actor}", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, target.Shields)) - case None => ; - } - } - - /** - * na - * @param target na - * @param attribution na - * @param lastShot na - */ - def HandleVehicleDamageAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = { - //alert occupants to damage source - target.Seats.values.filter(seat => { - seat.isOccupied && seat.Occupant.get.isAlive - }).foreach(seat => { - val tplayer = seat.Occupant.get - continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(attribution, tplayer.GUID)) - }) - //alert cargo occupants to damage source - target.CargoHolds.values.foreach(hold => { - hold.Occupant match { - case Some(cargo) => - cargo.Health = 0 - cargo.Shields = 0 - cargo.History(lastShot) - HandleVehicleDamageAwareness(cargo, attribution, lastShot) - case None => ; - } - }) - } - - /** - * na - * @param target na - * @param attribution na - * @param lastShot na - */ - def HandleVehicleDestructionAwareness(target : Vehicle, lastShot : ResolvedProjectile) : Unit = { - val playerGUID = player.GUID - val continentId = continent.Id - //alert to vehicle death (hence, occupants' deaths) - target.Seats.values.filter(seat => { - seat.isOccupied && seat.Occupant.get.isAlive - }).foreach(seat => { - val tplayer = seat.Occupant.get - val tplayerGUID = tplayer.GUID - continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID)) - continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self - }) - //vehicle wreckage has no weapons - target.Weapons.values - .filter { - _.Equipment.nonEmpty - } - .foreach(slot => { - val wep = slot.Equipment.get - continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID)) - }) - target.CargoHolds.values.foreach(hold => { - hold.Occupant match { - case Some(cargo) => - cargo.Health = 0 - cargo.Shields = 0 - cargo.Position += Vector3.z(1) - cargo.History(lastShot) //necessary to kill cargo vehicle occupants //TODO: collision damage - HandleVehicleDestructionAwareness(cargo, lastShot) //might cause redundant packets - case None => ; - } - }) - target.Definition match { - case GlobalDefinitions.ams => - target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying) - case GlobalDefinitions.router => - target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying) - BeforeUnloadVehicle(target) - continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None)) - case _ => ; - } - continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(target.GUID, playerGUID, playerGUID, target.Position)) - continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), continent)) - continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, continent, Some(1 minute))) - } - /** * na * @param target na @@ -5531,6 +5416,15 @@ class WorldSessionActor extends Actor with MDCContextAware { case _ => ; } + case msg @ WeaponJammedMessage(weapon_guid) => + FindWeapon match { + case Some(tool : Tool) => + log.info(s"WeaponJammed: ${tool.Definition.Name}@${weapon_guid.guid}") + //TODO + case _ => + log.info(s"WeaponJammed: ${weapon_guid.guid}") + } + case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) => log.info(s"WeaponFire: $msg") if(player.isShielded) { @@ -5680,34 +5574,6 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => ; } -// FindProjectileEntry(projectile_guid) match { -// case Some(projectile) => -// val allTargets = (continent.GUID(direct_victim_uid) match { -// case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) => -// HandleDealingDamage(target, ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position).get) -// Seq(target) -// case _ => -// Nil -// }) ++ -// targets -// .map { a => continent.GUID(a.uid) } -// .collect { -// case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) => -// HandleDealingDamage(target, ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos).get) -// target -// } -// if(projectile.profile.JammerProjectile) { -// val jammableTargets = FindJammerTargetsInScope(projectile.profile, allTargets) -// jammableTargets -// .zip(FindJammerDuration(projectile.profile, jammableTargets)) -// .collect { case (target, Some(time)) => -// //TODO jamming messages here -// } -// } -// -// case None => ; -// } - case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) => log.info(s"Lash: $msg") continent.GUID(victim_guid) match { @@ -9870,7 +9736,6 @@ class WorldSessionActor extends Actor with MDCContextAware { case GlobalDefinitions.ams if vehicle.Faction == player.Faction => log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...") continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent)) - None case GlobalDefinitions.router => //this may repeat for multiple players on the same continent but that's okay(?) log.info("BeforeUnload: cleaning up after a router ...") @@ -10337,7 +10202,7 @@ class WorldSessionActor extends Actor with MDCContextAware { taskResolver ! UnregisterProjectile(projectile) projectiles(local_index) match { case Some(obj) if !obj.isResolved => obj.Miss - case None => ; + case _ => ; } projectilesToCleanUp(local_index) = false } @@ -10399,18 +10264,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - def FindJammerDuration(jammer : JammingUnit, target : PlanetSideGameObject) : Option[Int] = { - jammer.JammedEffectDuration - .collect { case (TargetValidation(_, test), duration) if test(target) => duration } - .toList - .sortWith(_ > _) - .headOption - } - - def FindJammerDuration(jammer : JammingUnit, targets : Seq[PlanetSideGameObject]) : Seq[Option[Int]] = { - targets.map { target => FindJammerDuration(jammer, target) } - } - def DeactivateImplants() : Unit = { DeactivateImplantDarkLight() DeactivateImplantSurge() @@ -10444,7 +10297,7 @@ class WorldSessionActor extends Actor with MDCContextAware { if(!jammeredSoundTimer.isCancelled) { CancelJammeredSound() } - jammeredEquipment.foreach { id => sendResponse(GenericObjectActionMessage(id, 152)) } + jammeredEquipment.foreach { id => sendResponse(PlanetsideAttributeMessage(id, 24, 0)) } jammeredEquipment = Nil }