mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-14 16:04:41 +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
|
package net.psforever.objects.definition
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.Projectiles
|
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 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.
|
* 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.
|
* `Tool` objects emit `ProjectileDefinition` objects and that is later wrapped into a `Projectile` object.
|
||||||
* @param objectId the object's identifier number
|
* @param objectId the object's identifier number
|
||||||
*/
|
*/
|
||||||
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||||
|
with JammingUnit
|
||||||
with StandardDamageProfile {
|
with StandardDamageProfile {
|
||||||
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
|
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
|
||||||
private var acceleration : Int = 0
|
private var acceleration : Int = 0
|
||||||
|
|
@ -32,7 +30,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||||
private var autoLock : Boolean = false
|
private var autoLock : Boolean = false
|
||||||
private var additionalEffect : Boolean = false
|
private var additionalEffect : Boolean = false
|
||||||
private var jammerProjectile : Boolean = false
|
private var jammerProjectile : Boolean = false
|
||||||
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = new mutable.ListBuffer()
|
|
||||||
//derived calculations
|
//derived calculations
|
||||||
private var distanceMax : Float = 0f
|
private var distanceMax : Float = 0f
|
||||||
private var distanceFromAcceleration : Float = 0f
|
private var distanceFromAcceleration : Float = 0f
|
||||||
|
|
@ -154,10 +151,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||||
JammerProjectile
|
JammerProjectile
|
||||||
}
|
}
|
||||||
|
|
||||||
def HasJammedEffectDuration : Boolean = jammedEffectDuration.isEmpty
|
|
||||||
|
|
||||||
def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Duration)] = jammedEffectDuration
|
|
||||||
|
|
||||||
def DistanceMax : Float = distanceMax //accessor only
|
def DistanceMax : Float = distanceMax //accessor only
|
||||||
|
|
||||||
def DistanceFromAcceleration : Float = distanceFromAcceleration //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.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.definition.KitDefinition
|
import net.psforever.objects.definition.KitDefinition
|
||||||
import net.psforever.objects.serverobject.terminals.TerminalDefinition
|
import net.psforever.objects.serverobject.terminals.TerminalDefinition
|
||||||
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
import net.psforever.types.{ExoSuitType, ImplantType}
|
||||||
|
|
||||||
abstract class VitalsActivity(target : SourceEntry) {
|
abstract class VitalsActivity(target : SourceEntry) {
|
||||||
|
|
@ -102,9 +103,9 @@ object Vitality {
|
||||||
* upon a given vital object.
|
* upon a given vital object.
|
||||||
* @param func a function literal
|
* @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.
|
* 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;
|
* @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
|
* 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)
|
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
||||||
extends ResolutionCalculations {
|
extends ResolutionCalculations {
|
||||||
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ trait ResolutionCalculations {
|
||||||
}
|
}
|
||||||
|
|
||||||
object ResolutionCalculations {
|
object ResolutionCalculations {
|
||||||
type Output = (Any)=>Unit
|
type Output = Any=>ResolvedProjectile
|
||||||
type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile)=>Output
|
type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile)=>Output
|
||||||
|
|
||||||
def NoDamage(data : ResolvedProjectile)(a : Int, b : Int) : Int = 0
|
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) = {
|
def SubtractWithRemainder(current : Int, damage : Int) : (Int, Int) = {
|
||||||
val a = Math.max(0, current - damage)
|
val a = Math.max(0, current - damage)
|
||||||
|
|
@ -123,41 +123,44 @@ object ResolutionCalculations {
|
||||||
* @param data the historical `ResolvedProjectile` information
|
* @param data the historical `ResolvedProjectile` information
|
||||||
* @param target the `Player` object to be affected by these damage values (at some point)
|
* @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 {
|
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||||
case player : Player =>
|
target match {
|
||||||
var (a, b) = damageValues
|
case player : Player =>
|
||||||
var result = (0, 0)
|
var (a, b) = damageValues
|
||||||
//TODO Personal Shield implant test should go here and modify the values a and b
|
var result = (0, 0)
|
||||||
if(player.isAlive && !(a == 0 && b == 0)) {
|
//TODO Personal Shield implant test should go here and modify the values a and b
|
||||||
player.History(data)
|
if(player.isAlive && !(a == 0 && b == 0)) {
|
||||||
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
player.History(data)
|
||||||
// Subtract armour damage from capacitor
|
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
||||||
result = SubtractWithRemainder(player.Capacitor.toInt, b)
|
// Subtract armour damage from capacitor
|
||||||
player.Capacitor = result._1
|
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
|
b = result._2
|
||||||
|
|
||||||
// Then follow up with health damage if any capacitor is left
|
// Then bleed through to health if armour ran out
|
||||||
result = SubtractWithRemainder(player.Capacitor.toInt, a)
|
result = SubtractWithRemainder(player.Health, b)
|
||||||
player.Capacitor = result._1
|
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
|
a = result._2
|
||||||
}
|
}
|
||||||
|
case _ =>
|
||||||
// Subtract any remaining armour damage from armour
|
}
|
||||||
result = SubtractWithRemainder(player.Armor, b)
|
data
|
||||||
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 _ =>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,9 +170,9 @@ object ResolutionCalculations {
|
||||||
* @param data the historical `ResolvedProjectile` information
|
* @param data the historical `ResolvedProjectile` information
|
||||||
* @param target the `Vehicle` object to be affected by these damage values (at some point)
|
* @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 {
|
def VehicleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||||
case vehicle : Vehicle =>
|
target match {
|
||||||
if(vehicle.Health > 0) {
|
case vehicle : Vehicle if vehicle.Health > 0 && damage > 0 =>
|
||||||
vehicle.History(data)
|
vehicle.History(data)
|
||||||
val shields = vehicle.Shields
|
val shields = vehicle.Shields
|
||||||
if(shields > damage) {
|
if(shields > damage) {
|
||||||
|
|
@ -182,57 +185,58 @@ object ResolutionCalculations {
|
||||||
else {
|
else {
|
||||||
vehicle.Health = vehicle.Health - damage
|
vehicle.Health = vehicle.Health - damage
|
||||||
}
|
}
|
||||||
}
|
case _ => ;
|
||||||
case _ => ;
|
}
|
||||||
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||||
case ce : SimpleDeployable =>
|
target match {
|
||||||
if(ce.Health > 0) {
|
case ce : SimpleDeployable if ce.Health > 0 && damage > 0 =>
|
||||||
ce.Health -= damage
|
ce.Health -= damage
|
||||||
ce.History(data)
|
ce.History(data)
|
||||||
}
|
case turret : FacilityTurret if turret.Health > 0 =>
|
||||||
case turret : FacilityTurret =>
|
|
||||||
if(turret.Health > 0) {
|
|
||||||
turret.Health -= damage
|
turret.Health -= damage
|
||||||
turret.History(data)
|
turret.History(data)
|
||||||
}
|
case _ => ;
|
||||||
case _ =>
|
}
|
||||||
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||||
case ce : ComplexDeployable =>
|
target match {
|
||||||
if(ce.Shields > 0) {
|
case ce : ComplexDeployable if ce.Health > 0 && damage > 0 =>
|
||||||
if(damage > ce.Shields) {
|
ce.History(data)
|
||||||
ce.Health -= (damage - ce.Shields)
|
if(ce.Shields > 0) {
|
||||||
ce.Shields = 0
|
if(damage > ce.Shields) {
|
||||||
|
ce.Health -= (damage - ce.Shields)
|
||||||
|
ce.Shields = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ce.Shields -= damage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ce.Shields -= damage
|
ce.Health -= damage
|
||||||
}
|
}
|
||||||
ce.History(data)
|
|
||||||
}
|
|
||||||
else if(ce.Health > 0) {
|
|
||||||
ce.Health -= damage
|
|
||||||
ce.History(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
case ce : TurretDeployable =>
|
case ce : TurretDeployable if ce.Health > 0 && damage > 0 =>
|
||||||
if(ce.Shields > 0) {
|
ce.History(data)
|
||||||
if(damage > ce.Shields) {
|
if(ce.Shields > 0) {
|
||||||
ce.Health -= (damage - ce.Shields)
|
if(damage > ce.Shields) {
|
||||||
ce.Shields = 0
|
ce.Health -= (damage - ce.Shields)
|
||||||
|
ce.Shields = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ce.Shields -= damage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
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.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.Container
|
import net.psforever.objects.inventory.Container
|
||||||
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
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 ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ConcealPlayer(player_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 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 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 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
|
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.Player
|
||||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
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 ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
||||||
final case class ConcealPlayer() extends Response
|
final case class ConcealPlayer() extends Response
|
||||||
final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) 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 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 DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
||||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@ class DamageModelTests extends Specification {
|
||||||
tplayer.Armor mustEqual 50
|
tplayer.Armor mustEqual 50
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
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)
|
func(tplayer)
|
||||||
tplayer.Health mustEqual 87
|
tplayer.Health mustEqual 87
|
||||||
|
|
@ -338,7 +338,7 @@ class DamageModelTests extends Specification {
|
||||||
tplayer.Armor mustEqual 50
|
tplayer.Armor mustEqual 50
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
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)
|
func(tplayer)
|
||||||
tplayer.Health mustEqual 98
|
tplayer.Health mustEqual 98
|
||||||
|
|
@ -352,7 +352,7 @@ class DamageModelTests extends Specification {
|
||||||
tplayer.Armor mustEqual 50
|
tplayer.Armor mustEqual 50
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
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
|
tplayer.Armor = 0
|
||||||
|
|
||||||
func(tplayer)
|
func(tplayer)
|
||||||
|
|
@ -365,7 +365,7 @@ class DamageModelTests extends Specification {
|
||||||
vehicle.Health mustEqual 650
|
vehicle.Health mustEqual 650
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
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)
|
func(vehicle)
|
||||||
vehicle.Health mustEqual 641
|
vehicle.Health mustEqual 641
|
||||||
|
|
@ -378,7 +378,7 @@ class DamageModelTests extends Specification {
|
||||||
vehicle.Shields mustEqual 10
|
vehicle.Shields mustEqual 10
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
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)
|
func(vehicle)
|
||||||
vehicle.Health mustEqual 650
|
vehicle.Health mustEqual 650
|
||||||
|
|
@ -392,7 +392,7 @@ class DamageModelTests extends Specification {
|
||||||
vehicle.Shields mustEqual 10
|
vehicle.Shields mustEqual 10
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
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)
|
func(vehicle)
|
||||||
vehicle.Health mustEqual 650
|
vehicle.Health mustEqual 650
|
||||||
|
|
@ -407,7 +407,7 @@ class DamageModelTests extends Specification {
|
||||||
vehicle.Health mustEqual 650
|
vehicle.Health mustEqual 650
|
||||||
|
|
||||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
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)
|
func(vehicle)
|
||||||
vehicle.Health mustEqual 641
|
vehicle.Health mustEqual 641
|
||||||
|
|
|
||||||
|
|
@ -698,7 +698,7 @@ class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
|
||||||
|
|
||||||
"not charge vehicle shields if recently damaged" in {
|
"not charge vehicle shields if recently damaged" in {
|
||||||
assert(vehicle.Shields == 0)
|
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)
|
val msg = receiveOne(200 milliseconds)
|
||||||
assert(msg.isInstanceOf[Vitality.DamageResolution])
|
assert(msg.isInstanceOf[Vitality.DamageResolution])
|
||||||
|
|
|
||||||
|
|
@ -1270,7 +1270,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
val originalHealth = player.Health
|
val originalHealth = player.Health
|
||||||
val originalArmor = player.Armor
|
val originalArmor = player.Armor
|
||||||
val originalCapacitor = player.Capacitor.toInt
|
val originalCapacitor = player.Capacitor.toInt
|
||||||
resolution_function(target)
|
val cause = resolution_function(target)
|
||||||
val health = player.Health
|
val health = player.Health
|
||||||
val armor = player.Armor
|
val armor = player.Armor
|
||||||
val capacitor = player.Capacitor.toInt
|
val capacitor = player.Capacitor.toInt
|
||||||
|
|
@ -1301,10 +1301,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
KillPlayer(player)
|
KillPlayer(player)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//first damage entry -> most recent damage source -> killing blow
|
//damage cause -> recent damage source -> killing blow?
|
||||||
target.History.find(p => p.isInstanceOf[DamagingActivity]) match {
|
cause match {
|
||||||
case Some(data : DamageFromProjectile) =>
|
case data : ResolvedProjectile =>
|
||||||
val owner = data.data.projectile.owner
|
val owner = data.projectile.owner
|
||||||
owner match {
|
owner match {
|
||||||
case pSource : PlayerSource =>
|
case pSource : PlayerSource =>
|
||||||
continent.LivePlayers.find(_.Name == pSource.Name) match {
|
continent.LivePlayers.find(_.Name == pSource.Name) match {
|
||||||
|
|
@ -1316,11 +1316,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(DamageWithPositionMessage(damageToHealth + damageToArmor, vSource.Position))
|
sendResponse(DamageWithPositionMessage(damageToHealth + damageToArmor, vSource.Position))
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
continent.Activity ! Zone.HotSpot.Activity(owner, data.Target, target.Position)
|
continent.Activity ! Zone.HotSpot.Activity(owner, data.target, target.Position)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(cause.projectile.profile.JammerProjectile) {
|
||||||
|
val radius = cause.projectile.profile.DamageRadius
|
||||||
|
FindJammerDuration(cause.projectile.profile, target) match {
|
||||||
|
case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius =>
|
||||||
|
//TODO jammer messaging here
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 54, 1))
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
||||||
|
|
@ -5609,6 +5618,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case Some(projectile) =>
|
case Some(projectile) =>
|
||||||
projectile.Position = explosion_pos
|
projectile.Position = explosion_pos
|
||||||
projectile.Velocity = projectile_vel
|
projectile.Velocity = projectile_vel
|
||||||
|
//direct_victim_uid
|
||||||
continent.GUID(direct_victim_uid) match {
|
continent.GUID(direct_victim_uid) match {
|
||||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
|
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
|
||||||
|
|
@ -5618,6 +5628,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
//other victims
|
||||||
targets.foreach(elem => {
|
targets.foreach(elem => {
|
||||||
continent.GUID(elem.uid) match {
|
continent.GUID(elem.uid) match {
|
||||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
|
|
@ -5642,6 +5653,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindProjectileEntry(projectile_guid) match {
|
||||||
|
// case Some(projectile) =>
|
||||||
|
// val allTargets = (continent.GUID(direct_victim_uid) match {
|
||||||
|
// case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
|
// HandleDealingDamage(target, ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position).get)
|
||||||
|
// Seq(target)
|
||||||
|
// case _ =>
|
||||||
|
// Nil
|
||||||
|
// }) ++
|
||||||
|
// targets
|
||||||
|
// .map { a => continent.GUID(a.uid) }
|
||||||
|
// .collect {
|
||||||
|
// case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
|
// HandleDealingDamage(target, ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos).get)
|
||||||
|
// target
|
||||||
|
// }
|
||||||
|
// if(projectile.profile.JammerProjectile) {
|
||||||
|
// val jammableTargets = FindJammerTargetsInScope(projectile.profile, allTargets)
|
||||||
|
// jammableTargets
|
||||||
|
// .zip(FindJammerDuration(projectile.profile, jammableTargets))
|
||||||
|
// .collect { case (target, Some(time)) =>
|
||||||
|
// //TODO jamming messages here
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// case None => ;
|
||||||
|
// }
|
||||||
|
|
||||||
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
||||||
log.info(s"Lash: $msg")
|
log.info(s"Lash: $msg")
|
||||||
continent.GUID(victim_guid) match {
|
continent.GUID(victim_guid) match {
|
||||||
|
|
@ -7245,7 +7284,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
|
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
|
||||||
continent.Ground ! Zone.Ground.DropItem(item2, pos, orient)
|
continent.Ground ! Zone.Ground.DropItem(item2, pos, orient)
|
||||||
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, sourceOrientZ)) //ground
|
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, sourceOrientZ)) //ground
|
||||||
val objDef = item2.Definition
|
val objDef = item2.Definition
|
||||||
destination match {
|
destination match {
|
||||||
case obj : Vehicle =>
|
case obj : Vehicle =>
|
||||||
continent.VehicleEvents ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid))
|
continent.VehicleEvents ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid))
|
||||||
|
|
@ -8284,8 +8323,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
*/
|
*/
|
||||||
def FindProximityUnitTargetsInScope(terminal : Terminal with ProximityUnit) : Seq[PlanetSideGameObject] = {
|
def FindProximityUnitTargetsInScope(terminal : Terminal with ProximityUnit) : Seq[PlanetSideGameObject] = {
|
||||||
terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet collect {
|
terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet collect {
|
||||||
case ProximityTarget.Player => Some(player)
|
case EffectTarget.Category.Player => Some(player)
|
||||||
case ProximityTarget.Vehicle | ProximityTarget.Aircraft => continent.GUID(player.VehicleSeated)
|
case EffectTarget.Category.Vehicle | EffectTarget.Category.Aircraft => continent.GUID(player.VehicleSeated)
|
||||||
} collect {
|
} collect {
|
||||||
case Some(a) => a
|
case Some(a) => a
|
||||||
} toSeq
|
} toSeq
|
||||||
|
|
@ -8548,8 +8587,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
||||||
FindProjectileEntry(projectile_guid) match {
|
FindProjectileEntry(projectile_guid) match {
|
||||||
case Some(projectile) =>
|
case Some(projectile) =>
|
||||||
val index = projectile_guid.guid - Projectile.BaseUID
|
ResolveProjectileEntry(projectile, resolution, target, pos)
|
||||||
ResolveProjectileEntry(projectile, index, resolution, target, pos)
|
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
|
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
|
||||||
None
|
None
|
||||||
|
|
@ -8588,11 +8626,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
projectile.Resolve()
|
ResolveProjectileEntry(projectile, resolution, target, pos)
|
||||||
Some(ResolvedProjectile(resolution, projectile, SourceEntry(target), target.DamageModel, pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param projectile the projectile object
|
||||||
|
* @param resolution the resolution status to promote the projectile
|
||||||
|
* @return a copy of the projectile
|
||||||
|
*/
|
||||||
|
def ResolveProjectileEntry(projectile : Projectile, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
||||||
|
projectile.Resolve()
|
||||||
|
Some(ResolvedProjectile(resolution, projectile, SourceEntry(target), target.DamageModel, pos))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common activities/procedure when a player mounts a valid object.
|
* Common activities/procedure when a player mounts a valid object.
|
||||||
* @param tplayer the player
|
* @param tplayer the player
|
||||||
|
|
@ -10275,6 +10323,76 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
projectilesToCleanUp(local_index) = false
|
projectilesToCleanUp(local_index) = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def FindJammerTargetsInScope(jammer : Any with JammingUnit) : Seq[PlanetSideGameObject] = {
|
||||||
|
(jammer match {
|
||||||
|
case p : ProjectileDefinition if !p.JammerProjectile => None
|
||||||
|
case p => Some(p)
|
||||||
|
}) match {
|
||||||
|
case Some(p : JammingUnit) =>
|
||||||
|
p.JammedEffectDuration
|
||||||
|
.map { case (a, _) => a }
|
||||||
|
.collect {
|
||||||
|
case TargetValidation(EffectTarget.Category.Player, test) if test(player) => Some(player)
|
||||||
|
case TargetValidation(EffectTarget.Category.Vehicle, test) if {
|
||||||
|
continent.GUID(player.VehicleSeated) match {
|
||||||
|
case Some(v) => test(v)
|
||||||
|
case None => false
|
||||||
|
}
|
||||||
|
} => continent.GUID(player.VehicleSeated)
|
||||||
|
case TargetValidation(EffectTarget.Category.Aircraft, test) if {
|
||||||
|
continent.GUID(player.VehicleSeated) match {
|
||||||
|
case Some(v) => test(v)
|
||||||
|
case None => false
|
||||||
|
}
|
||||||
|
} => continent.GUID(player.VehicleSeated)
|
||||||
|
} collect {
|
||||||
|
case Some(a) => a
|
||||||
|
} toSeq
|
||||||
|
case _ =>
|
||||||
|
Seq.empty[PlanetSideGameObject]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def CompileJammerTests(jammer : JammingUnit) : Iterable[EffectTarget.Validation.Value] = {
|
||||||
|
jammer.JammedEffectDuration map { case (TargetValidation(_, test), _) => test }
|
||||||
|
}
|
||||||
|
|
||||||
|
def CompileJammerTests(jammer : JammingUnit, target : EffectTarget.Category.Value) : Iterable[EffectTarget.Validation.Value] = {
|
||||||
|
jammer.JammedEffectDuration collect { case (TargetValidation(filter, test), _) if filter == target => test }
|
||||||
|
}
|
||||||
|
|
||||||
|
def FindJammerTargetsInScope(jammer : JammingUnit, scope : Seq[PlanetSideGUID], zone : Zone) : Seq[(PlanetSideGUID, PlanetSideGameObject)] = {
|
||||||
|
val tests = CompileJammerTests(jammer)
|
||||||
|
(for {
|
||||||
|
uid <- scope
|
||||||
|
obj = zone.GUID(uid)
|
||||||
|
if obj.nonEmpty
|
||||||
|
} yield (uid, obj.get))
|
||||||
|
.collect {
|
||||||
|
case out @ (_, b) if tests.foldLeft(false)(_ || _(b)) => out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def FindJammerTargetsInScope(jammer : JammingUnit, scope : Seq[PlanetSideGameObject]) : Seq[PlanetSideGameObject] = {
|
||||||
|
val tests = CompileJammerTests(jammer)
|
||||||
|
scope collect {
|
||||||
|
case a if tests.foldLeft(false)(_ || _(a)) => a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def FindJammerDuration(jammer : JammingUnit, target : PlanetSideGameObject) : Option[Duration] = {
|
||||||
|
jammer.JammedEffectDuration
|
||||||
|
.collect { case (TargetValidation(_, test), duration) if test(target) => duration }
|
||||||
|
.toList
|
||||||
|
.sortWith(_.toMillis > _.toMillis)
|
||||||
|
.headOption
|
||||||
|
}
|
||||||
|
|
||||||
|
def FindJammerDuration(jammer : JammingUnit, targets : Seq[PlanetSideGameObject]) : Seq[Option[Duration]] = {
|
||||||
|
targets.map { target => FindJammerDuration(jammer, target) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def failWithError(error : String) = {
|
def failWithError(error : String) = {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
sendResponse(ConnectionClose())
|
sendResponse(ConnectionClose())
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import akka.actor.Props
|
||||||
import akka.routing.RandomPool
|
import akka.routing.RandomPool
|
||||||
import actor.base.ActorTest
|
import actor.base.ActorTest
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
|
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
|
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
|
||||||
|
|
@ -370,8 +371,8 @@ class ChangeFireStateStopTest extends ActorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DamageTest extends ActorTest {
|
class DamageTest extends ActorTest {
|
||||||
val test_func_ref : (Any)=>Unit = {
|
val test_func_ref : Any=>ResolvedProjectile = {
|
||||||
def test_func(o : Any) : Unit = { }
|
def test_func(o : Any) : ResolvedProjectile = { null }
|
||||||
test_func
|
test_func
|
||||||
}
|
}
|
||||||
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue