mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-03 12:10:22 +00:00
jammering criteria selection and determination added; applying calculations to damage target (for projectiles) exposes the underlying cause of the damage
This commit is contained in:
parent
375edbbf94
commit
686676f9b9
11 changed files with 243 additions and 109 deletions
|
|
@ -2,18 +2,16 @@
|
|||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.ballistics.Projectiles
|
||||
import net.psforever.objects.serverobject.terminals.TargetValidation
|
||||
import net.psforever.objects.equipment.JammingUnit
|
||||
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
/**
|
||||
* The definition that outlines the damage-dealing characteristics of any projectile.
|
||||
* `Tool` objects emit `ProjectileDefinition` objects and that is later wrapped into a `Projectile` object.
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||
with JammingUnit
|
||||
with StandardDamageProfile {
|
||||
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
|
||||
private var acceleration : Int = 0
|
||||
|
|
@ -32,7 +30,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
private var autoLock : Boolean = false
|
||||
private var additionalEffect : Boolean = false
|
||||
private var jammerProjectile : Boolean = false
|
||||
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = new mutable.ListBuffer()
|
||||
//derived calculations
|
||||
private var distanceMax : Float = 0f
|
||||
private var distanceFromAcceleration : Float = 0f
|
||||
|
|
@ -154,10 +151,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
JammerProjectile
|
||||
}
|
||||
|
||||
def HasJammedEffectDuration : Boolean = jammedEffectDuration.isEmpty
|
||||
|
||||
def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = jammedEffectDuration
|
||||
|
||||
def DistanceMax : Float = distanceMax //accessor only
|
||||
|
||||
def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
import net.psforever.objects.serverobject.terminals.TargetValidation
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
trait JammingUnit {
|
||||
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = new mutable.ListBuffer()
|
||||
|
||||
def HasJammedEffectDuration : Boolean = jammedEffectDuration.isEmpty
|
||||
|
||||
def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = jammedEffectDuration
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import net.psforever.objects.PlanetSideGameObject
|
|||
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry, VehicleSource}
|
||||
import net.psforever.objects.definition.KitDefinition
|
||||
import net.psforever.objects.serverobject.terminals.TerminalDefinition
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
||||
|
||||
abstract class VitalsActivity(target : SourceEntry) {
|
||||
|
|
@ -102,9 +103,9 @@ object Vitality {
|
|||
* upon a given vital object.
|
||||
* @param func a function literal
|
||||
*/
|
||||
final case class Damage(func : (Any)=>Unit)
|
||||
final case class Damage(func : ResolutionCalculations.Output)
|
||||
|
||||
final case class DamageOn(obj : Vitality, func : (Any)=>Unit)
|
||||
final case class DamageOn(obj : Vitality, func : ResolutionCalculations.Output)
|
||||
|
||||
/**
|
||||
* Report that a vitals object must be updated due to damage.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import net.psforever.objects.vital.projectile.ProjectileCalculations
|
|||
* @tparam A an internal type that converts between `calcFunc`'s output and `applyFunc`'s input;
|
||||
* never has to be defined explicitly, but will be checked upon object definition
|
||||
*/
|
||||
abstract class DamageResistCalculations[A](calcFunc : (ResolvedProjectile)=>((Int, Int)=>A),
|
||||
abstract class DamageResistCalculations[A](calcFunc : ResolvedProjectile=>(Int, Int)=>A,
|
||||
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
||||
extends ResolutionCalculations {
|
||||
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ trait ResolutionCalculations {
|
|||
}
|
||||
|
||||
object ResolutionCalculations {
|
||||
type Output = (Any)=>Unit
|
||||
type Output = Any=>ResolvedProjectile
|
||||
type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile)=>Output
|
||||
|
||||
def NoDamage(data : ResolvedProjectile)(a : Int, b : Int) : Int = 0
|
||||
|
|
@ -107,7 +107,7 @@ object ResolutionCalculations {
|
|||
}
|
||||
}
|
||||
|
||||
def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : Unit = { }
|
||||
def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = data
|
||||
|
||||
def SubtractWithRemainder(current : Int, damage : Int) : (Int, Int) = {
|
||||
val a = Math.max(0, current - damage)
|
||||
|
|
@ -123,41 +123,44 @@ object ResolutionCalculations {
|
|||
* @param data the historical `ResolvedProjectile` information
|
||||
* @param target the `Player` object to be affected by these damage values (at some point)
|
||||
*/
|
||||
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case player : Player =>
|
||||
var (a, b) = damageValues
|
||||
var result = (0, 0)
|
||||
//TODO Personal Shield implant test should go here and modify the values a and b
|
||||
if(player.isAlive && !(a == 0 && b == 0)) {
|
||||
player.History(data)
|
||||
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
||||
// Subtract armour damage from capacitor
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, b)
|
||||
player.Capacitor = result._1
|
||||
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case player : Player =>
|
||||
var (a, b) = damageValues
|
||||
var result = (0, 0)
|
||||
//TODO Personal Shield implant test should go here and modify the values a and b
|
||||
if(player.isAlive && !(a == 0 && b == 0)) {
|
||||
player.History(data)
|
||||
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
||||
// Subtract armour damage from capacitor
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, b)
|
||||
player.Capacitor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then follow up with health damage if any capacitor is left
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, a)
|
||||
player.Capacitor = result._1
|
||||
a = result._2
|
||||
}
|
||||
|
||||
// Subtract any remaining armour damage from armour
|
||||
result = SubtractWithRemainder(player.Armor, b)
|
||||
player.Armor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then follow up with health damage if any capacitor is left
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, a)
|
||||
player.Capacitor = result._1
|
||||
// Then bleed through to health if armour ran out
|
||||
result = SubtractWithRemainder(player.Health, b)
|
||||
player.Health = result._1
|
||||
b = result._2
|
||||
|
||||
// Finally, apply health damage to health
|
||||
result = SubtractWithRemainder(player.Health, a)
|
||||
player.Health = result._1
|
||||
a = result._2
|
||||
}
|
||||
|
||||
// Subtract any remaining armour damage from armour
|
||||
result = SubtractWithRemainder(player.Armor, b)
|
||||
player.Armor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then bleed through to health if armour ran out
|
||||
result = SubtractWithRemainder(player.Health, b)
|
||||
player.Health = result._1
|
||||
b = result._2
|
||||
|
||||
// Finally, apply health damage to health
|
||||
result = SubtractWithRemainder(player.Health, a)
|
||||
player.Health = result._1
|
||||
a = result._2
|
||||
}
|
||||
case _ =>
|
||||
case _ =>
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -167,9 +170,9 @@ object ResolutionCalculations {
|
|||
* @param data the historical `ResolvedProjectile` information
|
||||
* @param target the `Vehicle` object to be affected by these damage values (at some point)
|
||||
*/
|
||||
def VehicleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case vehicle : Vehicle =>
|
||||
if(vehicle.Health > 0) {
|
||||
def VehicleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case vehicle : Vehicle if vehicle.Health > 0 && damage > 0 =>
|
||||
vehicle.History(data)
|
||||
val shields = vehicle.Shields
|
||||
if(shields > damage) {
|
||||
|
|
@ -182,57 +185,58 @@ object ResolutionCalculations {
|
|||
else {
|
||||
vehicle.Health = vehicle.Health - damage
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case ce : SimpleDeployable =>
|
||||
if(ce.Health > 0) {
|
||||
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case ce : SimpleDeployable if ce.Health > 0 && damage > 0 =>
|
||||
ce.Health -= damage
|
||||
ce.History(data)
|
||||
}
|
||||
case turret : FacilityTurret =>
|
||||
if(turret.Health > 0) {
|
||||
case turret : FacilityTurret if turret.Health > 0 =>
|
||||
turret.Health -= damage
|
||||
turret.History(data)
|
||||
}
|
||||
case _ =>
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case ce : ComplexDeployable =>
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case ce : ComplexDeployable if ce.Health > 0 && damage > 0 =>
|
||||
ce.History(data)
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
}
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
ce.Health -= damage
|
||||
}
|
||||
ce.History(data)
|
||||
}
|
||||
else if(ce.Health > 0) {
|
||||
ce.Health -= damage
|
||||
ce.History(data)
|
||||
}
|
||||
|
||||
case ce : TurretDeployable =>
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
case ce : TurretDeployable if ce.Health > 0 && damage > 0 =>
|
||||
ce.History(data)
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
}
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
ce.Health -= damage
|
||||
}
|
||||
ce.History(data)
|
||||
}
|
||||
else if(ce.Health > 0) {
|
||||
ce.Health -= damage
|
||||
ce.History(data)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -31,7 +32,7 @@ object AvatarAction {
|
|||
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||
final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amount: Int) extends Action
|
||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : Any=>Unit) extends Action
|
||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : ResolutionCalculations.Output) extends Action
|
||||
final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action
|
||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package services.avatar
|
|||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
|
|
@ -25,7 +26,7 @@ object AvatarResponse {
|
|||
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
||||
final case class ConcealPlayer() extends Response
|
||||
final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) extends Response
|
||||
final case class DamageResolution(target : Player, resolution_function : Any=>Unit) extends Response
|
||||
final case class DamageResolution(target : Player, resolution_function : ResolutionCalculations.Output) extends Response
|
||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response
|
||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 87
|
||||
|
|
@ -338,7 +338,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 98
|
||||
|
|
@ -352,7 +352,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
tplayer.Armor = 0
|
||||
|
||||
func(tplayer)
|
||||
|
|
@ -365,7 +365,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Health mustEqual 650
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 641
|
||||
|
|
@ -378,7 +378,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Shields mustEqual 10
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 650
|
||||
|
|
@ -392,7 +392,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Shields mustEqual 10
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 650
|
||||
|
|
@ -407,7 +407,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Health mustEqual 650
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 641
|
||||
|
|
|
|||
|
|
@ -698,7 +698,7 @@ class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
|
|||
|
||||
"not charge vehicle shields if recently damaged" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj)})
|
||||
vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj); obj })
|
||||
|
||||
val msg = receiveOne(200 milliseconds)
|
||||
assert(msg.isInstanceOf[Vitality.DamageResolution])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue