diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
new file mode 100644
index 000000000..8fe9d38cd
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
@@ -0,0 +1,36 @@
+//Copyright (c) 2020 PSForever
+package net.psforever.objects.ballistics
+
+/**
+ * Projectile quality is an external aspect of projectiles
+ * that is not dependent on hard-coded definitions of the entities
+ * used to compose the projectile such as the knowlegde of the emitting `Tool` (weapon).
+ * A flag or a damage modifier, depending on use.
+ * To the extent that it can be used as a numeric modifier,
+ * insists on defining a numeric modifier component rather to what it is trying to express.
+ * That numeric modifier does not have to be used for anything.
+ */
+sealed trait ProjectileQuality {
+ def mod: Float
+}
+
+/**
+ * Implement the numeric modifier with as one.
+ */
+sealed trait SameAsQuality extends ProjectileQuality {
+ def mod: Float = 1f
+}
+
+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). */
+ case object AggravatesTarget extends SameAsQuality
+
+ /** The complete lack of quality. Even the numeric modifier is zeroed. */
+ case object Zeroed extends ProjectileQuality { def mod = 0f }
+
+ /** Assign a custom numeric qualifier value, usually to be applied to damage calculations. */
+ case class Modified(mod: Float) extends ProjectileQuality
+}
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 5c528dc86..6dc233a51 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
@@ -38,8 +38,18 @@ trait AuraEffectBehavior {
id
}
- def StartAuraEffect(effect: Aura, duration: Long): Long = {
- StartAuraEffect(GetUnusedEffectId, effect, duration)
+ def StartAuraEffect(effect: Aura, duration: Long): Option[Long] = {
+ 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
+ }
}
def StartAuraEffect(id: Long, effect: Aura, duration: Long): Long = {
@@ -130,6 +140,8 @@ trait AuraEffectBehavior {
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])
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/aggravated/AggravatedBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
similarity index 82%
rename from common/src/main/scala/net/psforever/objects/serverobject/aggravated/AggravatedBehavior.scala
rename to common/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
index 0e1feff3f..e2a477e96 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/aggravated/AggravatedBehavior.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
@@ -1,10 +1,9 @@
// Copyright (c) 2020 PSForever
-package net.psforever.objects.serverobject.aggravated
+package net.psforever.objects.serverobject.damage
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.ballistics._
-import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
-import net.psforever.objects.serverobject.damage.Damageable
+import net.psforever.objects.serverobject.aura.Aura
import net.psforever.objects.vital.{DamageType, Vitality}
import scala.collection.mutable
@@ -17,31 +16,33 @@ trait AggravatedBehavior {
mutable.LongMap.empty[AggravatedBehavior.Entry]
private val aggravationToTimer: mutable.LongMap[Cancellable] =
mutable.LongMap.empty[Cancellable]
+ /** ongoing flag to indicate whether the target is being afflicted by any form of aggravated damage */
+ private var ongoingAggravated: Boolean = false
def AggravatedObject: AggravatedBehavior.Target
- def TryAggravationEffect(data: ResolvedProjectile): Option[AggravatedDamage] = {
- data.projectile.profile.Aggravated match {
+ def TryAggravationEffectActivate(data: ResolvedProjectile): Option[AggravatedDamage] = {
+ val projectile = data.projectile
+ projectile.profile.Aggravated match {
case Some(damage)
- if data.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
+ if projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
damage.effect_type != Aura.Nothing &&
- damage.targets.exists(validation => validation.test(AggravatedObject)) =>
- TryAggravationEffect(damage, data)
+ (projectile.quality == ProjectileQuality.AggravatesTarget ||
+ damage.targets.exists(validation => validation.test(AggravatedObject))) =>
+ TryAggravationEffectActivate(damage, data)
case _ =>
None
}
}
- private def TryAggravationEffect(aggravation: AggravatedDamage, data: ResolvedProjectile): Option[AggravatedDamage] = {
+ private def TryAggravationEffectActivate(
+ aggravation: AggravatedDamage,
+ data: ResolvedProjectile
+ ): Option[AggravatedDamage] = {
val effect = aggravation.effect_type
- val obj = AggravatedObject
if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
- val auraEffects = obj.Aura
- if(auraEffects.contains(effect) && aggravation.cumulative_damage_degrade) {
- SetupAggravationEntry(aggravation, data)
- Some(aggravation)
- }
- else if(obj.AddEffectToAura(effect).diff(auraEffects).contains(effect)) {
+ val sameEffect = entryIdToEntry.values.filter(entry => entry.effect == effect)
+ if(sameEffect.isEmpty || sameEffect.nonEmpty && aggravation.cumulative_damage_degrade) {
SetupAggravationEntry(aggravation, data)
Some(aggravation)
}
@@ -91,6 +92,7 @@ trait AggravatedBehavior {
PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
//pair id with timer
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AggravatedBehavior.Aggravate(id, iterations))
+ ongoingAggravated = true
true
case _ =>
false
@@ -156,6 +158,7 @@ trait AggravatedBehavior {
def RemoveAggravatedEntry(id: Long): Aura = {
entryIdToEntry.remove(id) match {
case Some(entry) =>
+ ongoingAggravated = entryIdToEntry.nonEmpty
entry.data.projectile.profile.Aggravated.get.effect_type
case _ =>
Aura.Nothing
@@ -181,22 +184,24 @@ trait AggravatedBehavior {
aggravationToTimer.clear
}
+ def AggravatedReaction: Boolean = ongoingAggravated
+
private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
val data = entry.data
val model = data.damage_model
val aggravatedProjectileData = ResolvedProjectile(
data.resolution,
- data.projectile.quality(entry.qualityPerTick(tick)),
+ data.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
data.target,
model,
data.hit_pos
)
- TakesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
+ takesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
}
}
object AggravatedBehavior {
- type Target = AuraEffectBehavior.Target with Vitality
+ type Target = Damageable.Target
private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index 23e5d2832..5d4dae490 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -7750,7 +7750,20 @@ class SessionActor extends Actor with MDCContextAware {
None
} else {
projectile.Resolve()
- Some(ResolvedProjectile(resolution, projectile, SourceEntry(target), target.DamageModel, pos))
+ val outProjectile = if(projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)) {
+ val quality = projectile.profile.Aggravated match {
+ case Some(aggravation)
+ if aggravation.targets.exists(validation => validation.test(target)) =>
+ ProjectileQuality.AggravatesTarget
+ case _ =>
+ ProjectileQuality.Normal
+ }
+ projectile.quality(quality)
+ }
+ else {
+ projectile
+ }
+ Some(ResolvedProjectile(resolution, outProjectile, SourceEntry(target), target.DamageModel, pos))
}
}
diff --git a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
index 66c261abd..252d3e0c4 100644
--- a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
@@ -9,7 +9,9 @@ import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.Damageable
-import net.psforever.objects.vital.{StandardResolutions, Vitality}
+import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
+import net.psforever.objects.vital.StandardResolutions
import net.psforever.objects.zones.Zone
import net.psforever.types.{PlanetSideGUID, Vector3}
import net.psforever.services.Service
@@ -63,18 +65,20 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
case _ => ;
}
- protected def TakesDamage: Receive = {
- case Vitality.Damage(applyDamageTo) =>
- if (mine.CanDamage) {
- val originalHealth = mine.Health
- val cause = applyDamageTo(mine)
- val damage = originalHealth - mine.Health
- if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
- ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
- } else {
- mine.Health = originalHealth
- }
+ override protected def PerformDamage(
+ target: Target,
+ applyDamageTo: Output
+ ): Unit = {
+ if (mine.CanDamage) {
+ val originalHealth = mine.Health
+ val cause = applyDamageTo(mine)
+ val damage = originalHealth - mine.Health
+ if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
+ ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
+ } else {
+ mine.Health = originalHealth
}
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index af02396e2..8d20e52d1 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -2452,13 +2452,14 @@ object GlobalDefinitions {
comet_projectile.DamageRadius = 1.0f
comet_projectile.ProjectileDamageType = DamageType.Aggravated
comet_projectile.Aggravated = AggravatedDamage(
- AggravatedInfo(DamageType.Direct, 0.2f, 500),
+ AggravatedInfo(DamageType.Direct, 0.25f, 500), //originally, .2
Aura.Comet,
- AggravatedTiming(2000, 4),
+ AggravatedTiming(2000, 3),
10f,
List(
TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
- TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle)
+ TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle),
+ TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret)
)
)
comet_projectile.InitialVelocity = 80
diff --git a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
index feb1f1293..75a375ec8 100644
--- a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
@@ -44,7 +44,6 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
def JammableObject = gen
def DamageableObject = gen
def RepairableObject = gen
- private var handleDamageToShields: Boolean = false
def receive: Receive =
jammableBehavior
@@ -90,18 +89,20 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
target,
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
)
- handleDamageToShields = damageToShields > 0
- HandleDamage(target, cause, damageToHealth)
+ HandleDamage(target, cause, (damageToHealth, damageToShields))
} else {
gen.Health = originalHealth
gen.Shields = originalShields
}
}
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- super.DamageAwareness(target, cause, amount)
- ShieldGeneratorControl.DamageAwareness(gen, cause, handleDamageToShields)
- handleDamageToShields = false
+ override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val (damageToHealth, damageToShields) = amount match {
+ case (a: Int, b: Int) => (a, b)
+ case _ => (0, 0)
+ }
+ super.DamageAwareness(target, cause, damageToHealth)
+ ShieldGeneratorControl.DamageAwareness(gen, cause, damageToShields > 0)
}
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala
index 2c3b5e8dd..fd9f85697 100644
--- a/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -75,6 +75,11 @@ class TurretControl(turret: TurretDeployable)
def DamageableObject = turret
def RepairableObject = turret
+ override def postStop(): Unit = {
+ super.postStop()
+ damageableWeaponTurretPostStop()
+ }
+
def receive: Receive =
checkBehavior
.orElse(jammableBehavior)
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index ef7b8225c..e9a3b9568 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -8,16 +8,17 @@ 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.aggravated.AggravatedBehavior
import net.psforever.objects.serverobject.aura.AuraEffectBehavior
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
-import net.psforever.objects.vital.{PlayerSuicide, Vitality}
+import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.PlayerSuicide
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
-import net.psforever.objects.serverobject.damage.Damageable
+import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.Repairable
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vital._
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
@@ -491,29 +492,31 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case _ => ;
}
- protected def TakesDamage: Receive = {
- case Vitality.Damage(applyDamageTo) =>
- if (player.isAlive && !player.spectator) {
- val originalHealth = player.Health
- val originalArmor = player.Armor
- val originalStamina = player.avatar.stamina
- val originalCapacitor = player.Capacitor.toInt
- val cause = applyDamageTo(player)
- val health = player.Health
- val armor = player.Armor
- val stamina = player.avatar.stamina
- val capacitor = player.Capacitor.toInt
- val damageToHealth = originalHealth - health
- val damageToArmor = originalArmor - armor
- val damageToStamina = originalStamina - stamina
- val damageToCapacitor = originalCapacitor - capacitor
- HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
- if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
- damageLog.info(
- s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
- )
- }
+ override protected def PerformDamage(
+ target: Target,
+ applyDamageTo: Output
+ ): Unit = {
+ if (player.isAlive && !player.spectator) {
+ val originalHealth = player.Health
+ val originalArmor = player.Armor
+ val originalStamina = player.avatar.stamina
+ val originalCapacitor = player.Capacitor.toInt
+ val cause = applyDamageTo(player)
+ val health = player.Health
+ val armor = player.Armor
+ val stamina = player.avatar.stamina
+ val capacitor = player.Capacitor.toInt
+ val damageToHealth = originalHealth - health
+ val damageToArmor = originalArmor - armor
+ val damageToStamina = originalStamina - stamina
+ val damageToCapacitor = originalCapacitor - capacitor
+ HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
+ if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
+ damageLog.info(
+ s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
+ )
}
+ }
}
/**
@@ -521,76 +524,110 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
* @param target na
*/
def HandleDamage(
- target: Player,
- cause: ResolvedProjectile,
- damageToHealth: Int,
- damageToArmor: Int,
- damageToStamina: Int,
- damageToCapacitor: Int
- ): Unit = {
- val targetGUID = target.GUID
- val zone = target.Zone
- val zoneId = zone.id
- val events = zone.AvatarEvents
- val health = target.Health
+ target: Player,
+ cause: ResolvedProjectile,
+ damageToHealth: Int,
+ damageToArmor: Int,
+ damageToStamina: Int,
+ damageToCapacitor: Int
+ ): Unit = {
+ //always do armor update
if (damageToArmor > 0) {
- events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 4, target.Armor))
+ val zone = target.Zone
+ zone.AvatarEvents ! AvatarServiceMessage(
+ zone.id,
+ AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor)
+ )
}
- if (health > 0) {
- if (damageToCapacitor > 0) {
- events ! AvatarServiceMessage(
- target.Name,
- AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
- )
- }
- if (damageToHealth > 0 || damageToStamina > 0) {
- target.History(cause)
- if (damageToHealth > 0) {
- events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
- }
- if (damageToStamina > 0) {
- avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
- }
- //activity on map
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
- //alert damage source
- DamageAwareness(target, cause)
- }
- //special effects
- if (Damageable.CanJammer(target, cause)) {
- TryJammerEffectActivate(target, cause)
- }
- TryAggravationEffect(cause) match {
- case Some(aggravation) =>
- StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
- case _ => ;
- }
+ //choose
+ if (target.Health > 0) {
+ DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
} else {
DestructionAwareness(target, Some(cause))
}
}
- /**
- * na
- * @param target na
- * @param cause na
- */
- def DamageAwareness(target: Player, cause: ResolvedProjectile): Unit = {
- val zone = target.Zone
- zone.AvatarEvents ! AvatarServiceMessage(
- target.Name,
- cause.projectile.owner match {
- case pSource: PlayerSource => //player damage
- val name = pSource.Name
- zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID)
- case None =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
+ def DamageAwareness(
+ target: Player,
+ cause: ResolvedProjectile,
+ damageToHealth: Int,
+ damageToArmor: Int,
+ damageToStamina: Int,
+ damageToCapacitor: Int
+ ): Unit = {
+ val targetGUID = target.GUID
+ val zone = target.Zone
+ val zoneId = zone.id
+ val events = zone.AvatarEvents
+ val health = target.Health
+ var announceConfrontation = damageToArmor > 0
+ //special effects
+ if (Damageable.CanJammer(target, cause)) {
+ TryJammerEffectActivate(target, cause)
+ }
+ val aggravated: Boolean = TryAggravationEffectActivate(cause) match {
+ case Some(aggravation) =>
+ StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
+ announceConfrontation = true //useful if initial damage (to anything) is zero
+ //initial damage for aggravation, but never treat as "aggravated"
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+ //log historical event
+ target.History(cause)
+ //stat changes
+ if (damageToCapacitor > 0) {
+ events ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
+ )
+ announceConfrontation = true
+ }
+ if (damageToStamina > 0) {
+ avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
+ announceConfrontation = true
+ }
+ if (damageToHealth > 0) {
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
+ announceConfrontation = true
+ }
+ val countableDamage = damageToHealth + damageToArmor
+ if(announceConfrontation) {
+ if (!aggravated) {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ cause.projectile.owner match {
+ case pSource: PlayerSource => //player damage
+ val name = pSource.Name
+ zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
+ case Some(tplayer) =>
+ AvatarAction.HitHint(tplayer.GUID, target.GUID)
+ case None =>
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
+ }
+ case source =>
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
}
- case source =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
+ )
}
- )
+ else {
+ //general alert
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, Vector3.Zero))
+ )
+ }
+ }
+ if (aggravated) {
+ events ! AvatarServiceMessage(
+ zoneId,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, AggravatedDamageMessage(targetGUID, countableDamage))
+ )
+ }
}
/**
diff --git a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
index d40d9de8f..a00b79e42 100644
--- a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
@@ -41,7 +41,7 @@ final case class Projectile(
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3,
- quality: Float = 1f,
+ quality: ProjectileQuality = ProjectileQuality.Normal,
id: Long = Projectile.idGenerator.getAndIncrement(),
fire_time: Long = System.nanoTime
) extends PlanetSideGameObject {
@@ -67,8 +67,8 @@ final case class Projectile(
* @param value the new quality
* @return a new `Projectile` entity
*/
- def quality(value: Float): Projectile =
- Projectile(
+ def quality(value: ProjectileQuality): Projectile = {
+ val projectile = Projectile(
profile,
tool_def,
fire_mode,
@@ -80,6 +80,10 @@ final case class Projectile(
id,
fire_time
)
+ if(isMiss) projectile.Miss()
+ else if(isResolved) projectile.Resolve()
+ projectile
+ }
/**
* Mark the projectile as being "encountered" or "managed" at least once.
diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala
index 6448daf85..56736d35b 100644
--- a/src/main/scala/net/psforever/objects/ce/Deployable.scala
+++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala
@@ -33,7 +33,7 @@ object Deployable {
def Includes(category: DeployableCategory.Value): List[DeployedItem.Value] = {
(for {
- (ce, cat) <- deployablesToCategories
+ (ce: DeployedItem.Value, cat: DeployableCategory.Value) <- deployablesToCategories
if cat == category
} yield ce) toList
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
index 8fcde16c6..e36b3c95c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.resolution.ResolutionCalculations
/**
* The base "control" `Actor` mixin for damage-handling code.
@@ -24,16 +25,35 @@ trait Damageable {
*/
def DamageableObject: Damageable.Target
- /** the official mixin hook; `orElse` onto the "control" `Actor` `receive` */
- final val takesDamage: Receive = TakesDamage
+ /** the official mixin hook;
+ * `orElse` onto the "control" `Actor` `receive`; or,
+ * cite the `originalTakesDamage` protocol during inheritance overrides */
+ val takesDamage: Receive = {
+ case Vitality.Damage(damage_func) =>
+ val obj = DamageableObject
+ if (obj.CanDamage) {
+ PerformDamage(obj, damage_func)
+ }
+ }
+
+ /** a duplicate of the core implementation for the default mixin hook, for use in overriding */
+ final val originalTakesDamage: Receive = {
+ case Vitality.Damage(damage_func) =>
+ val obj = DamageableObject
+ if (obj.CanDamage) {
+ PerformDamage(obj, damage_func)
+ }
+ }
/**
- * Implementation of the mixin hook will be provided by a child class.
- * Override this method only when directly implementing.
- * @see `takesDamage`
- * @see `DamageableAmenity.PerformDamage`
+ * Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
+ * By default, only take an interest in the change of "health".
+ * If implementing custom damage with no new message handling, override this method.
+ * @see `ResolutionCalculations.Output`
+ * @param target the entity to be damaged
+ * @param applyDamageTo the function that applies the damage to the target in a target-tailored fashion
*/
- protected def TakesDamage: Receive
+ protected def PerformDamage(target: Damageable.Target, applyDamageTo: ResolutionCalculations.Output): Unit
}
object Damageable {
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
index e45917476..a2db8e3c9 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
@@ -1,10 +1,8 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import akka.actor.Actor.Receive
import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
-import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
@@ -42,23 +40,6 @@ trait DamageableEntity extends Damageable {
DamageLog(s"${name.substring(slashPoint + 1, name.length - 1)}: $msg")
}
- /**
- * Catch the expected damage message and apply checks to the target.
- * If adding custom message handling in an future child implementation,
- * override this method and call `super.TakesDamage.orElse { ... }`.
- * @see `Damageable.TakesDamage`
- * @see `ResolutionCalcultions.Output`
- * @see `Vitality.CanDamage`
- * @see `Vitality.Damage`
- */
- protected def TakesDamage: Receive = {
- case Vitality.Damage(damage_func) =>
- val obj = DamageableObject
- if (obj.CanDamage) {
- PerformDamage(obj, damage_func)
- }
- }
-
/**
* Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
* By default, only take an interest in the change of "health".
@@ -108,7 +89,7 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param damage the amount of damage
*/
- protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Int): Unit = {
+ protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Any): Unit = {
if (!target.Destroyed && target.Health <= target.Definition.DamageDestroysAt) {
DestructionAwareness(target, cause)
} else {
@@ -122,8 +103,12 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param amount the amount of damage
*/
- protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- DamageableEntity.DamageAwareness(target, cause, amount)
+ protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ amount match {
+ case value: Int =>
+ DamageableEntity.DamageAwareness(target, cause, value)
+ case _ => ;
+ }
}
/**
@@ -162,16 +147,22 @@ object DamageableEntity {
if (Damageable.CanJammer(target, cause)) {
target.Actor ! JammableUnit.Jammered(cause)
}
- if (amount > 0) {
+ if (DamageToHealth(target, cause, amount)) {
+ target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ }
+ }
+
+ def DamageToHealth(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Boolean = {
+ if (amount > 0 && !target.Destroyed) {
val zone = target.Zone
- if (!target.Destroyed) {
- val tguid = target.GUID
- zone.AvatarEvents ! AvatarServiceMessage(
- zone.id,
- AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
- )
- }
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.AvatarEvents ! AvatarServiceMessage(
+ zone.id,
+ AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health)
+ )
+ true
+ }
+ else {
+ false
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
index cc3e5a8bb..abfb7922e 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
@@ -26,8 +26,13 @@ object DamageableMountable {
* @see `Zone.LivePlayers`
* @param target the entity being damaged
* @param cause historical information about the damage
+ * @param countableDamage the amount of damage being done, translating to the intensity of the damage indicator
*/
- def DamageAwareness(target: Damageable.Target with Mountable, cause: ResolvedProjectile): Unit = {
+ def DamageAwareness(
+ target: Damageable.Target with Mountable,
+ cause: ResolvedProjectile,
+ countableDamage: Int
+ ): Unit = {
val zone = target.Zone
val events = zone.AvatarEvents
val occupants = target.Seats.values.collect {
@@ -38,9 +43,10 @@ object DamageableMountable {
case pSource: PlayerSource => //player damage
val name = pSource.Name
(zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(player) => AvatarAction.HitHint(player.GUID, player.GUID)
+ case Some(player) =>
+ AvatarAction.HitHint(player.GUID, player.GUID)
case None =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
}) match {
case AvatarAction.HitHint(_, guid) =>
occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) }
@@ -48,7 +54,7 @@ object DamageableMountable {
occupants.map { tplayer => (tplayer.Name, msg) }
}
case source => //object damage
- val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
+ val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
occupants.map { tplayer => (tplayer.Name, msg) }
}).foreach {
case (channel, msg) =>
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
index c3d26a3dc..ceea8af0c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
@@ -1,43 +1,55 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import akka.actor.Actor.Receive
+import akka.actor.Actor
import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.DamageType
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.DamageWithPositionMessage
+import net.psforever.types.Vector3
import scala.concurrent.duration._
/**
* The "control" `Actor` mixin for damage-handling code for `Vehicle` objects.
*/
-trait DamageableVehicle extends DamageableEntity {
+trait DamageableVehicle
+ extends DamageableEntity
+ with AggravatedBehavior {
+ _ : Actor =>
- /** vehicles (may) have shields; they need to be handled */
- private var handleDamageToShields: Boolean = false
+ def damageableVehiclePostStop(): Unit = {
+ EndAllAggravation()
+ }
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
private var reportDamageToVehicle: Boolean = false
def DamageableObject: Vehicle
+ def AggravatedObject : Vehicle = DamageableObject
- override protected def TakesDamage: Receive =
- super.TakesDamage.orElse {
- case DamageableVehicle.Damage(cause, damage) =>
- //cargo vehicles inherit feedback from carrier
- reportDamageToVehicle = damage > 0
- DamageAwareness(DamageableObject, cause, amount = 0)
+ override val takesDamage: Receive =
+ originalTakesDamage
+ .orElse(aggravatedBehavior)
+ .orElse {
+ case DamageableVehicle.Damage(cause, damage) =>
+ //cargo vehicles inherit feedback from carrier
+ reportDamageToVehicle = damage > 0
+ DamageAwareness(DamageableObject, cause, amount = 0)
- case DamageableVehicle.Destruction(cause) =>
- //cargo vehicles are destroyed when carrier is destroyed
- val obj = DamageableObject
- obj.Health = 0
- obj.History(cause)
- DestructionAwareness(obj, cause)
- }
+ case DamageableVehicle.Destruction(cause) =>
+ //cargo vehicles are destroyed when carrier is destroyed
+ val obj = DamageableObject
+ obj.Health = 0
+ obj.History(cause)
+ DestructionAwareness(obj, cause)
+ }
/**
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
@@ -62,53 +74,13 @@ trait DamageableVehicle extends DamageableEntity {
target,
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
)
- handleDamageToShields = damageToShields > 0
- HandleDamage(target, cause, damageToHealth + damageToShields)
+ HandleDamage(target, cause, (damageToHealth, damageToShields))
} else {
obj.Health = originalHealth
obj.Shields = originalShields
}
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
- val obj = DamageableObject
- val handleShields = handleDamageToShields
- handleDamageToShields = false
- val handleReport = reportDamageToVehicle || amount > 0
- reportDamageToVehicle = false
- if (Damageable.CanDamageOrJammer(target, amount, cause)) {
- super.DamageAwareness(target, cause, amount)
- }
- if (handleReport) {
- DamageableMountable.DamageAwareness(obj, cause)
- }
- DamageableVehicle.DamageAwareness(obj, cause, amount, handleShields)
- }
-
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
- super.DestructionAwareness(target, cause)
- val obj = DamageableObject
- DamageableMountable.DestructionAwareness(obj, cause)
- DamageableVehicle.DestructionAwareness(obj, cause)
- DamageableWeaponTurret.DestructionAwareness(obj, cause)
- }
-}
-
-object DamageableVehicle {
-
- /**
- * Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
- * @param cause historical information about damage
- */
- private case class Damage(cause: ResolvedProjectile, amount: Int)
-
- /**
- * Message for instructing the target's cargo vehicles that their carrier is destroyed,
- * and they should be destroyed too.
- * @param cause historical information about damage
- */
- private case class Destruction(cause: ResolvedProjectile)
-
/**
* Most all vehicles and the weapons mounted to them can jam
* if the projectile that strikes (near) them has jammering properties.
@@ -121,25 +93,76 @@ object DamageableVehicle {
* @see `VehicleServiceMessage`
* @param target the entity being destroyed
* @param cause historical information about the damage
- * @param damage how much damage was performed
- * @param damageToShields dispatch a shield strength update
+ * @param amount how much damage was performed
*/
- def DamageAwareness(target: Vehicle, cause: ResolvedProjectile, damage: Int, damageToShields: Boolean): Unit = {
- //alert cargo occupants to damage source
- target.CargoHolds.values.foreach(hold => {
- hold.Occupant match {
- case Some(cargo) =>
- cargo.Actor ! DamageableVehicle.Damage(cause, damage + (if (damageToShields) 1 else 0))
- case None => ;
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val obj = DamageableObject
+ val zone = target.Zone
+ val events = zone.VehicleEvents
+ val targetGUID = target.GUID
+ val zoneId = zone.id
+ val vehicleChannel = s"${obj.Actor}"
+ val (damageToHealth, damageToShields, totalDamage) = amount match {
+ case (a: Int, b: Int) => (a, b, a+b)
+ case _ => (0, 0, 0)
+ }
+ var announceConfrontation: Boolean = reportDamageToVehicle || totalDamage > 0
+ val aggravated = TryAggravationEffectActivate(cause) match {
+ case Some(_) =>
+ announceConfrontation = true
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+ reportDamageToVehicle = false
+
+ //log historical event
+ target.History(cause)
+ //damage
+ if (Damageable.CanDamageOrJammer(target, totalDamage, cause)) {
+ //jammering
+ if (Damageable.CanJammer(target, cause)) {
+ target.Actor ! JammableUnit.Jammered(cause)
}
- })
- //shields
- if (damageToShields) {
- val zone = target.Zone
- zone.VehicleEvents ! VehicleServiceMessage(
- s"${target.Actor}",
- VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
- )
+ //stat changes
+ if (damageToShields > 0) {
+ events ! VehicleServiceMessage(
+ vehicleChannel,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, obj.Shields)
+ )
+ announceConfrontation = true
+ }
+ if (damageToHealth > 0) {
+ events ! VehicleServiceMessage(
+ zoneId,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
+ )
+ announceConfrontation = true
+ }
+ }
+ if (announceConfrontation) {
+ if (aggravated) {
+ val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(totalDamage, Vector3.Zero))
+ obj.Seats.values
+ .collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
+ .foreach { channel =>
+ events ! VehicleServiceMessage(channel, msg)
+ }
+ }
+ else {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ DamageableMountable.DamageAwareness(obj, cause, totalDamage)
+ }
+ //alert cargo occupants to damage source
+ obj.CargoHolds.values.foreach(hold => {
+ hold.Occupant match {
+ case Some(cargo) =>
+ cargo.Actor ! DamageableVehicle.Damage(cause, totalDamage)
+ case None => ;
+ }
+ })
}
}
@@ -164,10 +187,15 @@ object DamageableVehicle {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Vehicle, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ super.DestructionAwareness(target, cause)
+ val obj = DamageableObject
+ DamageableMountable.DestructionAwareness(obj, cause)
val zone = target.Zone
+ //aggravation cancel
+ EndAllAggravation()
//cargo vehicles die with us
- target.CargoHolds.values.foreach(hold => {
+ obj.CargoHolds.values.foreach(hold => {
hold.Occupant match {
case Some(cargo) =>
cargo.Actor ! DamageableVehicle.Destruction(cause)
@@ -175,10 +203,10 @@ object DamageableVehicle {
}
})
//special considerations for certain vehicles
- Vehicles.BeforeUnloadVehicle(target, zone)
+ Vehicles.BeforeUnloadVehicle(obj, zone)
//shields
- if (target.Shields > 0) {
- target.Shields = 0
+ if (obj.Shields > 0) {
+ obj.Shields = 0
zone.VehicleEvents ! VehicleServiceMessage(
zone.id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
@@ -186,5 +214,22 @@ object DamageableVehicle {
}
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
target.ClearHistory()
+ DamageableWeaponTurret.DestructionAwareness(obj, cause)
}
}
+
+object DamageableVehicle {
+
+ /**
+ * Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
+ * @param cause historical information about damage
+ */
+ private case class Damage(cause: ResolvedProjectile, amount: Int)
+
+ /**
+ * Message for instructing the target's cargo vehicles that their carrier is destroyed,
+ * and they should be destroyed too.
+ * @param cause historical information about damage
+ */
+ private case class Destruction(cause: ResolvedProjectile)
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
index b7fab97a5..f4ff4b2c5 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
@@ -1,30 +1,97 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
+import akka.actor.Actor
import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
import net.psforever.objects.vehicles.MountedWeapons
+import net.psforever.objects.vital.DamageType
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.DamageWithPositionMessage
+import net.psforever.types.Vector3
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.support.TurretUpgrader
-import net.psforever.services.vehicle.VehicleServiceMessage
+import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
/**
* The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects.
*/
-trait DamageableWeaponTurret extends DamageableEntity {
- def DamageableObject: Damageable.Target with WeaponTurret
+trait DamageableWeaponTurret
+ extends DamageableEntity
+ with AggravatedBehavior {
+ _: Actor =>
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- super.DamageAwareness(target, cause, amount)
- if (amount > 0) {
- DamageableMountable.DamageAwareness(DamageableObject, cause)
+ def damageableWeaponTurretPostStop(): Unit = {
+ EndAllAggravation()
+ }
+
+ def DamageableObject: Damageable.Target with WeaponTurret
+ def AggravatedObject: Damageable.Target with WeaponTurret = DamageableObject
+
+ override val takesDamage: Receive = originalTakesDamage.orElse(aggravatedBehavior)
+
+ override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val obj = DamageableObject
+ val zone = target.Zone
+ val events = zone.VehicleEvents
+ val targetGUID = target.GUID
+ val zoneId = zone.id
+ val damageToHealth = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ var announceConfrontation: Boolean = damageToHealth > 0
+ val aggravated = TryAggravationEffectActivate(cause) match {
+ case Some(_) =>
+ announceConfrontation = true
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+
+ //log historical event
+ target.History(cause)
+ //damage
+ if (Damageable.CanDamageOrJammer(target, damageToHealth, cause)) {
+ //jammering
+ if (Damageable.CanJammer(target, cause)) {
+ target.Actor ! JammableUnit.Jammered(cause)
+ }
+ //stat changes
+ //TODO some turrets have shields
+ if (damageToHealth > 0) {
+ DamageableMountable.DamageAwareness(DamageableObject, cause, damageToHealth)
+ events ! VehicleServiceMessage(
+ zoneId,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
+ )
+ announceConfrontation = true
+ }
+ }
+ if (announceConfrontation) {
+ if (aggravated) {
+ val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(damageToHealth, Vector3.Zero))
+ obj.Seats.values
+ .collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
+ .foreach { channel =>
+ events ! VehicleServiceMessage(channel, msg)
+ }
+ }
+ else {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ DamageableMountable.DamageAwareness(obj, cause, damageToHealth)
+ }
}
}
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
super.DestructionAwareness(target, cause)
val obj = DamageableObject
+ EndAllAggravation()
DamageableWeaponTurret.DestructionAwareness(obj, cause)
DamageableMountable.DestructionAwareness(obj, cause)
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
index 799b339fc..e4258dbaa 100644
--- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
@@ -88,9 +88,13 @@ class GeneratorControl(gen: Generator)
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
super.DamageAwareness(target, cause, amount)
- GeneratorControl.DamageAwareness(gen, cause, amount)
+ val damageTo = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ GeneratorControl.DamageAwareness(gen, cause, damageTo)
}
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
index ec5b7346e..2bbb5ed46 100644
--- a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
@@ -72,9 +72,13 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
}
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
super.DamageAwareness(target, cause, amount)
- DamageableMountable.DamageAwareness(DamageableObject, cause)
+ val damageTo = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ DamageableMountable.DamageAwareness(DamageableObject, cause, damageTo)
}
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
index 44a85f831..a7e3436c9 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -44,6 +44,11 @@ class FacilityTurretControl(turret: FacilityTurret)
// Used for timing ammo recharge for vanu turrets in caves
var weaponAmmoRechargeTimer = Default.Cancellable
+ override def postStop(): Unit = {
+ super.postStop()
+ damageableWeaponTurretPostStop()
+ }
+
def receive: Receive =
checkBehavior
.orElse(jammableBehavior)
diff --git a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index 5f6dee3dc..355698d47 100644
--- a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -10,11 +10,8 @@ import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
-import net.psforever.objects.serverobject.aggravated.AggravatedBehavior
-import net.psforever.objects.serverobject.aura.AuraEffectBehavior
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
-import net.psforever.objects.serverobject.damage.Damageable.Target
-import net.psforever.objects.serverobject.damage.DamageableVehicle
+import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
import net.psforever.objects.serverobject.hackable.GenericHackables
@@ -55,8 +52,7 @@ class VehicleControl(vehicle: Vehicle)
with JammableMountedWeapons
with ContainableBehavior
with AntTransferBehavior
- with AggravatedBehavior
- with AuraEffectBehavior {
+ with AggravatedBehavior {
//make control actors belonging to utilities when making control actor belonging to vehicle
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
@@ -79,10 +75,6 @@ class VehicleControl(vehicle: Vehicle)
def ChargeTransferObject = vehicle
- def AuraTargetObject = vehicle
-
- def AggravatedObject = vehicle
-
if(vehicle.Definition == GlobalDefinitions.ant) {
findChargeTargetFunc = Vehicles.FindANTChargingSource
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
@@ -98,14 +90,13 @@ class VehicleControl(vehicle: Vehicle)
override def postStop(): Unit = {
super.postStop()
+ damageableVehiclePostStop()
decaying = false
decayTimer.cancel()
vehicle.Utilities.values.foreach { util =>
context.stop(util().Actor)
util().Actor = Default.Actor
}
- EndAllEffects()
- EndAllAggravation()
}
def Enabled: Receive =
@@ -114,8 +105,6 @@ class VehicleControl(vehicle: Vehicle)
.orElse(cargoBehavior)
.orElse(jammableBehavior)
.orElse(takesDamage)
- .orElse(aggravatedBehavior)
- .orElse(auraBehavior)
.orElse(canBeRepairedByNanoDispenser)
.orElse(containerBehavior)
.orElse(antBehavior)
@@ -592,30 +581,6 @@ class VehicleControl(vehicle: Vehicle)
}
out
}
-
- override def DamageAwareness(
- target: Target,
- cause: ResolvedProjectile,
- amount: Int
- ): Unit = {
- TryAggravationEffect(cause) match {
- case Some(aggravation) =>
- StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
- case _ => ;
- }
- super.DamageAwareness(target, cause, amount)
- }
-
- override def DestructionAwareness(
- target: Target,
- cause: ResolvedProjectile
- ): Unit = {
- //aura effects cancel
- EndAllEffects()
- //aggravation cancel
- EndAllAggravation()
- super.DestructionAwareness(target, cause)
- }
}
object VehicleControl {
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 d544e5260..e1be38de0 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.vital.damage
import net.psforever.objects.GlobalDefinitions
-import net.psforever.objects.ballistics.{PlayerSource, ProjectileResolution, ResolvedProjectile, VehicleSource}
+import net.psforever.objects.ballistics._
import net.psforever.objects.vital.DamageType
import net.psforever.types.{ExoSuitType, Vector3}
@@ -163,7 +163,8 @@ object DamageModifiers {
damage: Int,
data: ResolvedProjectile
): Int = {
- if (data.resolution == resolution) {
+ if (data.resolution == resolution &&
+ data.projectile.quality == ProjectileQuality.AggravatesTarget) {
(data.projectile.profile.Aggravated, data.target) match {
case (Some(aggravation), p: PlayerSource) =>
val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
@@ -230,7 +231,8 @@ object DamageModifiers {
def Calculate: DamageModifiers.Format = formula
private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirect) {
+ 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) =>
aggravation.info.find(_.damage_type == DamageType.Direct) match {
@@ -257,7 +259,7 @@ object DamageModifiers {
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
aggravation.info.find(_.damage_type == DamageType.Direct) match {
case Some(infos) =>
- (math.floor(damage * infos.degradation_percentage) * data.projectile.quality) toInt
+ (math.floor(damage * infos.degradation_percentage) * data.projectile.quality.mod) toInt
case _ =>
damage
}
@@ -274,8 +276,19 @@ object DamageModifiers {
def Calculate: DamageModifiers.Format = formula
private def formula(damage: Int, data: ResolvedProjectile): Int = {
- if (data.resolution == ProjectileResolution.AggravatedDirect) {
- 0
+ if (data.resolution == ProjectileResolution.AggravatedDirect &&
+ data.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ damage
+ }
} else {
damage
}
@@ -288,8 +301,13 @@ object DamageModifiers {
private def formula(damage: Int, data: ResolvedProjectile): Int = {
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
data.projectile.profile.Aggravated match {
- case Some(_) =>
- (damage * data.projectile.quality) toInt
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
case _ =>
0
}
diff --git a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
index 8f2a09e18..3e6406b4f 100644
--- a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
@@ -5,10 +5,13 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
import net.psforever.types.Vector3
import scodec.Codec
import scodec.codecs._
+import shapeless.{::, HNil}
/**
* Dispatched by the server to indicate a source of damage affecting the player.
- * Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.
+ * Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.
+ * Setting the position to the world origin, however,
+ * can cause the damage tick mark to point towards the previous damaging entity in some situations.
*
* The player will be shown a fading, outwards drifting, red tick mark.
* The location will indicate a general direction towards the source.
@@ -27,5 +30,14 @@ object DamageWithPositionMessage extends Marshallable[DamageWithPositionMessage]
implicit val codec: Codec[DamageWithPositionMessage] = (
("unk" | uint8L) ::
("pos" | Vector3.codec_pos)
- ).as[DamageWithPositionMessage]
+ ).xmap[DamageWithPositionMessage] (
+ {
+ case unk :: pos :: HNil =>
+ DamageWithPositionMessage(math.min(0, math.max(unk, 255)), pos)
+ },
+ {
+ case DamageWithPositionMessage(unk, pos) =>
+ unk :: pos :: HNil
+ }
+ )
}