adjusted CommonFieldData to support jammering effect flag; fixed tests; made jammering sound and status contingent on state, but made cancelling sound and status always call up

This commit is contained in:
FateJH 2019-12-30 08:50:16 -05:00
parent 879be93863
commit 6c76997675
33 changed files with 518 additions and 353 deletions

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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 */

View file

@ -74,7 +74,7 @@ object AvatarConverter {
alt_model_flag,
false,
None,
false,
obj.Jammed,
None,
v5 = None,
PlanetSideGUID(0)

View file

@ -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)
// }
}
}
}

View file

@ -23,7 +23,7 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
alternate = false,
true,
None,
false,
jammered = obj.Jammed,
Some(false),
None,
obj.Owner match {

View file

@ -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)

View file

@ -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"))
}
}

View file

@ -23,7 +23,7 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
alternate = false,
false,
None,
false,
jammered = obj.Jammed,
Some(true),
None,
obj.Owner match {

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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))
}
}

View file

@ -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

View file

@ -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 _ =>
}
}

View file

@ -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
)
}

View file

@ -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)

View file

@ -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
)
}

View file

@ -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) =>

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 _ => ;