diff --git a/common/src/main/scala/net/psforever/objects/SensorDeployable.scala b/common/src/main/scala/net/psforever/objects/SensorDeployable.scala index 71d3373a..d06688a2 100644 --- a/common/src/main/scala/net/psforever/objects/SensorDeployable.scala +++ b/common/src/main/scala/net/psforever/objects/SensorDeployable.scala @@ -61,31 +61,35 @@ class SensorDeployableControl(sensor : SensorDeployable) extends Actor } override def StartJammeredSound(target : Any, dur : Int) : Unit = target match { - case obj : PlanetSideServerObject => + case obj : PlanetSideServerObject if !jammedSound => obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)) super.StartJammeredSound(obj, dur) case _ => ; } override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { - case obj : PlanetSideServerObject => + case obj : PlanetSideServerObject with JammableUnit if !obj.Jammed => sensor.Zone.LocalEvents ! LocalServiceMessage(sensor.Zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)) super.StartJammeredStatus(obj, dur) case _ => ; } - override def CancelJammeredSound(target : Any) : Unit = target match { - case obj : PlanetSideServerObject => - obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)) - super.CancelJammeredSound(obj) - case _ => ; + override def CancelJammeredSound(target : Any) : Unit = { + target match { + case obj : PlanetSideServerObject if jammedSound => + obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)) + case _ => ; + } + super.CancelJammeredSound(target) } - override def CancelJammeredStatus(target : Any) : Unit = target match { - case obj : PlanetSideServerObject => - sensor.Zone.LocalEvents ! LocalServiceMessage(sensor.Zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)) - super.CancelJammeredStatus(obj) - case _ => ; + override def CancelJammeredStatus(target : Any) : Unit = { + target match { + case obj : PlanetSideServerObject with JammableUnit if obj.Jammed => + sensor.Zone.LocalEvents ! LocalServiceMessage(sensor.Zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)) + case _ => ; + } + super.CancelJammeredStatus(target) } } diff --git a/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala index d304bb93..96a243b3 100644 --- a/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala +++ b/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala @@ -60,19 +60,21 @@ class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor override def StartJammeredSound(target : Any, dur : Int) : Unit = { } override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { - case obj : PlanetSideServerObject => - obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)) + case obj : PlanetSideServerObject with JammableUnit if !obj.Jammed => + obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)) super.StartJammeredStatus(obj, dur) case _ => ; } override def CancelJammeredSound(target : Any) : Unit = { } - override def CancelJammeredStatus(target : Any) : Unit = target match { - case obj : PlanetSideServerObject => - obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)) - super.CancelJammeredStatus(obj) - case _ => ; + override def CancelJammeredStatus(target : Any) : Unit = { + target match { + case obj : PlanetSideServerObject with JammableUnit if obj.Jammed => + obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)) + case _ => ; + } + super.CancelJammeredStatus(target) } } diff --git a/common/src/main/scala/net/psforever/objects/Tool.scala b/common/src/main/scala/net/psforever/objects/Tool.scala index 6702808d..e5aec38c 100644 --- a/common/src/main/scala/net/psforever/objects/Tool.scala +++ b/common/src/main/scala/net/psforever/objects/Tool.scala @@ -18,7 +18,8 @@ import scala.annotation.tailrec * @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields */ class Tool(private val toolDef : ToolDefinition) extends Equipment - with FireModeSwitch[FireModeDefinition] { + with FireModeSwitch[FireModeDefinition] + with JammableUnit { /** index of the current fire mode on the `ToolDefinition`'s list of fire modes */ private var fireModeIndex : Int = toolDef.DefaultFireModeIndex /** current ammunition slot being used by this fire mode */ diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala index 4b7a217f..8ed7f5f2 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala @@ -74,7 +74,7 @@ object AvatarConverter { alt_model_flag, false, None, - false, + obj.Jammed, None, v5 = None, PlanetSideGUID(0) diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala index 7a4414a5..7d716e56 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.definition.converter -import net.psforever.objects.Player +import net.psforever.objects.{Player, Tool} import net.psforever.objects.equipment.{Equipment, EquipmentSlot} import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate._ @@ -136,17 +136,38 @@ class CharacterSelectConverter extends AvatarConverter { } else { val slot : EquipmentSlot = iter.next - if(slot.Equipment.isDefined) { - val equip : Equipment = slot.Equipment.get - recursiveMakeHolsters( - iter, - list :+ AvatarConverter.BuildDetailedEquipment(index, equip), - index + 1 - ) - } - else { - recursiveMakeHolsters(iter, list, index + 1) + slot.Equipment match { + case Some(equip : Tool) => + val jammed = equip.Jammed + equip.Jammed = false + val slot = AvatarConverter.BuildDetailedEquipment(index, equip) + equip.Jammed = jammed + recursiveMakeHolsters( + iter, + list :+ slot, + index + 1 + ) + case Some(equip) => + recursiveMakeHolsters( + iter, + list :+ AvatarConverter.BuildDetailedEquipment(index, equip), + index + 1 + ) + case _ => + recursiveMakeHolsters(iter, list, index + 1) } +// if(slot.Equipment.isDefined) { +// +// val equip : Equipment = slot.Equipment.get +// recursiveMakeHolsters( +// iter, +// list :+ AvatarConverter.BuildDetailedEquipment(index, equip), +// index + 1 +// ) +// } +// else { +// recursiveMakeHolsters(iter, list, index + 1) +// } } } } diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/FieldTurretConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/FieldTurretConverter.scala index 9ced731e..63b0e21a 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/FieldTurretConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/FieldTurretConverter.scala @@ -23,7 +23,7 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() { alternate = false, true, None, - false, + jammered = obj.Jammed, Some(false), None, obj.Owner match { diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala index 75b77a9c..6e50230f 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala @@ -21,7 +21,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl alternate = false, v1 = false, v2 = None, - v3 = false, + jammered = obj.Jammed, None, None, obj.Owner match { @@ -45,7 +45,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl alternate = true, v1 = false, v2 = None, - v3 = false, + jammered = obj.Jammed, None, None, PlanetSideGUID(0) diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/SmallDeployableConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/SmallDeployableConverter.scala index 7b10159b..a9175aab 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/SmallDeployableConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/SmallDeployableConverter.scala @@ -3,6 +3,7 @@ package net.psforever.objects.definition.converter import net.psforever.objects.ce.Deployable import net.psforever.objects.PlanetSideGameObject +import net.psforever.objects.equipment.JammableUnit import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate._ @@ -15,11 +16,14 @@ class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObjec PlacementData(obj.Position, obj.Orientation), CommonFieldData( obj.Faction, - false, - false, + bops = false, + alternate = false, false, None, - false, + jammered = obj match { + case o : JammableUnit => o.Jammed + case _ => false + }, Some(false), None, obj.Owner match { @@ -33,4 +37,4 @@ class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObjec override def DetailedConstructorData(obj : PlanetSideGameObject with Deployable) : Try[CommonFieldDataWithPlacement] = Failure(new Exception("converter should not be used to generate detailed small deployable data")) -} \ No newline at end of file +} diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/SmallTurretConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/SmallTurretConverter.scala index ec01eccc..d740e62a 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/SmallTurretConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/SmallTurretConverter.scala @@ -23,7 +23,7 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() { alternate = false, false, None, - false, + jammered = obj.Jammed, Some(true), None, obj.Owner match { diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/ToolConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/ToolConverter.scala index a7886d44..a51804be 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/ToolConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/ToolConverter.scala @@ -21,7 +21,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() { alternate = false, true, None, - false, + obj.Jammed, None, None, PlanetSideGUID(0) @@ -45,7 +45,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() { alternate = false, true, None, - false, + obj.Jammed, None, None, PlanetSideGUID(0) diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala index e0eb5461..2444bef4 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala @@ -25,7 +25,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() { alternate = false, v1 = false, v2 = None, - v3 = false, + jammered = obj.Jammed, v4 = Some(false), v5 = None, obj.Owner match { @@ -56,7 +56,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() { alternate = true, v1 = false, v2 = None, - v3 = false, + jammered = obj.Jammed, v4 = Some(false), v5 = None, guid = PlanetSideGUID(0) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EffectTarget.scala b/common/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala similarity index 98% rename from common/src/main/scala/net/psforever/objects/serverobject/terminals/EffectTarget.scala rename to common/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala index 38e790df..8871250a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EffectTarget.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.objects.serverobject.terminals +package net.psforever.objects.equipment import net.psforever.objects._ import net.psforever.objects.ce.DeployableCategory 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 cab7622a..9d211e4e 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala @@ -5,7 +5,6 @@ import akka.actor.{Actor, Cancellable} import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject, Tool} import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.serverobject.PlanetSideServerObject -import net.psforever.objects.serverobject.terminals.TargetValidation import net.psforever.objects.vehicles.MountedWeapons import net.psforever.objects.zones.ZoneAware import net.psforever.types.Vector3 @@ -15,7 +14,12 @@ import services.vehicle.{VehicleAction, VehicleServiceMessage} import scala.collection.mutable import scala.concurrent.duration._ +/** + * A property conferred to game objects that can be affected by an electromagnetic pulse. + * Being "jammered" is a status that causes weakness due to temporary equipment disabling or the elimination of certain objects. + */ trait JammableUnit { + /** being jammed (jammered) is an on/off state */ private var jammed : Boolean = false def Jammed : Boolean = jammed @@ -27,16 +31,34 @@ trait JammableUnit { } object JammableUnit { + /** + * A message for generic jammering. + * Currently, unused. + */ final case class Jammer() - + /** + * A message for jammering due to a projectile. + * @param cause information pertaining to the projectile + */ final case class Jammered(cause : ResolvedProjectile) - + /** + * Stop the auditory aspect of being jammered. + */ final case class ClearJammeredSound() - + /** + * Stop the status effects of being jammered. + */ final case class ClearJammeredStatus() } +/** + * A property conferred onto game objects that can induce the effects of an electromagnetic pulse. + * @see `TargetValidation` + * @see `EffectTarget` + */ trait JammingUnit { + /** a list of qualifying conditional tests for determining if an object is to be affected by the jammered status; + * if qualifying, that object will be inflicted with a number of milliseconds of the jammered status */ private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer() def HasJammedEffectDuration : Boolean = jammedEffectDuration.isEmpty @@ -45,6 +67,15 @@ trait JammingUnit { } object JammingUnit { + /** + * Determine whether an object that can be jammered is to be jammered by this source, + * and for how long. + * If the object succeeds for multiple qualification tests, + * prioritize the lengthiest duration. + * @param jammer the source of the "jammered" status + * @param target the object to be determined if affected by the source's jammering + * @return the duration to be jammered, if any, in milliseconds + */ def FindJammerDuration(jammer : JammingUnit, target : PlanetSideGameObject) : Option[Int] = { jammer.JammedEffectDuration .collect { case (TargetValidation(_, test), duration) if test(target) => duration } @@ -53,18 +84,45 @@ object JammingUnit { .headOption } + /** + * Determine whether a group of objects that can be jammered is to be jammered by this source, + * and for how long. + * If the object succeeds for multiple qualification tests, + * prioritize the lengthiest duration. + * @param jammer the source of the "jammered" status + * @param targets the objects to be determined if affected by the source's jammering + * @return the indexed durations to be jammered, if any, in milliseconds + */ def FindJammerDuration(jammer : JammingUnit, targets : Seq[PlanetSideGameObject]) : Seq[Option[Int]] = { targets.map { target => FindJammerDuration(jammer, target) } } } +/** + * An `Actor` control object mix-in that manages common responses to the "jammerable" status. + * Two aspects to jammering are supported - + * a telling buzzing sound that follows the affected target + * and actual effects upon the target's actions - + * and are controlled independently. + * The primary purpose of this behavior is to control timers that toggle the states of these two aspects. + */ trait JammableBehavior { - _ : Actor => + this : Actor => + /** flag for jammed sound */ + protected var jammedSound : Boolean = false + /** the sound timer */ protected var jammeredSoundTimer : Cancellable = DefaultCancellable.obj + /** the effect timer */ protected var jammeredStatusTimer : Cancellable = DefaultCancellable.obj + /** `ZoneAware` is used for callback to the event systems */ def JammableObject : PlanetSideServerObject with JammableUnit with ZoneAware + /** + * If the target can be validated against, affect it with the jammered status. + * @param target the objects to be determined if affected by the source's jammering + * @param cause the source of the "jammered" status + */ def TryJammerEffectActivate(target : Any, cause : ResolvedProjectile) : Unit = target match { case obj : PlanetSideServerObject => val radius = cause.projectile.profile.DamageRadius @@ -77,12 +135,30 @@ trait JammableBehavior { case _ => ; } + /** + * Activate a distinctive buzzing sound effect. + * Due to considerations of the object that is the target, this is left to be implemented by a subclass. + * We merely start the timer. + * @param target an object that can be affected by the jammered status + * @param dur the duration of the timer, in milliseconds; + * by default, 30000 + */ def StartJammeredSound(target : Any, dur : Int = 30000) : Unit = { - import scala.concurrent.ExecutionContext.Implicits.global - jammeredSoundTimer.cancel - jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, JammableUnit.ClearJammeredSound()) + if(!jammedSound) { + jammedSound = true + import scala.concurrent.ExecutionContext.Implicits.global + jammeredSoundTimer.cancel + jammeredSoundTimer = context.system.scheduler.scheduleOnce(30 seconds, self, JammableUnit.ClearJammeredSound()) + } } + /** + * Deactivate the effects of the jammered status. + * Due to considerations of the object that is the target, this is left to be implemented by a subclass. + * We merely stop the timer. + * @param target an object that can be affected by the jammered status + * @param dur the duration of the timer, in milliseconds + */ def StartJammeredStatus(target : Any, dur : Int) : Unit = { JammableObject.Jammed = true jammeredStatusTimer.cancel @@ -90,10 +166,23 @@ trait JammableBehavior { jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredStatus()) } + /** + * Deactivate a distinctive buzzing sound effect. + * Due to considerations of the object that is the target, this is left to be implemented by a subclass. + * We merely stop the timer. + * @param target an object that can be affected by the jammered status + */ def CancelJammeredSound(target : Any) : Unit = { + jammedSound = false jammeredSoundTimer.cancel } + /** + * Deactivate the effects of the jammered status. + * Due to considerations of the object that is the target, this is left to be implemented by a subclass. + * We merely stop the timer. + * @param target an object that can be affected by the jammered status + */ def CancelJammeredStatus(target : Any) : Unit = { JammableObject.Jammed = false jammeredStatusTimer.cancel @@ -111,47 +200,71 @@ trait JammableBehavior { } } +/** + * A common mix-in variation to manage common responses to the "jammerable" status for game objects with mounted weapons. + * @see `MountedWeapons` + * @see `Service` + * @see `VehicleAction` + * @see `VehicleService` + * @see `VehicleServiceMessage` + * @see `Zone.VehicleEvents` + */ trait JammableMountedWeapons extends JammableBehavior { _ : Actor => - override def StartJammeredSound(target : Any, dur : Int) : Unit = target match { - case obj : PlanetSideServerObject with MountedWeapons => - obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)) - super.StartJammeredSound(obj, dur) - case _ => ; + override def StartJammeredSound(target : Any, dur : Int) : Unit = { + target match { + case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if !jammedSound => + obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)) + super.StartJammeredSound(target, dur) + case _ => ; + } } - override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { - case obj : PlanetSideServerObject with MountedWeapons => - JammableMountedWeapons.JammeredStatus(obj, 1) - super.StartJammeredStatus(obj, dur) - case _ => ; + override def StartJammeredStatus(target : Any, dur : Int) : Unit = { + target match { + case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if !obj.Jammed => + JammableMountedWeapons.JammeredStatus(obj, 1) + super.StartJammeredStatus(target, dur) + case _ => ; + } } - override def CancelJammeredSound(target : Any) : Unit = target match { - case obj : PlanetSideServerObject => - obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)) - super.CancelJammeredSound(obj) - case _ => ; + override def CancelJammeredSound(target : Any) : Unit = { + target match { + case obj : PlanetSideServerObject if jammedSound => + obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)) + case _ => ; + } + super.CancelJammeredSound(target) } - override def CancelJammeredStatus(target : Any) : Unit = target match { - case obj : PlanetSideServerObject with MountedWeapons => - JammableMountedWeapons.JammeredStatus(obj, 0) - super.CancelJammeredStatus(obj) - case _ => ; + override def CancelJammeredStatus(target : Any) : Unit = { + target match { + case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if obj.Jammed => + JammableMountedWeapons.JammeredStatus(obj, 0) + case _ => ; + } + super.CancelJammeredStatus(target) } } object JammableMountedWeapons { + /** + * Retrieve all of the weapons on a `MountedWeapons` target object and apply a jammered status effect to each. + * @param target an object that can be affected by the jammered status + * @param statusCode the jammered status condition; + * 0 for deactivation; + * 1 for activation + */ def JammeredStatus(target : PlanetSideServerObject with MountedWeapons, statusCode : Int) : Unit = { val zone = target.Zone val zoneId = zone.Id - zone.VehicleEvents ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 27, statusCode)) target.Weapons.values .map { _.Equipment } .collect { case Some(item : Tool) => + item.Jammed = statusCode==1 zone.VehicleEvents ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, item.GUID, 27, statusCode)) } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala index 4278cec0..e2b9f889 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.equipment.EffectTarget import scala.collection.mutable 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 85933857..78b6a72e 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -115,7 +115,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor } sender ! FactionAffinity.AssertFactionAffinity(vehicle, faction) - case Vehicle.PrepareForDeletion => + case Vehicle.PrepareForDeletion() => CancelJammeredSound(vehicle) CancelJammeredStatus(vehicle) context.become(Disabled) @@ -126,10 +126,10 @@ class VehicleControl(vehicle : Vehicle) extends Actor def Disabled : Receive = checkBehavior .orElse(dismountBehavior) .orElse { - case Vehicle.Reactivate => + case Vehicle.Reactivate() => context.become(Enabled) - case _ => ; + case _ => } } diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala index 7e2f302a..54f38376 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala @@ -327,7 +327,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] { } else if(data.faction == PlanetSideEmpire.NEUTRAL) { Attempt.successful( - CommonFieldData(faction, data.bops, data.alternate, data.v1, data.v2, data.v3, None, data.v5, PlanetSideGUID(0)) :: + CommonFieldData(faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, None, data.v5, PlanetSideGUID(0)) :: name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil ) } diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala index 6c9ef68c..7620af20 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CommonFieldData.scala @@ -8,13 +8,13 @@ import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} -final case class CommonFieldDataExtra(unk1 : Int, unk2 : Boolean) extends StreamBitSize { +final case class CommonFieldDataExtra(unk1 : Option[Int], unk2 : Boolean) extends StreamBitSize { override def bitsize : Long = 17L } object CommonFieldDataExtra { - implicit val codec : Codec[CommonFieldDataExtra] = ( - ("unk1" | uint16L) :: + def codec(unk1 : Boolean) : Codec[CommonFieldDataExtra] = ( + ("unk1" | conditional(unk1, uint16L)) :: //not sure what flags this field ("unk2" | bool) ).as[CommonFieldDataExtra] } @@ -28,10 +28,9 @@ object CommonFieldDataExtra { * when set on a tool, that tool will be rendered nonfunctional instead (though it can still be equipped) * @param v1 na * @param v2 na; - * optional data whose reading is triggered in unknown conditions; - * flag a weapon as "jammered" - * @param v3 na; - * for weapons, works like `alternate` + * optional data whose reading is triggered in unknown conditions + * @param jammered flag as "jammered;" + * set on most game objects, that object will produce the characteristic jammered buzz * @param v4 na; * a field used by a second encoding format for this data * @param v5 na; @@ -43,7 +42,7 @@ final case class CommonFieldData(faction : PlanetSideEmpire.Value, alternate : Boolean, v1 : Boolean, v2 : Option[CommonFieldDataExtra], - v3 : Boolean, + jammered : Boolean, v4 : Option[Boolean], v5 : Option[Int], guid : PlanetSideGUID @@ -64,7 +63,7 @@ final case class CommonFieldData(faction : PlanetSideEmpire.Value, 23L + extraSize + v4Size + v5Size } - def apply(flag : Boolean) : CommonFieldData = CommonFieldData(faction, bops, alternate, v1, v2, v3, Some(flag), v5, guid) + def apply(flag : Boolean) : CommonFieldData = CommonFieldData(faction, bops, alternate, v1, v2, jammered, Some(flag), v5, guid) } object CommonFieldData extends Marshallable[CommonFieldData] { @@ -100,8 +99,8 @@ object CommonFieldData extends Marshallable[CommonFieldData] { ("bops" | bool) :: ("alternate" | bool) :: ("v1" | bool) :: //the purpose of this bit changes depending on the previous bit - conditional(extra, "v2" | CommonFieldDataExtra.codec) :: - ("v3" | bool) :: + conditional(extra, "v2" | CommonFieldDataExtra.codec(unk1 = false)) :: + ("jammered" | bool) :: optional(bool, "v5" | uint16L) :: ("guid" | PlanetSideGUID.codec) ).xmap[CommonFieldData] ( @@ -122,8 +121,8 @@ object CommonFieldData extends Marshallable[CommonFieldData] { ("bops" | bool) :: ("alternate" | bool) :: ("v1" | bool) :: //though the code path differs depending on the previous bit, this one gets read one way or another - conditional(extra, "v2" | CommonFieldDataExtra.codec) :: - ("v3" | bool) :: + conditional(extra, "v2" | CommonFieldDataExtra.codec(unk1 = false)) :: + ("jammered" | bool) :: optional(bool, "v5" | uint16L) :: ("v4" | bool) :: ("guid" | PlanetSideGUID.codec) diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/OneMannedFieldTurretData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/OneMannedFieldTurretData.scala index 7035050d..7140afd6 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/OneMannedFieldTurretData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/OneMannedFieldTurretData.scala @@ -71,7 +71,7 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] { OneMannedFieldTurretData( CommonFieldDataWithPlacement( deploy.pos, - CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.v3, data.v4, data.v5, player) + CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, data.v4, data.v5, player) ), newHealth, newInternals @@ -92,7 +92,7 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] { Attempt.successful( CommonFieldDataWithPlacement( pos, - CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.v3, data.v4, data.v5, PlanetSideGUID(0)) + CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, data.v4, data.v5, PlanetSideGUID(0)) ) :: data.guid :: false :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil ) } diff --git a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala index de832306..4e45c037 100644 --- a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala +++ b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala @@ -26,7 +26,7 @@ class VehicleRemover extends RemoverActor { val vehicle = entry.obj.asInstanceOf[Vehicle] val vehicleGUID = vehicle.GUID val zoneId = entry.zone.Id - vehicle.Actor ! Vehicle.PrepareForDeletion + vehicle.Actor ! Vehicle.PrepareForDeletion() //escape being someone else's cargo (vehicle.MountedIn match { case Some(carrierGUID) => diff --git a/common/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala b/common/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala index 49d16d86..a2e07d99 100644 --- a/common/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala +++ b/common/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala @@ -29,7 +29,7 @@ class AegisShieldGeneratorDataTest extends Specification { basic.data.alternate mustEqual false basic.data.v1 mustEqual true basic.data.v2.isDefined mustEqual false - basic.data.v3 mustEqual false + basic.data.jammered mustEqual false basic.data.v5.isDefined mustEqual false basic.data.guid mustEqual PlanetSideGUID(2366) diff --git a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala index 5a8f6343..19b51356 100644 --- a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala +++ b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala @@ -43,7 +43,7 @@ class CharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Reinforced @@ -162,7 +162,7 @@ class CharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Reinforced @@ -231,7 +231,7 @@ class CharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.MAX diff --git a/common/src/test/scala/game/objectcreate/OneMannedFieldTurretDataTest.scala b/common/src/test/scala/game/objectcreate/OneMannedFieldTurretDataTest.scala index b94884ab..79e8e32e 100644 --- a/common/src/test/scala/game/objectcreate/OneMannedFieldTurretDataTest.scala +++ b/common/src/test/scala/game/objectcreate/OneMannedFieldTurretDataTest.scala @@ -28,7 +28,7 @@ class OneMannedFieldTurretDataTest extends Specification { deploy.alternate mustEqual false deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.contains(false) mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(2502) diff --git a/common/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala b/common/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala index 8e09c859..a6cf8062 100644 --- a/common/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala +++ b/common/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala @@ -29,7 +29,7 @@ class RemoteProjectileDataTest extends Specification { deploy.alternate mustEqual false deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.isEmpty mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(0) @@ -63,7 +63,7 @@ class RemoteProjectileDataTest extends Specification { deploy.alternate mustEqual false deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.isEmpty mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(0) diff --git a/common/src/test/scala/game/objectcreate/SmallTurretDataTest.scala b/common/src/test/scala/game/objectcreate/SmallTurretDataTest.scala index fc901305..929cd99d 100644 --- a/common/src/test/scala/game/objectcreate/SmallTurretDataTest.scala +++ b/common/src/test/scala/game/objectcreate/SmallTurretDataTest.scala @@ -30,7 +30,7 @@ class SmallTurretDataTest extends Specification { deploy.alternate mustEqual true deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.contains(false) mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(7742) @@ -61,7 +61,7 @@ class SmallTurretDataTest extends Specification { deploy.alternate mustEqual false deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.contains(true) mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(8208) diff --git a/common/src/test/scala/game/objectcreate/TRAPDataTest.scala b/common/src/test/scala/game/objectcreate/TRAPDataTest.scala index 6e899523..63c81efa 100644 --- a/common/src/test/scala/game/objectcreate/TRAPDataTest.scala +++ b/common/src/test/scala/game/objectcreate/TRAPDataTest.scala @@ -28,7 +28,7 @@ class TRAPDataTest extends Specification { deploy.alternate mustEqual false deploy.v1 mustEqual true deploy.v2.isEmpty mustEqual true - deploy.v3 mustEqual false + deploy.jammered mustEqual false deploy.v4.contains(true) mustEqual true deploy.v5.isEmpty mustEqual true deploy.guid mustEqual PlanetSideGUID(4748) diff --git a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala index 1178a619..81b17668 100644 --- a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala +++ b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala @@ -71,7 +71,7 @@ class DetailedCharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual true a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Standard @@ -259,7 +259,7 @@ class DetailedCharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Standard @@ -444,7 +444,7 @@ class DetailedCharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual true a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.MAX @@ -652,7 +652,7 @@ class DetailedCharacterDataTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual true a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Agile @@ -1104,7 +1104,7 @@ class DetailedCharacterDataTest extends Specification { cdata.data.alternate mustEqual false cdata.data.v1 mustEqual true cdata.data.v2.isEmpty mustEqual true - cdata.data.v3 mustEqual false + cdata.data.jammered mustEqual false cdata.data.v4.isEmpty mustEqual true cdata.data.v5.isEmpty mustEqual true cdata.data.guid mustEqual PlanetSideGUID(0) @@ -1160,7 +1160,7 @@ class DetailedCharacterDataTest extends Specification { a.data.alternate mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Standard @@ -1310,7 +1310,7 @@ class DetailedCharacterDataTest extends Specification { a.data.alternate mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Standard diff --git a/common/src/test/scala/game/objectcreatedetailed/DetailedConstructionToolDataTest.scala b/common/src/test/scala/game/objectcreatedetailed/DetailedConstructionToolDataTest.scala index a363b799..c4e16b0b 100644 --- a/common/src/test/scala/game/objectcreatedetailed/DetailedConstructionToolDataTest.scala +++ b/common/src/test/scala/game/objectcreatedetailed/DetailedConstructionToolDataTest.scala @@ -31,7 +31,7 @@ class DetailedConstructionToolDataTest extends Specification { cdata.alternate mustEqual false cdata.v1 mustEqual true cdata.v2.isEmpty mustEqual true - cdata.v3 mustEqual false + cdata.jammered mustEqual false cdata.v4.isEmpty mustEqual true cdata.v5.isEmpty mustEqual true cdata.guid mustEqual PlanetSideGUID(0) @@ -71,7 +71,7 @@ class DetailedConstructionToolDataTest extends Specification { cdata.alternate mustEqual false cdata.v1 mustEqual true cdata.v2.isEmpty mustEqual true - cdata.v3 mustEqual false + cdata.jammered mustEqual false cdata.v4.isEmpty mustEqual true cdata.v5.isEmpty mustEqual true cdata.guid mustEqual PlanetSideGUID(0) @@ -111,7 +111,7 @@ class DetailedConstructionToolDataTest extends Specification { cdata.alternate mustEqual false cdata.v1 mustEqual true cdata.v2.isEmpty mustEqual true - cdata.v3 mustEqual false + cdata.jammered mustEqual false cdata.v4.isEmpty mustEqual true cdata.v5.contains(564) mustEqual true cdata.guid mustEqual PlanetSideGUID(0) @@ -139,7 +139,7 @@ class DetailedConstructionToolDataTest extends Specification { cdata.alternate mustEqual false cdata.v1 mustEqual false cdata.v2.isEmpty mustEqual true - cdata.v3 mustEqual false + cdata.jammered mustEqual false cdata.v4.isEmpty mustEqual true cdata.v5.isEmpty mustEqual true cdata.guid mustEqual PlanetSideGUID(0) diff --git a/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala index 4cb2c906..ea206eab 100644 --- a/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala +++ b/common/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala @@ -32,7 +32,7 @@ class MountedVehiclesTest extends Specification { vdata.data.bops mustEqual false vdata.data.alternate mustEqual false vdata.data.v1 mustEqual false - vdata.data.v3 mustEqual false + vdata.data.jammered mustEqual false vdata.data.v5.isEmpty mustEqual true vdata.data.guid mustEqual PlanetSideGUID(3776) vdata.health mustEqual 255 @@ -57,7 +57,7 @@ class MountedVehiclesTest extends Specification { a.data.bops mustEqual false a.data.v1 mustEqual false a.data.v2.isEmpty mustEqual true - a.data.v3 mustEqual false + a.data.jammered mustEqual false a.data.v4.isEmpty mustEqual true a.data.v5.isEmpty mustEqual true a.exosuit mustEqual ExoSuitType.Agile diff --git a/common/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala index 849e0d3d..ca0aaeec 100644 --- a/common/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala +++ b/common/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala @@ -31,7 +31,7 @@ class NonstandardVehiclesTest extends Specification { basic.data.alternate mustEqual false basic.data.v1 mustEqual true basic.data.v2.isDefined mustEqual false - basic.data.v3 mustEqual false + basic.data.jammered mustEqual false basic.data.v5.isDefined mustEqual false basic.data.guid mustEqual PlanetSideGUID(0) diff --git a/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala index 5d5dcc4c..a0e38289 100644 --- a/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala +++ b/common/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala @@ -173,7 +173,7 @@ class NormalVehiclesTest extends Specification { vdata.faction mustEqual PlanetSideEmpire.NC vdata.alternate mustEqual false vdata.v1 mustEqual true - vdata.v3 mustEqual false + vdata.jammered mustEqual false vdata.v5.isEmpty mustEqual true vdata.guid mustEqual PlanetSideGUID(0) diff --git a/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala b/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala index a7e2e093..6ad1af20 100644 --- a/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala +++ b/common/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala @@ -29,7 +29,7 @@ class UtilityVehiclesTest extends Specification { ant.data.faction mustEqual PlanetSideEmpire.VS ant.data.alternate mustEqual false ant.data.v1 mustEqual true - ant.data.v3 mustEqual false + ant.data.jammered mustEqual false ant.data.v5.isEmpty mustEqual true ant.data.guid mustEqual PlanetSideGUID(0) ant.driveState mustEqual DriveState.Mobile @@ -59,7 +59,7 @@ class UtilityVehiclesTest extends Specification { ams.data.faction mustEqual PlanetSideEmpire.VS ams.data.alternate mustEqual false ams.data.v1 mustEqual false - ams.data.v3 mustEqual false + ams.data.jammered mustEqual false ams.data.v5.isEmpty mustEqual true ams.data.guid mustEqual PlanetSideGUID(2885) ams.driveState mustEqual DriveState.Deployed diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala index d1401251..66a3104d 100644 --- a/common/src/test/scala/objects/VehicleTest.scala +++ b/common/src/test/scala/objects/VehicleTest.scala @@ -2,6 +2,7 @@ package objects import akka.actor.Props +import akka.testkit.TestProbe import base.ActorTest import net.psforever.objects._ import net.psforever.objects.ballistics.{PlayerSource, Projectile, ProjectileResolution, ResolvedProjectile} @@ -9,9 +10,11 @@ import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition} import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.vehicles._ import net.psforever.objects.vital.{VehicleShieldCharge, Vitality} +import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.PlanetSideGUID import net.psforever.types._ import org.specs2.mutable._ +import services.vehicle.{VehicleAction, VehicleServiceMessage} import scala.concurrent.duration._ @@ -324,66 +327,82 @@ class VehicleControlStopMountingTest extends ActorTest { val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) vehicle.GUID = PlanetSideGUID(3) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = new TestProbe(system).ref //necessary + } + vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4) + val probe = new TestProbe(system) - vehicle.Actor ! Mountable.TryMount(player1, 0) - val reply = receiveOne(Duration.create(100, "ms")) + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) + val reply = probe.receiveOne(Duration.create(200, "ms")) assert(reply.isInstanceOf[Mountable.MountMessages]) - vehicle.Actor ! Vehicle.PrepareForDeletion - vehicle.Actor ! Mountable.TryMount(player2, 1) - expectNoMsg(Duration.create(200, "ms")) + vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref) + vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) + probe.expectNoMsg(Duration.create(200, "ms")) //assertion failed: received unexpected message MountMessages(CanMount } } } class VehicleControlRestartMountingTest extends ActorTest { + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar2) + player2.GUID = PlanetSideGUID(2) + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(3) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = new TestProbe(system).ref + } + vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4) + val probe = new TestProbe(system) + "Vehicle Control" should { "reactivate and resume handling mount messages" in { - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar2) - player2.GUID = PlanetSideGUID(2) - val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) - vehicle.GUID = PlanetSideGUID(3) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) + probe.receiveOne(Duration.create(200, "ms")) //discard + vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref) + vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) + probe.expectNoMsg(Duration.create(200, "ms")) - vehicle.Actor ! Mountable.TryMount(player1, 0) - receiveOne(Duration.create(100, "ms")) //discard - vehicle.Actor ! Vehicle.PrepareForDeletion - vehicle.Actor ! Mountable.TryMount(player2, 1) - expectNoMsg(Duration.create(200, "ms")) - - vehicle.Actor ! Vehicle.Reactivate - vehicle.Actor ! Mountable.TryMount(player2, 1) - val reply = receiveOne(Duration.create(100, "ms")) + vehicle.Actor.tell(Vehicle.Reactivate(), probe.ref) + vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) + val reply = probe.receiveOne(Duration.create(200, "ms")) assert(reply.isInstanceOf[Mountable.MountMessages]) } } } class VehicleControlAlwaysDismountTest extends ActorTest { + val probe = new TestProbe(system) + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar2) + player2.GUID = PlanetSideGUID(2) + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(3) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = new TestProbe(system).ref + } + vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4) + "Vehicle Control" should { "always allow dismount messages" in { - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar2) - player2.GUID = PlanetSideGUID(2) - val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) - vehicle.GUID = PlanetSideGUID(3) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - vehicle.Actor ! Mountable.TryMount(player1, 0) - receiveOne(Duration.create(100, "ms")) //discard - vehicle.Actor ! Mountable.TryMount(player2, 1) - receiveOne(Duration.create(100, "ms")) //discard + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) + probe.receiveOne(Duration.create(100, "ms")) //discard + vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) + probe.receiveOne(Duration.create(100, "ms")) //discard - vehicle.Actor ! Mountable.TryDismount(player2, 1) //player2 requests dismount - val reply1 = receiveOne(Duration.create(100, "ms")) + vehicle.Actor.tell(Mountable.TryDismount(player2, 1), probe.ref) //player2 requests dismount + val reply1 = probe.receiveOne(Duration.create(100, "ms")) assert(reply1.isInstanceOf[Mountable.MountMessages]) assert(reply1.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player2 dismounts - vehicle.Actor ! Vehicle.PrepareForDeletion - vehicle.Actor ! Mountable.TryDismount(player1, 0) //player1 requests dismount - val reply2 = receiveOne(Duration.create(100, "ms")) + vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref) + vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref) //player1 requests dismount + val reply2 = probe.receiveOne(Duration.create(100, "ms")) assert(reply2.isInstanceOf[Mountable.MountMessages]) assert(reply2.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player1 dismounts } @@ -391,8 +410,9 @@ class VehicleControlAlwaysDismountTest extends ActorTest { } class VehicleControlMountingBlockedExosuitTest extends ActorTest { + val probe = new TestProbe(system) def checkCanNotMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanNotMount]) @@ -402,7 +422,7 @@ class VehicleControlMountingBlockedExosuitTest extends ActorTest { } def checkCanMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanMount]) @@ -410,49 +430,49 @@ class VehicleControlMountingBlockedExosuitTest extends ActorTest { assert(false) } } + val vehicle = Vehicle(GlobalDefinitions.apc_tr) + vehicle.GUID = PlanetSideGUID(10) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + + val player1 = Player(VehicleTest.avatar1) + player1.ExoSuit = ExoSuitType.Reinforced + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar1) + player2.ExoSuit = ExoSuitType.MAX + player2.GUID = PlanetSideGUID(2) + val player3 = Player(VehicleTest.avatar1) + player3.ExoSuit = ExoSuitType.Agile + player3.GUID = PlanetSideGUID(3) "Vehicle Control" should { "block players from sitting if their exo-suit is not allowed by the seat" in { - val vehicle = Vehicle(GlobalDefinitions.apc_tr) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - - val player1 = Player(VehicleTest.avatar1) - player1.ExoSuit = ExoSuitType.Reinforced - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar1) - player2.ExoSuit = ExoSuitType.MAX - player2.GUID = PlanetSideGUID(2) - val player3 = Player(VehicleTest.avatar1) - player3.ExoSuit = ExoSuitType.Agile - player3.GUID = PlanetSideGUID(3) - //disallow - vehicle.Actor ! Mountable.TryMount(player1, 0) //Reinforced in non-MAX seat + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) //Reinforced in non-MAX seat checkCanNotMount() - vehicle.Actor ! Mountable.TryMount(player2, 0) //MAX in non-Reinforced seat + vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref) //MAX in non-Reinforced seat checkCanNotMount() - vehicle.Actor ! Mountable.TryMount(player2, 1) //MAX in non-MAX seat + vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) //MAX in non-MAX seat checkCanNotMount() - vehicle.Actor ! Mountable.TryMount(player1, 9) //Reinforced in MAX-only seat + vehicle.Actor.tell(Mountable.TryMount(player1, 9), probe.ref) //Reinforced in MAX-only seat checkCanNotMount() - vehicle.Actor ! Mountable.TryMount(player3, 9) //Agile in MAX-only seat + vehicle.Actor.tell(Mountable.TryMount(player3, 9), probe.ref) //Agile in MAX-only seat checkCanNotMount() //allow - vehicle.Actor ! Mountable.TryMount(player1, 1) + vehicle.Actor.tell(Mountable.TryMount(player1, 1), probe.ref) checkCanMount() - vehicle.Actor ! Mountable.TryMount(player2, 9) + vehicle.Actor.tell(Mountable.TryMount(player2, 9), probe.ref) checkCanMount() - vehicle.Actor ! Mountable.TryMount(player3, 0) + vehicle.Actor.tell(Mountable.TryMount(player3, 0), probe.ref) checkCanMount() } } } class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest { + val probe = new TestProbe(system) def checkCanNotMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanNotMount]) @@ -462,7 +482,7 @@ class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest { } def checkCanMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanMount]) @@ -470,32 +490,33 @@ class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest { assert(false) } } + val vehicle = Vehicle(GlobalDefinitions.apc_tr) + vehicle.GUID = PlanetSideGUID(10) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar1) + player2.GUID = PlanetSideGUID(2) "Vehicle Control" should { //11 June 2018: Group is not supported yet so do not bother testing it "block players from sitting if the seat does not allow it" in { - val vehicle = Vehicle(GlobalDefinitions.apc_tr) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar1) - player2.GUID = PlanetSideGUID(2) vehicle.PermissionGroup(2,3) //passenger group -> empire - vehicle.Actor ! Mountable.TryMount(player1, 3) //passenger seat + vehicle.Actor.tell(Mountable.TryMount(player1, 3), probe.ref) //passenger seat checkCanMount() vehicle.PermissionGroup(2,0) //passenger group -> locked - vehicle.Actor ! Mountable.TryMount(player2, 4) //passenger seat + vehicle.Actor.tell(Mountable.TryMount(player2, 4), probe.ref) //passenger seat checkCanNotMount() } } } class VehicleControlMountingDriverSeatTest extends ActorTest { + val probe = new TestProbe(system) def checkCanMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanMount]) @@ -503,19 +524,18 @@ class VehicleControlMountingDriverSeatTest extends ActorTest { assert(false) } } + val vehicle = Vehicle(GlobalDefinitions.apc_tr) + vehicle.GUID = PlanetSideGUID(10) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) "Vehicle Control" should { "allow players to sit in the driver seat, even if it is locked, if the vehicle is unowned" in { - val vehicle = Vehicle(GlobalDefinitions.apc_tr) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked assert(vehicle.Seats(0).Occupant.isEmpty) assert(vehicle.Owner.isEmpty) - vehicle.Actor ! Mountable.TryMount(player1, 0) + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) checkCanMount() assert(vehicle.Seats(0).Occupant.nonEmpty) } @@ -523,8 +543,9 @@ class VehicleControlMountingDriverSeatTest extends ActorTest { } class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest { + val probe = new TestProbe(system) def checkCanNotMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanNotMount]) @@ -534,7 +555,7 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest { } def checkCanMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanMount]) @@ -542,30 +563,28 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest { assert(false) } } + val vehicle = Vehicle(GlobalDefinitions.apc_tr) + vehicle.GUID = PlanetSideGUID(10) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar1) + player2.GUID = PlanetSideGUID(2) "Vehicle Control" should { "block players that are not the current owner from sitting in the driver seat (locked)" in { - val vehicle = Vehicle(GlobalDefinitions.apc_tr) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar1) - player2.GUID = PlanetSideGUID(2) - assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked assert(vehicle.Seats(0).Occupant.isEmpty) vehicle.Owner = player1.GUID - vehicle.Actor ! Mountable.TryMount(player1, 0) + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) checkCanMount() assert(vehicle.Seats(0).Occupant.nonEmpty) - vehicle.Actor ! Mountable.TryDismount(player1, 0) - receiveOne(Duration.create(100, "ms")) //discard + vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref) + probe.receiveOne(Duration.create(100, "ms")) //discard assert(vehicle.Seats(0).Occupant.isEmpty) - vehicle.Actor ! Mountable.TryMount(player2, 0) + vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref) checkCanNotMount() assert(vehicle.Seats(0).Occupant.isEmpty) } @@ -573,8 +592,9 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest { } class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest { + val probe = new TestProbe(system) def checkCanMount() : Unit = { - val reply = receiveOne(Duration.create(100, "ms")) + val reply = probe.receiveOne(Duration.create(100, "ms")) reply match { case msg : Mountable.MountMessages => assert(msg.response.isInstanceOf[Mountable.CanMount]) @@ -582,31 +602,29 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest { assert(false) } } + val vehicle = Vehicle(GlobalDefinitions.apc_tr) + vehicle.GUID = PlanetSideGUID(10) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + val player1 = Player(VehicleTest.avatar1) + player1.GUID = PlanetSideGUID(1) + val player2 = Player(VehicleTest.avatar1) + player2.GUID = PlanetSideGUID(2) "Vehicle Control" should { "allow players that are not the current owner to sit in the driver seat (empire)" in { - val vehicle = Vehicle(GlobalDefinitions.apc_tr) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - - val player1 = Player(VehicleTest.avatar1) - player1.GUID = PlanetSideGUID(1) - val player2 = Player(VehicleTest.avatar1) - player2.GUID = PlanetSideGUID(2) - vehicle.PermissionGroup(0,3) //passenger group -> empire assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Empire)) //driver group -> empire assert(vehicle.Seats(0).Occupant.isEmpty) vehicle.Owner = player1.GUID //owner set - vehicle.Actor ! Mountable.TryMount(player1, 0) + vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) checkCanMount() assert(vehicle.Seats(0).Occupant.nonEmpty) - vehicle.Actor ! Mountable.TryDismount(player1, 0) - receiveOne(Duration.create(100, "ms")) //discard + vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref) + probe.receiveOne(Duration.create(100, "ms")) //discard assert(vehicle.Seats(0).Occupant.isEmpty) - vehicle.Actor ! Mountable.TryMount(player2, 0) + vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref) checkCanMount() assert(vehicle.Seats(0).Occupant.nonEmpty) } @@ -614,26 +632,37 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest { } class VehicleControlShieldsChargingTest extends ActorTest { + val probe = new TestProbe(system) val vehicle = Vehicle(GlobalDefinitions.fury) vehicle.GUID = PlanetSideGUID(10) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = probe.ref + } "charge vehicle shields" in { assert(vehicle.Shields == 0) assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) - vehicle.Actor ! Vehicle.ChargeShields(15) - val msg = receiveOne(500 milliseconds) - assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge]) + vehicle.Actor ! Vehicle.ChargeShields(15) + val msg = probe.receiveOne(500 milliseconds) + assert(msg match { + case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(10), 68, 15)) => true + case _ => false + }) assert(vehicle.Shields == 15) assert(vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) } } class VehicleControlShieldsNotChargingVehicleDeadTest extends ActorTest { + val probe = new TestProbe(system) val vehicle = Vehicle(GlobalDefinitions.fury) vehicle.GUID = PlanetSideGUID(10) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = probe.ref + } "not charge vehicle shields if the vehicle is destroyed" in { assert(vehicle.Health > 0) @@ -641,18 +670,22 @@ class VehicleControlShieldsNotChargingVehicleDeadTest extends ActorTest { assert(vehicle.Health == 0) assert(vehicle.Shields == 0) assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) - vehicle.Actor ! Vehicle.ChargeShields(15) + vehicle.Actor.tell(Vehicle.ChargeShields(15), probe.ref) - expectNoMsg(1 seconds) + probe.expectNoMsg(1 seconds) assert(vehicle.Shields == 0) assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) } } class VehicleControlShieldsNotChargingVehicleShieldsFullTest extends ActorTest { + val probe = new TestProbe(system) val vehicle = Vehicle(GlobalDefinitions.fury) vehicle.GUID = PlanetSideGUID(10) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = probe.ref + } "not charge vehicle shields if the vehicle is destroyed" in { assert(vehicle.Shields == 0) @@ -661,54 +694,67 @@ class VehicleControlShieldsNotChargingVehicleShieldsFullTest extends ActorTest { assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) vehicle.Actor ! Vehicle.ChargeShields(15) - expectNoMsg(1 seconds) + probe.expectNoMsg(1 seconds) assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]})) } } class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest { + val probe = new TestProbe(system) val vehicle = Vehicle(GlobalDefinitions.fury) vehicle.GUID = PlanetSideGUID(10) vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") + vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { + VehicleEvents = probe.ref + } "charge vehicle shields" in { assert(vehicle.Shields == 0) - vehicle.Actor ! Vehicle.ChargeShields(15) - val msg = receiveOne(200 milliseconds) - assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge]) + vehicle.Actor ! Vehicle.ChargeShields(15) + val msg = probe.receiveOne(200 milliseconds) + //assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge]) + assert(msg match { + case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(10), 68, 15)) => true + case _ => false + }) assert(vehicle.Shields == 15) - vehicle.Actor ! Vehicle.ChargeShields(15) - expectNoMsg(200 milliseconds) + vehicle.Actor ! Vehicle.ChargeShields(15) + probe.expectNoMsg(200 milliseconds) assert(vehicle.Shields == 15) } } -class VehicleControlShieldsNotChargingDamagedTest extends ActorTest { - val vehicle = Vehicle(GlobalDefinitions.fury) - vehicle.GUID = PlanetSideGUID(10) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") - // - val beamer_wep = Tool(GlobalDefinitions.beamer) - val p_source = PlayerSource( Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) ) - val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero) - val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel - val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime) - - "not charge vehicle shields if recently damaged" in { - assert(vehicle.Shields == 0) - vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj); obj }) - - val msg = receiveOne(200 milliseconds) - assert(msg.isInstanceOf[Vitality.DamageResolution]) - assert(vehicle.Shields == 0) - vehicle.Actor ! Vehicle.ChargeShields(15) - - expectNoMsg(200 milliseconds) - assert(vehicle.Shields == 0) - } -} +//TODO implement message protocol for zone startup completion +//class VehicleControlShieldsNotChargingDamagedTest extends ActorTest { +// val probe = new TestProbe(system) +// val vehicle = Vehicle(GlobalDefinitions.fury) +// vehicle.GUID = PlanetSideGUID(10) +// vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") +// vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) { +// VehicleEvents = probe.ref +// } +// // +// val beamer_wep = Tool(GlobalDefinitions.beamer) +// val p_source = PlayerSource( Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) ) +// val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero) +// val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel +// val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime) +// +// "not charge vehicle shields if recently damaged" in { +// assert(vehicle.Shields == 0) +// vehicle.Actor.tell(Vitality.Damage({case v : Vehicle => v.History(obj); obj }), probe.ref) +// +// val msg = probe.receiveOne(200 milliseconds) +// assert(msg.isInstanceOf[Vitality.DamageResolution]) +// assert(vehicle.Shields == 0) +// vehicle.Actor.tell(Vehicle.ChargeShields(15), probe.ref) +// +// probe.expectNoMsg(200 milliseconds) +// assert(vehicle.Shields == 0) +// } +//} object VehicleTest { import net.psforever.objects.Avatar diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 5f2a7ab6..86d71313 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -3326,15 +3326,15 @@ class WorldSessionActor extends Actor //player.Position = Vector3(4262.211f ,4067.0625f ,262.35938f) //z6, Akna.tower //player.Orientation = Vector3(0f, 0f, 132.1875f) // player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting - player.Slot(0).Equipment = Tool(jammer_grenade) //Tool(GlobalDefinitions.StandardPistol(player.Faction)) + player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(player.Faction)) player.Slot(2).Equipment = Tool(suppressor) player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction)) - player.Slot(6).Equipment = ConstructionItem(ace) //AmmoBox(bullet_9mm) - player.Slot(9).Equipment = ConstructionItem(ace) //AmmoBox(bullet_9mm) - player.Slot(12).Equipment = ConstructionItem(ace) //AmmoBox(bullet_9mm) - player.Slot(33).Equipment = ConstructionItem(ace) //AmmoBox(bullet_9mm_AP) - player.Slot(36).Equipment = ConstructionItem(ace) //AmmoBox(GlobalDefinitions.StandardPistolAmmo(player.Faction)) - player.Slot(39).Equipment = Tool(jammer_grenade) //SimpleItem(remote_electronics_kit) + player.Slot(6).Equipment = AmmoBox(bullet_9mm) + player.Slot(9).Equipment = AmmoBox(bullet_9mm) + 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.Locker.Inventory += 0 -> SimpleItem(remote_electronics_kit) player.Inventory.Items.foreach { _.obj.Faction = faction } player.Actor = self @@ -3741,9 +3741,6 @@ class WorldSessionActor extends Actor DeactivateImplants() } //implants and stamina management finish - if(is_crouching && !player.Crouching) { - // ... - } player.Position = pos player.Velocity = vel player.Orientation = Vector3(player.Orientation.x, pitch, yaw) @@ -10071,68 +10068,20 @@ class WorldSessionActor extends Actor projectilesToCleanUp(local_index) = false } - def FindJammerTargetsInScope(jammer : Any with JammingUnit) : Seq[PlanetSideGameObject] = { - (jammer match { - case p : ProjectileDefinition if !p.JammerProjectile => None - case p => Some(p) - }) match { - case Some(p : JammingUnit) => - p.JammedEffectDuration - .map { case (a, _) => a } - .collect { - case TargetValidation(EffectTarget.Category.Player, test) if test(player) => Some(player) - case TargetValidation(EffectTarget.Category.Vehicle, test) if { - continent.GUID(player.VehicleSeated) match { - case Some(v) => test(v) - case None => false - } - } => continent.GUID(player.VehicleSeated) - case TargetValidation(EffectTarget.Category.Aircraft, test) if { - continent.GUID(player.VehicleSeated) match { - case Some(v) => test(v) - case None => false - } - } => continent.GUID(player.VehicleSeated) - } collect { - case Some(a) => a - } toSeq - case _ => - Seq.empty[PlanetSideGameObject] - } - } - - def CompileJammerTests(jammer : JammingUnit) : Iterable[EffectTarget.Validation.Value] = { - jammer.JammedEffectDuration map { case (TargetValidation(_, test), _) => test } - } - - def CompileJammerTests(jammer : JammingUnit, target : EffectTarget.Category.Value) : Iterable[EffectTarget.Validation.Value] = { - jammer.JammedEffectDuration collect { case (TargetValidation(filter, test), _) if filter == target => test } - } - - def FindJammerTargetsInScope(jammer : JammingUnit, scope : Seq[PlanetSideGUID], zone : Zone) : Seq[(PlanetSideGUID, PlanetSideGameObject)] = { - val tests = CompileJammerTests(jammer) - (for { - uid <- scope - obj = zone.GUID(uid) - if obj.nonEmpty - } yield (uid, obj.get)) - .collect { - case out @ (_, b) if tests.foldLeft(false)(_ || _(b)) => out - } - } - - def FindJammerTargetsInScope(jammer : JammingUnit, scope : Seq[PlanetSideGameObject]) : Seq[PlanetSideGameObject] = { - val tests = CompileJammerTests(jammer) - scope collect { - case a if tests.foldLeft(false)(_ || _(a)) => a - } - } - + /** + * Deactivate all active implants. + * This method is intended to support only the current Live server implants that are functional, + * the darklight vision implant and the surge implant. + */ def DeactivateImplants() : Unit = { DeactivateImplantDarkLight() DeactivateImplantSurge() } + /** + * Deactivate the darklight vision implant. + * This method is intended to support only the current Live server implants. + */ def DeactivateImplantDarkLight() : Unit = { if(avatar.Implants(0).Active) { avatar.Implants(0).Active = false @@ -10142,6 +10091,10 @@ class WorldSessionActor extends Actor } } + /** + * Deactivate the surge implant. + * This method is intended to support only the current Live server implants. + */ def DeactivateImplantSurge() : Unit = { if(avatar.Implants(1).Active) { avatar.Implants(1).Active = false @@ -10151,34 +10104,39 @@ class WorldSessionActor extends Actor } } - override def TryJammerEffectActivate(target : Any, cause : ResolvedProjectile) : Unit = target match { - case obj : Player => - val radius = cause.projectile.profile.DamageRadius - JammingUnit.FindJammerDuration(cause.projectile.profile, obj) match { - case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius => - DeactivateImplants() - skipStaminaRegenForTurns = 5 - StartJammeredSound(obj) - StartJammeredStatus(obj, dur) - case _ => ; - } - case _ => ; - } - + /** + * Start the jammered buzzing. + * @see `JammableHevaior.StartJammeredSound` + * @param target an object that can be affected by the jammered status + * @param dur the duration of the timer, in milliseconds; + * by default, 30000 + */ override def StartJammeredSound(target : Any, dur : Int) : Unit = target match { - case obj : Player => + case obj : Player if !jammedSound => sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 1)) continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 1)) super.StartJammeredSound(obj, dur) case _ => ; } + /** + * Perform a variety of tasks to indicate being jammered. + * Deactivate implants (should also uninitialize them), + * delay stamina regeneration for a certain number of turns, + * and set the jammered status on specific holstered equipment. + * @see `JammableHevaior.StartJammeredStatus` + * @param target an object that can be affected by the jammered status + * @param dur the duration of the timer, in milliseconds + */ override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match { - case obj : Player => + case obj : Player if !obj.Jammed => + DeactivateImplants() + skipStaminaRegenForTurns = 10 jammeredEquipment = (jammeredEquipment ++ obj.Holsters() .map { _.Equipment } .collect { - case Some(item) if item.Size != EquipmentSize.Melee => + case Some(item : Tool) if item.Size != EquipmentSize.Melee => + item.Jammed = true sendResponse(PlanetsideAttributeMessage(item.GUID, 27, 1)) item.GUID }).distinct @@ -10186,17 +10144,33 @@ class WorldSessionActor extends Actor case _ => ; } + /** + * Stop the jammered buzzing. + * @see `JammableHevaior.CancelJammeredSound` + * @param target an object that can be affected by the jammered status + */ override def CancelJammeredSound(target : Any) : Unit = target match { - case obj : Player => + case obj : Player if jammedSound => sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 0)) continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 0)) super.CancelJammeredSound(obj) case _ => ; } + /** + * Reset jammered status of previously-affected equipment. + * @see `JammableHevaior.CancelJammeredStatus` + * @param target an object that can be affected by the jammered status + */ override def CancelJammeredStatus(target : Any) : Unit = target match { - case obj : Player => - jammeredEquipment.foreach { id => sendResponse(PlanetsideAttributeMessage(id, 27, 0)) } + case obj : Player if obj.Jammed => + jammeredEquipment.foreach { id => + continent.GUID(id) match { + case Some(item : JammableUnit) => item.Jammed = false + case _ => ; + } + sendResponse(PlanetsideAttributeMessage(id, 27, 0)) + } jammeredEquipment = Nil super.CancelJammeredStatus(obj) case _ => ;