mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-25 09:33:33 +00:00
woring Starfire damage calculations; projectiles have open-ended quality modifier
This commit is contained in:
parent
66eb3b5b95
commit
80c1a34fb0
7 changed files with 106 additions and 58 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
import net.psforever.objects.equipment.TargetValidation
|
||||
import net.psforever.objects.serverobject.aggravated.Aura
|
||||
|
|
@ -11,7 +11,7 @@ final case class AggravatedInfo(damage_type: DamageType.Value,
|
|||
}
|
||||
|
||||
final case class AggravatedDamage(info: List[AggravatedInfo],
|
||||
effect_type: Aura.Value,
|
||||
effect_type: Aura,
|
||||
duration: Long,
|
||||
max_factor: Float,
|
||||
cumulative_damage_degrade: Boolean,
|
||||
|
|
@ -20,7 +20,7 @@ final case class AggravatedDamage(info: List[AggravatedInfo],
|
|||
|
||||
object AggravatedDamage {
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura.Value,
|
||||
effect_type: Aura,
|
||||
duration: Long,
|
||||
max_factor: Float,
|
||||
targets: List[TargetValidation]): AggravatedDamage =
|
||||
|
|
@ -35,7 +35,7 @@ object AggravatedDamage {
|
|||
)
|
||||
|
||||
def apply(info: AggravatedInfo,
|
||||
effect_type: Aura.Value,
|
||||
effect_type: Aura,
|
||||
duration: Long,
|
||||
max_factor: Float,
|
||||
vanu_aggravated: Boolean,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
|
||||
object Aura extends Enumeration {
|
||||
final val None = Value(0)
|
||||
final val Plasma = Value(1)
|
||||
final val Comet = Value(2)
|
||||
final val Napalm = Value(4)
|
||||
final val Fire = Value(8)
|
||||
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,20 +1,21 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.aggravated
|
||||
|
||||
import net.psforever.objects.serverobject.aggravated.{Aura => AuraEffect}
|
||||
|
||||
trait AuraContainer {
|
||||
private var aura : Set[AuraEffect.Value] = Set.empty[AuraEffect.Value]
|
||||
private var aura : Set[AuraEffect] = Set.empty[AuraEffect]
|
||||
|
||||
def Aura : Set[AuraEffect.Value] = aura
|
||||
def Aura : Set[AuraEffect] = aura
|
||||
|
||||
def AddEffectToAura(effect : AuraEffect.Value) : Set[AuraEffect.Value] = {
|
||||
def AddEffectToAura(effect : AuraEffect) : Set[AuraEffect] = {
|
||||
if(effect != AuraEffect.None) {
|
||||
aura = aura + effect
|
||||
}
|
||||
Aura
|
||||
}
|
||||
|
||||
def RemoveEffectFromAura(effect : AuraEffect.Value) : Set[AuraEffect.Value] = {
|
||||
def RemoveEffectFromAura(effect : AuraEffect) : Set[AuraEffect] = {
|
||||
aura = aura - effect
|
||||
Aura
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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 net.psforever.types.Vector3
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -15,16 +14,16 @@ import scala.concurrent.duration._
|
|||
trait AuraEffectBehavior {
|
||||
_ : Actor with Damageable =>
|
||||
private var activeEffectIndex: Long = 0
|
||||
private val effectToEntryId: mutable.HashMap[Aura.Value, List[Long]] =
|
||||
mutable.HashMap.empty[Aura.Value, List[Long]]
|
||||
private val entryIdToTimer: mutable.LongMap[Cancellable] =
|
||||
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
|
||||
def AuraTargetObject: AuraEffectBehavior.Target
|
||||
|
||||
val auraBehavior : Receive = {
|
||||
val auraBehavior: Receive = {
|
||||
case AuraEffectBehavior.Aggravate(id, 0, 0) =>
|
||||
CancelEffectTimer(id)
|
||||
PerformCleanupEffect(id)
|
||||
|
|
@ -33,8 +32,8 @@ trait AuraEffectBehavior {
|
|||
RemoveEffectEntry(id)
|
||||
RetimeEvent(id, iteration = 0, Some(leftoverTime), leftoverTime = 0)
|
||||
|
||||
case AuraEffectBehavior.Aggravate(id, iteration, leftover) => ;
|
||||
RetimeEventAndPerformAggravation(id, iteration - 1, None, leftover)
|
||||
case AuraEffectBehavior.Aggravate(id, iteration, leftover) =>
|
||||
RetimeEventAndPerformAggravation(id, iteration, None, leftover)
|
||||
}
|
||||
|
||||
private def RetimeEvent(
|
||||
|
|
@ -45,22 +44,13 @@ trait AuraEffectBehavior {
|
|||
): Option[AuraEffectBehavior.Entry] = {
|
||||
CancelEffectTimer(id)
|
||||
entryIdToEntry.get(id) match {
|
||||
case Some(oldEntry) =>
|
||||
val target = SourceEntry(AuraTargetObject)
|
||||
val entry = PairIdWithAggravationEntry(
|
||||
id,
|
||||
oldEntry.effect,
|
||||
oldEntry.retime,
|
||||
oldEntry.data,
|
||||
target,
|
||||
target.Position - oldEntry.data.target.Position
|
||||
)
|
||||
case out @ Some(oldEntry) =>
|
||||
entryIdToTimer += id -> context.system.scheduler.scheduleOnce(
|
||||
time.getOrElse(entry.retime) milliseconds,
|
||||
time.getOrElse(oldEntry.retime) milliseconds,
|
||||
self,
|
||||
AuraEffectBehavior.Aggravate(id, iteration, leftoverTime)
|
||||
)
|
||||
Some(entry)
|
||||
out
|
||||
case _ =>
|
||||
PerformCleanupEffect(id)
|
||||
None
|
||||
|
|
@ -68,9 +58,9 @@ trait AuraEffectBehavior {
|
|||
}
|
||||
|
||||
private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long], leftoverTime: Long) : Unit = {
|
||||
RetimeEvent(id, iteration, time, leftoverTime) match {
|
||||
RetimeEvent(id, iteration - 1, time, leftoverTime) match {
|
||||
case Some(entry) =>
|
||||
PerformAggravation(entry)
|
||||
PerformAggravation(entry, iteration)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -116,7 +106,7 @@ trait AuraEffectBehavior {
|
|||
}
|
||||
|
||||
private def CheckForUniqueUnqueuedProjectile(projectile : Projectile) : Boolean = {
|
||||
!entryIdToEntry.values.exists { entry => entry.data.projectile eq projectile }
|
||||
!entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
|
||||
}
|
||||
|
||||
private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile) : Unit = {
|
||||
|
|
@ -131,38 +121,50 @@ trait AuraEffectBehavior {
|
|||
case None | Some(Nil) => effectToEntryId += effect -> List(id)
|
||||
case Some(list) => effectToEntryId -> (list :+ id)
|
||||
}
|
||||
//pair id with timer
|
||||
val inflictionRate = 1000 //info.infliction_rate
|
||||
val iterations = (aggravation.duration / inflictionRate).toInt
|
||||
val leftoverTime = aggravation.duration - (iterations * inflictionRate)
|
||||
entryIdToTimer += id -> context.system.scheduler.scheduleOnce(inflictionRate milliseconds, self, AuraEffectBehavior.Aggravate(id, iterations, leftoverTime))
|
||||
//setup timer data
|
||||
val tick = 1000 //each second
|
||||
val duration = aggravation.duration
|
||||
val iterations = (duration / tick).toInt
|
||||
val leftoverTime = duration - (iterations * tick)
|
||||
//quality per tick
|
||||
val totalPower = (duration.toFloat / info.infliction_rate).toInt - 1
|
||||
val averagePowerPerTick = math.max(1, totalPower.toFloat / iterations).toInt
|
||||
val lastTickRemainder = totalPower - averagePowerPerTick * iterations
|
||||
val qualityPerTick: List[Int] = if (lastTickRemainder > 0) {
|
||||
0 +: List.fill[Int](iterations - 1)(averagePowerPerTick) :+ (lastTickRemainder + averagePowerPerTick)
|
||||
}
|
||||
else {
|
||||
0 +: List.fill[Int](iterations)(averagePowerPerTick)
|
||||
}
|
||||
//pair id with entry
|
||||
PairIdWithAggravationEntry(id, effect, inflictionRate, data, data.target, Vector3.Zero)
|
||||
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.Value,
|
||||
retime:Long,
|
||||
effect: Aura,
|
||||
retime: Long,
|
||||
data: ResolvedProjectile,
|
||||
target: SourceEntry,
|
||||
offset: Vector3
|
||||
powerOffset: List[Int]
|
||||
): AuraEffectBehavior.Entry = {
|
||||
val aggravatedDamageInfo = ResolvedProjectile(
|
||||
AuraEffectBehavior.burning(data.resolution),
|
||||
data.projectile,
|
||||
target,
|
||||
data.damage_model,
|
||||
data.hit_pos + offset
|
||||
data.hit_pos
|
||||
)
|
||||
val entry = AuraEffectBehavior.Entry(id, effect, retime, aggravatedDamageInfo)
|
||||
val entry = AuraEffectBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
|
||||
entryIdToEntry += id -> entry
|
||||
entry
|
||||
}
|
||||
|
||||
def RemoveEffectEntry(id: Long) : Aura.Value = {
|
||||
def RemoveEffectEntry(id: Long) : Aura = {
|
||||
entryIdToEntry.remove(id) match {
|
||||
case Some(entry) =>
|
||||
entry.data.projectile.profile.Aggravated.get.effect_type
|
||||
|
|
@ -174,7 +176,7 @@ trait AuraEffectBehavior {
|
|||
}
|
||||
}
|
||||
|
||||
def CleanupEffect(id: Long) : Aura.Value = {
|
||||
def CleanupEffect(id: Long) : Aura = {
|
||||
//remove and cancel timer
|
||||
entryIdToTimer.remove(id) match {
|
||||
case Some(timer) => timer.cancel
|
||||
|
|
@ -220,15 +222,23 @@ trait AuraEffectBehavior {
|
|||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
|
||||
}
|
||||
|
||||
private def PerformAggravation(entry: AuraEffectBehavior.Entry) : Unit = {
|
||||
TakesDamage.apply(Vitality.Damage(entry.data.damage_model.Calculate(entry.data)))
|
||||
private def PerformAggravation(entry: AuraEffectBehavior.Entry, tick: Int = 0) : Unit = {
|
||||
val data = entry.data
|
||||
val info = ResolvedProjectile(
|
||||
data.resolution,
|
||||
data.projectile.quality(entry.qualityPerTick(tick).toFloat),
|
||||
data.target,
|
||||
data.damage_model,
|
||||
data.hit_pos
|
||||
)
|
||||
TakesDamage.apply(Vitality.Damage(info.damage_model.Calculate(info)))
|
||||
}
|
||||
}
|
||||
|
||||
object AuraEffectBehavior {
|
||||
type Target = PlanetSideServerObject with Vitality with AuraContainer
|
||||
|
||||
private case class Entry(id: Long, effect: Aura.Value, retime: Long, data: ResolvedProjectile)
|
||||
private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Int])
|
||||
|
||||
private case class Aggravate(id: Long, iterations: Int, leftover: Long)
|
||||
|
||||
|
|
|
|||
|
|
@ -2321,7 +2321,7 @@ object GlobalDefinitions {
|
|||
aphelion_starfire_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.25f, 250),
|
||||
Aura.None,
|
||||
0,
|
||||
2000,
|
||||
0f,
|
||||
true,
|
||||
List(TargetValidation(EffectTarget.Category.Aircraft, EffectTarget.Validation.Aircraft))
|
||||
|
|
@ -2454,7 +2454,7 @@ object GlobalDefinitions {
|
|||
comet_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.2f, 500),
|
||||
Aura.Comet,
|
||||
0,
|
||||
2000,
|
||||
10f,
|
||||
List(
|
||||
TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
|
||||
|
|
@ -3910,7 +3910,7 @@ object GlobalDefinitions {
|
|||
starfire_projectile.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Direct, 0.25f, 250),
|
||||
Aura.Comet,
|
||||
3000,
|
||||
2000,
|
||||
0f,
|
||||
true,
|
||||
List(TargetValidation(EffectTarget.Category.Aircraft, EffectTarget.Validation.Aircraft))
|
||||
|
|
@ -3923,7 +3923,6 @@ object GlobalDefinitions {
|
|||
starfire_projectile.Packet = projectileConverter
|
||||
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
|
||||
starfire_projectile.Modifiers = List(
|
||||
DamageModifiers.StarfireAggravated,
|
||||
DamageModifiers.StarfireAggravatedBurn,
|
||||
DamageModifiers.RadialDegrade
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
|
||||
import net.psforever.objects.entity.SimpleWorldEntity
|
||||
|
|
@ -25,6 +27,9 @@ import net.psforever.types.Vector3
|
|||
* if not, then it is a type of vehicle (and owner should have a positive `seated` field)
|
||||
* @param shot_origin where the projectile started
|
||||
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
||||
* @param quality na
|
||||
* @param id an exclusive identifier for this projectile;
|
||||
* normally generated internally, but can be manually set
|
||||
* @param fire_time when the weapon discharged was recorded;
|
||||
* defaults to `System.nanoTime`
|
||||
*/
|
||||
|
|
@ -36,6 +41,8 @@ final case class Projectile(
|
|||
attribute_to: Int,
|
||||
shot_origin: Vector3,
|
||||
shot_angle: Vector3,
|
||||
quality: Float = 1f,
|
||||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||
fire_time: Long = System.nanoTime
|
||||
) extends PlanetSideGameObject {
|
||||
Position = shot_origin
|
||||
|
|
@ -52,6 +59,28 @@ final case class Projectile(
|
|||
val current: SimpleWorldEntity = new SimpleWorldEntity()
|
||||
private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved
|
||||
|
||||
/**
|
||||
* Create a copy of this projectile with all the same information
|
||||
* save for the quality.
|
||||
* Used mainly for aggravated damage.
|
||||
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
|
||||
* @param value the new quality
|
||||
* @return a new `Projectile` entity
|
||||
*/
|
||||
def quality(value: Float): Projectile =
|
||||
Projectile(
|
||||
profile,
|
||||
tool_def,
|
||||
fire_mode,
|
||||
owner,
|
||||
attribute_to,
|
||||
shot_origin,
|
||||
shot_angle,
|
||||
value,
|
||||
id,
|
||||
fire_time
|
||||
)
|
||||
|
||||
/**
|
||||
* Mark the projectile as being "encountered" or "managed" at least once.
|
||||
*/
|
||||
|
|
@ -80,6 +109,8 @@ object Projectile {
|
|||
*/
|
||||
final val rangeUID: Int = 40150
|
||||
|
||||
private val idGenerator: AtomicLong = new AtomicLong
|
||||
|
||||
/**
|
||||
* Overloaded constructor for an `Unresolved` projectile.
|
||||
* @param profile an explanation of the damage that can be performed by this discharge
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ object DamageModifiers {
|
|||
case (Some(aggravation), v : VehicleSource) if GlobalDefinitions.isFlightVehicle(v.Definition) =>
|
||||
aggravation.info.find(_.damage_type == DamageType.Direct) match {
|
||||
case Some(infos) =>
|
||||
(damage * infos.degradation_percentage) toInt
|
||||
(math.floor(damage * infos.degradation_percentage) * data.projectile.quality) toInt
|
||||
case _ =>
|
||||
damage
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue