mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 23:44:40 +00:00
proper jammering behavior for both infantry and vehicles; moved certain vehicle operations onto the VehicleControl actor
This commit is contained in:
parent
cf8faa207d
commit
bb26c5d56e
7 changed files with 290 additions and 186 deletions
|
|
@ -1,10 +1,25 @@
|
||||||
// Copyright (c) 2019 PSForever
|
// Copyright (c) 2019 PSForever
|
||||||
package net.psforever.objects.equipment
|
package net.psforever.objects.equipment
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.serverobject.terminals.TargetValidation
|
import net.psforever.objects.serverobject.terminals.TargetValidation
|
||||||
|
|
||||||
import scala.collection.mutable
|
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 {
|
trait JammingUnit {
|
||||||
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer()
|
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer()
|
||||||
|
|
@ -13,3 +28,17 @@ trait JammingUnit {
|
||||||
|
|
||||||
def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = jammedEffectDuration
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,13 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor
|
||||||
case Vitality.Damage(damage_func) =>
|
case Vitality.Damage(damage_func) =>
|
||||||
if(turret.Health > 0) {
|
if(turret.Health > 0) {
|
||||||
val originalHealth = turret.Health
|
val originalHealth = turret.Health
|
||||||
damage_func(turret)
|
val cause = damage_func(turret)
|
||||||
val health = turret.Health
|
val health = turret.Health
|
||||||
val damageToHealth = originalHealth - health
|
val damageToHealth = originalHealth - health
|
||||||
val name = turret.Actor.toString
|
val name = turret.Actor.toString
|
||||||
val slashPoint = name.lastIndexOf("/")
|
val slashPoint = name.lastIndexOf("/")
|
||||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
|
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 _ => ;
|
case _ => ;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef}
|
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, Tool, Vehicle}
|
||||||
import net.psforever.objects.ballistics.VehicleSource
|
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.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
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.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`.<br>
|
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||||
|
|
@ -22,6 +31,9 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
with DeploymentBehavior
|
with DeploymentBehavior
|
||||||
with MountableBehavior.Mount
|
with MountableBehavior.Mount
|
||||||
with MountableBehavior.Dismount {
|
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
|
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||||
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
||||||
|
|
||||||
|
|
@ -73,15 +85,17 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
if(vehicle.Health > 0) {
|
if(vehicle.Health > 0) {
|
||||||
val originalHealth = vehicle.Health
|
val originalHealth = vehicle.Health
|
||||||
val originalShields = vehicle.Shields
|
val originalShields = vehicle.Shields
|
||||||
damage_func(vehicle)
|
val cause = damage_func(vehicle)
|
||||||
val health = vehicle.Health
|
val health = vehicle.Health
|
||||||
val shields = vehicle.Shields
|
val shields = vehicle.Shields
|
||||||
val damageToHealth = originalHealth - health
|
val damageToHealth = originalHealth - health
|
||||||
val damageToShields = originalShields - shields
|
val damageToShields = originalShields - shields
|
||||||
val name = vehicle.Actor.toString
|
VehicleControl.HandleVehicleDamageResolution(vehicle, cause, damageToHealth + damageToShields)
|
||||||
val slashPoint = name.lastIndexOf("/")
|
if(damageToHealth > 0 || damageToShields > 0) {
|
||||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields")
|
val name = vehicle.Actor.toString
|
||||||
sender ! Vitality.DamageResolution(vehicle)
|
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) =>
|
case Vehicle.ChargeShields(amount) =>
|
||||||
|
|
@ -91,7 +105,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))) {
|
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))) {
|
||||||
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
||||||
vehicle.Shields = vehicle.Shields + 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) =>
|
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||||
|
|
@ -101,6 +115,15 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
}
|
}
|
||||||
sender ! FactionAffinity.AssertFactionAffinity(vehicle, faction)
|
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 =>
|
case Vehicle.PrepareForDeletion =>
|
||||||
context.become(Disabled)
|
context.become(Disabled)
|
||||||
|
|
||||||
|
|
@ -110,14 +133,61 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
def Disabled : Receive = checkBehavior
|
def Disabled : Receive = checkBehavior
|
||||||
.orElse(dismountBehavior)
|
.orElse(dismountBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
|
case VehicleControl.ClearJammeredSound() =>
|
||||||
|
CancelJammeredSound(vehicle)
|
||||||
|
|
||||||
|
case VehicleControl.ClearJammeredStatus() =>
|
||||||
|
StopJammeredStatus(vehicle)
|
||||||
|
|
||||||
case Vehicle.Reactivate =>
|
case Vehicle.Reactivate =>
|
||||||
context.become(Enabled)
|
context.become(Enabled)
|
||||||
|
|
||||||
case _ => ;
|
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 {
|
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 net.psforever.objects.vital.{DamageFromProjectile, VehicleShieldCharge, VitalsActivity}
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
|
@ -136,4 +206,121 @@ object VehicleControl {
|
||||||
case _ => false
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,5 +111,5 @@ object Vitality {
|
||||||
* Report that a vitals object must be updated due to damage.
|
* Report that a vitals object must be updated due to damage.
|
||||||
* @param obj the vital object
|
* @param obj the vital object
|
||||||
*/
|
*/
|
||||||
final case class DamageResolution(obj : Vitality)
|
final case class DamageResolution(obj : Vitality, cause : ResolvedProjectile)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,8 +283,8 @@ class LocalService(zone : Zone) extends Actor {
|
||||||
|
|
||||||
//synchronized damage calculations
|
//synchronized damage calculations
|
||||||
case Vitality.DamageOn(target : Deployable, func) =>
|
case Vitality.DamageOn(target : Deployable, func) =>
|
||||||
func(target)
|
val cause = func(target)
|
||||||
sender ! Vitality.DamageResolution(target)
|
sender ! Vitality.DamageResolution(target, cause)
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.warn(s"Unhandled message $msg from $sender")
|
log.warn(s"Unhandled message $msg from $sender")
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@
|
||||||
package services.vehicle
|
package services.vehicle
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
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.ballistics.VehicleSource
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit}
|
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.vital.RepairFromTerm
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||||
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
|
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
|
||||||
import net.psforever.types.DriveState
|
import net.psforever.types.DriveState
|
||||||
|
import services.local.LocalServiceMessage
|
||||||
import services.{GenericEventBus, RemoverActor, Service}
|
import services.{GenericEventBus, RemoverActor, Service}
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -253,3 +255,36 @@ class VehicleService(zone : Zone) extends Actor {
|
||||||
.map(util => util().asInstanceOf[SpawnTube])
|
.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 _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1104,10 +1104,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) =>
|
case HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) =>
|
||||||
HandleHackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction)
|
HandleHackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction)
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : Vehicle) =>
|
case Vitality.DamageResolution(target : TrapDeployable, _) =>
|
||||||
HandleVehicleDamageResolution(target)
|
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : TrapDeployable) =>
|
|
||||||
//tank_traps
|
//tank_traps
|
||||||
val guid = target.GUID
|
val guid = target.GUID
|
||||||
val health = target.Health
|
val health = target.Health
|
||||||
|
|
@ -1116,7 +1113,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
AnnounceDestroyDeployable(target, None)
|
AnnounceDestroyDeployable(target, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : SensorDeployable) =>
|
case Vitality.DamageResolution(target : SensorDeployable, _) =>
|
||||||
//sensors
|
//sensors
|
||||||
val guid = target.GUID
|
val guid = target.GUID
|
||||||
val health = target.Health
|
val health = target.Health
|
||||||
|
|
@ -1125,7 +1122,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
AnnounceDestroyDeployable(target, Some(0 seconds))
|
AnnounceDestroyDeployable(target, Some(0 seconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : SimpleDeployable) =>
|
case Vitality.DamageResolution(target : SimpleDeployable, _) =>
|
||||||
//boomers, mines
|
//boomers, mines
|
||||||
if(target.Health <= 0) {
|
if(target.Health <= 0) {
|
||||||
//update if destroyed
|
//update if destroyed
|
||||||
|
|
@ -1134,10 +1131,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
AnnounceDestroyDeployable(target, Some(0 seconds))
|
AnnounceDestroyDeployable(target, Some(0 seconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : TurretDeployable) =>
|
case Vitality.DamageResolution(target : TurretDeployable, _) =>
|
||||||
HandleTurretDeployableDamageResolution(target)
|
HandleTurretDeployableDamageResolution(target)
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : ComplexDeployable) =>
|
case Vitality.DamageResolution(target : ComplexDeployable, _) =>
|
||||||
//shield_generators
|
//shield_generators
|
||||||
val health = target.Health
|
val health = target.Health
|
||||||
val guid = target.GUID
|
val guid = target.GUID
|
||||||
|
|
@ -1146,15 +1143,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
AnnounceDestroyDeployable(target, None)
|
AnnounceDestroyDeployable(target, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Vitality.DamageResolution(target : FacilityTurret) =>
|
case Vitality.DamageResolution(target : FacilityTurret, _) =>
|
||||||
HandleFacilityTurretDamageResolution(target)
|
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")
|
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) =>
|
case ResponseToSelf(pkt) =>
|
||||||
log.info(s"Received a direct message: $pkt")
|
log.info(s"Received a direct message: $pkt")
|
||||||
sendResponse(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
|
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 =>
|
case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius =>
|
||||||
//implants
|
//implants
|
||||||
DeactivateImplants()
|
DeactivateImplants()
|
||||||
|
|
@ -1344,12 +1338,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, ClearJammeredSound())
|
jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, ClearJammeredSound())
|
||||||
//jammered status
|
//jammered status
|
||||||
skipStaminaRegenForTurns = 5
|
skipStaminaRegenForTurns = 5
|
||||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, 0))
|
|
||||||
jammeredEquipment = player.Holsters()
|
jammeredEquipment = player.Holsters()
|
||||||
.map { _.Equipment }
|
.map { _.Equipment }
|
||||||
.collect {
|
.collect {
|
||||||
case Some(item) if item.Size != EquipmentSize.Melee =>
|
case Some(item) if item.Size != EquipmentSize.Melee =>
|
||||||
sendResponse(GenericObjectActionMessage(item.GUID, 156))
|
sendResponse(PlanetsideAttributeMessage(item.GUID, 24, 1))
|
||||||
item.GUID
|
item.GUID
|
||||||
}
|
}
|
||||||
jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, ClearJammeredStatus())
|
jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, ClearJammeredStatus())
|
||||||
|
|
@ -2493,7 +2486,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
case VehicleResponse.ConcealPlayer(player_guid) =>
|
case VehicleResponse.ConcealPlayer(player_guid) =>
|
||||||
sendResponse(GenericObjectActionMessage(player_guid, 9))
|
sendResponse(GenericObjectActionMessage(player_guid, 9))
|
||||||
//sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
|
|
||||||
|
|
||||||
case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) =>
|
case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid) {
|
||||||
|
|
@ -2969,113 +2961,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
msgs
|
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
|
* na
|
||||||
* @param target na
|
* @param target na
|
||||||
|
|
@ -5531,6 +5416,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case _ => ;
|
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) =>
|
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
|
||||||
log.info(s"WeaponFire: $msg")
|
log.info(s"WeaponFire: $msg")
|
||||||
if(player.isShielded) {
|
if(player.isShielded) {
|
||||||
|
|
@ -5680,34 +5574,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case None => ;
|
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) =>
|
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
||||||
log.info(s"Lash: $msg")
|
log.info(s"Lash: $msg")
|
||||||
continent.GUID(victim_guid) match {
|
continent.GUID(victim_guid) match {
|
||||||
|
|
@ -9870,7 +9736,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case GlobalDefinitions.ams if vehicle.Faction == player.Faction =>
|
case GlobalDefinitions.ams if vehicle.Faction == player.Faction =>
|
||||||
log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...")
|
log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...")
|
||||||
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
|
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
|
||||||
None
|
|
||||||
case GlobalDefinitions.router =>
|
case GlobalDefinitions.router =>
|
||||||
//this may repeat for multiple players on the same continent but that's okay(?)
|
//this may repeat for multiple players on the same continent but that's okay(?)
|
||||||
log.info("BeforeUnload: cleaning up after a router ...")
|
log.info("BeforeUnload: cleaning up after a router ...")
|
||||||
|
|
@ -10337,7 +10202,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
taskResolver ! UnregisterProjectile(projectile)
|
taskResolver ! UnregisterProjectile(projectile)
|
||||||
projectiles(local_index) match {
|
projectiles(local_index) match {
|
||||||
case Some(obj) if !obj.isResolved => obj.Miss
|
case Some(obj) if !obj.isResolved => obj.Miss
|
||||||
case None => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
projectilesToCleanUp(local_index) = false
|
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 = {
|
def DeactivateImplants() : Unit = {
|
||||||
DeactivateImplantDarkLight()
|
DeactivateImplantDarkLight()
|
||||||
DeactivateImplantSurge()
|
DeactivateImplantSurge()
|
||||||
|
|
@ -10444,7 +10297,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
if(!jammeredSoundTimer.isCancelled) {
|
if(!jammeredSoundTimer.isCancelled) {
|
||||||
CancelJammeredSound()
|
CancelJammeredSound()
|
||||||
}
|
}
|
||||||
jammeredEquipment.foreach { id => sendResponse(GenericObjectActionMessage(id, 152)) }
|
jammeredEquipment.foreach { id => sendResponse(PlanetsideAttributeMessage(id, 24, 0)) }
|
||||||
jammeredEquipment = Nil
|
jammeredEquipment = Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue