mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
moved aggravation damage into damage implementations of turrets and vehicles, rather than directly into ther immediate control agencies; revamp projectile quality modifiers; comet now does initial damage and one less tick of aggravation; the implementation progression of damageable entities is different now
This commit is contained in:
parent
89d7aea633
commit
fc89355acf
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.<br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* 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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue