diff --git a/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala b/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
index cf445115..7fad9e15 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
@@ -4,20 +4,54 @@ import net.psforever.objects.equipment.TargetValidation
import net.psforever.objects.serverobject.aura.Aura
import net.psforever.objects.vital.DamageType
+/**
+ * In what manner of pacing the aggravated damage ticks are applied.
+ * @param duration for how long the over-all effect is applied
+ * @param ticks a custom number of damage applications,
+ * as opposed to whatever calculations normally estimate the number of applications
+ */
final case class AggravatedTiming(duration: Long, ticks: Option[Int])
object AggravatedTiming {
+ /**
+ * Overloaded constructor that only defines the duration.
+ * @param duration for how long the over-all effect lasts
+ * @return an `AggravatedTiming` object
+ */
def apply(duration: Long): AggravatedTiming = AggravatedTiming(duration, None)
+ /**
+ * Overloaded constructor.
+ * @param duration for how long the over-all effect lasts
+ * @param ticks a custom number of damage applications
+ * @return an `AggravatedTiming` object
+ */
def apply(duration: Long, ticks: Int): AggravatedTiming = AggravatedTiming(duration, Some(ticks))
}
+/**
+ * Aggravation damage has components that are mainly divided by the `DamageType` they inflict.
+ * Only `Direct` and `Splash` are valid damage types, however.
+ * @param damage_type the type of damage
+ * @param degradation_percentage by how much the damage is degraded
+ * @param infliction_rate how often the damage is inflicted (ms)
+ */
final case class AggravatedInfo(damage_type: DamageType.Value,
degradation_percentage: Float,
infliction_rate: Long) {
assert(damage_type == DamageType.Direct || damage_type == DamageType.Splash, s"aggravated damage is an unsupported type - $damage_type")
}
+/**
+ * Information related to the aggravated damage.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na (if the target is a mechanized assault exo-suit?)
+ * @param cumulative_damage_degrade na (can multiple instances of this type of aggravated damage apply to the same target at once?)
+ * @param vanu_aggravated na (search me)
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
final case class AggravatedDamage(info: List[AggravatedInfo],
effect_type: Aura,
timing: AggravatedTiming,
@@ -27,6 +61,14 @@ final case class AggravatedDamage(info: List[AggravatedInfo],
targets: List[TargetValidation])
object AggravatedDamage {
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
def apply(info: AggravatedInfo,
effect_type: Aura,
timing: AggravatedTiming,
@@ -42,6 +84,15 @@ object AggravatedDamage {
targets
)
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na
+ * @param vanu_aggravated na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
def apply(info: AggravatedInfo,
effect_type: Aura,
timing: AggravatedTiming,
@@ -58,6 +109,14 @@ object AggravatedDamage {
targets
)
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param duration for how long the over-all effect is applied
+ * @param max_factor na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
def apply(info: AggravatedInfo,
effect_type: Aura,
duration: Long,
@@ -73,6 +132,15 @@ object AggravatedDamage {
targets
)
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param duration for how long the over-all effect is applied
+ * @param max_factor na
+ * @param vanu_aggravated na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
def apply(info: AggravatedInfo,
effect_type: Aura,
duration: Long,
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
index 8fe9d38c..9a630845 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
@@ -15,7 +15,7 @@ sealed trait ProjectileQuality {
}
/**
- * Implement the numeric modifier with as one.
+ * Implement the numeric modifier with the value as one.
*/
sealed trait SameAsQuality extends ProjectileQuality {
def mod: Float = 1f
@@ -25,7 +25,7 @@ object ProjectileQuality {
/** Standard projectile quality. More of a flag than a modifier. */
case object Normal extends SameAsQuality
- /** Quality that flags the first stage of aggravation (setup). */
+ /** Quality that flags the first stage of aggravation (initial damage). */
case object AggravatesTarget extends SameAsQuality
/** The complete lack of quality. Even the numeric modifier is zeroed. */
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala
index 6dc233a5..51683d18 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala
@@ -2,19 +2,28 @@
package net.psforever.objects.serverobject.aura
import akka.actor.{Actor, Cancellable}
+import net.psforever.objects.Default
import net.psforever.objects.serverobject.PlanetSideServerObject
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
+/**
+ * A mixin that governs the addition, display, and removal of aura particle effects
+ * on a target with control agency.
+ * @see `Aura`
+ * @see `AuraContainer`
+ * @see `PlayerControl`
+ */
trait AuraEffectBehavior {
_ : Actor =>
- private var activeEffectIndex: Long = 0
- private val effectToEntryId: mutable.HashMap[Aura, List[Long]] =
- mutable.HashMap.empty[Aura, List[Long]]
- private val effectIdToTimer: mutable.LongMap[Cancellable] =
- mutable.LongMap.empty[Cancellable]
+ /** active aura effects are monotonic, but the timer will be updated for continuing and cancelling effects as well
+ * only effects that are initialized to this mapping are approved for display on this target
+ * key - aura effect; value - the timer for that effect
+ * @see `ApplicableEffect`
+ */
+ private val effectToTimer: mutable.HashMap[Aura, Cancellable] = mutable.HashMap.empty[Aura, Cancellable]
def AuraTargetObject: AuraEffectBehavior.Target
@@ -22,144 +31,142 @@ trait AuraEffectBehavior {
case AuraEffectBehavior.StartEffect(effect, duration) =>
StartAuraEffect(effect, duration)
- case AuraEffectBehavior.EndEffect(Some(id), None) =>
- EndAuraEffect(id)
-
- case AuraEffectBehavior.EndEffect(None, Some(effect)) =>
- EndAuraEffect(effect)
+ case AuraEffectBehavior.EndEffect(effect) =>
+ EndAuraEffectAndUpdate(effect)
case AuraEffectBehavior.EndAllEffects() =>
EndAllEffectsAndUpdate()
}
- final def GetUnusedEffectId: Long = {
- val id = activeEffectIndex
- activeEffectIndex += 1
- id
+ /**
+ * Only pre-apporved aura effects will be emitted by this target.
+ * @param effect the aura effect
+ */
+ def ApplicableEffect(effect: Aura): Unit = {
+ //create entry
+ effectToTimer += effect -> Default.Cancellable
}
- def StartAuraEffect(effect: Aura, duration: Long): Option[Long] = {
+ /**
+ * An aura particle effect is to be emitted by the target.
+ * If the effect was not previously applied to the target in an ongoing manner,
+ * animate it appropriately.
+ * @param effect the effect to be emitted
+ * @param duration for how long the effect will be emitted
+ * @return the active effect index number
+ */
+ def StartAuraEffect(effect: Aura, duration: Long): Unit = {
val obj = AuraTargetObject
- val auraEffects = obj.Aura
- if (obj.Aura.contains(effect)) {
- effectToEntryId.getOrElse(effect, List[Long](AuraEffectBehavior.InvalidEffectId)).headOption //grab an available active effect id
- }
- else if(obj.AddEffectToAura(effect).diff(auraEffects).contains(effect)) {
- Some(StartAuraEffect(GetUnusedEffectId, effect, duration))
- }
- else {
- None
+ val auraEffectsBefore = obj.Aura.size
+ if(StartAuraTimer(effect, duration) && obj.AddEffectToAura(effect).size > auraEffectsBefore) {
+ //new effect; update visuals
+ UpdateAuraEffect(AuraTargetObject)
}
}
- def StartAuraEffect(id: Long, effect: Aura, duration: Long): Long = {
+ /**
+ * As long as the effect has been approved for this target,
+ * the timer will either start if it is stopped or has never been started,
+ * or the timer will stop and be recreated with the new duration if is currently running.
+ * @param effect the effect to be emitted
+ * @param duration for how long the effect will be emitted
+ * @return `true`, if the timer was started or restarted;
+ * `false`, otherwise
+ */
+ private def StartAuraTimer(effect: Aura, duration: Long): Boolean = {
//pair aura effect with id
- effectToEntryId.get(effect) match {
- case None | Some(Nil) => effectToEntryId += effect -> List(id)
- case Some(list) => effectToEntryId -> (list :+ id)
- }
- //pair id with timer
- effectIdToTimer += id -> context.system.scheduler.scheduleOnce(duration milliseconds, self, AuraEffectBehavior.EndEffect(id))
- //update visuals
- UpdateAuraEffect(AuraTargetObject)
- id
- }
-
- def EndAuraEffect(id: Long): Unit = {
- EndActiveEffect(id) match {
- case Aura.Nothing => ;
- case effect =>
- CancelEffectTimer(id)
- val obj = AuraTargetObject
- obj.RemoveEffectFromAura(effect)
- UpdateAuraEffect(obj)
- }
- }
-
- def EndActiveEffect(id: Long): Aura = {
- effectToEntryId.find { case (_, ids) => ids.contains(id) } match {
- case Some((effect, ids)) if ids.size == 1 =>
- effectToEntryId.remove(effect)
- effect
- case Some((effect, ids)) =>
- effectToEntryId += effect -> ids.filterNot(_ == id)
- Aura.Nothing
+ (effectToTimer.get(effect) match {
case None =>
- Aura.Nothing
+ None
+ case Some(timer) =>
+ timer.cancel()
+ Some(effect)
+ }) match {
+ case None =>
+ false
+ case Some(_) =>
+ //paired id with timer; retime
+ effectToTimer(effect) =
+ context.system.scheduler.scheduleOnce(duration milliseconds, self, AuraEffectBehavior.EndEffect(effect))
+ true
}
}
- def CancelEffectTimer(id: Long) : Unit = {
- effectIdToTimer.remove(id) match {
- case Some(timer) => timer.cancel
- case _ => ;
- }
- }
-
- def EndAuraEffect(effect: Aura): Unit = {
- effectToEntryId.remove(effect) match {
- case Some(idList) =>
- idList.foreach { id =>
- val obj = AuraTargetObject
- CancelEffectTimer(id)
- obj.RemoveEffectFromAura(effect)
- UpdateAuraEffect(obj)
- }
- case _ => ;
+ /**
+ * Stop the target entity from emitting the aura particle effect, if it currently is.
+ * @param effect the target effect
+ * @return `true`, if the effect was being emitted but has been stopped
+ * `false`, if the effect was not approved or is not being emitted
+ */
+ def EndAuraEffect(effect: Aura): Boolean = {
+ effectToTimer.get(effect) match {
+ case Some(timer) if !timer.isCancelled =>
+ timer.cancel()
+ effectToTimer(effect) = Default.Cancellable
+ AuraTargetObject.RemoveEffectFromAura(effect)
+ true
+ case _ =>
+ false
}
}
+ /**
+ * Stop the target entity from emitting all aura particle effects.
+ */
def EndAllEffects() : Unit = {
- effectIdToTimer.values.foreach { _.cancel }
- effectIdToTimer.clear
- effectToEntryId.clear
+ effectToTimer.keysIterator.foreach { effect =>
+ effectToTimer(effect).cancel()
+ effectToTimer(effect) = Default.Cancellable
+ }
val obj = AuraTargetObject
obj.Aura.foreach { obj.RemoveEffectFromAura }
}
+ /**
+ * Stop the target entity from emitting the aura particle effect, if it currently is.
+ * If the effect has been stopped, animate the new particle effect state.
+ */
+ def EndAuraEffectAndUpdate(effect: Aura) : Unit = {
+ if(EndAuraEffect(effect)) {
+ UpdateAuraEffect(AuraTargetObject)
+ }
+ }
+
+ /**
+ * Stop the target entity from emitting all aura particle effects.
+ * Animate the new particle effect state.
+ */
def EndAllEffectsAndUpdate() : Unit = {
EndAllEffects()
UpdateAuraEffect(AuraTargetObject)
}
- def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = {
- import services.avatar.{AvatarAction, AvatarServiceMessage}
- val zone = target.Zone
- val value = target.Aura.foldLeft(0)(_ + AuraEffectBehavior.effectToAttributeValue(_))
- zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
- }
-
- def TestForEffect(id: Long): Aura = {
- effectToEntryId.find { case (_, ids) => ids.contains(id) } match {
- case Some((effect, _)) => effect
- case _ => Aura.Nothing
+ /**
+ * Is the target entity emitting the aura effect?
+ * @param effect the effect being tested
+ * @return `true`, if the effect is currently being emitted;
+ * `false`, otherwise
+ */
+ def TestForEffect(effect: Aura): Boolean = {
+ effectToTimer.get(effect) match {
+ case None => false
+ case Some(timer) => timer.isCancelled
}
}
+
+ /**
+ * An override callback to display aura effects emitted.
+ * @param target the entity from which the aura effects are being emitted
+ */
+ def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit
}
object AuraEffectBehavior {
type Target = PlanetSideServerObject with AuraContainer
- final val InvalidEffectId = -1
-
final case class StartEffect(effect: Aura, duration: Long)
- final case class EndEffect(id: Option[Long], aura: Option[Aura])
-
- object EndEffect {
- def apply(id: Long): EndEffect = EndEffect(Some(id), None)
-
- def apply(aura: Aura): EndEffect = EndEffect(None, Some(aura))
- }
+ final case class EndEffect(aura: Aura)
final case class EndAllEffects()
-
- private def effectToAttributeValue(effect: Aura): Int = effect match {
- case Aura.None => 0
- case Aura.Plasma => 1
- case Aura.Comet => 2
- case Aura.Napalm => 4
- case Aura.Fire => 8
- case _ => Int.MinValue
- }
}
diff --git a/common/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala b/common/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala
index faa48528..2456bee2 100644
--- a/common/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala
@@ -7,12 +7,17 @@ import scodec.Codec
import scodec.codecs._
/**
- * na
- * @param guid na
- * @param unk na
+ * Dispatched from the server to cause a damage reaction from a specific target.
+ * Infantry targets should be the primary target of this packet, as indicated by their identifier.
+ * Infantry targets display their flinch animation.
+ * All targets yelp in agony.
+ * Infantry targets use their assigned voice.
+ * Non-infantry targets use the "grizzled"(?) voice.
+ * @param guid the target entity's global unique identifier
+ * @param damage the amount of damsge being simulated
*/
final case class AggravatedDamageMessage(guid : PlanetSideGUID,
- unk : Long)
+ damage : Long)
extends PlanetSideGamePacket {
type Packet = AggravatedDamageMessage
def opcode = GamePacketOpcode.AggravatedDamageMessage
@@ -22,6 +27,6 @@ final case class AggravatedDamageMessage(guid : PlanetSideGUID,
object AggravatedDamageMessage extends Marshallable[AggravatedDamageMessage] {
implicit val codec : Codec[AggravatedDamageMessage] = (
("guid" | PlanetSideGUID.codec) ::
- ("unk" | uint32L)
+ ("damage" | uint32L)
).as[AggravatedDamageMessage]
}
diff --git a/common/src/test/scala/objects/AuraTest.scala b/common/src/test/scala/objects/AuraTest.scala
new file mode 100644
index 00000000..a4af3983
--- /dev/null
+++ b/common/src/test/scala/objects/AuraTest.scala
@@ -0,0 +1,237 @@
+// Copyright (c) 2020 PSForever
+package objects
+
+import akka.actor.{Actor, ActorRef, Props}
+import akka.testkit.TestProbe
+import base.ActorTest
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.serverobject.aura.AuraEffectBehavior.Target
+import net.psforever.objects.serverobject.aura.{Aura, AuraContainer, AuraEffectBehavior}
+import net.psforever.types.PlanetSideEmpire
+import org.specs2.mutable.Specification
+
+import scala.concurrent.duration._
+
+class AuraContainerTest extends Specification {
+ "AuraContainer" should {
+ "have no default effects" in {
+ new AuraTest.Entity().Aura.isEmpty mustEqual true
+ }
+
+ "add effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ }
+
+ "do nothing if adding repeated effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ }
+
+ "remove effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.RemoveEffectFromAura(Aura.Plasma)
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ }
+
+ "do nothing if no effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.RemoveEffectFromAura(Aura.Plasma)
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ }
+
+ "do nothing if trying to remove wrong effect" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ obj.RemoveEffectFromAura(Aura.Fire)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ }
+ }
+}
+
+class AuraEffectBehaviorInitTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+
+ "AuraEffectBehavior" should {
+ "init" in {
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, ActorRef.noSender), "aura-test-actor")
+ expectNoMessage(500 milliseconds)
+ }
+ }
+}
+
+class AuraEffectBehaviorStartEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "start effect (ends naturally)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ expectNoMessage(2000 milliseconds)
+ assert(obj.Aura.contains(Aura.Plasma))
+ val msg2 = updateProbe.receiveOne(750 milliseconds)
+ assert(
+ msg2 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.isEmpty)
+ }
+ }
+}
+
+class AuraEffectBehaviorNoRedundantStartEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "not start an effect if already active" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ expectNoMessage(1000 milliseconds) //wait for half of the effect's duration
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ updateProbe.expectNoMessage(1500 milliseconds)
+ //effect has not ended naturally
+ assert(obj.Aura.contains(Aura.Plasma))
+ }
+ }
+}
+
+class AuraEffectBehaviorNoStartUnsupportedEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor") //supports Plasma only
+
+ "AuraEffectBehavior" should {
+ "not start an effect that is not approved" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Fire, 2500)
+ assert(obj.Aura.isEmpty)
+ updateProbe.expectNoMessage(2000 milliseconds)
+ }
+ }
+}
+
+
+
+class AuraEffectBehaviorEndEarlyTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "start effect (ends early)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Plasma)
+ val msg2 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg2 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.isEmpty)
+ }
+ }
+}
+
+class AuraEffectBehaviorEndNothingTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "can not end an effect that is not supported (hence, not started)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.size == 1)
+ obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Fire)
+ updateProbe.expectNoMessage(1000 milliseconds)
+ assert(obj.Aura.size == 1)
+ }
+ }
+}
+
+object AuraTest {
+ class Agency(obj: AuraEffectBehavior.Target, updateRef: ActorRef) extends Actor with AuraEffectBehavior {
+ def AuraTargetObject : Target = obj
+ ApplicableEffect(Aura.Plasma)
+
+ def receive: Receive = auraBehavior.orElse {
+ case _ => ;
+ }
+
+ def UpdateAuraEffect(target : Target) : Unit = {
+ updateRef ! DoUpdateAuraEffect()
+ }
+ }
+
+ class Entity extends PlanetSideServerObject with AuraContainer {
+ def Faction = PlanetSideEmpire.NEUTRAL
+ def Definition = null
+ }
+
+ final case class DoUpdateAuraEffect()
+}
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index e9a3b956..bed0afbb 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -8,7 +8,7 @@ import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.loadouts.Loadout
-import net.psforever.objects.serverobject.aura.AuraEffectBehavior
+import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.vital.PlayerSuicide
@@ -36,13 +36,18 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
with ContainableBehavior
with AggravatedBehavior
with AuraEffectBehavior {
- def JammableObject = player
+
+ def JammableObject = player
def DamageableObject = player
def ContainerObject = player
def AggravatedObject = player
+ ApplicableEffect(Aura.Plasma)
+ ApplicableEffect(Aura.Napalm)
+ ApplicableEffect(Aura.Comet)
+ ApplicableEffect(Aura.Fire)
def AuraTargetObject = player
@@ -922,4 +927,28 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)
)
}
+
+ def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = {
+ import services.avatar.{AvatarAction, AvatarServiceMessage}
+ val zone = target.Zone
+ val value = target.Aura.foldLeft(0)(_ + PlayerControl.auraEffectToAttributeValue(_))
+ zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
+ }
+}
+
+object PlayerControl {
+ /**
+ * Transform an applicable Aura effect into its `PlanetsideAttributeMessage` value.
+ * @see `Aura`
+ * @see `PlanetsideAttributeMessage`
+ * @param effect the aura effect
+ * @return the attribute value for that effect
+ */
+ private def auraEffectToAttributeValue(effect: Aura): Int = effect match {
+ case Aura.Plasma => 1
+ case Aura.Comet => 2
+ case Aura.Napalm => 4
+ case Aura.Fire => 8
+ case _ => 0
+ }
}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
index e1be38de..e0009f1e 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
@@ -1,7 +1,6 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.vital.damage
-import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.ballistics._
import net.psforever.objects.vital.DamageType
import net.psforever.types.{ExoSuitType, Vector3}
@@ -135,26 +134,68 @@ object DamageModifiers {
}
}
+ /*
+ Below this point are the calculations for sources of aggravated damage.
+ For the most part, these calculations are individualistic and arbitrary.
+ They exist in their current form to satisfy observed shots to kill (STK) of specific weapon systems
+ according to 2012 standards of the Youtube video series by TheLegendaryNarwhal.
+ */
+ /**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Direct`.
+ */
case object InfantryAggravatedDirect extends Mod {
def Calculate: DamageModifiers.Format =
BaseAggravatedFormula(ProjectileResolution.AggravatedDirect, DamageType.Direct)
}
+ /**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Splash`.
+ */
case object InfantryAggravatedSplash extends Mod {
def Calculate: DamageModifiers.Format =
BaseAggravatedFormula(ProjectileResolution.AggravatedSplash, DamageType.Splash)
}
+ /**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Direct`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
case object InfantryAggravatedDirectBurn extends Mod {
def Calculate: DamageModifiers.Format =
BaseAggravatedBurnFormula(ProjectileResolution.AggravatedDirectBurn, DamageType.Direct)
}
+ /**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Splash`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
case object InfantryAggravatedSplashBurn extends Mod {
def Calculate: DamageModifiers.Format =
BaseAggravatedBurnFormula(ProjectileResolution.AggravatedSplashBurn, DamageType.Splash)
}
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that initial damage application for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirect`
+ * @see `InfantryAggravatedSplash`
+ * @see `PlayerSource`
+ * @see `ProjectileTarget.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
private def BaseAggravatedFormula(
resolution: ProjectileResolution.Value,
damageType : DamageType.Value
@@ -186,6 +227,24 @@ object DamageModifiers {
}
}
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that damage application burn for each tick for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * Vanilla infantry incorporate their resistance value into a slightly different calculation than usual.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirectBurn`
+ * @see `InfantryAggravatedSplashBurn`
+ * @see `PlayerSource`
+ * @see `ResolvedProjectile`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
private def BaseAggravatedBurnFormula(
resolution: ProjectileResolution.Value,
damageType : DamageType.Value
@@ -207,16 +266,11 @@ object DamageModifiers {
(damage * degradation * aggravation.max_factor) toInt
} else {
val resist = data.damage_model.ResistUsing(data)(data)
+ //add resist to offset resist subtraction later
if (damage > resist) {
((damage - resist) * degradation).toInt + resist
} else {
- val degradedDamage = damage * degradation
- if (degradedDamage > resist) {
- degradedDamage toInt
- }
- else {
- damage
- }
+ (damage * degradation).toInt + resist
}
}
case _ =>
@@ -227,14 +281,21 @@ object DamageModifiers {
}
}
+ /**
+ * The initial application of aggravated damage against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ */
case object StarfireAggravated extends Mod {
def Calculate: DamageModifiers.Format = formula
private def formula(damage: Int, data: ResolvedProjectile): Int = {
if (data.resolution == ProjectileResolution.AggravatedDirect &&
data.projectile.quality == ProjectileQuality.AggravatesTarget) {
- (data.projectile.profile.Aggravated, data.target) match {
- case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
aggravation.info.find(_.damage_type == DamageType.Direct) match {
case Some(infos) =>
(damage * infos.degradation_percentage + damage) toInt
@@ -250,13 +311,21 @@ object DamageModifiers {
}
}
+ /**
+ * The ongoing application of aggravated damage ticks against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ * @see `ResolvedProjectile`
+ */
case object StarfireAggravatedBurn extends Mod {
def Calculate: DamageModifiers.Format = formula
private def formula(damage: Int, data: ResolvedProjectile): Int = {
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
- (data.projectile.profile.Aggravated, data.target) match {
- case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
aggravation.info.find(_.damage_type == DamageType.Direct) match {
case Some(infos) =>
(math.floor(damage * infos.degradation_percentage) * data.projectile.quality.mod) toInt
@@ -272,6 +341,13 @@ object DamageModifiers {
}
}
+ /**
+ * The initial application of aggravated damage against a target.
+ * Primarily for use in the comet weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ */
case object CometAggravated extends Mod {
def Calculate: DamageModifiers.Format = formula
@@ -295,6 +371,14 @@ object DamageModifiers {
}
}
+ /**
+ * The ongoing application of aggravated damage ticks against a target.
+ * Primarily for use in the comet weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ * @see `ResolvedProjectile`
+ */
case object CometAggravatedBurn extends Mod {
def Calculate: DamageModifiers.Format = formula
diff --git a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
index 181a67b2..f88bcf03 100644
--- a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
@@ -19,7 +19,7 @@ import shapeless.{::, HNil}
* @param unk3b if no global unique identifier (above), the name of the entity absorbing the damage
* @param unk3c if no global unique identifier (above), the object type of the entity absorbing the damage
* @param unk3d na
- * @param unk4 na
+ * @param unk4 an indicator for the target-specific vital statistic being affected
* @param unk5 the amount of damage
* @param unk6 na
*/
@@ -66,6 +66,13 @@ final case class DamageFeedbackMessage(
}
object DamageFeedbackMessage extends Marshallable[DamageFeedbackMessage] {
+ def apply(unk1: Int,
+ unk2: PlanetSideGUID,
+ unk3: PlanetSideGUID,
+ unk4: Int,
+ unk5: Long): DamageFeedbackMessage =
+ DamageFeedbackMessage(unk1, true, Some(unk2), None, None, true, Some(unk3), None, None, None, unk4, unk5, 0)
+
implicit val codec: Codec[DamageFeedbackMessage] = (
("unk1" | uint4) ::
(bool >>:~ { u2 =>
diff --git a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
index 3e6406b4..d6e4a2df 100644
--- a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
@@ -33,7 +33,7 @@ object DamageWithPositionMessage extends Marshallable[DamageWithPositionMessage]
).xmap[DamageWithPositionMessage] (
{
case unk :: pos :: HNil =>
- DamageWithPositionMessage(math.min(0, math.max(unk, 255)), pos)
+ DamageWithPositionMessage(math.max(0, math.min(unk, 255)), pos)
},
{
case DamageWithPositionMessage(unk, pos) =>
diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala
index 63c59290..d5973874 100644
--- a/src/test/scala/objects/DamageableTest.scala
+++ b/src/test/scala/objects/DamageableTest.scala
@@ -626,7 +626,7 @@ class DamageableMountableDamageTest extends ActorTest {
msg1_3(1) match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
) =>
true
case _ => false
@@ -737,8 +737,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
+ val vehicleProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
+ zone.VehicleEvents = vehicleProbe.ref
val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2
turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control")
turret.Zone = zone
@@ -787,16 +789,17 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
assert(turret.Health == turret.Definition.DefaultHealth)
turret.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(500 milliseconds)
+ val msg12 = vehicleProbe.receiveOne(500 milliseconds)
+ val msg3 = activityProbe.receiveOne(500 milliseconds)
+ val msg4 = avatarProbe.receiveOne(500 milliseconds)
assert(
- msg1_3.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
- case _ => false
+ msg12 match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(2), 0, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
activity.defender == turretSource &&
@@ -805,10 +808,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
}
)
assert(
- msg1_3(1) match {
+ msg4 match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
) =>
true
case _ => false
@@ -1130,40 +1133,36 @@ class DamageableVehicleDamageTest extends ActorTest {
assert(atv.Shields == 1)
atv.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(200 milliseconds)
- val msg4 = vehicleProbe.receiveOne(200 milliseconds)
+ val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
+ val msg3 = activityProbe.receiveOne(200 milliseconds)
+ val msg4 = avatarProbe.receiveOne(200 milliseconds)
assert(
- msg1_3.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
+ msg12.head match {
+ case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg12(1) match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
+ case _ => false
+ }
+ )
+ assert(
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
- activity.defender == vehicleSource &&
- activity.location == Vector3(1, 0, 0)
- case _ => false
- }
- )
- assert(
- msg1_3(1) match {
- case AvatarServiceMessage(
- "TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
- ) =>
- true
+ activity.defender == vehicleSource &&
+ activity.location == Vector3(1, 0, 0)
case _ => false
}
)
assert(
msg4 match {
- case VehicleServiceMessage(
- channel,
- VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
- ) if channel.equals(atv.Actor.toString) =>
+ case AvatarServiceMessage(
+ "TestCharacter2",
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(9, Vector3(2, 0, 0)))
+ ) =>
true
case _ => false
}
@@ -1264,17 +1263,23 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
assert(atv.Shields == 1)
lodestar.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_35 = avatarProbe.receiveN(3, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(200 milliseconds)
- val msg4 = vehicleProbe.receiveOne(200 milliseconds)
+ val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
+ val msg3 = activityProbe.receiveOne(200 milliseconds)
+ val msg45 = avatarProbe.receiveN(2,200 milliseconds)
assert(
- msg1_35.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
+ msg12.head match {
+ case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg12(1) match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
+ case _ => false
+ }
+ )
+ assert(
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
activity.defender == vehicleSource &&
@@ -1283,30 +1288,20 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
}
)
assert(
- msg1_35(1) match {
+ msg45.head match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
) =>
true
case _ => false
}
)
assert(
- msg4 match {
- case VehicleServiceMessage(
- channel,
- VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
- ) if channel.equals(lodestar.Actor.toString) =>
- true
- case _ => false
- }
- )
- assert(
- msg1_35(2) match {
+ msg45(1) match {
case AvatarServiceMessage(
"TestCharacter3",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
) =>
true
case _ => false
diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala
index 56b34460..9b261bdf 100644
--- a/src/test/scala/objects/PlayerControlTest.scala
+++ b/src/test/scala/objects/PlayerControlTest.scala
@@ -413,14 +413,13 @@ class PlayerControlDamageTest extends ActorTest {
)
assert(
msg_avatar(1) match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
+ case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
- case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) =>
- true
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
@@ -437,7 +436,7 @@ class PlayerControlDamageTest extends ActorTest {
msg_avatar(3) match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(17, Vector3(2, 0, 0)))
) =>
true
case _ => false