mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-11 14:34:40 +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
22 changed files with 570 additions and 315 deletions
|
|
@ -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
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
def StartAuraEffect(effect: Aura, duration: Long): Long = {
|
def StartAuraEffect(effect: Aura, duration: Long): Option[Long] = {
|
||||||
StartAuraEffect(GetUnusedEffectId, effect, duration)
|
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 = {
|
def StartAuraEffect(id: Long, effect: Aura, duration: Long): Long = {
|
||||||
|
|
@ -130,6 +140,8 @@ trait AuraEffectBehavior {
|
||||||
object AuraEffectBehavior {
|
object AuraEffectBehavior {
|
||||||
type Target = PlanetSideServerObject with AuraContainer
|
type Target = PlanetSideServerObject with AuraContainer
|
||||||
|
|
||||||
|
final val InvalidEffectId = -1
|
||||||
|
|
||||||
final case class StartEffect(effect: Aura, duration: Long)
|
final case class StartEffect(effect: Aura, duration: Long)
|
||||||
|
|
||||||
final case class EndEffect(id: Option[Long], aura: Option[Aura])
|
final case class EndEffect(id: Option[Long], aura: Option[Aura])
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.serverobject.aggravated
|
package net.psforever.objects.serverobject.damage
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.objects.ballistics._
|
import net.psforever.objects.ballistics._
|
||||||
import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
|
import net.psforever.objects.serverobject.aura.Aura
|
||||||
import net.psforever.objects.serverobject.damage.Damageable
|
|
||||||
import net.psforever.objects.vital.{DamageType, Vitality}
|
import net.psforever.objects.vital.{DamageType, Vitality}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
@ -17,31 +16,33 @@ trait AggravatedBehavior {
|
||||||
mutable.LongMap.empty[AggravatedBehavior.Entry]
|
mutable.LongMap.empty[AggravatedBehavior.Entry]
|
||||||
private val aggravationToTimer: mutable.LongMap[Cancellable] =
|
private val aggravationToTimer: mutable.LongMap[Cancellable] =
|
||||||
mutable.LongMap.empty[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 AggravatedObject: AggravatedBehavior.Target
|
||||||
|
|
||||||
def TryAggravationEffect(data: ResolvedProjectile): Option[AggravatedDamage] = {
|
def TryAggravationEffectActivate(data: ResolvedProjectile): Option[AggravatedDamage] = {
|
||||||
data.projectile.profile.Aggravated match {
|
val projectile = data.projectile
|
||||||
|
projectile.profile.Aggravated match {
|
||||||
case Some(damage)
|
case Some(damage)
|
||||||
if data.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
|
if projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
|
||||||
damage.effect_type != Aura.Nothing &&
|
damage.effect_type != Aura.Nothing &&
|
||||||
damage.targets.exists(validation => validation.test(AggravatedObject)) =>
|
(projectile.quality == ProjectileQuality.AggravatesTarget ||
|
||||||
TryAggravationEffect(damage, data)
|
damage.targets.exists(validation => validation.test(AggravatedObject))) =>
|
||||||
|
TryAggravationEffectActivate(damage, data)
|
||||||
case _ =>
|
case _ =>
|
||||||
None
|
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 effect = aggravation.effect_type
|
||||||
val obj = AggravatedObject
|
|
||||||
if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
|
if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
|
||||||
val auraEffects = obj.Aura
|
val sameEffect = entryIdToEntry.values.filter(entry => entry.effect == effect)
|
||||||
if(auraEffects.contains(effect) && aggravation.cumulative_damage_degrade) {
|
if(sameEffect.isEmpty || sameEffect.nonEmpty && aggravation.cumulative_damage_degrade) {
|
||||||
SetupAggravationEntry(aggravation, data)
|
|
||||||
Some(aggravation)
|
|
||||||
}
|
|
||||||
else if(obj.AddEffectToAura(effect).diff(auraEffects).contains(effect)) {
|
|
||||||
SetupAggravationEntry(aggravation, data)
|
SetupAggravationEntry(aggravation, data)
|
||||||
Some(aggravation)
|
Some(aggravation)
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +92,7 @@ trait AggravatedBehavior {
|
||||||
PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
|
PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
|
||||||
//pair id with timer
|
//pair id with timer
|
||||||
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AggravatedBehavior.Aggravate(id, iterations))
|
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AggravatedBehavior.Aggravate(id, iterations))
|
||||||
|
ongoingAggravated = true
|
||||||
true
|
true
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
|
|
@ -156,6 +158,7 @@ trait AggravatedBehavior {
|
||||||
def RemoveAggravatedEntry(id: Long): Aura = {
|
def RemoveAggravatedEntry(id: Long): Aura = {
|
||||||
entryIdToEntry.remove(id) match {
|
entryIdToEntry.remove(id) match {
|
||||||
case Some(entry) =>
|
case Some(entry) =>
|
||||||
|
ongoingAggravated = entryIdToEntry.nonEmpty
|
||||||
entry.data.projectile.profile.Aggravated.get.effect_type
|
entry.data.projectile.profile.Aggravated.get.effect_type
|
||||||
case _ =>
|
case _ =>
|
||||||
Aura.Nothing
|
Aura.Nothing
|
||||||
|
|
@ -181,22 +184,24 @@ trait AggravatedBehavior {
|
||||||
aggravationToTimer.clear
|
aggravationToTimer.clear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def AggravatedReaction: Boolean = ongoingAggravated
|
||||||
|
|
||||||
private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
|
private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
|
||||||
val data = entry.data
|
val data = entry.data
|
||||||
val model = data.damage_model
|
val model = data.damage_model
|
||||||
val aggravatedProjectileData = ResolvedProjectile(
|
val aggravatedProjectileData = ResolvedProjectile(
|
||||||
data.resolution,
|
data.resolution,
|
||||||
data.projectile.quality(entry.qualityPerTick(tick)),
|
data.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
|
||||||
data.target,
|
data.target,
|
||||||
model,
|
model,
|
||||||
data.hit_pos
|
data.hit_pos
|
||||||
)
|
)
|
||||||
TakesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
|
takesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AggravatedBehavior {
|
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])
|
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
|
None
|
||||||
} else {
|
} else {
|
||||||
projectile.Resolve()
|
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.equipment.JammableUnit
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.damage.Damageable
|
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.objects.zones.Zone
|
||||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
@ -63,18 +65,20 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def TakesDamage: Receive = {
|
override protected def PerformDamage(
|
||||||
case Vitality.Damage(applyDamageTo) =>
|
target: Target,
|
||||||
if (mine.CanDamage) {
|
applyDamageTo: Output
|
||||||
val originalHealth = mine.Health
|
): Unit = {
|
||||||
val cause = applyDamageTo(mine)
|
if (mine.CanDamage) {
|
||||||
val damage = originalHealth - mine.Health
|
val originalHealth = mine.Health
|
||||||
if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
|
val cause = applyDamageTo(mine)
|
||||||
ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
|
val damage = originalHealth - mine.Health
|
||||||
} else {
|
if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
|
||||||
mine.Health = originalHealth
|
ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
|
||||||
}
|
} else {
|
||||||
|
mine.Health = originalHealth
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2452,13 +2452,14 @@ object GlobalDefinitions {
|
||||||
comet_projectile.DamageRadius = 1.0f
|
comet_projectile.DamageRadius = 1.0f
|
||||||
comet_projectile.ProjectileDamageType = DamageType.Aggravated
|
comet_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||||
comet_projectile.Aggravated = AggravatedDamage(
|
comet_projectile.Aggravated = AggravatedDamage(
|
||||||
AggravatedInfo(DamageType.Direct, 0.2f, 500),
|
AggravatedInfo(DamageType.Direct, 0.25f, 500), //originally, .2
|
||||||
Aura.Comet,
|
Aura.Comet,
|
||||||
AggravatedTiming(2000, 4),
|
AggravatedTiming(2000, 3),
|
||||||
10f,
|
10f,
|
||||||
List(
|
List(
|
||||||
TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
|
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
|
comet_projectile.InitialVelocity = 80
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
||||||
def JammableObject = gen
|
def JammableObject = gen
|
||||||
def DamageableObject = gen
|
def DamageableObject = gen
|
||||||
def RepairableObject = gen
|
def RepairableObject = gen
|
||||||
private var handleDamageToShields: Boolean = false
|
|
||||||
|
|
||||||
def receive: Receive =
|
def receive: Receive =
|
||||||
jammableBehavior
|
jammableBehavior
|
||||||
|
|
@ -90,18 +89,20 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
||||||
target,
|
target,
|
||||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||||
)
|
)
|
||||||
handleDamageToShields = damageToShields > 0
|
HandleDamage(target, cause, (damageToHealth, damageToShields))
|
||||||
HandleDamage(target, cause, damageToHealth)
|
|
||||||
} else {
|
} else {
|
||||||
gen.Health = originalHealth
|
gen.Health = originalHealth
|
||||||
gen.Shields = originalShields
|
gen.Shields = originalShields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||||
super.DamageAwareness(target, cause, amount)
|
val (damageToHealth, damageToShields) = amount match {
|
||||||
ShieldGeneratorControl.DamageAwareness(gen, cause, handleDamageToShields)
|
case (a: Int, b: Int) => (a, b)
|
||||||
handleDamageToShields = false
|
case _ => (0, 0)
|
||||||
|
}
|
||||||
|
super.DamageAwareness(target, cause, damageToHealth)
|
||||||
|
ShieldGeneratorControl.DamageAwareness(gen, cause, damageToShields > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,11 @@ class TurretControl(turret: TurretDeployable)
|
||||||
def DamageableObject = turret
|
def DamageableObject = turret
|
||||||
def RepairableObject = turret
|
def RepairableObject = turret
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
super.postStop()
|
||||||
|
damageableWeaponTurretPostStop()
|
||||||
|
}
|
||||||
|
|
||||||
def receive: Receive =
|
def receive: Receive =
|
||||||
checkBehavior
|
checkBehavior
|
||||||
.orElse(jammableBehavior)
|
.orElse(jammableBehavior)
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,17 @@ import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||||
import net.psforever.objects.loadouts.Loadout
|
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.aura.AuraEffectBehavior
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
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.{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.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.repair.Repairable
|
import net.psforever.objects.serverobject.repair.Repairable
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.vital._
|
import net.psforever.objects.vital._
|
||||||
|
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||||
|
|
@ -491,29 +492,31 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def TakesDamage: Receive = {
|
override protected def PerformDamage(
|
||||||
case Vitality.Damage(applyDamageTo) =>
|
target: Target,
|
||||||
if (player.isAlive && !player.spectator) {
|
applyDamageTo: Output
|
||||||
val originalHealth = player.Health
|
): Unit = {
|
||||||
val originalArmor = player.Armor
|
if (player.isAlive && !player.spectator) {
|
||||||
val originalStamina = player.avatar.stamina
|
val originalHealth = player.Health
|
||||||
val originalCapacitor = player.Capacitor.toInt
|
val originalArmor = player.Armor
|
||||||
val cause = applyDamageTo(player)
|
val originalStamina = player.avatar.stamina
|
||||||
val health = player.Health
|
val originalCapacitor = player.Capacitor.toInt
|
||||||
val armor = player.Armor
|
val cause = applyDamageTo(player)
|
||||||
val stamina = player.avatar.stamina
|
val health = player.Health
|
||||||
val capacitor = player.Capacitor.toInt
|
val armor = player.Armor
|
||||||
val damageToHealth = originalHealth - health
|
val stamina = player.avatar.stamina
|
||||||
val damageToArmor = originalArmor - armor
|
val capacitor = player.Capacitor.toInt
|
||||||
val damageToStamina = originalStamina - stamina
|
val damageToHealth = originalHealth - health
|
||||||
val damageToCapacitor = originalCapacitor - capacitor
|
val damageToArmor = originalArmor - armor
|
||||||
HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
val damageToStamina = originalStamina - stamina
|
||||||
if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
|
val damageToCapacitor = originalCapacitor - capacitor
|
||||||
damageLog.info(
|
HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||||
s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$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
|
* @param target na
|
||||||
*/
|
*/
|
||||||
def HandleDamage(
|
def HandleDamage(
|
||||||
target: Player,
|
target: Player,
|
||||||
cause: ResolvedProjectile,
|
cause: ResolvedProjectile,
|
||||||
damageToHealth: Int,
|
damageToHealth: Int,
|
||||||
damageToArmor: Int,
|
damageToArmor: Int,
|
||||||
damageToStamina: Int,
|
damageToStamina: Int,
|
||||||
damageToCapacitor: Int
|
damageToCapacitor: Int
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val targetGUID = target.GUID
|
//always do armor update
|
||||||
val zone = target.Zone
|
|
||||||
val zoneId = zone.id
|
|
||||||
val events = zone.AvatarEvents
|
|
||||||
val health = target.Health
|
|
||||||
if (damageToArmor > 0) {
|
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) {
|
//choose
|
||||||
if (damageToCapacitor > 0) {
|
if (target.Health > 0) {
|
||||||
events ! AvatarServiceMessage(
|
DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||||
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 _ => ;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DestructionAwareness(target, Some(cause))
|
DestructionAwareness(target, Some(cause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
def DamageAwareness(
|
||||||
* na
|
target: Player,
|
||||||
* @param target na
|
cause: ResolvedProjectile,
|
||||||
* @param cause na
|
damageToHealth: Int,
|
||||||
*/
|
damageToArmor: Int,
|
||||||
def DamageAwareness(target: Player, cause: ResolvedProjectile): Unit = {
|
damageToStamina: Int,
|
||||||
val zone = target.Zone
|
damageToCapacitor: Int
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
): Unit = {
|
||||||
target.Name,
|
val targetGUID = target.GUID
|
||||||
cause.projectile.owner match {
|
val zone = target.Zone
|
||||||
case pSource: PlayerSource => //player damage
|
val zoneId = zone.id
|
||||||
val name = pSource.Name
|
val events = zone.AvatarEvents
|
||||||
zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
|
val health = target.Health
|
||||||
case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID)
|
var announceConfrontation = damageToArmor > 0
|
||||||
case None =>
|
//special effects
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
|
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,
|
attribute_to: Int,
|
||||||
shot_origin: Vector3,
|
shot_origin: Vector3,
|
||||||
shot_angle: Vector3,
|
shot_angle: Vector3,
|
||||||
quality: Float = 1f,
|
quality: ProjectileQuality = ProjectileQuality.Normal,
|
||||||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||||
fire_time: Long = System.nanoTime
|
fire_time: Long = System.nanoTime
|
||||||
) extends PlanetSideGameObject {
|
) extends PlanetSideGameObject {
|
||||||
|
|
@ -67,8 +67,8 @@ final case class Projectile(
|
||||||
* @param value the new quality
|
* @param value the new quality
|
||||||
* @return a new `Projectile` entity
|
* @return a new `Projectile` entity
|
||||||
*/
|
*/
|
||||||
def quality(value: Float): Projectile =
|
def quality(value: ProjectileQuality): Projectile = {
|
||||||
Projectile(
|
val projectile = Projectile(
|
||||||
profile,
|
profile,
|
||||||
tool_def,
|
tool_def,
|
||||||
fire_mode,
|
fire_mode,
|
||||||
|
|
@ -80,6 +80,10 @@ final case class Projectile(
|
||||||
id,
|
id,
|
||||||
fire_time
|
fire_time
|
||||||
)
|
)
|
||||||
|
if(isMiss) projectile.Miss()
|
||||||
|
else if(isResolved) projectile.Resolve()
|
||||||
|
projectile
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the projectile as being "encountered" or "managed" at least once.
|
* 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] = {
|
def Includes(category: DeployableCategory.Value): List[DeployedItem.Value] = {
|
||||||
(for {
|
(for {
|
||||||
(ce, cat) <- deployablesToCategories
|
(ce: DeployedItem.Value, cat: DeployableCategory.Value) <- deployablesToCategories
|
||||||
if cat == category
|
if cat == category
|
||||||
} yield ce) toList
|
} 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.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base "control" `Actor` mixin for damage-handling code.
|
* The base "control" `Actor` mixin for damage-handling code.
|
||||||
|
|
@ -24,16 +25,35 @@ trait Damageable {
|
||||||
*/
|
*/
|
||||||
def DamageableObject: Damageable.Target
|
def DamageableObject: Damageable.Target
|
||||||
|
|
||||||
/** the official mixin hook; `orElse` onto the "control" `Actor` `receive` */
|
/** the official mixin hook;
|
||||||
final val takesDamage: Receive = TakesDamage
|
* `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.
|
* Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
|
||||||
* Override this method only when directly implementing.
|
* By default, only take an interest in the change of "health".
|
||||||
* @see `takesDamage`
|
* If implementing custom damage with no new message handling, override this method.
|
||||||
* @see `DamageableAmenity.PerformDamage`
|
* @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 {
|
object Damageable {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
//Copyright (c) 2020 PSForever
|
//Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.serverobject.damage
|
package net.psforever.objects.serverobject.damage
|
||||||
|
|
||||||
import akka.actor.Actor.Receive
|
|
||||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||||
import net.psforever.objects.equipment.JammableUnit
|
import net.psforever.objects.equipment.JammableUnit
|
||||||
import net.psforever.objects.vital.Vitality
|
|
||||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
@ -42,23 +40,6 @@ trait DamageableEntity extends Damageable {
|
||||||
DamageLog(s"${name.substring(slashPoint + 1, name.length - 1)}: $msg")
|
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.
|
* 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".
|
* 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 cause historical information about the damage
|
||||||
* @param damage the amount of 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) {
|
if (!target.Destroyed && target.Health <= target.Definition.DamageDestroysAt) {
|
||||||
DestructionAwareness(target, cause)
|
DestructionAwareness(target, cause)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -122,8 +103,12 @@ trait DamageableEntity extends Damageable {
|
||||||
* @param cause historical information about the damage
|
* @param cause historical information about the damage
|
||||||
* @param amount the amount of damage
|
* @param amount the amount of damage
|
||||||
*/
|
*/
|
||||||
protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||||
DamageableEntity.DamageAwareness(target, cause, amount)
|
amount match {
|
||||||
|
case value: Int =>
|
||||||
|
DamageableEntity.DamageAwareness(target, cause, value)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -162,16 +147,22 @@ object DamageableEntity {
|
||||||
if (Damageable.CanJammer(target, cause)) {
|
if (Damageable.CanJammer(target, cause)) {
|
||||||
target.Actor ! JammableUnit.Jammered(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
|
val zone = target.Zone
|
||||||
if (!target.Destroyed) {
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
val tguid = target.GUID
|
zone.id,
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health)
|
||||||
zone.id,
|
)
|
||||||
AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
|
true
|
||||||
)
|
}
|
||||||
}
|
else {
|
||||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,13 @@ object DamageableMountable {
|
||||||
* @see `Zone.LivePlayers`
|
* @see `Zone.LivePlayers`
|
||||||
* @param target the entity being damaged
|
* @param target the entity being damaged
|
||||||
* @param cause historical information about the damage
|
* @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 zone = target.Zone
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val occupants = target.Seats.values.collect {
|
val occupants = target.Seats.values.collect {
|
||||||
|
|
@ -38,9 +43,10 @@ object DamageableMountable {
|
||||||
case pSource: PlayerSource => //player damage
|
case pSource: PlayerSource => //player damage
|
||||||
val name = pSource.Name
|
val name = pSource.Name
|
||||||
(zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
|
(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 =>
|
case None =>
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
|
||||||
}) match {
|
}) match {
|
||||||
case AvatarAction.HitHint(_, guid) =>
|
case AvatarAction.HitHint(_, guid) =>
|
||||||
occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) }
|
occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) }
|
||||||
|
|
@ -48,7 +54,7 @@ object DamageableMountable {
|
||||||
occupants.map { tplayer => (tplayer.Name, msg) }
|
occupants.map { tplayer => (tplayer.Name, msg) }
|
||||||
}
|
}
|
||||||
case source => //object damage
|
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) }
|
occupants.map { tplayer => (tplayer.Name, msg) }
|
||||||
}).foreach {
|
}).foreach {
|
||||||
case (channel, msg) =>
|
case (channel, msg) =>
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,55 @@
|
||||||
//Copyright (c) 2020 PSForever
|
//Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.serverobject.damage
|
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.{Vehicle, Vehicles}
|
||||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||||
|
import net.psforever.objects.equipment.JammableUnit
|
||||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
|
import net.psforever.objects.vital.DamageType
|
||||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
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._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "control" `Actor` mixin for damage-handling code for `Vehicle` objects.
|
* 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 */
|
def damageableVehiclePostStop(): Unit = {
|
||||||
private var handleDamageToShields: Boolean = false
|
EndAllAggravation()
|
||||||
|
}
|
||||||
|
|
||||||
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
|
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
|
||||||
private var reportDamageToVehicle: Boolean = false
|
private var reportDamageToVehicle: Boolean = false
|
||||||
|
|
||||||
def DamageableObject: Vehicle
|
def DamageableObject: Vehicle
|
||||||
|
def AggravatedObject : Vehicle = DamageableObject
|
||||||
|
|
||||||
override protected def TakesDamage: Receive =
|
override val takesDamage: Receive =
|
||||||
super.TakesDamage.orElse {
|
originalTakesDamage
|
||||||
case DamageableVehicle.Damage(cause, damage) =>
|
.orElse(aggravatedBehavior)
|
||||||
//cargo vehicles inherit feedback from carrier
|
.orElse {
|
||||||
reportDamageToVehicle = damage > 0
|
case DamageableVehicle.Damage(cause, damage) =>
|
||||||
DamageAwareness(DamageableObject, cause, amount = 0)
|
//cargo vehicles inherit feedback from carrier
|
||||||
|
reportDamageToVehicle = damage > 0
|
||||||
|
DamageAwareness(DamageableObject, cause, amount = 0)
|
||||||
|
|
||||||
case DamageableVehicle.Destruction(cause) =>
|
case DamageableVehicle.Destruction(cause) =>
|
||||||
//cargo vehicles are destroyed when carrier is destroyed
|
//cargo vehicles are destroyed when carrier is destroyed
|
||||||
val obj = DamageableObject
|
val obj = DamageableObject
|
||||||
obj.Health = 0
|
obj.Health = 0
|
||||||
obj.History(cause)
|
obj.History(cause)
|
||||||
DestructionAwareness(obj, cause)
|
DestructionAwareness(obj, cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
|
* 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,
|
target,
|
||||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||||
)
|
)
|
||||||
handleDamageToShields = damageToShields > 0
|
HandleDamage(target, cause, (damageToHealth, damageToShields))
|
||||||
HandleDamage(target, cause, damageToHealth + damageToShields)
|
|
||||||
} else {
|
} else {
|
||||||
obj.Health = originalHealth
|
obj.Health = originalHealth
|
||||||
obj.Shields = originalShields
|
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
|
* Most all vehicles and the weapons mounted to them can jam
|
||||||
* if the projectile that strikes (near) them has jammering properties.
|
* if the projectile that strikes (near) them has jammering properties.
|
||||||
|
|
@ -121,25 +93,76 @@ object DamageableVehicle {
|
||||||
* @see `VehicleServiceMessage`
|
* @see `VehicleServiceMessage`
|
||||||
* @param target the entity being destroyed
|
* @param target the entity being destroyed
|
||||||
* @param cause historical information about the damage
|
* @param cause historical information about the damage
|
||||||
* @param damage how much damage was performed
|
* @param amount how much damage was performed
|
||||||
* @param damageToShields dispatch a shield strength update
|
|
||||||
*/
|
*/
|
||||||
def DamageAwareness(target: Vehicle, cause: ResolvedProjectile, damage: Int, damageToShields: Boolean): Unit = {
|
override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
|
||||||
//alert cargo occupants to damage source
|
val obj = DamageableObject
|
||||||
target.CargoHolds.values.foreach(hold => {
|
val zone = target.Zone
|
||||||
hold.Occupant match {
|
val events = zone.VehicleEvents
|
||||||
case Some(cargo) =>
|
val targetGUID = target.GUID
|
||||||
cargo.Actor ! DamageableVehicle.Damage(cause, damage + (if (damageToShields) 1 else 0))
|
val zoneId = zone.id
|
||||||
case None => ;
|
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)
|
||||||
}
|
}
|
||||||
})
|
//stat changes
|
||||||
//shields
|
if (damageToShields > 0) {
|
||||||
if (damageToShields) {
|
events ! VehicleServiceMessage(
|
||||||
val zone = target.Zone
|
vehicleChannel,
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, obj.Shields)
|
||||||
s"${target.Actor}",
|
)
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.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 target the entity being destroyed
|
||||||
* @param cause historical information about the damage
|
* @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
|
val zone = target.Zone
|
||||||
|
//aggravation cancel
|
||||||
|
EndAllAggravation()
|
||||||
//cargo vehicles die with us
|
//cargo vehicles die with us
|
||||||
target.CargoHolds.values.foreach(hold => {
|
obj.CargoHolds.values.foreach(hold => {
|
||||||
hold.Occupant match {
|
hold.Occupant match {
|
||||||
case Some(cargo) =>
|
case Some(cargo) =>
|
||||||
cargo.Actor ! DamageableVehicle.Destruction(cause)
|
cargo.Actor ! DamageableVehicle.Destruction(cause)
|
||||||
|
|
@ -175,10 +203,10 @@ object DamageableVehicle {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
//special considerations for certain vehicles
|
//special considerations for certain vehicles
|
||||||
Vehicles.BeforeUnloadVehicle(target, zone)
|
Vehicles.BeforeUnloadVehicle(obj, zone)
|
||||||
//shields
|
//shields
|
||||||
if (target.Shields > 0) {
|
if (obj.Shields > 0) {
|
||||||
target.Shields = 0
|
obj.Shields = 0
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.id,
|
zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
|
||||||
|
|
@ -186,5 +214,22 @@ object DamageableVehicle {
|
||||||
}
|
}
|
||||||
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
||||||
target.ClearHistory()
|
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
|
//Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.serverobject.damage
|
package net.psforever.objects.serverobject.damage
|
||||||
|
|
||||||
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||||
|
import net.psforever.objects.equipment.JammableUnit
|
||||||
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
|
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
|
||||||
import net.psforever.objects.vehicles.MountedWeapons
|
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.Service
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.vehicle.support.TurretUpgrader
|
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.
|
* The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects.
|
||||||
*/
|
*/
|
||||||
trait DamageableWeaponTurret extends DamageableEntity {
|
trait DamageableWeaponTurret
|
||||||
def DamageableObject: Damageable.Target with WeaponTurret
|
extends DamageableEntity
|
||||||
|
with AggravatedBehavior {
|
||||||
|
_: Actor =>
|
||||||
|
|
||||||
override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
|
def damageableWeaponTurretPostStop(): Unit = {
|
||||||
super.DamageAwareness(target, cause, amount)
|
EndAllAggravation()
|
||||||
if (amount > 0) {
|
}
|
||||||
DamageableMountable.DamageAwareness(DamageableObject, cause)
|
|
||||||
|
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 = {
|
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
|
||||||
super.DestructionAwareness(target, cause)
|
super.DestructionAwareness(target, cause)
|
||||||
val obj = DamageableObject
|
val obj = DamageableObject
|
||||||
|
EndAllAggravation()
|
||||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||||
DamageableMountable.DestructionAwareness(obj, cause)
|
DamageableMountable.DestructionAwareness(obj, cause)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,13 @@ class GeneratorControl(gen: Generator)
|
||||||
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
|
!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)
|
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 = {
|
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)
|
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 = {
|
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
|
// Used for timing ammo recharge for vanu turrets in caves
|
||||||
var weaponAmmoRechargeTimer = Default.Cancellable
|
var weaponAmmoRechargeTimer = Default.Cancellable
|
||||||
|
|
||||||
|
override def postStop(): Unit = {
|
||||||
|
super.postStop()
|
||||||
|
damageableWeaponTurretPostStop()
|
||||||
|
}
|
||||||
|
|
||||||
def receive: Receive =
|
def receive: Receive =
|
||||||
checkBehavior
|
checkBehavior
|
||||||
.orElse(jammableBehavior)
|
.orElse(jammableBehavior)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,8 @@ import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
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.containable.{Containable, ContainableBehavior}
|
||||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
||||||
import net.psforever.objects.serverobject.damage.DamageableVehicle
|
|
||||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
|
|
@ -55,8 +52,7 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
with JammableMountedWeapons
|
with JammableMountedWeapons
|
||||||
with ContainableBehavior
|
with ContainableBehavior
|
||||||
with AntTransferBehavior
|
with AntTransferBehavior
|
||||||
with AggravatedBehavior
|
with AggravatedBehavior {
|
||||||
with AuraEffectBehavior {
|
|
||||||
|
|
||||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||||
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
||||||
|
|
@ -79,10 +75,6 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
|
|
||||||
def ChargeTransferObject = vehicle
|
def ChargeTransferObject = vehicle
|
||||||
|
|
||||||
def AuraTargetObject = vehicle
|
|
||||||
|
|
||||||
def AggravatedObject = vehicle
|
|
||||||
|
|
||||||
if(vehicle.Definition == GlobalDefinitions.ant) {
|
if(vehicle.Definition == GlobalDefinitions.ant) {
|
||||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||||
|
|
@ -98,14 +90,13 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
|
damageableVehiclePostStop()
|
||||||
decaying = false
|
decaying = false
|
||||||
decayTimer.cancel()
|
decayTimer.cancel()
|
||||||
vehicle.Utilities.values.foreach { util =>
|
vehicle.Utilities.values.foreach { util =>
|
||||||
context.stop(util().Actor)
|
context.stop(util().Actor)
|
||||||
util().Actor = Default.Actor
|
util().Actor = Default.Actor
|
||||||
}
|
}
|
||||||
EndAllEffects()
|
|
||||||
EndAllAggravation()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def Enabled: Receive =
|
def Enabled: Receive =
|
||||||
|
|
@ -114,8 +105,6 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
.orElse(cargoBehavior)
|
.orElse(cargoBehavior)
|
||||||
.orElse(jammableBehavior)
|
.orElse(jammableBehavior)
|
||||||
.orElse(takesDamage)
|
.orElse(takesDamage)
|
||||||
.orElse(aggravatedBehavior)
|
|
||||||
.orElse(auraBehavior)
|
|
||||||
.orElse(canBeRepairedByNanoDispenser)
|
.orElse(canBeRepairedByNanoDispenser)
|
||||||
.orElse(containerBehavior)
|
.orElse(containerBehavior)
|
||||||
.orElse(antBehavior)
|
.orElse(antBehavior)
|
||||||
|
|
@ -592,30 +581,6 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
}
|
}
|
||||||
out
|
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 {
|
object VehicleControl {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package net.psforever.objects.vital.damage
|
package net.psforever.objects.vital.damage
|
||||||
|
|
||||||
import net.psforever.objects.GlobalDefinitions
|
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.objects.vital.DamageType
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.{ExoSuitType, Vector3}
|
||||||
|
|
||||||
|
|
@ -163,7 +163,8 @@ object DamageModifiers {
|
||||||
damage: Int,
|
damage: Int,
|
||||||
data: ResolvedProjectile
|
data: ResolvedProjectile
|
||||||
): Int = {
|
): Int = {
|
||||||
if (data.resolution == resolution) {
|
if (data.resolution == resolution &&
|
||||||
|
data.projectile.quality == ProjectileQuality.AggravatesTarget) {
|
||||||
(data.projectile.profile.Aggravated, data.target) match {
|
(data.projectile.profile.Aggravated, data.target) match {
|
||||||
case (Some(aggravation), p: PlayerSource) =>
|
case (Some(aggravation), p: PlayerSource) =>
|
||||||
val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
|
val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
|
||||||
|
|
@ -230,7 +231,8 @@ object DamageModifiers {
|
||||||
def Calculate: DamageModifiers.Format = formula
|
def Calculate: DamageModifiers.Format = formula
|
||||||
|
|
||||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
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 {
|
(data.projectile.profile.Aggravated, data.target) match {
|
||||||
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
|
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
|
||||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||||
|
|
@ -257,7 +259,7 @@ object DamageModifiers {
|
||||||
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
|
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
|
||||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||||
case Some(infos) =>
|
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 _ =>
|
case _ =>
|
||||||
damage
|
damage
|
||||||
}
|
}
|
||||||
|
|
@ -274,8 +276,19 @@ object DamageModifiers {
|
||||||
def Calculate: DamageModifiers.Format = formula
|
def Calculate: DamageModifiers.Format = formula
|
||||||
|
|
||||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||||
if (data.resolution == ProjectileResolution.AggravatedDirect) {
|
if (data.resolution == ProjectileResolution.AggravatedDirect &&
|
||||||
0
|
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 {
|
} else {
|
||||||
damage
|
damage
|
||||||
}
|
}
|
||||||
|
|
@ -288,8 +301,13 @@ object DamageModifiers {
|
||||||
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
private def formula(damage: Int, data: ResolvedProjectile): Int = {
|
||||||
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
|
if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
|
||||||
data.projectile.profile.Aggravated match {
|
data.projectile.profile.Aggravated match {
|
||||||
case Some(_) =>
|
case Some(aggravation) =>
|
||||||
(damage * data.projectile.quality) toInt
|
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||||
|
case Some(infos) =>
|
||||||
|
damage - (damage * infos.degradation_percentage) toInt
|
||||||
|
case _ =>
|
||||||
|
damage
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatched by the server to indicate a source of damage affecting the player.
|
* 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>
|
* <br>
|
||||||
* The player will be shown a fading, outwards drifting, red tick mark.
|
* The player will be shown a fading, outwards drifting, red tick mark.
|
||||||
* The location will indicate a general direction towards the source.
|
* The location will indicate a general direction towards the source.
|
||||||
|
|
@ -27,5 +30,14 @@ object DamageWithPositionMessage extends Marshallable[DamageWithPositionMessage]
|
||||||
implicit val codec: Codec[DamageWithPositionMessage] = (
|
implicit val codec: Codec[DamageWithPositionMessage] = (
|
||||||
("unk" | uint8L) ::
|
("unk" | uint8L) ::
|
||||||
("pos" | Vector3.codec_pos)
|
("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…
Add table
Add a link
Reference in a new issue