mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
separated aggravation behavior and uara management behavior; moved files into packages and deleted old files
This commit is contained in:
parent
a79fc6bd2f
commit
89d7aea633
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
import net.psforever.objects.equipment.TargetValidation
|
||||
import net.psforever.objects.serverobject.aggravated.Aura
|
||||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.vital.DamageType
|
||||
|
||||
final case class AggravatedTiming(duration: Long, ticks: Option[Int])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
|
||||
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.vital.{DamageType, Vitality}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AggravatedBehavior {
|
||||
_ : Actor with Damageable =>
|
||||
private val entryIdToEntry: mutable.LongMap[AggravatedBehavior.Entry] =
|
||||
mutable.LongMap.empty[AggravatedBehavior.Entry]
|
||||
private val aggravationToTimer: mutable.LongMap[Cancellable] =
|
||||
mutable.LongMap.empty[Cancellable]
|
||||
|
||||
def AggravatedObject: AggravatedBehavior.Target
|
||||
|
||||
def TryAggravationEffect(data: ResolvedProjectile): Option[AggravatedDamage] = {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(damage)
|
||||
if data.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
|
||||
damage.effect_type != Aura.Nothing &&
|
||||
damage.targets.exists(validation => validation.test(AggravatedObject)) =>
|
||||
TryAggravationEffect(damage, data)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def TryAggravationEffect(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)) {
|
||||
SetupAggravationEntry(aggravation, data)
|
||||
Some(aggravation)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def CheckForUniqueUnqueuedProjectile(projectile : Projectile): Boolean = {
|
||||
!entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
|
||||
}
|
||||
|
||||
private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile): Boolean = {
|
||||
val effect = aggravation.effect_type
|
||||
aggravation.info.find(_.damage_type == AggravatedBehavior.basicDamageType(data.resolution)) match {
|
||||
case Some(info) =>
|
||||
val timing = aggravation.timing
|
||||
val duration = timing.duration
|
||||
//setup effect
|
||||
val id = data.projectile.id
|
||||
//setup timer data
|
||||
val (tick: Long, iterations: Int) = timing.ticks match {
|
||||
case Some(n) if n < 1 =>
|
||||
val rate = info.infliction_rate
|
||||
(rate, (duration / rate).toInt)
|
||||
case Some(ticks) =>
|
||||
(duration / ticks, ticks)
|
||||
case None =>
|
||||
(1000L, (duration / 1000).toInt)
|
||||
}
|
||||
//val leftoverTime = duration - (tick * iterations)
|
||||
//quality per tick
|
||||
val totalPower = (duration.toFloat / info.infliction_rate).toInt - 1
|
||||
val averagePowerPerTick = totalPower.toFloat / iterations
|
||||
val lastTickRemainder = totalPower - averagePowerPerTick * iterations
|
||||
val qualityPerTick: List[Float] = if (lastTickRemainder > 0) {
|
||||
0f +: List.fill[Float](iterations - 1)(averagePowerPerTick) :+ (lastTickRemainder + averagePowerPerTick)
|
||||
}
|
||||
else {
|
||||
0f +: List.fill[Float](iterations)(averagePowerPerTick)
|
||||
}
|
||||
//pair id with entry
|
||||
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))
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private def PairIdWithAggravationEntry(
|
||||
id: Long,
|
||||
effect: Aura,
|
||||
retime: Long,
|
||||
data: ResolvedProjectile,
|
||||
target: SourceEntry,
|
||||
powerOffset: List[Float]
|
||||
): AggravatedBehavior.Entry = {
|
||||
val aggravatedDamageInfo = ResolvedProjectile(
|
||||
AggravatedBehavior.burning(data.resolution),
|
||||
data.projectile,
|
||||
target,
|
||||
data.damage_model,
|
||||
data.hit_pos
|
||||
)
|
||||
val entry = AggravatedBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
|
||||
entryIdToEntry += id -> entry
|
||||
entry
|
||||
}
|
||||
|
||||
val aggravatedBehavior: Receive = {
|
||||
case AggravatedBehavior.Aggravate(id, 0) =>
|
||||
AggravationCleanup(id)
|
||||
|
||||
case AggravatedBehavior.Aggravate(id, iteration) =>
|
||||
RetimeEventAndPerformAggravation(id, iteration, None)
|
||||
}
|
||||
|
||||
private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long]) : Unit = {
|
||||
RetimeAggravation(id, iteration - 1, time) match {
|
||||
case Some(entry) =>
|
||||
PerformAggravation(entry, iteration)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
private def RetimeAggravation(
|
||||
id: Long,
|
||||
iteration: Int,
|
||||
time: Option[Long]
|
||||
): Option[AggravatedBehavior.Entry] = {
|
||||
CleanupAggravationTimer(id)
|
||||
entryIdToEntry.get(id) match {
|
||||
case out @ Some(oldEntry) =>
|
||||
aggravationToTimer += id -> context.system.scheduler.scheduleOnce(
|
||||
time.getOrElse(oldEntry.retime) milliseconds,
|
||||
self,
|
||||
AggravatedBehavior.Aggravate(id, iteration)
|
||||
)
|
||||
out
|
||||
case _ =>
|
||||
AggravationCleanup(id)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveAggravatedEntry(id: Long): Aura = {
|
||||
entryIdToEntry.remove(id) match {
|
||||
case Some(entry) =>
|
||||
entry.data.projectile.profile.Aggravated.get.effect_type
|
||||
case _ =>
|
||||
Aura.Nothing
|
||||
}
|
||||
}
|
||||
|
||||
def CleanupAggravationTimer(id: Long): Unit = {
|
||||
//remove and cancel timer
|
||||
aggravationToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def AggravationCleanup(id: Long): Unit = {
|
||||
RemoveAggravatedEntry(id)
|
||||
CleanupAggravationTimer(id)
|
||||
}
|
||||
|
||||
def EndAllAggravation(): Unit = {
|
||||
entryIdToEntry.clear
|
||||
aggravationToTimer.values.foreach { _.cancel }
|
||||
aggravationToTimer.clear
|
||||
}
|
||||
|
||||
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.target,
|
||||
model,
|
||||
data.hit_pos
|
||||
)
|
||||
TakesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
|
||||
}
|
||||
}
|
||||
|
||||
object AggravatedBehavior {
|
||||
type Target = AuraEffectBehavior.Target with Vitality
|
||||
|
||||
private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
|
||||
|
||||
private case class Aggravate(id: Long, iterations: Int)
|
||||
|
||||
private def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn
|
||||
case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn
|
||||
case _ => resolution
|
||||
}
|
||||
}
|
||||
|
||||
private def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn =>
|
||||
DamageType.Direct
|
||||
case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn =>
|
||||
DamageType.Splash
|
||||
case _ =>
|
||||
DamageType.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
|
||||
sealed class Aura(val id: Int)
|
||||
|
||||
object Aura {
|
||||
final case object None extends Aura(id = 0)
|
||||
|
||||
final case object Plasma extends Aura(id = 1)
|
||||
|
||||
final case object Comet extends Aura(id = 2)
|
||||
|
||||
final case object Napalm extends Aura(id = 4)
|
||||
|
||||
final case object Fire extends Aura(id = 8)
|
||||
}
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.ballistics._
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.vital.{DamageType, Vitality}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AuraEffectBehavior {
|
||||
_ : Actor with Damageable =>
|
||||
private var activeEffectIndex: Long = 0
|
||||
private val effectToEntryId: mutable.HashMap[Aura, List[Long]] =
|
||||
mutable.HashMap.empty[Aura, List[Long]]
|
||||
private val entryIdToTimer: mutable.LongMap[Cancellable] =
|
||||
mutable.LongMap.empty[Cancellable]
|
||||
private val entryIdToEntry: mutable.LongMap[AuraEffectBehavior.Entry] =
|
||||
mutable.LongMap.empty[AuraEffectBehavior.Entry]
|
||||
|
||||
def AuraTargetObject: AuraEffectBehavior.Target
|
||||
|
||||
val auraBehavior: Receive = {
|
||||
case AuraEffectBehavior.Aggravate(id, 0, 0) =>
|
||||
CancelEffectTimer(id)
|
||||
PerformCleanupEffect(id)
|
||||
|
||||
case AuraEffectBehavior.Aggravate(id, 0, leftoverTime) =>
|
||||
RemoveEffectEntry(id)
|
||||
RetimeEvent(id, iteration = 0, Some(leftoverTime), leftoverTime = 0)
|
||||
|
||||
case AuraEffectBehavior.Aggravate(id, iteration, leftover) =>
|
||||
RetimeEventAndPerformAggravation(id, iteration, None, leftover)
|
||||
}
|
||||
|
||||
private def RetimeEvent(
|
||||
id: Long,
|
||||
iteration: Int,
|
||||
time: Option[Long],
|
||||
leftoverTime: Long
|
||||
): Option[AuraEffectBehavior.Entry] = {
|
||||
CancelEffectTimer(id)
|
||||
entryIdToEntry.get(id) match {
|
||||
case out @ Some(oldEntry) =>
|
||||
entryIdToTimer += id -> context.system.scheduler.scheduleOnce(
|
||||
time.getOrElse(oldEntry.retime) milliseconds,
|
||||
self,
|
||||
AuraEffectBehavior.Aggravate(id, iteration, leftoverTime)
|
||||
)
|
||||
out
|
||||
case _ =>
|
||||
PerformCleanupEffect(id)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long], leftoverTime: Long) : Unit = {
|
||||
RetimeEvent(id, iteration - 1, time, leftoverTime) match {
|
||||
case Some(entry) =>
|
||||
PerformAggravation(entry, iteration)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def CancelEffectTimer(id: Long) : Unit = {
|
||||
entryIdToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def PerformCleanupEffect(id: Long) : Unit = {
|
||||
CleanupEffect(id) match {
|
||||
case Aura.None => ;
|
||||
case _ => UpdateAggravatedEffect(AuraTargetObject)
|
||||
}
|
||||
}
|
||||
|
||||
def TryAggravationEffect(data: ResolvedProjectile) : Unit = {
|
||||
data.projectile.profile.Aggravated match {
|
||||
case Some(damage)
|
||||
if data.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
|
||||
damage.effect_type != Aura.None && //TODO aphelion starfire
|
||||
damage.targets.exists(validation => validation.test(AuraTargetObject)) =>
|
||||
TryAggravationEffect(damage, data)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
private def TryAggravationEffect(aggravation: AggravatedDamage, data: ResolvedProjectile) : Unit = {
|
||||
val effect = aggravation.effect_type
|
||||
val obj = AuraTargetObject
|
||||
if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
|
||||
val auraEffects = obj.Aura
|
||||
if(auraEffects.contains(effect) && aggravation.cumulative_damage_degrade) {
|
||||
SetupAggravationEntry(aggravation, data)
|
||||
}
|
||||
else if(obj.AddEffectToAura(effect).diff(auraEffects).contains(effect)) {
|
||||
SetupAggravationEntry(aggravation, data)
|
||||
UpdateAggravatedEffect(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def CheckForUniqueUnqueuedProjectile(projectile : Projectile) : Boolean = {
|
||||
!entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
|
||||
}
|
||||
|
||||
private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile) : Unit = {
|
||||
val effect = aggravation.effect_type
|
||||
aggravation.info.find(_.damage_type == AuraEffectBehavior.basicDamageType(data.resolution)) match {
|
||||
case Some(info) =>
|
||||
//get unused id
|
||||
val id = activeEffectIndex
|
||||
activeEffectIndex += 1
|
||||
//pair aura effect with id
|
||||
effectToEntryId.get(effect) match {
|
||||
case None | Some(Nil) => effectToEntryId += effect -> List(id)
|
||||
case Some(list) => effectToEntryId -> (list :+ id)
|
||||
}
|
||||
//setup timer data
|
||||
val timing = aggravation.timing
|
||||
val duration = timing.duration
|
||||
val (tick: Long, iterations: Int) = timing.ticks match {
|
||||
case Some(n) if n < 1 =>
|
||||
val rate = info.infliction_rate
|
||||
(rate, (duration / rate).toInt)
|
||||
case Some(ticks) =>
|
||||
(duration / ticks, ticks)
|
||||
case None =>
|
||||
(1000, (duration / 1000).toInt)
|
||||
}
|
||||
val leftoverTime = duration - (tick * iterations)
|
||||
//quality per tick
|
||||
val totalPower = (duration.toFloat / info.infliction_rate).toInt - 1
|
||||
val averagePowerPerTick = totalPower.toFloat / iterations
|
||||
val lastTickRemainder = totalPower - averagePowerPerTick * iterations
|
||||
val qualityPerTick: List[Float] = if (lastTickRemainder > 0) {
|
||||
0f +: List.fill[Float](iterations - 1)(averagePowerPerTick) :+ (lastTickRemainder + averagePowerPerTick)
|
||||
}
|
||||
else {
|
||||
0f +: List.fill[Float](iterations)(averagePowerPerTick)
|
||||
}
|
||||
//pair id with entry
|
||||
PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
|
||||
//pair id with timer
|
||||
entryIdToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AuraEffectBehavior.Aggravate(id, iterations, leftoverTime))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
private def PairIdWithAggravationEntry(
|
||||
id: Long,
|
||||
effect: Aura,
|
||||
retime: Long,
|
||||
data: ResolvedProjectile,
|
||||
target: SourceEntry,
|
||||
powerOffset: List[Float]
|
||||
): AuraEffectBehavior.Entry = {
|
||||
val aggravatedDamageInfo = ResolvedProjectile(
|
||||
AuraEffectBehavior.burning(data.resolution),
|
||||
data.projectile,
|
||||
target,
|
||||
data.damage_model,
|
||||
data.hit_pos
|
||||
)
|
||||
val entry = AuraEffectBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
|
||||
entryIdToEntry += id -> entry
|
||||
entry
|
||||
}
|
||||
|
||||
def RemoveEffectEntry(id: Long) : Aura = {
|
||||
entryIdToEntry.remove(id) match {
|
||||
case Some(entry) =>
|
||||
entry.data.projectile.profile.Aggravated.get.effect_type
|
||||
case None =>
|
||||
effectToEntryId.find { case (_, values) => values.contains(id) } match {
|
||||
case Some((effect, _)) => effect
|
||||
case _ => Aura.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def CleanupEffect(id: Long) : Aura = {
|
||||
//remove and cancel timer
|
||||
entryIdToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel
|
||||
case _ => ;
|
||||
}
|
||||
//remove entry and cache effect
|
||||
val out = RemoveEffectEntry(id)
|
||||
//remove id and, if now unsupported, effect
|
||||
(effectToEntryId.get(out) match {
|
||||
case Some(list) => (out, list.filterNot(_ == id))
|
||||
case _ => (Aura.None, Nil)
|
||||
}) match {
|
||||
case (Aura.None, _) =>
|
||||
Aura.None
|
||||
case (effect, Nil) =>
|
||||
AuraTargetObject.RemoveEffectFromAura(effect)
|
||||
effectToEntryId.remove(effect)
|
||||
effect
|
||||
case (effect, list) =>
|
||||
effectToEntryId += effect -> list
|
||||
Aura.None
|
||||
}
|
||||
}
|
||||
|
||||
def EndAllEffects() : Unit = {
|
||||
entryIdToEntry.clear
|
||||
entryIdToTimer.values.foreach { _.cancel }
|
||||
entryIdToTimer.clear
|
||||
effectToEntryId.clear
|
||||
val obj = AuraTargetObject
|
||||
obj.Aura.foreach { obj.RemoveEffectFromAura }
|
||||
}
|
||||
|
||||
def EndAllEffectsAndUpdate() : Unit = {
|
||||
EndAllEffects()
|
||||
UpdateAggravatedEffect(AuraTargetObject)
|
||||
}
|
||||
|
||||
def UpdateAggravatedEffect(target: AuraEffectBehavior.Target) : Unit = {
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
val zone = target.Zone
|
||||
val value = target.Aura.foldLeft(0)(_ + _.id)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
|
||||
}
|
||||
|
||||
private def PerformAggravation(entry: AuraEffectBehavior.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.target,
|
||||
model,
|
||||
data.hit_pos
|
||||
)
|
||||
TakesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
|
||||
}
|
||||
}
|
||||
|
||||
object AuraEffectBehavior {
|
||||
type Target = PlanetSideServerObject with Vitality with AuraContainer
|
||||
|
||||
private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
|
||||
|
||||
private case class Aggravate(id: Long, iterations: Int, leftover: Long)
|
||||
|
||||
private def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn
|
||||
case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn
|
||||
case _ => resolution
|
||||
}
|
||||
}
|
||||
|
||||
private def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = {
|
||||
resolution match {
|
||||
case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn =>
|
||||
DamageType.Direct
|
||||
case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn =>
|
||||
DamageType.Splash
|
||||
case _ =>
|
||||
DamageType.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
/**
|
||||
* An effect that can be emitted by a target game object entity.
|
||||
*/
|
||||
sealed class Aura()
|
||||
|
||||
/**
|
||||
* Visual effects emitted by a target, usually a `Player` entity.
|
||||
* Most often paired with aggravated damage.
|
||||
* Unrelated to the effects emitted by obtaining and transporting
|
||||
* the lattice logic unit, or a facility module, or the rabbit ball.
|
||||
*/
|
||||
object Aura {
|
||||
/** Since `None` is an actual effect, the "no effect" default is repurposed as "Nothing". */
|
||||
final case object Nothing extends Aura
|
||||
|
||||
/** Conferred by the `aphelion_starfire_projectile`. */
|
||||
final case object None extends Aura
|
||||
|
||||
/** A green emission. */
|
||||
final case object Plasma extends Aura
|
||||
|
||||
/** A purple emission. */
|
||||
final case object Comet extends Aura
|
||||
|
||||
/** A white and yellow starburst emission. */
|
||||
final case object Napalm extends Aura
|
||||
|
||||
/** A red and orange emission. */
|
||||
final case object Fire extends Aura
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
import net.psforever.objects.serverobject.aggravated.{Aura => AuraEffect}
|
||||
import net.psforever.objects.serverobject.aura.{Aura => AuraEffect}
|
||||
|
||||
/**
|
||||
* An entity that can display specific special effects that decorate its model.
|
||||
* These animations confer information about the nature of some status that is affecting the target entity.
|
||||
*/
|
||||
trait AuraContainer {
|
||||
private var aura : Set[AuraEffect] = Set.empty[AuraEffect]
|
||||
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aura
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
trait AuraEffectBehavior {
|
||||
_ : Actor =>
|
||||
private var activeEffectIndex: Long = 0
|
||||
private val effectToEntryId: mutable.HashMap[Aura, List[Long]] =
|
||||
mutable.HashMap.empty[Aura, List[Long]]
|
||||
private val effectIdToTimer: mutable.LongMap[Cancellable] =
|
||||
mutable.LongMap.empty[Cancellable]
|
||||
|
||||
def AuraTargetObject: AuraEffectBehavior.Target
|
||||
|
||||
val auraBehavior: Receive = {
|
||||
case AuraEffectBehavior.StartEffect(effect, duration) =>
|
||||
StartAuraEffect(effect, duration)
|
||||
|
||||
case AuraEffectBehavior.EndEffect(Some(id), None) =>
|
||||
EndAuraEffect(id)
|
||||
|
||||
case AuraEffectBehavior.EndEffect(None, Some(effect)) =>
|
||||
EndAuraEffect(effect)
|
||||
|
||||
case AuraEffectBehavior.EndAllEffects() =>
|
||||
EndAllEffectsAndUpdate()
|
||||
}
|
||||
|
||||
final def GetUnusedEffectId: Long = {
|
||||
val id = activeEffectIndex
|
||||
activeEffectIndex += 1
|
||||
id
|
||||
}
|
||||
|
||||
def StartAuraEffect(effect: Aura, duration: Long): Long = {
|
||||
StartAuraEffect(GetUnusedEffectId, effect, duration)
|
||||
}
|
||||
|
||||
def StartAuraEffect(id: Long, effect: Aura, duration: Long): Long = {
|
||||
//pair aura effect with id
|
||||
effectToEntryId.get(effect) match {
|
||||
case None | Some(Nil) => effectToEntryId += effect -> List(id)
|
||||
case Some(list) => effectToEntryId -> (list :+ id)
|
||||
}
|
||||
//pair id with timer
|
||||
effectIdToTimer += id -> context.system.scheduler.scheduleOnce(duration milliseconds, self, AuraEffectBehavior.EndEffect(id))
|
||||
//update visuals
|
||||
UpdateAuraEffect(AuraTargetObject)
|
||||
id
|
||||
}
|
||||
|
||||
def EndAuraEffect(id: Long): Unit = {
|
||||
EndActiveEffect(id) match {
|
||||
case Aura.Nothing => ;
|
||||
case effect =>
|
||||
CancelEffectTimer(id)
|
||||
val obj = AuraTargetObject
|
||||
obj.RemoveEffectFromAura(effect)
|
||||
UpdateAuraEffect(obj)
|
||||
}
|
||||
}
|
||||
|
||||
def EndActiveEffect(id: Long): Aura = {
|
||||
effectToEntryId.find { case (_, ids) => ids.contains(id) } match {
|
||||
case Some((effect, ids)) if ids.size == 1 =>
|
||||
effectToEntryId.remove(effect)
|
||||
effect
|
||||
case Some((effect, ids)) =>
|
||||
effectToEntryId += effect -> ids.filterNot(_ == id)
|
||||
Aura.Nothing
|
||||
case None =>
|
||||
Aura.Nothing
|
||||
}
|
||||
}
|
||||
|
||||
def CancelEffectTimer(id: Long) : Unit = {
|
||||
effectIdToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def EndAuraEffect(effect: Aura): Unit = {
|
||||
effectToEntryId.remove(effect) match {
|
||||
case Some(idList) =>
|
||||
idList.foreach { id =>
|
||||
val obj = AuraTargetObject
|
||||
CancelEffectTimer(id)
|
||||
obj.RemoveEffectFromAura(effect)
|
||||
UpdateAuraEffect(obj)
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def EndAllEffects() : Unit = {
|
||||
effectIdToTimer.values.foreach { _.cancel }
|
||||
effectIdToTimer.clear
|
||||
effectToEntryId.clear
|
||||
val obj = AuraTargetObject
|
||||
obj.Aura.foreach { obj.RemoveEffectFromAura }
|
||||
}
|
||||
|
||||
def EndAllEffectsAndUpdate() : Unit = {
|
||||
EndAllEffects()
|
||||
UpdateAuraEffect(AuraTargetObject)
|
||||
}
|
||||
|
||||
def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = {
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
val zone = target.Zone
|
||||
val value = target.Aura.foldLeft(0)(_ + AuraEffectBehavior.effectToAttributeValue(_))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
|
||||
}
|
||||
|
||||
def TestForEffect(id: Long): Aura = {
|
||||
effectToEntryId.find { case (_, ids) => ids.contains(id) } match {
|
||||
case Some((effect, _)) => effect
|
||||
case _ => Aura.Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AuraEffectBehavior {
|
||||
type Target = PlanetSideServerObject with AuraContainer
|
||||
|
||||
final case class StartEffect(effect: Aura, duration: Long)
|
||||
|
||||
final case class EndEffect(id: Option[Long], aura: Option[Aura])
|
||||
|
||||
object EndEffect {
|
||||
def apply(id: Long): EndEffect = EndEffect(Some(id), None)
|
||||
|
||||
def apply(aura: Aura): EndEffect = EndEffect(None, Some(aura))
|
||||
}
|
||||
|
||||
final case class EndAllEffects()
|
||||
|
||||
private def effectToAttributeValue(effect: Aura): Int = effect match {
|
||||
case Aura.None => 0
|
||||
case Aura.Plasma => 1
|
||||
case Aura.Comet => 2
|
||||
case Aura.Napalm => 4
|
||||
case Aura.Fire => 8
|
||||
case _ => Int.MinValue
|
||||
}
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
//package net.psforever.objects.vital
|
||||
//
|
||||
//import net.psforever.objects.vital.StandardAmenityDamage.None
|
||||
//import net.psforever.objects.vital.StandardDeployableDamage.None
|
||||
//import net.psforever.objects.vital.damage._
|
||||
//import net.psforever.objects.vital.damage.DamageCalculations._
|
||||
//import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
//
|
||||
///**
|
||||
// * A protected super class for calculating "no damage."
|
||||
// * Used for `NoDamage` but also for the base of `*LashDamage` calculation objects
|
||||
// * to maintain the polymorphic identity of `DamageCalculations`.
|
||||
// */
|
||||
//protected class NoDamageBase
|
||||
// extends DamageCalculations(
|
||||
// DamageCalculations.NoDamage,
|
||||
// DamageWithModifiers(NoDamageAgainst),
|
||||
// TooFar
|
||||
// )
|
||||
//
|
||||
//object NoDamage extends NoDamageBase
|
||||
//
|
||||
//object InfantryHitDamage
|
||||
// extends DamageCalculations(
|
||||
// DirectHitDamageWithDegrade,
|
||||
// DamageWithModifiers(DamageAgainstExoSuit),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object MaxHitDamage
|
||||
// extends DamageCalculations(
|
||||
// DirectHitDamageWithDegrade,
|
||||
// DamageWithModifiers(DamageAgainstMaxSuit),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object VehicleHitDamage
|
||||
// extends DamageCalculations(
|
||||
// DirectHitDamageWithDegrade,
|
||||
// DamageWithModifiers(DamageAgainstVehicle),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object AircraftHitDamage
|
||||
// extends DamageCalculations(
|
||||
// DirectHitDamageWithDegrade,
|
||||
// DamageWithModifiers(DamageAgainstAircraft),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object InfantrySplashDamage
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstExoSuit),
|
||||
// DistanceFromExplosionToTarget
|
||||
// )
|
||||
//
|
||||
//object MaxSplashDamage
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstMaxSuit),
|
||||
// DistanceFromExplosionToTarget
|
||||
// )
|
||||
//
|
||||
//object VehicleSplashDamage
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstVehicle),
|
||||
// DistanceFromExplosionToTarget
|
||||
// )
|
||||
//
|
||||
//object AircraftSplashDamage
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstAircraft),
|
||||
// DistanceFromExplosionToTarget
|
||||
// )
|
||||
//
|
||||
//object InfantrySplashDamageDirect
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstAircraft),
|
||||
// NoDistance
|
||||
// )
|
||||
//
|
||||
//object InfantryLashDamage
|
||||
// extends DamageCalculations(
|
||||
// LashDamage,
|
||||
// DamageWithModifiers(DamageAgainstExoSuit),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object MaxLashDamage
|
||||
// extends DamageCalculations(
|
||||
// LashDamage,
|
||||
// DamageWithModifiers(DamageAgainstMaxSuit),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object VehicleLashDamage
|
||||
// extends DamageCalculations(
|
||||
// LashDamage,
|
||||
// DamageWithModifiers(DamageAgainstVehicle),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object AircraftLashDamage
|
||||
// extends DamageCalculations(
|
||||
// LashDamage,
|
||||
// DamageWithModifiers(DamageAgainstAircraft),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object AmenityHitDamage
|
||||
// extends DamageCalculations(
|
||||
// DirectHitDamageWithDegrade,
|
||||
// DamageWithModifiers(DamageAgainstVehicle),
|
||||
// DistanceBetweenTargetandSource
|
||||
// )
|
||||
//
|
||||
//object AmenitySplashDamage
|
||||
// extends DamageCalculations(
|
||||
// SplashDamageWithRadialDegrade,
|
||||
// DamageWithModifiers(DamageAgainstVehicle),
|
||||
// DistanceFromExplosionToTarget
|
||||
// )
|
||||
//
|
||||
//object NoDamageSelection extends DamageSelection {
|
||||
// def Direct = None
|
||||
// def Splash = None
|
||||
// def Lash = None
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
//
|
||||
//object StandardInfantryDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = InfantryHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = InfantrySplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = InfantryLashDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = InfantrySplashDamage.Calculate
|
||||
//}
|
||||
//
|
||||
//object StandardMaxDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = MaxHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = MaxSplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = MaxLashDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
//
|
||||
//object StandardVehicleDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = VehicleHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = VehicleSplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = VehicleLashDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
//
|
||||
//object StandardAircraftDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = AircraftHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = AircraftSplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = AircraftLashDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
//
|
||||
//object StandardDeployableDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = VehicleHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = VehicleSplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = NoDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
//
|
||||
//object StandardAmenityDamage extends DamageSelection {
|
||||
// def Direct : ProjectileCalculations.Form = AmenityHitDamage.Calculate
|
||||
// def Splash : ProjectileCalculations.Form = AmenitySplashDamage.Calculate
|
||||
// def Lash : ProjectileCalculations.Form = NoDamage.Calculate
|
||||
// def Aggravated : ProjectileCalculations.Form = None
|
||||
//}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
//package net.psforever.objects.vital.damage
|
||||
//
|
||||
//import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
//import net.psforever.objects.vital.{DamageType, NoDamage}
|
||||
//import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
//
|
||||
///**
|
||||
// * Maintain information about three primary forms of damage calculation
|
||||
// * and a means to test which calculation is valid in a given situation.
|
||||
// */
|
||||
//trait DamageSelection {
|
||||
// final def None: ProjectileCalculations.Form = NoDamage
|
||||
//
|
||||
// def Direct : ProjectileCalculations.Form
|
||||
// def Splash : ProjectileCalculations.Form
|
||||
// def Lash : ProjectileCalculations.Form
|
||||
// def Aggravated : ProjectileCalculations.Form
|
||||
//
|
||||
// def apply(data : ResolvedProjectile) : ProjectileCalculations.Form = data.projectile.profile.ProjectileDamageType match {
|
||||
// case DamageType.Direct => Direct
|
||||
// case DamageType.Splash => Splash
|
||||
// case DamageType.Lash => Lash
|
||||
// case DamageType.Aggravated => Aggravated
|
||||
// case _ => None
|
||||
// }
|
||||
//
|
||||
// def apply(res : DamageType.Value) : ProjectileCalculations.Form = res match {
|
||||
// case DamageType.Direct => Direct
|
||||
// case DamageType.Splash => Splash
|
||||
// case DamageType.Lash => Lash
|
||||
// case DamageType.Aggravated => Aggravated
|
||||
// case _ => None
|
||||
// }
|
||||
//}
|
||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.definition._
|
|||
import net.psforever.objects.definition.converter._
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.aggravated.Aura
|
||||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.serverobject.doors.DoorDefinition
|
||||
import net.psforever.objects.serverobject.generator.GeneratorDefinition
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot,
|
|||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.aggravated.AuraContainer
|
||||
import net.psforever.objects.serverobject.aura.AuraContainer
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||
import net.psforever.objects.zones.ZoneAware
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem,
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.aggravated.AuraContainer
|
||||
import net.psforever.objects.serverobject.aura.AuraContainer
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.AmenityOwner
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ 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.AuraEffectBehavior
|
||||
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.{CommonMessages, PlanetSideServerObject}
|
||||
|
|
@ -32,6 +33,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
with JammableBehavior
|
||||
with Damageable
|
||||
with ContainableBehavior
|
||||
with AggravatedBehavior
|
||||
with AuraEffectBehavior {
|
||||
def JammableObject = player
|
||||
|
||||
|
|
@ -39,6 +41,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
|
||||
def ContainerObject = player
|
||||
|
||||
def AggravatedObject = player
|
||||
|
||||
def AuraTargetObject = player
|
||||
|
||||
private[this] val log = org.log4s.getLogger(player.Name)
|
||||
|
|
@ -58,11 +62,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
lockerControlAgent ! akka.actor.PoisonPill
|
||||
player.avatar.locker.Actor = Default.Actor
|
||||
EndAllEffects()
|
||||
EndAllAggravation()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
jammableBehavior
|
||||
.orElse(takesDamage)
|
||||
.orElse(aggravatedBehavior)
|
||||
.orElse(auraBehavior)
|
||||
.orElse(containerBehavior)
|
||||
.orElse {
|
||||
|
|
@ -554,7 +560,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
if (Damageable.CanJammer(target, cause)) {
|
||||
TryJammerEffectActivate(target, cause)
|
||||
}
|
||||
TryAggravationEffect(cause)
|
||||
TryAggravationEffect(cause) match {
|
||||
case Some(aggravation) =>
|
||||
StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
|
||||
case _ => ;
|
||||
}
|
||||
} else {
|
||||
DestructionAwareness(target, Some(cause))
|
||||
}
|
||||
|
|
@ -609,6 +619,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
target.Die
|
||||
//aura effects cancel
|
||||
EndAllEffects()
|
||||
//aggravation cancel
|
||||
EndAllAggravation()
|
||||
//unjam
|
||||
CancelJammeredSound(target)
|
||||
CancelJammeredStatus(target)
|
||||
|
|
|
|||
|
|
@ -10,7 +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.AuraEffectBehavior
|
||||
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
|
||||
|
|
@ -54,11 +55,12 @@ class VehicleControl(vehicle: Vehicle)
|
|||
with JammableMountedWeapons
|
||||
with ContainableBehavior
|
||||
with AntTransferBehavior
|
||||
with AggravatedBehavior
|
||||
with AuraEffectBehavior {
|
||||
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
|
||||
|
||||
|
||||
def MountableObject = vehicle
|
||||
|
||||
def CargoObject = vehicle
|
||||
|
|
@ -79,6 +81,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
|
||||
def AuraTargetObject = vehicle
|
||||
|
||||
def AggravatedObject = vehicle
|
||||
|
||||
if(vehicle.Definition == GlobalDefinitions.ant) {
|
||||
findChargeTargetFunc = Vehicles.FindANTChargingSource
|
||||
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
|
||||
|
|
@ -101,6 +105,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
util().Actor = Default.Actor
|
||||
}
|
||||
EndAllEffects()
|
||||
EndAllAggravation()
|
||||
}
|
||||
|
||||
def Enabled: Receive =
|
||||
|
|
@ -109,6 +114,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
.orElse(cargoBehavior)
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(aggravatedBehavior)
|
||||
.orElse(auraBehavior)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(containerBehavior)
|
||||
|
|
@ -592,7 +598,11 @@ class VehicleControl(vehicle: Vehicle)
|
|||
cause: ResolvedProjectile,
|
||||
amount: Int
|
||||
): Unit = {
|
||||
TryAggravationEffect(cause)
|
||||
TryAggravationEffect(cause) match {
|
||||
case Some(aggravation) =>
|
||||
StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
|
||||
case _ => ;
|
||||
}
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
}
|
||||
|
||||
|
|
@ -602,6 +612,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
): Unit = {
|
||||
//aura effects cancel
|
||||
EndAllEffects()
|
||||
//aggravation cancel
|
||||
EndAllAggravation()
|
||||
super.DestructionAwareness(target, cause)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
// 0x68
|
||||
case 0x68 => game.DroppodFreefallingMessage.decode
|
||||
case 0x69 => game.AvatarFirstTimeEventMessage.decode
|
||||
case 0x6a => noDecoder(AggravatedDamageMessage)
|
||||
case 0x6a => game.AggravatedDamageMessage.decode
|
||||
case 0x6b => game.TriggerSoundMessage.decode
|
||||
case 0x6c => game.LootItemMessage.decode
|
||||
case 0x6d => game.VehicleSubStateMessage.decode
|
||||
|
|
|
|||
|
|
@ -451,8 +451,8 @@ class DamageModelTests extends Specification {
|
|||
)
|
||||
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 54
|
||||
tplayer.Armor mustEqual 46
|
||||
tplayer.Health mustEqual 65
|
||||
tplayer.Armor mustEqual 35
|
||||
}
|
||||
|
||||
"resolve infantry targets in a specific way" in {
|
||||
|
|
|
|||
Loading…
Reference in a new issue