diff --git a/.codecov.yml b/.codecov.yml
index 8f90e18ca..f8fb716e4 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -2,19 +2,35 @@
comment: off
ignore:
+ - "common/src/main/scala/net/psforever/objects/ObjectType.scala"
- "common/src/main/scala/net/psforever/objects/avatar/Avatars.scala"
+ - "common/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala"
+ - "common/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala"
- "common/src/main/scala/net/psforever/objects/equipment/Ammo.scala"
- "common/src/main/scala/net/psforever/objects/equipment/CItem.scala"
- "common/src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala"
- "common/src/main/scala/net/psforever/objects/equipment/Kits.scala"
- "common/src/main/scala/net/psforever/objects/equipment/SItem.scala"
- "common/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala"
+ - "common/src/main/scala/net/psforever/objects/serverobject/pad/AutoDriveControls.scala"
+ - "common/src/main/scala/net/psforever/objects/serverobject/structures/StructureType.scala"
- "common/src/main/scala/net/psforever/objects/serverobject/turret/TurretUpgrade.scala"
- "common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala"
+ - "common/src/main/scala/net/psforever/objects/vehicles/CargoVehicleRestiction.scala"
+ - "common/src/main/scala/net/psforever/objects/vehicles/DestroyedVehicle.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/Turrets.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/DamageType.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/StandardResistances.scala"
+ - "common/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala"
- "common/src/main/scala/net/psforever/packet/crypto"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala"
@@ -24,7 +40,6 @@ ignore:
- "common/src/main/scala/net/psforever/packet/ControlPacketOpcode.scala"
- "common/src/main/scala/net/psforever/packet/CryptoPacketOpcode.scala"
- "common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala"
- - "common/src/main/scala/net/psforever/objects/ObjectType.scala"
- "common/src/main/scala/net/psforever/types/Angular.scala"
- "common/src/main/scala/net/psforever/types/CertificationType.scala"
- "common/src/main/scala/net/psforever/types/ChatMessageType.scala"
@@ -38,6 +53,8 @@ ignore:
- "common/src/main/scala/net/psforever/types/TransactionType.scala"
- "common/src/main/scala/services/avatar/AvatarAction.scala"
- "common/src/main/scala/services/avatar/AvatarResponse.scala"
+ - "common/src/main/scala/services/galaxy/GalaxyAction.scala"
+ - "common/src/main/scala/services/galaxy/GalaxyResponse.scala"
- "common/src/main/scala/services/local/LocalAction.scala"
- "common/src/main/scala/services/local/LocalResponse.scala"
- "common/src/main/scala/services/vehicle/VehicleAction.scala"
diff --git a/common/src/main/scala/net/psforever/objects/Avatar.scala b/common/src/main/scala/net/psforever/objects/Avatar.scala
index 3166d5b64..8c2f4cd47 100644
--- a/common/src/main/scala/net/psforever/objects/Avatar.scala
+++ b/common/src/main/scala/net/psforever/objects/Avatar.scala
@@ -177,7 +177,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
}
}
- def Definition : AvatarDefinition = Avatar.definition
+ def Definition : AvatarDefinition = GlobalDefinitions.avatar
/*
Merit Commendations and Ribbons
@@ -210,8 +210,6 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
}
object Avatar {
- final private val definition : AvatarDefinition = new AvatarDefinition(121)
-
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Avatar = {
new Avatar(name, faction, sex, head, voice)
}
diff --git a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
index fa36256df..2a2539a58 100644
--- a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala
@@ -3,6 +3,8 @@ package net.psforever.objects
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.inventory.InventoryTile
+import net.psforever.objects.vital._
+import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.types.ExoSuitType
/**
@@ -10,12 +12,16 @@ import net.psforever.types.ExoSuitType
* Players are influenced by the exo-suit they wear in a variety of ways, with speed and available equipment slots being major differences.
* @param suitType the `Enumeration` corresponding to this exo-suit
*/
-class ExoSuitDefinition(private val suitType : ExoSuitType.Value) {
+class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends ResistanceProfileMutators
+ with DamageResistanceModel {
protected var permission : Int = 0 //TODO certification type?
protected var maxArmor : Int = 0
protected val holsters : Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
protected var inventoryScale : InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
protected var inventoryOffset : Int = 0
+ Damage = StandardInfantryDamage
+ Resistance = StandardInfantryResistance
+ Model = StandardResolutions.Infantry
def SuitType : ExoSuitType.Value = suitType
@@ -79,6 +85,12 @@ class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends
obj.MaxArmor = MaxArmor
obj.InventoryScale = InventoryScale
obj.InventoryOffset = InventoryOffset
+ obj.ResistanceDirectHit = ResistanceDirectHit
+ obj.ResistanceSplash = ResistanceSplash
+ obj.ResistanceAggravated = ResistanceAggravated
+ obj.Damage = Damage
+ obj.Resistance = Resistance
+ obj.Model = Model
(0 until 5).foreach(index => { obj.Holster(index, Holster(index)) })
obj
}
@@ -109,6 +121,9 @@ object ExoSuitDefinition {
Standard.Holster(0, EquipmentSize.Pistol)
Standard.Holster(2, EquipmentSize.Rifle)
Standard.Holster(4, EquipmentSize.Melee)
+ Standard.ResistanceDirectHit = 4
+ Standard.ResistanceSplash = 15
+ Standard.ResistanceAggravated = 8
final val Agile = ExoSuitDefinition(ExoSuitType.Agile)
Agile.MaxArmor = 100
@@ -118,6 +133,9 @@ object ExoSuitDefinition {
Agile.Holster(1, EquipmentSize.Pistol)
Agile.Holster(2, EquipmentSize.Rifle)
Agile.Holster(4, EquipmentSize.Melee)
+ Agile.ResistanceDirectHit = 6
+ Agile.ResistanceSplash = 25
+ Agile.ResistanceAggravated = 10
final val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
Reinforced.permission = 1
@@ -129,6 +147,9 @@ object ExoSuitDefinition {
Reinforced.Holster(2, EquipmentSize.Rifle)
Reinforced.Holster(3, EquipmentSize.Rifle)
Reinforced.Holster(4, EquipmentSize.Melee)
+ Reinforced.ResistanceDirectHit = 10
+ Reinforced.ResistanceSplash = 35
+ Reinforced.ResistanceAggravated = 12
final val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
Infiltration.permission = 1
@@ -145,6 +166,11 @@ object ExoSuitDefinition {
MAX.InventoryOffset = 6
MAX.Holster(0, EquipmentSize.Max)
MAX.Holster(4, EquipmentSize.Melee)
+ MAX.ResistanceDirectHit = 6
+ MAX.ResistanceSplash = 35
+ MAX.ResistanceAggravated = 10
+ MAX.Damage = StandardMaxDamage
+ MAX.Model = StandardResolutions.Max
def apply(suitType : ExoSuitType.Value) : ExoSuitDefinition = {
new ExoSuitDefinition(suitType)
diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 9a0559e48..eec948f9c 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -1,12 +1,13 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
+import net.psforever.objects.ballistics.Projectiles
import net.psforever.objects.definition._
import net.psforever.objects.definition.converter._
-import net.psforever.objects.serverobject.doors.DoorDefinition
import net.psforever.objects.equipment.CItem.DeployedItem
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
+import net.psforever.objects.serverobject.doors.DoorDefinition
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
import net.psforever.objects.serverobject.locks.IFFLockDefinition
import net.psforever.objects.serverobject.mblocker.LockerDefinition
@@ -14,15 +15,19 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
-import net.psforever.objects.ballistics.{DamageType, Projectiles}
import net.psforever.objects.serverobject.turret.{MannedTurretDefinition, TurretUpgrade}
-import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
+import net.psforever.objects.vehicles.{DestroyedVehicle, SeatArmorRestriction, UtilityType}
+import net.psforever.objects.vital.DamageType
import net.psforever.types.PlanetSideEmpire
import scala.collection.mutable
import scala.concurrent.duration._
object GlobalDefinitions {
+ /*
+ characters
+ */
+ val avatar = new AvatarDefinition(121)
/*
Implants
*/
@@ -1551,6 +1556,7 @@ object GlobalDefinitions {
bullet_105mm_projectile.ProjectileDamageType = DamageType.Splash
bullet_105mm_projectile.InitialVelocity = 100
bullet_105mm_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(bullet_105mm_projectile)
bullet_12mm_projectile.Name= "12mmbullet_projectile"
bullet_12mm_projectile.Damage0 = 25
@@ -1563,6 +1569,7 @@ object GlobalDefinitions {
bullet_12mm_projectile.DegradeMultiplier = 0.5f
bullet_12mm_projectile.InitialVelocity = 500
bullet_12mm_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_12mm_projectile)
bullet_12mm_projectileb.Name = "12mmbullet_projectileb"
// TODO for later, maybe : set_resource_parent 12mmbullet_projectileb game_objects 12mmbullet_projectile
@@ -1576,6 +1583,7 @@ object GlobalDefinitions {
bullet_12mm_projectileb.DegradeMultiplier = 0.5f
bullet_12mm_projectileb.InitialVelocity = 500
bullet_12mm_projectileb.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_12mm_projectileb)
bullet_150mm_projectile.Name = "150mmbullet_projectile"
bullet_150mm_projectile.Damage0 = 150
@@ -1588,6 +1596,7 @@ object GlobalDefinitions {
bullet_150mm_projectile.ProjectileDamageType = DamageType.Splash
bullet_150mm_projectile.InitialVelocity = 100
bullet_150mm_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(bullet_150mm_projectile)
bullet_15mm_apc_projectile.Name = "15mmbullet_apc_projectile"
// TODO for later, maybe : set_resource_parent 15mmbullet_apc_projectile game_objects 15mmbullet_projectile
@@ -1601,6 +1610,7 @@ object GlobalDefinitions {
bullet_15mm_apc_projectile.DegradeMultiplier = 0.5f
bullet_15mm_apc_projectile.InitialVelocity = 500
bullet_15mm_apc_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_15mm_apc_projectile)
bullet_15mm_projectile.Name = "15mmbullet_projectile"
bullet_15mm_projectile.Damage0 = 21
@@ -1613,6 +1623,7 @@ object GlobalDefinitions {
bullet_15mm_projectile.DegradeMultiplier = 0.5f
bullet_15mm_projectile.InitialVelocity = 500
bullet_15mm_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_15mm_projectile)
bullet_20mm_apc_projectile.Name = "20mmbullet_apc_projectile"
// TODO for later, maybe : set_resource_parent 20mmbullet_apc_projectile game_objects 20mmbullet_projectile
@@ -1626,6 +1637,7 @@ object GlobalDefinitions {
bullet_20mm_apc_projectile.DegradeMultiplier = 0.5f
bullet_20mm_apc_projectile.InitialVelocity = 500
bullet_20mm_apc_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_20mm_apc_projectile)
bullet_20mm_projectile.Name = "20mmbullet_projectile"
bullet_20mm_projectile.Damage0 = 20
@@ -1638,6 +1650,7 @@ object GlobalDefinitions {
bullet_20mm_projectile.DegradeMultiplier = 0.5f
bullet_20mm_projectile.InitialVelocity = 500
bullet_20mm_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_20mm_projectile)
bullet_25mm_projectile.Name = "25mmbullet_projectile"
bullet_25mm_projectile.Damage0 = 25
@@ -1648,6 +1661,7 @@ object GlobalDefinitions {
bullet_25mm_projectile.DegradeMultiplier = 0.5f
bullet_25mm_projectile.InitialVelocity = 500
bullet_25mm_projectile.Lifespan = 0.6f
+ ProjectileDefinition.CalculateDerivedFields(bullet_25mm_projectile)
bullet_35mm_projectile.Name = "35mmbullet_projectile"
bullet_35mm_projectile.Damage0 = 40
@@ -1658,6 +1672,7 @@ object GlobalDefinitions {
bullet_35mm_projectile.DegradeMultiplier = 0.5f
bullet_35mm_projectile.InitialVelocity = 200
bullet_35mm_projectile.Lifespan = 1.5f
+ ProjectileDefinition.CalculateDerivedFields(bullet_35mm_projectile)
bullet_75mm_apc_projectile.Name = "75mmbullet_apc_projectile"
// TODO for later, maybe : set_resource_parent 75mmbullet_apc_projectile game_objects 75mmbullet_projectile
@@ -1668,6 +1683,7 @@ object GlobalDefinitions {
bullet_75mm_apc_projectile.ProjectileDamageType = DamageType.Splash
bullet_75mm_apc_projectile.InitialVelocity = 100
bullet_75mm_apc_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(bullet_75mm_apc_projectile)
bullet_75mm_projectile.Name = "75mmbullet_projectile"
bullet_75mm_projectile.Damage0 = 75
@@ -1677,6 +1693,7 @@ object GlobalDefinitions {
bullet_75mm_projectile.ProjectileDamageType = DamageType.Splash
bullet_75mm_projectile.InitialVelocity = 100
bullet_75mm_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(bullet_75mm_projectile)
bullet_9mm_AP_projectile.Name = "9mmbullet_AP_projectile"
// TODO for later, maybe : set_resource_parent 9mmbullet_AP_projectile game_objects 9mmbullet_projectile
@@ -1688,6 +1705,7 @@ object GlobalDefinitions {
bullet_9mm_AP_projectile.InitialVelocity = 500
bullet_9mm_AP_projectile.Lifespan = 0.4f
bullet_9mm_AP_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(bullet_9mm_AP_projectile)
bullet_9mm_projectile.Name = "9mmbullet_projectile"
bullet_9mm_projectile.Damage0 = 18
@@ -1698,6 +1716,7 @@ object GlobalDefinitions {
bullet_9mm_projectile.InitialVelocity = 500
bullet_9mm_projectile.Lifespan = 0.4f
bullet_9mm_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(bullet_9mm_projectile)
anniversary_projectilea.Name = "anniversary_projectilea"
anniversary_projectilea.Damage0 = 30
@@ -1710,6 +1729,7 @@ object GlobalDefinitions {
anniversary_projectilea.DegradeMultiplier = 0.2f
anniversary_projectilea.InitialVelocity = 500
anniversary_projectilea.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(anniversary_projectilea)
anniversary_projectileb.Name = "anniversary_projectileb"
// TODO for later, maybe : set_resource_parent anniversary_projectileb game_objects anniversary_projectilea
@@ -1723,6 +1743,7 @@ object GlobalDefinitions {
anniversary_projectileb.DegradeMultiplier = 0.2f
anniversary_projectileb.InitialVelocity = 500
anniversary_projectileb.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(anniversary_projectileb)
aphelion_immolation_cannon_projectile.Name = "aphelion_immolation_cannon_projectile"
aphelion_immolation_cannon_projectile.Damage0 = 55
@@ -1735,6 +1756,7 @@ object GlobalDefinitions {
aphelion_immolation_cannon_projectile.ProjectileDamageType = DamageType.Splash
aphelion_immolation_cannon_projectile.InitialVelocity = 250
aphelion_immolation_cannon_projectile.Lifespan = 1.4f
+ ProjectileDefinition.CalculateDerivedFields(aphelion_immolation_cannon_projectile)
aphelion_laser_projectile.Name = "aphelion_laser_projectile"
aphelion_laser_projectile.Damage0 = 3
@@ -1747,6 +1769,7 @@ object GlobalDefinitions {
aphelion_laser_projectile.DegradeMultiplier = 0.5f
aphelion_laser_projectile.InitialVelocity = 500
aphelion_laser_projectile.Lifespan = 0.35f
+ ProjectileDefinition.CalculateDerivedFields(aphelion_laser_projectile)
aphelion_plasma_rocket_projectile.Name = "aphelion_plasma_rocket_projectile"
aphelion_plasma_rocket_projectile.Damage0 = 38
@@ -1761,6 +1784,7 @@ object GlobalDefinitions {
aphelion_plasma_rocket_projectile.ProjectileDamageType = DamageType.Splash
aphelion_plasma_rocket_projectile.InitialVelocity = 75
aphelion_plasma_rocket_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(aphelion_plasma_rocket_projectile)
aphelion_ppa_projectile.Name = "aphelion_ppa_projectile"
// TODO for later, maybe : set_resource_parent aphelion_ppa_projectile game_objects ppa_projectile
@@ -1776,6 +1800,7 @@ object GlobalDefinitions {
aphelion_ppa_projectile.DegradeMultiplier = 0.55f
aphelion_ppa_projectile.InitialVelocity = 350
aphelion_ppa_projectile.Lifespan = .7f
+ ProjectileDefinition.CalculateDerivedFields(aphelion_ppa_projectile)
aphelion_starfire_projectile.Name = "aphelion_starfire_projectile"
// TODO for later, maybe : set_resource_parent aphelion_starfire_projectile game_objects starfire_projectile
@@ -1789,6 +1814,7 @@ object GlobalDefinitions {
aphelion_starfire_projectile.InitialVelocity = 45
aphelion_starfire_projectile.Lifespan = 7f
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
+ ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile)
bolt_projectile.Name = "bolt_projectile"
bolt_projectile.Damage0 = 100
@@ -1799,6 +1825,7 @@ object GlobalDefinitions {
bolt_projectile.ProjectileDamageType = DamageType.Splash
bolt_projectile.InitialVelocity = 500
bolt_projectile.Lifespan = 1.0f
+ ProjectileDefinition.CalculateDerivedFields(bolt_projectile)
burster_projectile.Name = "burster_projectile"
burster_projectile.Damage0 = 18
@@ -1810,6 +1837,7 @@ object GlobalDefinitions {
burster_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
burster_projectile.InitialVelocity = 125
burster_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(burster_projectile)
chainblade_projectile.Name = "chainblade_projectile"
// TODO for later, maybe : set_resource_parent chainblade_projectile game_objects melee_ammo_projectile
@@ -1818,6 +1846,7 @@ object GlobalDefinitions {
chainblade_projectile.ProjectileDamageType = DamageType.Direct
chainblade_projectile.InitialVelocity = 100
chainblade_projectile.Lifespan = .02f
+ ProjectileDefinition.CalculateDerivedFields(chainblade_projectile)
colossus_100mm_projectile.Name = "colossus_100mm_projectile"
colossus_100mm_projectile.Damage0 = 58
@@ -1830,6 +1859,7 @@ object GlobalDefinitions {
colossus_100mm_projectile.ProjectileDamageType = DamageType.Splash
colossus_100mm_projectile.InitialVelocity = 100
colossus_100mm_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(colossus_100mm_projectile)
colossus_burster_projectile.Name = "colossus_burster_projectile"
// TODO for later, maybe : set_resource_parent colossus_burster_projectile game_objects burster_projectile
@@ -1844,6 +1874,7 @@ object GlobalDefinitions {
colossus_burster_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
colossus_burster_projectile.InitialVelocity = 175
colossus_burster_projectile.Lifespan = 2.5f
+ ProjectileDefinition.CalculateDerivedFields(colossus_burster_projectile)
colossus_chaingun_projectile.Name = "colossus_chaingun_projectile"
// TODO for later, maybe : set_resource_parent colossus_chaingun_projectile game_objects 35mmbullet_projectile
@@ -1857,6 +1888,7 @@ object GlobalDefinitions {
colossus_chaingun_projectile.DegradeMultiplier = 0.44f
colossus_chaingun_projectile.InitialVelocity = 500
colossus_chaingun_projectile.Lifespan = .50f
+ ProjectileDefinition.CalculateDerivedFields(colossus_chaingun_projectile)
colossus_cluster_bomb_projectile.Name = "colossus_cluster_bomb_projectile"
colossus_cluster_bomb_projectile.Damage0 = 40
@@ -1869,6 +1901,7 @@ object GlobalDefinitions {
colossus_cluster_bomb_projectile.ProjectileDamageType = DamageType.Splash
colossus_cluster_bomb_projectile.InitialVelocity = 75
colossus_cluster_bomb_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(colossus_cluster_bomb_projectile)
colossus_tank_cannon_projectile.Name = "colossus_tank_cannon_projectile"
// TODO for later, maybe : set_resource_parent colossus_tank_cannon_projectile game_objects 75mmbullet_projectile
@@ -1882,6 +1915,7 @@ object GlobalDefinitions {
colossus_tank_cannon_projectile.ProjectileDamageType = DamageType.Splash
colossus_tank_cannon_projectile.InitialVelocity = 165
colossus_tank_cannon_projectile.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(colossus_tank_cannon_projectile)
comet_projectile.Name = "comet_projectile"
comet_projectile.Damage0 = 15
@@ -1896,6 +1930,7 @@ object GlobalDefinitions {
comet_projectile.ProjectileDamageType = DamageType.Aggravated
comet_projectile.InitialVelocity = 80
comet_projectile.Lifespan = 3.1f
+ ProjectileDefinition.CalculateDerivedFields(comet_projectile)
dualcycler_projectile.Name = "dualcycler_projectile"
dualcycler_projectile.Damage0 = 18
@@ -1905,6 +1940,7 @@ object GlobalDefinitions {
dualcycler_projectile.DegradeMultiplier = .5f
dualcycler_projectile.InitialVelocity = 500
dualcycler_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(dualcycler_projectile)
dynomite_projectile.Name = "dynomite_projectile"
// TODO for later, maybe : set_resource_parent dynomite_projectile game_objects frag_grenade_projectile_enh
@@ -1915,6 +1951,7 @@ object GlobalDefinitions {
dynomite_projectile.ProjectileDamageType = DamageType.Splash
dynomite_projectile.InitialVelocity = 30
dynomite_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(dynomite_projectile)
energy_cell_projectile.Name = "energy_cell_projectile"
energy_cell_projectile.Damage0 = 18
@@ -1925,6 +1962,7 @@ object GlobalDefinitions {
energy_cell_projectile.InitialVelocity = 500
energy_cell_projectile.Lifespan = .4f
energy_cell_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(energy_cell_projectile)
energy_gun_nc_projectile.Name = "energy_gun_nc_projectile"
energy_gun_nc_projectile.Damage0 = 10
@@ -1932,6 +1970,7 @@ object GlobalDefinitions {
energy_gun_nc_projectile.ProjectileDamageType = DamageType.Direct
energy_gun_nc_projectile.InitialVelocity = 500
energy_gun_nc_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(energy_gun_nc_projectile)
energy_gun_tr_projectile.Name = "energy_gun_tr_projectile"
energy_gun_tr_projectile.Damage0 = 14
@@ -1941,6 +1980,7 @@ object GlobalDefinitions {
energy_gun_tr_projectile.DegradeMultiplier = .5f
energy_gun_tr_projectile.InitialVelocity = 500
energy_gun_tr_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(energy_gun_tr_projectile)
energy_gun_vs_projectile.Name = "energy_gun_vs_projectile"
energy_gun_vs_projectile.Damage0 = 25
@@ -1950,6 +1990,7 @@ object GlobalDefinitions {
energy_gun_vs_projectile.DegradeMultiplier = 0.5f
energy_gun_vs_projectile.InitialVelocity = 500
energy_gun_vs_projectile.Lifespan = .5f
+ ProjectileDefinition.CalculateDerivedFields(energy_gun_vs_projectile)
enhanced_energy_cell_projectile.Name = "enhanced_energy_cell_projectile"
// TODO for later, maybe : set_resource_parent enhanced_energy_cell_projectile game_objects energy_cell_projectile
@@ -1961,6 +2002,7 @@ object GlobalDefinitions {
enhanced_energy_cell_projectile.InitialVelocity = 500
enhanced_energy_cell_projectile.Lifespan = .4f
enhanced_energy_cell_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(enhanced_energy_cell_projectile)
enhanced_quasar_projectile.Name = "enhanced_quasar_projectile"
// TODO for later, maybe : set_resource_parent enhanced_quasar_projectile game_objects quasar_projectile
@@ -1971,6 +2013,7 @@ object GlobalDefinitions {
enhanced_quasar_projectile.DegradeMultiplier = 0.5f
enhanced_quasar_projectile.InitialVelocity = 500
enhanced_quasar_projectile.Lifespan = .4f
+ ProjectileDefinition.CalculateDerivedFields(enhanced_quasar_projectile)
falcon_projectile.Name = "falcon_projectile"
falcon_projectile.Damage0 = 35
@@ -1985,6 +2028,7 @@ object GlobalDefinitions {
falcon_projectile.ProjectileDamageType = DamageType.Splash
falcon_projectile.InitialVelocity = 120
falcon_projectile.Lifespan = 2.1f
+ ProjectileDefinition.CalculateDerivedFields(falcon_projectile)
firebird_missile_projectile.Name = "firebird_missile_projectile"
firebird_missile_projectile.Damage0 = 125
@@ -1999,6 +2043,7 @@ object GlobalDefinitions {
firebird_missile_projectile.ProjectileDamageType = DamageType.Splash
firebird_missile_projectile.InitialVelocity = 75
firebird_missile_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(firebird_missile_projectile)
flail_projectile.Name = "flail_projectile"
flail_projectile.Damage0 = 75
@@ -2013,6 +2058,7 @@ object GlobalDefinitions {
flail_projectile.DegradeMultiplier = 5f
flail_projectile.InitialVelocity = 75
flail_projectile.Lifespan = 40f
+ ProjectileDefinition.CalculateDerivedFields(flail_projectile)
flamethrower_fireball.Name = "flamethrower_fireball"
flamethrower_fireball.Damage0 = 30
@@ -2025,6 +2071,7 @@ object GlobalDefinitions {
flamethrower_fireball.ProjectileDamageType = DamageType.Aggravated
flamethrower_fireball.InitialVelocity = 15
flamethrower_fireball.Lifespan = 1.2f
+ ProjectileDefinition.CalculateDerivedFields(flamethrower_fireball)
flamethrower_projectile.Name = "flamethrower_projectile"
flamethrower_projectile.Damage0 = 10
@@ -2039,6 +2086,7 @@ object GlobalDefinitions {
flamethrower_projectile.DegradeMultiplier = 0.5f
flamethrower_projectile.InitialVelocity = 10
flamethrower_projectile.Lifespan = 2.0f
+ ProjectileDefinition.CalculateDerivedFields(flamethrower_projectile)
flux_cannon_apc_projectile.Name = "flux_cannon_apc_projectile"
// TODO for later, maybe : set_resource_parent flux_cannon_apc_projectile game_objects flux_cannon_thresher_projectile
@@ -2052,6 +2100,7 @@ object GlobalDefinitions {
flux_cannon_apc_projectile.ProjectileDamageType = DamageType.Direct
flux_cannon_apc_projectile.InitialVelocity = 300
flux_cannon_apc_projectile.Lifespan = 1f
+ ProjectileDefinition.CalculateDerivedFields(flux_cannon_apc_projectile)
flux_cannon_thresher_projectile.Name = "flux_cannon_thresher_projectile"
flux_cannon_thresher_projectile.Damage0 = 30
@@ -2064,6 +2113,7 @@ object GlobalDefinitions {
flux_cannon_thresher_projectile.ProjectileDamageType = DamageType.Splash
flux_cannon_thresher_projectile.InitialVelocity = 75
flux_cannon_thresher_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(flux_cannon_thresher_projectile)
fluxpod_projectile.Name = "fluxpod_projectile"
fluxpod_projectile.Damage0 = 110
@@ -2076,6 +2126,7 @@ object GlobalDefinitions {
fluxpod_projectile.ProjectileDamageType = DamageType.Splash
fluxpod_projectile.InitialVelocity = 80
fluxpod_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(fluxpod_projectile)
forceblade_projectile.Name = "forceblade_projectile"
// TODO for later, maybe : set_resource_parent forceblade_projectile game_objects melee_ammo_projectile
@@ -2084,6 +2135,7 @@ object GlobalDefinitions {
forceblade_projectile.ProjectileDamageType = DamageType.Direct
forceblade_projectile.InitialVelocity = 100
forceblade_projectile.Lifespan = .02f
+ ProjectileDefinition.CalculateDerivedFields(forceblade_projectile)
frag_cartridge_projectile.Name = "frag_cartridge_projectile"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile
@@ -2094,6 +2146,7 @@ object GlobalDefinitions {
frag_cartridge_projectile.ProjectileDamageType = DamageType.Splash
frag_cartridge_projectile.InitialVelocity = 30
frag_cartridge_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(frag_cartridge_projectile)
frag_cartridge_projectile_b.Name = "frag_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile_b game_objects frag_grenade_projectile_enh
@@ -2104,6 +2157,7 @@ object GlobalDefinitions {
frag_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
frag_cartridge_projectile_b.InitialVelocity = 30
frag_cartridge_projectile_b.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(frag_cartridge_projectile_b)
frag_grenade_projectile.Name = "frag_grenade_projectile"
frag_grenade_projectile.Damage0 = 75
@@ -2113,6 +2167,7 @@ object GlobalDefinitions {
frag_grenade_projectile.ProjectileDamageType = DamageType.Splash
frag_grenade_projectile.InitialVelocity = 30
frag_grenade_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(frag_grenade_projectile)
frag_grenade_projectile_enh.Name = "frag_grenade_projectile_enh"
// TODO for later, maybe : set_resource_parent frag_grenade_projectile_enh game_objects frag_grenade_projectile
@@ -2123,6 +2178,7 @@ object GlobalDefinitions {
frag_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
frag_grenade_projectile_enh.InitialVelocity = 30
frag_grenade_projectile_enh.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(frag_grenade_projectile_enh)
galaxy_gunship_gun_projectile.Name = "galaxy_gunship_gun_projectile"
// TODO for later, maybe : set_resource_parent galaxy_gunship_gun_projectile game_objects 35mmbullet_projectile
@@ -2134,6 +2190,7 @@ object GlobalDefinitions {
galaxy_gunship_gun_projectile.DegradeMultiplier = 0.6f
galaxy_gunship_gun_projectile.InitialVelocity = 400
galaxy_gunship_gun_projectile.Lifespan = 0.8f
+ ProjectileDefinition.CalculateDerivedFields(galaxy_gunship_gun_projectile)
gauss_cannon_projectile.Name = "gauss_cannon_projectile"
gauss_cannon_projectile.Damage0 = 190
@@ -2146,6 +2203,7 @@ object GlobalDefinitions {
gauss_cannon_projectile.ProjectileDamageType = DamageType.Splash
gauss_cannon_projectile.InitialVelocity = 150
gauss_cannon_projectile.Lifespan = 2.67f
+ ProjectileDefinition.CalculateDerivedFields(gauss_cannon_projectile)
grenade_projectile.Name = "grenade_projectile"
grenade_projectile.Damage0 = 50
@@ -2154,6 +2212,7 @@ object GlobalDefinitions {
grenade_projectile.ProjectileDamageType = DamageType.Splash
grenade_projectile.InitialVelocity = 15
grenade_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(grenade_projectile)
heavy_grenade_projectile.Name = "heavy_grenade_projectile"
heavy_grenade_projectile.Damage0 = 50
@@ -2166,6 +2225,7 @@ object GlobalDefinitions {
heavy_grenade_projectile.ProjectileDamageType = DamageType.Splash
heavy_grenade_projectile.InitialVelocity = 75
heavy_grenade_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(heavy_grenade_projectile)
heavy_rail_beam_projectile.Name = "heavy_rail_beam_projectile"
heavy_rail_beam_projectile.Damage0 = 75
@@ -2178,6 +2238,7 @@ object GlobalDefinitions {
heavy_rail_beam_projectile.ProjectileDamageType = DamageType.Splash
heavy_rail_beam_projectile.InitialVelocity = 600
heavy_rail_beam_projectile.Lifespan = .5f
+ ProjectileDefinition.CalculateDerivedFields(heavy_rail_beam_projectile)
heavy_sniper_projectile.Name = "heavy_sniper_projectile"
heavy_sniper_projectile.Damage0 = 55
@@ -2188,6 +2249,7 @@ object GlobalDefinitions {
heavy_sniper_projectile.ProjectileDamageType = DamageType.Splash
heavy_sniper_projectile.InitialVelocity = 500
heavy_sniper_projectile.Lifespan = 1.0f
+ ProjectileDefinition.CalculateDerivedFields(heavy_sniper_projectile)
hellfire_projectile.Name = "hellfire_projectile"
hellfire_projectile.Damage0 = 50
@@ -2202,6 +2264,7 @@ object GlobalDefinitions {
hellfire_projectile.ProjectileDamageType = DamageType.Splash
hellfire_projectile.InitialVelocity = 125
hellfire_projectile.Lifespan = 1.5f
+ ProjectileDefinition.CalculateDerivedFields(hellfire_projectile)
hunter_seeker_missile_dumbfire.Name = "hunter_seeker_missile_dumbfire"
hunter_seeker_missile_dumbfire.Damage0 = 50
@@ -2214,6 +2277,7 @@ object GlobalDefinitions {
hunter_seeker_missile_dumbfire.ProjectileDamageType = DamageType.Splash
hunter_seeker_missile_dumbfire.InitialVelocity = 40
hunter_seeker_missile_dumbfire.Lifespan = 6.3f
+ ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_dumbfire)
hunter_seeker_missile_projectile.Name = "hunter_seeker_missile_projectile"
hunter_seeker_missile_projectile.Damage0 = 50
@@ -2226,6 +2290,7 @@ object GlobalDefinitions {
hunter_seeker_missile_projectile.ProjectileDamageType = DamageType.Splash
hunter_seeker_missile_projectile.InitialVelocity = 40
hunter_seeker_missile_projectile.Lifespan = 6.3f
+ ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile)
jammer_cartridge_projectile.Name = "jammer_cartridge_projectile"
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile game_objects jammer_grenade_projectile
@@ -2236,6 +2301,7 @@ object GlobalDefinitions {
jammer_cartridge_projectile.ProjectileDamageType = DamageType.Splash
jammer_cartridge_projectile.InitialVelocity = 30
jammer_cartridge_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile_b game_objects jammer_grenade_projectile_enh
@@ -2246,6 +2312,7 @@ object GlobalDefinitions {
jammer_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
jammer_cartridge_projectile_b.InitialVelocity = 30
jammer_cartridge_projectile_b.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
jammer_grenade_projectile.Damage0 = 0
@@ -2255,6 +2322,7 @@ object GlobalDefinitions {
jammer_grenade_projectile.ProjectileDamageType = DamageType.Splash
jammer_grenade_projectile.InitialVelocity = 30
jammer_grenade_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh"
// TODO for later, maybe : set_resource_parent jammer_grenade_projectile_enh game_objects jammer_grenade_projectile
@@ -2265,6 +2333,7 @@ object GlobalDefinitions {
jammer_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
jammer_grenade_projectile_enh.InitialVelocity = 30
jammer_grenade_projectile_enh.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
katana_projectile.Name = "katana_projectile"
katana_projectile.Damage0 = 25
@@ -2272,6 +2341,7 @@ object GlobalDefinitions {
katana_projectile.ProjectileDamageType = DamageType.Direct
katana_projectile.InitialVelocity = 100
katana_projectile.Lifespan = .03f
+ ProjectileDefinition.CalculateDerivedFields(katana_projectile)
katana_projectileb.Name = "katana_projectileb"
// TODO for later, maybe : set_resource_parent katana_projectileb game_objects katana_projectile
@@ -2280,6 +2350,7 @@ object GlobalDefinitions {
katana_projectileb.ProjectileDamageType = DamageType.Direct
katana_projectileb.InitialVelocity = 100
katana_projectileb.Lifespan = .03f
+ ProjectileDefinition.CalculateDerivedFields(katana_projectileb)
lancer_projectile.Name = "lancer_projectile"
lancer_projectile.Damage0 = 25
@@ -2290,6 +2361,7 @@ object GlobalDefinitions {
lancer_projectile.ProjectileDamageType = DamageType.Direct
lancer_projectile.InitialVelocity = 500
lancer_projectile.Lifespan = 0.6f
+ ProjectileDefinition.CalculateDerivedFields(lancer_projectile)
lasher_projectile.Name = "lasher_projectile"
lasher_projectile.Damage0 = 30
@@ -2302,6 +2374,7 @@ object GlobalDefinitions {
lasher_projectile.DegradeMultiplier = 0.3f
lasher_projectile.InitialVelocity = 120
lasher_projectile.Lifespan = 0.75f
+ ProjectileDefinition.CalculateDerivedFields(lasher_projectile)
lasher_projectile_ap.Name = "lasher_projectile_ap"
lasher_projectile_ap.Damage0 = 12
@@ -2314,6 +2387,7 @@ object GlobalDefinitions {
lasher_projectile_ap.DegradeMultiplier = 0.3f
lasher_projectile_ap.InitialVelocity = 120
lasher_projectile_ap.Lifespan = 0.75f
+ ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap)
liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile"
liberator_bomb_cluster_bomblet_projectile.Damage0 = 75
@@ -2323,6 +2397,7 @@ object GlobalDefinitions {
liberator_bomb_cluster_bomblet_projectile.ProjectileDamageType = DamageType.Splash
liberator_bomb_cluster_bomblet_projectile.InitialVelocity = 0
liberator_bomb_cluster_bomblet_projectile.Lifespan = 30f
+ ProjectileDefinition.CalculateDerivedFields(liberator_bomb_cluster_bomblet_projectile)
liberator_bomb_cluster_projectile.Name = "liberator_bomb_cluster_projectile"
liberator_bomb_cluster_projectile.Damage0 = 75
@@ -2332,6 +2407,7 @@ object GlobalDefinitions {
liberator_bomb_cluster_projectile.ProjectileDamageType = DamageType.Direct
liberator_bomb_cluster_projectile.InitialVelocity = 0
liberator_bomb_cluster_projectile.Lifespan = 30f
+ ProjectileDefinition.CalculateDerivedFields(liberator_bomb_cluster_projectile)
liberator_bomb_projectile.Name = "liberator_bomb_projectile"
liberator_bomb_projectile.Damage0 = 250
@@ -2344,6 +2420,7 @@ object GlobalDefinitions {
liberator_bomb_projectile.ProjectileDamageType = DamageType.Splash
liberator_bomb_projectile.InitialVelocity = 0
liberator_bomb_projectile.Lifespan = 30f
+ ProjectileDefinition.CalculateDerivedFields(liberator_bomb_projectile)
maelstrom_grenade_projectile.Name = "maelstrom_grenade_projectile"
maelstrom_grenade_projectile.Damage0 = 32
@@ -2352,6 +2429,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile.InitialVelocity = 30
maelstrom_grenade_projectile.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile)
maelstrom_grenade_projectile_contact.Name = "maelstrom_grenade_projectile_contact"
// TODO for later, maybe : set_resource_parent maelstrom_grenade_projectile_contact game_objects maelstrom_grenade_projectile
@@ -2361,6 +2439,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile_contact.InitialVelocity = 30
maelstrom_grenade_projectile_contact.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(maelstrom_grenade_projectile_contact)
maelstrom_stream_projectile.Name = "maelstrom_stream_projectile"
maelstrom_stream_projectile.Damage0 = 15
@@ -2370,6 +2449,7 @@ object GlobalDefinitions {
maelstrom_stream_projectile.DegradeMultiplier = 0.5f
maelstrom_stream_projectile.InitialVelocity = 200
maelstrom_stream_projectile.Lifespan = 0.2f
+ ProjectileDefinition.CalculateDerivedFields(maelstrom_stream_projectile)
magcutter_projectile.Name = "magcutter_projectile"
// TODO for later, maybe : set_resource_parent magcutter_projectile game_objects melee_ammo_projectile
@@ -2378,6 +2458,7 @@ object GlobalDefinitions {
magcutter_projectile.ProjectileDamageType = DamageType.Direct
magcutter_projectile.InitialVelocity = 100
magcutter_projectile.Lifespan = .02f
+ ProjectileDefinition.CalculateDerivedFields(magcutter_projectile)
melee_ammo_projectile.Name = "melee_ammo_projectile"
melee_ammo_projectile.Damage0 = 25
@@ -2385,12 +2466,14 @@ object GlobalDefinitions {
melee_ammo_projectile.ProjectileDamageType = DamageType.Direct
melee_ammo_projectile.InitialVelocity = 100
melee_ammo_projectile.Lifespan = .02f
+ ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile)
meteor_common.Name = "meteor_common"
meteor_common.DamageAtEdge = .1f
meteor_common.ProjectileDamageType = DamageType.Splash
meteor_common.InitialVelocity = 0
meteor_common.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_common)
meteor_projectile_b_large.Name = "meteor_projectile_b_large"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_large game_objects meteor_common
@@ -2401,6 +2484,7 @@ object GlobalDefinitions {
meteor_projectile_b_large.ProjectileDamageType = DamageType.Splash
meteor_projectile_b_large.InitialVelocity = 0
meteor_projectile_b_large.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_large)
meteor_projectile_b_medium.Name = "meteor_projectile_b_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_medium game_objects meteor_common
@@ -2411,6 +2495,7 @@ object GlobalDefinitions {
meteor_projectile_b_medium.ProjectileDamageType = DamageType.Splash
meteor_projectile_b_medium.InitialVelocity = 0
meteor_projectile_b_medium.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_medium)
meteor_projectile_b_small.Name = "meteor_projectile_b_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_b_small game_objects meteor_common
@@ -2421,6 +2506,7 @@ object GlobalDefinitions {
meteor_projectile_b_small.ProjectileDamageType = DamageType.Splash
meteor_projectile_b_small.InitialVelocity = 0
meteor_projectile_b_small.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_b_small)
meteor_projectile_large.Name = "meteor_projectile_large"
// TODO for later, maybe : set_resource_parent meteor_projectile_large game_objects meteor_common
@@ -2431,6 +2517,7 @@ object GlobalDefinitions {
meteor_projectile_large.ProjectileDamageType = DamageType.Splash
meteor_projectile_large.InitialVelocity = 0
meteor_projectile_large.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_large)
meteor_projectile_medium.Name = "meteor_projectile_medium"
// TODO for later, maybe : set_resource_parent meteor_projectile_medium game_objects meteor_common
@@ -2441,6 +2528,7 @@ object GlobalDefinitions {
meteor_projectile_medium.ProjectileDamageType = DamageType.Splash
meteor_projectile_medium.InitialVelocity = 0
meteor_projectile_medium.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_medium)
meteor_projectile_small.Name = "meteor_projectile_small"
// TODO for later, maybe : set_resource_parent meteor_projectile_small game_objects meteor_common
@@ -2451,10 +2539,12 @@ object GlobalDefinitions {
meteor_projectile_small.ProjectileDamageType = DamageType.Splash
meteor_projectile_small.InitialVelocity = 0
meteor_projectile_small.Lifespan = 40
+ ProjectileDefinition.CalculateDerivedFields(meteor_projectile_small)
mine_projectile.Name = "mine_projectile"
mine_projectile.Lifespan = 0.01f
mine_projectile.InitialVelocity = 300
+ ProjectileDefinition.CalculateDerivedFields(mine_projectile)
mine_sweeper_projectile.Name = "mine_sweeper_projectile"
mine_sweeper_projectile.Damage0 = 0
@@ -2464,6 +2554,7 @@ object GlobalDefinitions {
mine_sweeper_projectile.ProjectileDamageType = DamageType.Splash
mine_sweeper_projectile.InitialVelocity = 30
mine_sweeper_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(mine_sweeper_projectile)
mine_sweeper_projectile_enh.Name = "mine_sweeper_projectile_enh"
mine_sweeper_projectile_enh.Damage0 = 0
@@ -2472,6 +2563,7 @@ object GlobalDefinitions {
mine_sweeper_projectile_enh.DamageRadius = 25f
mine_sweeper_projectile_enh.InitialVelocity = 30
mine_sweeper_projectile_enh.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(mine_sweeper_projectile_enh)
oicw_projectile.Name = "oicw_projectile"
oicw_projectile.Damage0 = 50
@@ -2483,6 +2575,7 @@ object GlobalDefinitions {
oicw_projectile.ProjectileDamageType = DamageType.Splash
oicw_projectile.InitialVelocity = 5
oicw_projectile.Lifespan = 6.1f
+ ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
pellet_gun_projectile.Name = "pellet_gun_projectile"
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
@@ -2492,6 +2585,7 @@ object GlobalDefinitions {
pellet_gun_projectile.InitialVelocity = 400
pellet_gun_projectile.Lifespan = 0.1875f
pellet_gun_projectile.UseDamage1Subtract = false
+ ProjectileDefinition.CalculateDerivedFields(pellet_gun_projectile)
peregrine_dual_machine_gun_projectile.Name = "peregrine_dual_machine_gun_projectile"
// TODO for later, maybe : set_resource_parent peregrine_dual_machine_gun_projectile game_objects 35mmbullet_projectile
@@ -2505,6 +2599,7 @@ object GlobalDefinitions {
peregrine_dual_machine_gun_projectile.DegradeMultiplier = 0.65f
peregrine_dual_machine_gun_projectile.InitialVelocity = 250
peregrine_dual_machine_gun_projectile.Lifespan = 1.1f
+ ProjectileDefinition.CalculateDerivedFields(peregrine_dual_machine_gun_projectile)
peregrine_mechhammer_projectile.Name = "peregrine_mechhammer_projectile"
peregrine_mechhammer_projectile.Damage0 = 5
@@ -2515,6 +2610,7 @@ object GlobalDefinitions {
peregrine_mechhammer_projectile.ProjectileDamageType = DamageType.Direct
peregrine_mechhammer_projectile.InitialVelocity = 500
peregrine_mechhammer_projectile.Lifespan = 0.4f
+ ProjectileDefinition.CalculateDerivedFields(peregrine_mechhammer_projectile)
peregrine_particle_cannon_projectile.Name = "peregrine_particle_cannon_projectile"
peregrine_particle_cannon_projectile.Damage0 = 70
@@ -2527,6 +2623,7 @@ object GlobalDefinitions {
peregrine_particle_cannon_projectile.ProjectileDamageType = DamageType.Splash
peregrine_particle_cannon_projectile.InitialVelocity = 500
peregrine_particle_cannon_projectile.Lifespan = .6f
+ ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_projectile)
peregrine_rocket_pod_projectile.Name = "peregrine_rocket_pod_projectile"
peregrine_rocket_pod_projectile.Damage0 = 30
@@ -2541,6 +2638,7 @@ object GlobalDefinitions {
peregrine_rocket_pod_projectile.ProjectileDamageType = DamageType.Splash
peregrine_rocket_pod_projectile.InitialVelocity = 200
peregrine_rocket_pod_projectile.Lifespan = 1.85f
+ ProjectileDefinition.CalculateDerivedFields(peregrine_rocket_pod_projectile)
peregrine_sparrow_projectile.Name = "peregrine_sparrow_projectile"
// TODO for later, maybe : set_resource_parent peregrine_sparrow_projectile game_objects sparrow_projectile
@@ -2556,6 +2654,7 @@ object GlobalDefinitions {
peregrine_sparrow_projectile.ProjectileDamageType = DamageType.Splash
peregrine_sparrow_projectile.InitialVelocity = 45
peregrine_sparrow_projectile.Lifespan = 7.5f
+ ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
phalanx_av_projectile.Name = "phalanx_av_projectile"
phalanx_av_projectile.Damage0 = 60
@@ -2565,6 +2664,7 @@ object GlobalDefinitions {
phalanx_av_projectile.ProjectileDamageType = DamageType.Splash
phalanx_av_projectile.InitialVelocity = 100
phalanx_av_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(phalanx_av_projectile)
phalanx_flak_projectile.Name = "phalanx_flak_projectile"
phalanx_flak_projectile.Damage0 = 15
@@ -2576,6 +2676,7 @@ object GlobalDefinitions {
phalanx_flak_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
phalanx_flak_projectile.InitialVelocity = 100
phalanx_flak_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(phalanx_flak_projectile)
phalanx_projectile.Name = "phalanx_projectile"
phalanx_projectile.Damage0 = 20
@@ -2588,6 +2689,7 @@ object GlobalDefinitions {
phalanx_projectile.DegradeMultiplier = 0.25f
phalanx_projectile.InitialVelocity = 400
phalanx_projectile.Lifespan = 1f
+ ProjectileDefinition.CalculateDerivedFields(phalanx_projectile)
phoenix_missile_guided_projectile.Name = "phoenix_missile_guided_projectile"
// TODO for later, maybe : set_resource_parent phoenix_missile_guided_projectile game_objects phoenix_missile_projectile
@@ -2603,6 +2705,7 @@ object GlobalDefinitions {
phoenix_missile_guided_projectile.ProjectileDamageType = DamageType.Splash
phoenix_missile_guided_projectile.InitialVelocity = 0
phoenix_missile_guided_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
phoenix_missile_projectile.Name = "phoenix_missile_projectile"
phoenix_missile_projectile.Damage0 = 80
@@ -2617,6 +2720,7 @@ object GlobalDefinitions {
phoenix_missile_projectile.ProjectileDamageType = DamageType.Splash
phoenix_missile_projectile.InitialVelocity = 0
phoenix_missile_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(phoenix_missile_projectile)
plasma_cartridge_projectile.Name = "plasma_cartridge_projectile"
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile game_objects plasma_grenade_projectile
@@ -2627,6 +2731,7 @@ object GlobalDefinitions {
plasma_cartridge_projectile.ProjectileDamageType = DamageType.Aggravated
plasma_cartridge_projectile.InitialVelocity = 30
plasma_cartridge_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile)
plasma_cartridge_projectile_b.Name = "plasma_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile_b game_objects plasma_grenade_projectile_B
@@ -2637,6 +2742,7 @@ object GlobalDefinitions {
plasma_cartridge_projectile_b.ProjectileDamageType = DamageType.Aggravated
plasma_cartridge_projectile_b.InitialVelocity = 30
plasma_cartridge_projectile_b.Lifespan = 2f
+ ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile_b)
plasma_grenade_projectile.Name = "plasma_grenade_projectile"
plasma_grenade_projectile.Damage0 = 40
@@ -2646,6 +2752,7 @@ object GlobalDefinitions {
plasma_grenade_projectile.ProjectileDamageType = DamageType.Aggravated
plasma_grenade_projectile.InitialVelocity = 30
plasma_grenade_projectile.Lifespan = 15f
+ ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile)
plasma_grenade_projectile_B.Name = "plasma_grenade_projectile_B"
// TODO for later, maybe : set_resource_parent plasma_grenade_projectile_B game_objects plasma_grenade_projectile
@@ -2656,6 +2763,7 @@ object GlobalDefinitions {
plasma_grenade_projectile_B.ProjectileDamageType = DamageType.Aggravated
plasma_grenade_projectile_B.InitialVelocity = 30
plasma_grenade_projectile_B.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile_B)
pounder_projectile.Name = "pounder_projectile"
pounder_projectile.Damage0 = 31
@@ -2668,6 +2776,7 @@ object GlobalDefinitions {
pounder_projectile.ProjectileDamageType = DamageType.Splash
pounder_projectile.InitialVelocity = 120
pounder_projectile.Lifespan = 2.5f
+ ProjectileDefinition.CalculateDerivedFields(pounder_projectile)
pounder_projectile_enh.Name = "pounder_projectile_enh"
// TODO for later, maybe : set_resource_parent pounder_projectile_enh game_objects pounder_projectile
@@ -2681,6 +2790,7 @@ object GlobalDefinitions {
pounder_projectile_enh.ProjectileDamageType = DamageType.Splash
pounder_projectile_enh.InitialVelocity = 120
pounder_projectile_enh.Lifespan = 3.2f
+ ProjectileDefinition.CalculateDerivedFields(pounder_projectile_enh)
ppa_projectile.Name = "ppa_projectile"
ppa_projectile.Damage0 = 20
@@ -2691,6 +2801,7 @@ object GlobalDefinitions {
ppa_projectile.ProjectileDamageType = DamageType.Direct
ppa_projectile.InitialVelocity = 400
ppa_projectile.Lifespan = .5f
+ ProjectileDefinition.CalculateDerivedFields(ppa_projectile)
pulsar_ap_projectile.Name = "pulsar_ap_projectile"
// TODO for later, maybe : set_resource_parent pulsar_ap_projectile game_objects pulsar_projectile
@@ -2702,6 +2813,7 @@ object GlobalDefinitions {
pulsar_ap_projectile.InitialVelocity = 500
pulsar_ap_projectile.Lifespan = .4f
pulsar_ap_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(pulsar_ap_projectile)
pulsar_projectile.Name = "pulsar_projectile"
pulsar_projectile.Damage0 = 20
@@ -2712,6 +2824,7 @@ object GlobalDefinitions {
pulsar_projectile.InitialVelocity = 500
pulsar_projectile.Lifespan = .4f
pulsar_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(pulsar_projectile)
quasar_projectile.Name = "quasar_projectile"
quasar_projectile.Damage0 = 18
@@ -2721,17 +2834,20 @@ object GlobalDefinitions {
quasar_projectile.DegradeMultiplier = 0.5f
quasar_projectile.InitialVelocity = 500
quasar_projectile.Lifespan = .4f
+ ProjectileDefinition.CalculateDerivedFields(quasar_projectile)
radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ?
radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct
radiator_grenade_projectile.InitialVelocity = 30
radiator_grenade_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(radiator_grenade_projectile)
radiator_sticky_projectile.Name = "radiator_sticky_projectile"
// TODO for later, maybe : set_resource_parent radiator_sticky_projectile game_objects radiator_grenade_projectile
radiator_sticky_projectile.ProjectileDamageType = DamageType.Direct
radiator_sticky_projectile.InitialVelocity = 30
radiator_sticky_projectile.Lifespan = 4f
+ ProjectileDefinition.CalculateDerivedFields(radiator_sticky_projectile)
reaver_rocket_projectile.Name = "reaver_rocket_projectile"
reaver_rocket_projectile.Damage0 = 25
@@ -2746,6 +2862,7 @@ object GlobalDefinitions {
reaver_rocket_projectile.ProjectileDamageType = DamageType.Splash
reaver_rocket_projectile.InitialVelocity = 100
reaver_rocket_projectile.Lifespan = 2.1f
+ ProjectileDefinition.CalculateDerivedFields(reaver_rocket_projectile)
rocket_projectile.Name = "rocket_projectile"
rocket_projectile.Damage0 = 50
@@ -2760,6 +2877,7 @@ object GlobalDefinitions {
rocket_projectile.ProjectileDamageType = DamageType.Splash
rocket_projectile.InitialVelocity = 50
rocket_projectile.Lifespan = 8f
+ ProjectileDefinition.CalculateDerivedFields(rocket_projectile)
rocklet_flak_projectile.Name = "rocklet_flak_projectile"
rocklet_flak_projectile.Damage0 = 20
@@ -2773,6 +2891,7 @@ object GlobalDefinitions {
rocklet_flak_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
rocklet_flak_projectile.InitialVelocity = 60
rocklet_flak_projectile.Lifespan = 3.2f
+ ProjectileDefinition.CalculateDerivedFields(rocklet_flak_projectile)
rocklet_jammer_projectile.Name = "rocklet_jammer_projectile"
rocklet_jammer_projectile.Damage0 = 0
@@ -2783,6 +2902,7 @@ object GlobalDefinitions {
rocklet_jammer_projectile.ProjectileDamageType = DamageType.Splash
rocklet_jammer_projectile.InitialVelocity = 50
rocklet_jammer_projectile.Lifespan = 8f
+ ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile)
scattercannon_projectile.Name = "scattercannon_projectile"
scattercannon_projectile.Damage0 = 11
@@ -2790,6 +2910,7 @@ object GlobalDefinitions {
scattercannon_projectile.ProjectileDamageType = DamageType.Direct
scattercannon_projectile.InitialVelocity = 400
scattercannon_projectile.Lifespan = 0.25f
+ ProjectileDefinition.CalculateDerivedFields(scattercannon_projectile)
scythe_projectile.Name = "scythe_projectile"
scythe_projectile.Damage0 = 30
@@ -2799,10 +2920,12 @@ object GlobalDefinitions {
scythe_projectile.DegradeMultiplier = 0.35f
scythe_projectile.InitialVelocity = 60
scythe_projectile.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(scythe_projectile)
scythe_projectile_slave.Name = "scythe_projectile_slave" // Todo how does it work ?
scythe_projectile_slave.InitialVelocity = 30
scythe_projectile_slave.Lifespan = 3f
+ ProjectileDefinition.CalculateDerivedFields(scythe_projectile_slave)
shotgun_shell_AP_projectile.Name = "shotgun_shell_AP_projectile"
// TODO for later, maybe : set_resource_parent shotgun_shell_AP_projectile game_objects shotgun_shell_projectile
@@ -2812,6 +2935,7 @@ object GlobalDefinitions {
shotgun_shell_AP_projectile.InitialVelocity = 400
shotgun_shell_AP_projectile.Lifespan = 0.25f
shotgun_shell_AP_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(shotgun_shell_AP_projectile)
shotgun_shell_projectile.Name = "shotgun_shell_projectile"
shotgun_shell_projectile.Damage0 = 12
@@ -2820,6 +2944,7 @@ object GlobalDefinitions {
shotgun_shell_projectile.InitialVelocity = 400
shotgun_shell_projectile.Lifespan = 0.25f
shotgun_shell_projectile.UseDamage1Subtract = true
+ ProjectileDefinition.CalculateDerivedFields(shotgun_shell_projectile)
six_shooter_projectile.Name = "six_shooter_projectile"
// TODO for later, maybe : set_resource_parent six_shooter_projectile game_objects 9mmbullet_projectile
@@ -2831,6 +2956,7 @@ object GlobalDefinitions {
six_shooter_projectile.InitialVelocity = 500
six_shooter_projectile.Lifespan = 0.4f
six_shooter_projectile.UseDamage1Subtract = false
+ ProjectileDefinition.CalculateDerivedFields(six_shooter_projectile)
skyguard_flak_cannon_projectile.Name = "skyguard_flak_cannon_projectile"
skyguard_flak_cannon_projectile.Damage0 = 15
@@ -2842,6 +2968,7 @@ object GlobalDefinitions {
skyguard_flak_cannon_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
skyguard_flak_cannon_projectile.InitialVelocity = 100
skyguard_flak_cannon_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(skyguard_flak_cannon_projectile)
sparrow_projectile.Name = "sparrow_projectile"
sparrow_projectile.Damage0 = 35
@@ -2854,6 +2981,7 @@ object GlobalDefinitions {
sparrow_projectile.ProjectileDamageType = DamageType.Splash
sparrow_projectile.InitialVelocity = 60
sparrow_projectile.Lifespan = 5.85f
+ ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
sparrow_secondary_projectile.Name = "sparrow_secondary_projectile"
// TODO for later, maybe : set_resource_parent sparrow_secondary_projectile game_objects sparrow_projectile
@@ -2867,6 +2995,7 @@ object GlobalDefinitions {
sparrow_secondary_projectile.ProjectileDamageType = DamageType.Splash
sparrow_secondary_projectile.InitialVelocity = 60
sparrow_secondary_projectile.Lifespan = 5.85f
+ ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
spiker_projectile.Name = "spiker_projectile"
// spiker_projectile.Damage0 = 75
@@ -2881,6 +3010,7 @@ object GlobalDefinitions {
spiker_projectile.ProjectileDamageType = DamageType.Splash
spiker_projectile.InitialVelocity = 40
spiker_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
spitfire_aa_ammo_projectile.Damage0 = 5
@@ -2894,6 +3024,7 @@ object GlobalDefinitions {
spitfire_aa_ammo_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
spitfire_aa_ammo_projectile.InitialVelocity = 100
spitfire_aa_ammo_projectile.Lifespan = 5f
+ ProjectileDefinition.CalculateDerivedFields(spitfire_aa_ammo_projectile)
spitfire_ammo_projectile.Name = "spitfire_ammo_projectile"
spitfire_ammo_projectile.Damage0 = 15
@@ -2903,6 +3034,7 @@ object GlobalDefinitions {
spitfire_ammo_projectile.DegradeMultiplier = 0.5f
spitfire_ammo_projectile.InitialVelocity = 100
spitfire_ammo_projectile.Lifespan = .5f
+ ProjectileDefinition.CalculateDerivedFields(spitfire_ammo_projectile)
starfire_projectile.Name = "starfire_projectile"
starfire_projectile.Damage0 = 16
@@ -2913,6 +3045,7 @@ object GlobalDefinitions {
starfire_projectile.ProjectileDamageType = DamageType.Aggravated
starfire_projectile.InitialVelocity = 45
starfire_projectile.Lifespan = 7.8f
+ ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
striker_missile_projectile.Name = "striker_missile_projectile"
striker_missile_projectile.Damage0 = 35
@@ -2927,6 +3060,7 @@ object GlobalDefinitions {
striker_missile_projectile.ProjectileDamageType = DamageType.Splash
striker_missile_projectile.InitialVelocity = 30
striker_missile_projectile.Lifespan = 4.2f
+ ProjectileDefinition.CalculateDerivedFields(striker_missile_projectile)
striker_missile_targeting_projectile.Name = "striker_missile_targeting_projectile"
// TODO for later, maybe : set_resource_parent striker_missile_targeting_projectile game_objects striker_missile_projectile
@@ -2942,6 +3076,7 @@ object GlobalDefinitions {
striker_missile_targeting_projectile.ProjectileDamageType = DamageType.Splash
striker_missile_targeting_projectile.InitialVelocity = 30
striker_missile_targeting_projectile.Lifespan = 4.2f
+ ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile)
trek_projectile.Name = "trek_projectile"
trek_projectile.Damage0 = 0
@@ -2954,6 +3089,7 @@ object GlobalDefinitions {
trek_projectile.ProjectileDamageType = DamageType.Direct
trek_projectile.InitialVelocity = 40
trek_projectile.Lifespan = 7f
+ ProjectileDefinition.CalculateDerivedFields(trek_projectile)
vanu_sentry_turret_projectile.Name = "vanu_sentry_turret_projectile"
vanu_sentry_turret_projectile.Damage0 = 25
@@ -2964,6 +3100,7 @@ object GlobalDefinitions {
vanu_sentry_turret_projectile.ProjectileDamageType = DamageType.Splash
vanu_sentry_turret_projectile.InitialVelocity = 240
vanu_sentry_turret_projectile.Lifespan = 1.3f
+ ProjectileDefinition.CalculateDerivedFields(vanu_sentry_turret_projectile)
vulture_bomb_projectile.Name = "vulture_bomb_projectile"
vulture_bomb_projectile.Damage0 = 175
@@ -2976,6 +3113,7 @@ object GlobalDefinitions {
vulture_bomb_projectile.ProjectileDamageType = DamageType.Splash
vulture_bomb_projectile.InitialVelocity = 0
vulture_bomb_projectile.Lifespan = 30f
+ ProjectileDefinition.CalculateDerivedFields(vulture_bomb_projectile)
vulture_nose_bullet_projectile.Name = "vulture_nose_bullet_projectile"
vulture_nose_bullet_projectile.Damage0 = 12
@@ -2988,6 +3126,7 @@ object GlobalDefinitions {
vulture_nose_bullet_projectile.DegradeMultiplier = 0.7f
vulture_nose_bullet_projectile.InitialVelocity = 500
vulture_nose_bullet_projectile.Lifespan = 0.46f
+ ProjectileDefinition.CalculateDerivedFields(vulture_nose_bullet_projectile)
vulture_tail_bullet_projectile.Name = "vulture_tail_bullet_projectile"
vulture_tail_bullet_projectile.Damage0 = 25
@@ -2998,6 +3137,7 @@ object GlobalDefinitions {
vulture_tail_bullet_projectile.DegradeMultiplier = 0.5f
vulture_tail_bullet_projectile.InitialVelocity = 500
vulture_tail_bullet_projectile.Lifespan = 0.6f
+ ProjectileDefinition.CalculateDerivedFields(vulture_tail_bullet_projectile)
wasp_gun_projectile.Name = "wasp_gun_projectile"
wasp_gun_projectile.Damage0 = 10
@@ -3010,6 +3150,7 @@ object GlobalDefinitions {
wasp_gun_projectile.DegradeMultiplier = 0.5f
wasp_gun_projectile.InitialVelocity = 500
wasp_gun_projectile.Lifespan = 0.5f
+ ProjectileDefinition.CalculateDerivedFields(wasp_gun_projectile)
wasp_rocket_projectile.Name = "wasp_rocket_projectile"
wasp_rocket_projectile.Damage0 = 35
@@ -3022,6 +3163,7 @@ object GlobalDefinitions {
wasp_rocket_projectile.ProjectileDamageType = DamageType.Splash
wasp_rocket_projectile.InitialVelocity = 60
wasp_rocket_projectile.Lifespan = 6.5f
+ ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
winchester_projectile.Name = "winchester_projectile"
// TODO for later, maybe : set_resource_parent winchester_projectile game_objects bolt_projectile
@@ -3033,6 +3175,7 @@ object GlobalDefinitions {
winchester_projectile.ProjectileDamageType = DamageType.Direct
winchester_projectile.InitialVelocity = 500
winchester_projectile.Lifespan = 0.6f
+ ProjectileDefinition.CalculateDerivedFields(winchester_projectile)
}
/**
@@ -4370,6 +4513,8 @@ object GlobalDefinitions {
*/
private def init_vehicles() : Unit = {
fury.Name = "fury"
+ fury.MaxHealth = 650
+ fury.MaxShields = 130 + 1
fury.Seats += 0 -> new SeatDefinition()
fury.Seats(0).Bailable = true
fury.Seats(0).ControlledWeapon = 1
@@ -4379,8 +4524,11 @@ object GlobalDefinitions {
fury.TrunkSize = InventoryTile.Tile1111
fury.TrunkOffset = 30
fury.AutoPilotSpeeds = (24, 10)
+ fury.DestroyedModel = Some(DestroyedVehicle.QuadAssault)
quadassault.Name = "quadassault"
+ quadassault.MaxHealth = 650
+ quadassault.MaxShields = 130 + 1
quadassault.Seats += 0 -> new SeatDefinition()
quadassault.Seats(0).Bailable = true
quadassault.Seats(0).ControlledWeapon = 1
@@ -4390,8 +4538,11 @@ object GlobalDefinitions {
quadassault.TrunkSize = InventoryTile.Tile1111
quadassault.TrunkOffset = 30
quadassault.AutoPilotSpeeds = (24, 10)
+ quadassault.DestroyedModel = Some(DestroyedVehicle.QuadAssault)
quadstealth.Name = "quadstealth"
+ quadstealth.MaxHealth = 650
+ quadstealth.MaxShields = 130 + 1
quadstealth.CanCloak = true
quadstealth.Seats += 0 -> new SeatDefinition()
quadstealth.Seats(0).Bailable = true
@@ -4401,8 +4552,11 @@ object GlobalDefinitions {
quadstealth.TrunkSize = InventoryTile.Tile1111
quadstealth.TrunkOffset = 30
quadstealth.AutoPilotSpeeds = (24, 10)
+ quadstealth.DestroyedModel = Some(DestroyedVehicle.QuadStealth)
two_man_assault_buggy.Name = "two_man_assault_buggy"
+ two_man_assault_buggy.MaxHealth = 1250
+ two_man_assault_buggy.MaxShields = 250 + 1
two_man_assault_buggy.Seats += 0 -> new SeatDefinition()
two_man_assault_buggy.Seats(0).Bailable = true
two_man_assault_buggy.Seats += 1 -> new SeatDefinition()
@@ -4414,8 +4568,11 @@ object GlobalDefinitions {
two_man_assault_buggy.TrunkSize = InventoryTile.Tile1511
two_man_assault_buggy.TrunkOffset = 30
two_man_assault_buggy.AutoPilotSpeeds = (22, 8)
+ two_man_assault_buggy.DestroyedModel = Some(DestroyedVehicle.TwoManAssaultBuggy)
skyguard.Name = "skyguard"
+ skyguard.MaxHealth = 1000
+ skyguard.MaxShields = 200 + 1
skyguard.Seats += 0 -> new SeatDefinition()
skyguard.Seats(0).Bailable = true
skyguard.Seats += 1 -> new SeatDefinition()
@@ -4428,8 +4585,11 @@ object GlobalDefinitions {
skyguard.TrunkSize = InventoryTile.Tile1511
skyguard.TrunkOffset = 30
skyguard.AutoPilotSpeeds = (22, 8)
+ skyguard.DestroyedModel = Some(DestroyedVehicle.Skyguard)
threemanheavybuggy.Name = "threemanheavybuggy"
+ threemanheavybuggy.MaxHealth = 1700
+ threemanheavybuggy.MaxShields = 340 + 1
threemanheavybuggy.Seats += 0 -> new SeatDefinition()
threemanheavybuggy.Seats(0).Bailable = true
threemanheavybuggy.Seats += 1 -> new SeatDefinition()
@@ -4446,8 +4606,11 @@ object GlobalDefinitions {
threemanheavybuggy.TrunkSize = InventoryTile.Tile1511
threemanheavybuggy.TrunkOffset = 30
threemanheavybuggy.AutoPilotSpeeds = (22, 8)
+ threemanheavybuggy.DestroyedModel = Some(DestroyedVehicle.ThreeManHeavyBuggy)
twomanheavybuggy.Name = "twomanheavybuggy"
+ twomanheavybuggy.MaxHealth = 1800
+ twomanheavybuggy.MaxShields = 360 + 1
twomanheavybuggy.Seats += 0 -> new SeatDefinition()
twomanheavybuggy.Seats(0).Bailable = true
twomanheavybuggy.Seats += 1 -> new SeatDefinition()
@@ -4459,8 +4622,11 @@ object GlobalDefinitions {
twomanheavybuggy.TrunkSize = InventoryTile.Tile1511
twomanheavybuggy.TrunkOffset = 30
twomanheavybuggy.AutoPilotSpeeds = (22, 8)
+ twomanheavybuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHeavyBuggy)
twomanhoverbuggy.Name = "twomanhoverbuggy"
+ twomanhoverbuggy.MaxHealth = 1600
+ twomanhoverbuggy.MaxShields = 320 + 1
twomanhoverbuggy.Seats += 0 -> new SeatDefinition()
twomanhoverbuggy.Seats(0).Bailable = true
twomanhoverbuggy.Seats += 1 -> new SeatDefinition()
@@ -4472,8 +4638,11 @@ object GlobalDefinitions {
twomanhoverbuggy.TrunkSize = InventoryTile.Tile1511
twomanhoverbuggy.TrunkOffset = 30
twomanhoverbuggy.AutoPilotSpeeds = (22, 10)
+ twomanhoverbuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHoverBuggy)
mediumtransport.Name = "mediumtransport"
+ mediumtransport.MaxHealth = 2500
+ mediumtransport.MaxShields = 500 + 1
mediumtransport.Seats += 0 -> new SeatDefinition()
mediumtransport.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
mediumtransport.Seats += 1 -> new SeatDefinition()
@@ -4492,8 +4661,11 @@ object GlobalDefinitions {
mediumtransport.TrunkSize = InventoryTile.Tile1515
mediumtransport.TrunkOffset = 30
mediumtransport.AutoPilotSpeeds = (18, 6)
+ mediumtransport.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
battlewagon.Name = "battlewagon"
+ battlewagon.MaxHealth = 2500
+ battlewagon.MaxShields = 500 + 1
battlewagon.Seats += 0 -> new SeatDefinition()
battlewagon.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
battlewagon.Seats += 1 -> new SeatDefinition()
@@ -4516,8 +4688,11 @@ object GlobalDefinitions {
battlewagon.TrunkSize = InventoryTile.Tile1515
battlewagon.TrunkOffset = 30
battlewagon.AutoPilotSpeeds = (18, 6)
+ battlewagon.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
thunderer.Name = "thunderer"
+ thunderer.MaxHealth = 2500
+ thunderer.MaxShields = 500 + 1
thunderer.Seats += 0 -> new SeatDefinition()
thunderer.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
thunderer.Seats += 1 -> new SeatDefinition()
@@ -4536,8 +4711,11 @@ object GlobalDefinitions {
thunderer.TrunkSize = InventoryTile.Tile1515
thunderer.TrunkOffset = 30
thunderer.AutoPilotSpeeds = (18, 6)
+ thunderer.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
aurora.Name = "aurora"
+ aurora.MaxHealth = 2500
+ aurora.MaxShields = 500 + 1
aurora.Seats += 0 -> new SeatDefinition()
aurora.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
aurora.Seats += 1 -> new SeatDefinition()
@@ -4556,8 +4734,11 @@ object GlobalDefinitions {
aurora.TrunkSize = InventoryTile.Tile1515
aurora.TrunkOffset = 30
aurora.AutoPilotSpeeds = (18, 6)
+ aurora.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
apc_tr.Name = "apc_tr"
+ apc_tr.MaxHealth = 6000
+ apc_tr.MaxShields = 1200 + 1
apc_tr.Seats += 0 -> new SeatDefinition()
apc_tr.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_tr.Seats += 1 -> new SeatDefinition()
@@ -4599,8 +4780,11 @@ object GlobalDefinitions {
apc_tr.TrunkSize = InventoryTile.Tile2016
apc_tr.TrunkOffset = 30
apc_tr.AutoPilotSpeeds = (16, 6)
+ apc_tr.DestroyedModel = Some(DestroyedVehicle.Apc)
apc_nc.Name = "apc_nc"
+ apc_nc.MaxHealth = 6000
+ apc_nc.MaxShields = 1200 + 1
apc_nc.Seats += 0 -> new SeatDefinition()
apc_nc.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_nc.Seats += 1 -> new SeatDefinition()
@@ -4642,8 +4826,11 @@ object GlobalDefinitions {
apc_nc.TrunkSize = InventoryTile.Tile2016
apc_nc.TrunkOffset = 30
apc_nc.AutoPilotSpeeds = (16, 6)
+ apc_nc.DestroyedModel = Some(DestroyedVehicle.Apc)
apc_vs.Name = "apc_vs"
+ apc_vs.MaxHealth = 6000
+ apc_vs.MaxShields = 1200 + 1
apc_vs.Seats += 0 -> new SeatDefinition()
apc_vs.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
apc_vs.Seats += 1 -> new SeatDefinition()
@@ -4685,8 +4872,11 @@ object GlobalDefinitions {
apc_vs.TrunkSize = InventoryTile.Tile2016
apc_vs.TrunkOffset = 30
apc_vs.AutoPilotSpeeds = (16, 6)
+ apc_vs.DestroyedModel = Some(DestroyedVehicle.Apc)
lightning.Name = "lightning"
+ lightning.MaxHealth = 2000
+ lightning.MaxShields = 400 + 1
lightning.Seats += 0 -> new SeatDefinition()
lightning.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
lightning.Seats(0).ControlledWeapon = 1
@@ -4696,8 +4886,11 @@ object GlobalDefinitions {
lightning.TrunkSize = InventoryTile.Tile1511
lightning.TrunkOffset = 30
lightning.AutoPilotSpeeds = (20, 8)
+ lightning.DestroyedModel = Some(DestroyedVehicle.Lightning)
prowler.Name = "prowler"
+ prowler.MaxHealth = 4800
+ prowler.MaxShields = 960 + 1
prowler.Seats += 0 -> new SeatDefinition()
prowler.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
prowler.Seats += 1 -> new SeatDefinition()
@@ -4712,8 +4905,11 @@ object GlobalDefinitions {
prowler.TrunkSize = InventoryTile.Tile1511
prowler.TrunkOffset = 30
prowler.AutoPilotSpeeds = (14, 6)
+ prowler.DestroyedModel = Some(DestroyedVehicle.Prowler)
vanguard.Name = "vanguard"
+ vanguard.MaxHealth = 5400
+ vanguard.MaxShields = 1080 + 1
vanguard.Seats += 0 -> new SeatDefinition()
vanguard.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
vanguard.Seats += 1 -> new SeatDefinition()
@@ -4724,8 +4920,11 @@ object GlobalDefinitions {
vanguard.TrunkSize = InventoryTile.Tile1511
vanguard.TrunkOffset = 30
vanguard.AutoPilotSpeeds = (16, 6)
+ vanguard.DestroyedModel = Some(DestroyedVehicle.Vanguard)
magrider.Name = "magrider"
+ magrider.MaxHealth = 4200
+ magrider.MaxShields = 840 + 1
magrider.Seats += 0 -> new SeatDefinition()
magrider.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
magrider.Seats(0).ControlledWeapon = 2
@@ -4738,9 +4937,12 @@ object GlobalDefinitions {
magrider.TrunkSize = InventoryTile.Tile1511
magrider.TrunkOffset = 30
magrider.AutoPilotSpeeds = (18, 6)
+ magrider.DestroyedModel = Some(DestroyedVehicle.Magrider)
val utilityConverter = new UtilityVehicleConverter
ant.Name = "ant"
+ ant.MaxHealth = 2000
+ ant.MaxShields = 400 + 1
ant.Seats += 0 -> new SeatDefinition()
ant.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ant.MountPoints += 1 -> 0
@@ -4751,8 +4953,11 @@ object GlobalDefinitions {
ant.AutoPilotSpeeds = (18, 6)
ant.MaximumCapacitor = 1500
ant.Packet = utilityConverter
+ ant.DestroyedModel = Some(DestroyedVehicle.Ant)
ams.Name = "ams"
+ ams.MaxHealth = 3000
+ ams.MaxShields = 600 + 1
ams.Seats += 0 -> new SeatDefinition()
ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ams.MountPoints += 1 -> 0
@@ -4767,9 +4972,12 @@ object GlobalDefinitions {
ams.DeconstructionTime = Some(20 minutes)
ams.AutoPilotSpeeds = (18, 6)
ams.Packet = utilityConverter
+ ams.DestroyedModel = Some(DestroyedVehicle.Ams)
val variantConverter = new VariantVehicleConverter
router.Name = "router"
+ router.MaxHealth = 4000
+ router.MaxShields = 800 + 1
router.Seats += 0 -> new SeatDefinition()
router.MountPoints += 1 -> 0
router.TrunkSize = InventoryTile.Tile1511
@@ -4780,8 +4988,11 @@ object GlobalDefinitions {
router.DeconstructionTime = Duration(20, "minutes")
router.AutoPilotSpeeds = (16, 6)
router.Packet = variantConverter
+ router.DestroyedModel = Some(DestroyedVehicle.Router)
switchblade.Name = "switchblade"
+ switchblade.MaxHealth = 1750
+ switchblade.MaxShields = 350 + 1
switchblade.Seats += 0 -> new SeatDefinition()
switchblade.Seats(0).ControlledWeapon = 1
switchblade.Weapons += 1 -> scythe
@@ -4794,8 +5005,11 @@ object GlobalDefinitions {
switchblade.UndeployTime = 2000
switchblade.AutoPilotSpeeds = (22, 8)
switchblade.Packet = variantConverter
+ switchblade.DestroyedModel = Some(DestroyedVehicle.Switchblade)
flail.Name = "flail"
+ flail.MaxHealth = 2400
+ flail.MaxShields = 480 + 1
flail.Seats += 0 -> new SeatDefinition()
flail.Seats(0).ControlledWeapon = 1
flail.Weapons += 1 -> flail_weapon
@@ -4807,8 +5021,11 @@ object GlobalDefinitions {
flail.UndeployTime = 2000
flail.AutoPilotSpeeds = (14, 6)
flail.Packet = variantConverter
+ flail.DestroyedModel = Some(DestroyedVehicle.Flail)
mosquito.Name = "mosquito"
+ mosquito.MaxHealth = 665
+ mosquito.MaxShields = 133 + 1
mosquito.Seats += 0 -> new SeatDefinition()
mosquito.Seats(0).Bailable = true
mosquito.Seats(0).ControlledWeapon = 1
@@ -4819,8 +5036,11 @@ object GlobalDefinitions {
mosquito.TrunkOffset = 30
mosquito.AutoPilotSpeeds = (0, 6)
mosquito.Packet = variantConverter
+ mosquito.DestroyedModel = Some(DestroyedVehicle.Mosquito)
lightgunship.Name = "lightgunship"
+ lightgunship.MaxHealth = 1000
+ lightgunship.MaxShields = 200 + 1
lightgunship.Seats += 0 -> new SeatDefinition()
lightgunship.Seats(0).Bailable = true
lightgunship.Seats(0).ControlledWeapon = 1
@@ -4831,8 +5051,11 @@ object GlobalDefinitions {
lightgunship.TrunkOffset = 30
lightgunship.AutoPilotSpeeds = (0, 4)
lightgunship.Packet = variantConverter
+ lightgunship.DestroyedModel = Some(DestroyedVehicle.LightGunship)
wasp.Name = "wasp"
+ wasp.MaxHealth = 515
+ wasp.MaxShields = 103 + 1
wasp.Seats += 0 -> new SeatDefinition()
wasp.Seats(0).Bailable = true
wasp.Seats(0).ControlledWeapon = 1
@@ -4843,8 +5066,11 @@ object GlobalDefinitions {
wasp.TrunkOffset = 30
wasp.AutoPilotSpeeds = (0, 6)
wasp.Packet = variantConverter
+ wasp.DestroyedModel = Some(DestroyedVehicle.Mosquito) //set_resource_parent wasp game_objects mosquito
liberator.Name = "liberator"
+ liberator.MaxHealth = 2500
+ liberator.MaxShields = 500 + 1
liberator.Seats += 0 -> new SeatDefinition()
liberator.Seats(0).ControlledWeapon = 3
liberator.Seats += 1 -> new SeatDefinition()
@@ -4862,8 +5088,11 @@ object GlobalDefinitions {
liberator.TrunkOffset = 30
liberator.AutoPilotSpeeds = (0, 4)
liberator.Packet = variantConverter
+ liberator.DestroyedModel = Some(DestroyedVehicle.Liberator)
vulture.Name = "vulture"
+ vulture.MaxHealth = 2500
+ vulture.MaxShields = 500 + 1
vulture.Seats += 0 -> new SeatDefinition()
vulture.Seats(0).ControlledWeapon = 3
vulture.Seats += 1 -> new SeatDefinition()
@@ -4881,8 +5110,11 @@ object GlobalDefinitions {
vulture.TrunkOffset = 30
vulture.AutoPilotSpeeds = (0, 4)
vulture.Packet = variantConverter
+ vulture.DestroyedModel = Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
dropship.Name = "dropship"
+ dropship.MaxHealth = 5000
+ dropship.MaxShields = 1000 + 1
dropship.Seats += 0 -> new SeatDefinition()
dropship.Seats += 1 -> new SeatDefinition()
dropship.Seats(1).Bailable = true
@@ -4932,8 +5164,11 @@ object GlobalDefinitions {
dropship.TrunkOffset = 30
dropship.AutoPilotSpeeds = (0, 4)
dropship.Packet = variantConverter
+ dropship.DestroyedModel = Some(DestroyedVehicle.Dropship)
galaxy_gunship.Name = "galaxy_gunship"
+ galaxy_gunship.MaxHealth = 6000
+ galaxy_gunship.MaxShields = 1200 + 1
galaxy_gunship.Seats += 0 -> new SeatDefinition()
galaxy_gunship.Seats += 1 -> new SeatDefinition()
galaxy_gunship.Seats(1).ControlledWeapon = 6
@@ -4960,8 +5195,11 @@ object GlobalDefinitions {
galaxy_gunship.TrunkOffset = 30
galaxy_gunship.AutoPilotSpeeds = (0, 4)
galaxy_gunship.Packet = variantConverter
+ galaxy_gunship.DestroyedModel = Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists
lodestar.Name = "lodestar"
+ lodestar.MaxHealth = 5000
+ lodestar.MaxShields = 1000 + 1
lodestar.Seats += 0 -> new SeatDefinition()
lodestar.MountPoints += 1 -> 0
lodestar.MountPoints += 2 -> 1
@@ -4970,8 +5208,11 @@ object GlobalDefinitions {
lodestar.TrunkOffset = 30
lodestar.AutoPilotSpeeds = (0, 4)
lodestar.Packet = variantConverter
+ lodestar.DestroyedModel = Some(DestroyedVehicle.Lodestar)
phantasm.Name = "phantasm"
+ phantasm.MaxHealth = 2500
+ phantasm.MaxShields = 500 + 1
phantasm.CanCloak = true
phantasm.Seats += 0 -> new SeatDefinition()
phantasm.Seats += 1 -> new SeatDefinition()
@@ -4991,5 +5232,6 @@ object GlobalDefinitions {
phantasm.TrunkOffset = 30
phantasm.AutoPilotSpeeds = (0, 6)
phantasm.Packet = variantConverter
+ phantasm.DestroyedModel = None //the adb calls out a phantasm_destroyed but no such asset exists
}
}
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 71398c5ed..8586b0a0a 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -6,13 +6,19 @@ import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.loadouts.Loadout
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.vital.resistance.ResistanceProfile
+import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._
import scala.annotation.tailrec
import scala.util.{Success, Try}
-class Player(private val core : Avatar) extends PlanetSideGameObject with FactionAffinity with Container {
+class Player(private val core : Avatar) extends PlanetSideGameObject
+ with FactionAffinity
+ with Vitality
+ with ResistanceProfile
+ with Container {
private var alive : Boolean = false
private var backpack : Boolean = false
private var health : Int = 0
@@ -42,7 +48,6 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
//SouNourS things
/** Last medkituse. */
var lastMedkit : Long = 0
- var death_by : Int = 0
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time : Int = -1
/** From PlanetsideAttributeMessage */
@@ -276,6 +281,14 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
ChangeSpecialAbility()
}
+ def ResistanceDirectHit = exosuit.ResistanceDirectHit
+
+ def ResistanceSplash = exosuit.ResistanceSplash
+
+ def ResistanceAggravated = exosuit.ResistanceAggravated
+
+ def RadiationShielding = exosuit.RadiationShielding
+
def LoadLoadout(line : Int) : Option[Loadout] = core.LoadLoadout(line)
def BEP : Long = core.BEP
@@ -475,6 +488,8 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
Continent
}
+ def DamageModel = exosuit.asInstanceOf[DamageResistanceModel]
+
def Definition : AvatarDefinition = core.Definition
def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index 3d56eb4c1..9306468dd 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.vehicles._
+import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
@@ -67,6 +68,8 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
with Mountable
with MountedWeapons
with Deployment
+ with Vitality
+ with StandardResistanceProfile
with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
@@ -142,7 +145,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
owner match {
case Some(_) =>
- if(Definition.CanBeOwned) { //e.g., base turrets
+ if(Definition.CanBeOwned) {
this.owner = owner
}
case None =>
@@ -152,11 +155,11 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
def Health : Int = {
- this.health
+ health
}
- def Health_=(health : Int) : Int = {
- this.health = health
+ def Health_=(assignHealth : Int) : Int = {
+ health = math.min(math.max(0, assignHealth), MaxHealth)
health
}
@@ -165,11 +168,11 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
def Shields : Int = {
- this.shields
+ shields
}
def Shields_=(strength : Int) : Int = {
- this.shields = strength
+ shields = math.min(math.max(0, strength), MaxShields)
Shields
}
@@ -178,12 +181,12 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
def Decal : Int = {
- this.decal
+ decal
}
- def Decal_=(decal : Int) : Int = {
- this.decal = decal
- decal
+ def Decal_=(logo : Int) : Int = {
+ decal = logo
+ Decal
}
def Jammered : Boolean = jammered
@@ -493,6 +496,8 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
*/
def TrunkLockState : VehicleLockState.Value = groupPermissions(3)
+ def DamageModel = Definition.asInstanceOf[DamageResistanceModel]
+
/**
* This is the definition entry that is used to store and unload pertinent information about the `Vehicle`.
* @return the vehicle's definition entry
@@ -534,6 +539,20 @@ object Vehicle {
*/
final case class Reactivate()
+ /**
+ * A request has been made to charge this vehicle's shields.
+ * @see `FacilityBenefitShieldChargeRequestMessage`
+ * @param amount the number of points to charge
+ */
+ final case class ChargeShields(amount : Int)
+
+ /**
+ * Following a successful shield charge tick, display the results of the update.
+ * @see `FacilityBenefitShieldChargeRequestMessage`
+ * @param vehicle the updated vehicle
+ */
+ final case class UpdateShieldsCharge(vehicle : Vehicle)
+
/**
* Overloaded constructor.
* @param vehicleDef the vehicle's definition entry
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala
new file mode 100644
index 000000000..e73365df0
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ObjectSource.scala
@@ -0,0 +1,31 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+
+final case class ObjectSource(obj : PlanetSideGameObject with FactionAffinity,
+ faction : PlanetSideEmpire.Value,
+ position : Vector3,
+ orientation : Vector3,
+ velocity : Option[Vector3] = None) extends SourceEntry {
+ override def Name = SourceEntry.NameFormat(obj.Definition.Name)
+ override def Faction = faction
+ def Definition = obj.Definition
+ def Position = position
+ def Orientation = orientation
+ def Velocity = velocity
+}
+
+object ObjectSource {
+ def apply(obj : PlanetSideGameObject with FactionAffinity) : ObjectSource = {
+ ObjectSource(
+ obj,
+ obj.Faction,
+ obj.Position,
+ obj.Orientation,
+ obj.Velocity
+ )
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
new file mode 100644
index 000000000..495c01365
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
@@ -0,0 +1,35 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.Player
+import net.psforever.objects.definition.ObjectDefinition
+import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
+
+final case class PlayerSource(name : String,
+ obj_def : ObjectDefinition,
+ faction : PlanetSideEmpire.Value,
+ exosuit : ExoSuitType.Value,
+ seated : Boolean,
+ health : Int,
+ armor : Int,
+ position : Vector3,
+ orientation : Vector3,
+ velocity : Option[Vector3] = None) extends SourceEntry {
+ override def Name = name
+ override def Faction = faction
+ def Definition = obj_def
+ def ExoSuit = exosuit
+ def Seated = seated
+ def Health = health
+ def Armor = armor
+ def Position = position
+ def Orientation = orientation
+ def Velocity = velocity
+}
+
+object PlayerSource {
+ def apply(tplayer : Player) : PlayerSource = {
+ PlayerSource(tplayer.Name, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty,
+ tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
index 790a4227d..fdc9ad8e9 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
@@ -1,60 +1,59 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
+import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
import net.psforever.objects.entity.SimpleWorldEntity
+import net.psforever.objects.equipment.FireModeDefinition
+import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.types.Vector3
/**
* A summation of weapon (`Tool`) discharge.
+ * @see `ProjectileDefinition`
+ * `ToolDefinition`
+ * `FireModeDefinition`
+ * `SourceEntry`
+ * `PlayerSource`
* @param profile an explanation of the damage that can be performed by this discharge
* @param tool_def the weapon that caused this discharge
+ * @param fire_mode the current fire mode of the tool used
+ * @param owner the agency that caused the weapon to produce this projectile;
+ * most often a player (`PlayerSource`)
+ * @param attribute_to an object ID that refers to the method of death that would be reported;
+ * usually the same as `tool_def.ObjectId`;
+ * 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 resolution whether this projectile has encountered a target or wall;
- * defaults to `Unresolved`
* @param fire_time when the weapon discharged was recorded;
* defaults to `System.nanoTime`
- * @param hit_time when the discharge had its resolution status updated
*/
final case class Projectile(profile : ProjectileDefinition,
tool_def : ToolDefinition,
+ fire_mode : FireModeDefinition,
+ owner : SourceEntry,
+ attribute_to : Int,
shot_origin : Vector3,
shot_angle : Vector3,
- resolution : ProjectileResolution.Value,
- fire_time : Long = System.nanoTime,
- hit_time : Long = 0) {
+ fire_time: Long = System.nanoTime) {
/** Information about the current world coordinates and orientation of the projectile */
val current : SimpleWorldEntity = new SimpleWorldEntity()
+ private var resolved : ProjectileResolution.Value = ProjectileResolution.Unresolved
/**
- * Give the projectile the suggested resolution status.
- * Update the world coordinates and orientation.
- * @param pos the current position
- * @param ang the current orientation
- * @param resolution the resolution status
- * @return a new projectile with the suggested resolution status, or the original projectile
+ * Mark the projectile as being "encountered" or "managed" at least once.
*/
- def Resolve(pos : Vector3, ang : Vector3, resolution : ProjectileResolution.Value) : Projectile = {
- val obj = Resolve(resolution)
- obj.current.Position = pos
- obj.current.Orientation = ang
- obj
+ def Resolve() : Unit = {
+ resolved = ProjectileResolution.Resolved
}
- /**
- * Give the projectile the suggested resolution status.
- * @param resolution the resolution status
- * @return a new projectile with the suggested resolution status, or the original projectile
- */
- def Resolve(resolution : ProjectileResolution.Value) : Projectile = {
- resolution match {
- case ProjectileResolution.Unresolved =>
- this
- case _ =>
- Projectile(profile, tool_def, shot_origin, shot_angle, resolution, fire_time, System.nanoTime)
- }
+ def Miss() : Unit = {
+ resolved = ProjectileResolution.MissedShot
}
+
+ def isResolved : Boolean = resolved == ProjectileResolution.Resolved || resolved == ProjectileResolution.MissedShot
+
+ def isMiss : Boolean = resolved == ProjectileResolution.MissedShot
}
object Projectile {
@@ -68,11 +67,28 @@ object Projectile {
* Overloaded constructor for an `Unresolved` projectile.
* @param profile an explanation of the damage that can be performed by this discharge
* @param tool_def the weapon that caused this discharge
+ * @param fire_mode the current fire mode of the tool used
+ * @param owner the agency that caused the weapon to produce this projectile
* @param shot_origin where the projectile started
* @param shot_angle in which direction the projectile was aimed when it was discharged
* @return the `Projectile` object
*/
- def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, shot_origin : Vector3, shot_angle : Vector3) : Projectile = {
- Projectile(profile, tool_def, shot_origin, shot_angle, ProjectileResolution.Unresolved)
+ def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, fire_mode : FireModeDefinition, owner : PlanetSideGameObject with FactionAffinity, shot_origin : Vector3, shot_angle : Vector3) : Projectile = {
+ Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle)
+ }
+
+ /**
+ * Overloaded constructor for an `Unresolved` projectile.
+ * @param profile an explanation of the damage that can be performed by this discharge
+ * @param tool_def the weapon that caused this discharge
+ * @param fire_mode the current fire mode of the tool used
+ * @param owner the agency that caused the weapon to produce this projectile
+ * @param attribute_to an object ID that refers to the method of death that would be reported
+ * @param shot_origin where the projectile started
+ * @param shot_angle in which direction the projectile was aimed when it was discharged
+ * @return the `Projectile` object
+ */
+ def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, fire_mode : FireModeDefinition, owner : PlanetSideGameObject with FactionAffinity, attribute_to : Int, shot_origin : Vector3, shot_angle : Vector3) : Projectile = {
+ Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle)
}
}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala b/common/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
new file mode 100644
index 000000000..79b24590a
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
@@ -0,0 +1,24 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.vital.DamageResistanceModel
+import net.psforever.types.Vector3
+
+/**
+ * An encapsulation of a projectile event that records sufficient historical information
+ * about the interaction of weapons discharge and a target
+ * to the point that the original event might be reconstructed.
+ * Reenacting the calculations of this entry should always produce the same values.
+ * @param resolution how the projectile hit was executed
+ * @param projectile the original projectile
+ * @param target what the projectile hit
+ * @param damage_model the kind of damage model to which the `target` is/was subject
+ * @param hit_pos where the projectile hit
+ * @param hit_time the sequence timing when the projectile hit the target
+ */
+final case class ResolvedProjectile(resolution : ProjectileResolution.Value,
+ projectile : Projectile,
+ target : SourceEntry,
+ damage_model : DamageResistanceModel,
+ hit_pos : Vector3,
+ hit_time : Long = System.nanoTime)
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala b/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala
new file mode 100644
index 000000000..5c2eed857
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/SourceEntry.scala
@@ -0,0 +1,42 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.definition.ObjectDefinition
+import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
+import net.psforever.objects.entity.WorldEntity
+import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+
+trait SourceEntry extends WorldEntity {
+ def Name : String = ""
+ def Definition : ObjectDefinition
+ def CharId : Long = 0L
+ def Faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
+ def Position_=(pos : Vector3) = Position
+ def Orientation_=(pos : Vector3) = Position
+ def Velocity_=(pos : Option[Vector3]) = Velocity
+}
+
+object SourceEntry {
+ final val None = new SourceEntry() {
+ def Definition = null
+ def Position = Vector3.Zero
+ def Orientation = Vector3.Zero
+ def Velocity = Some(Vector3.Zero)
+ }
+
+ def apply(target : PlanetSideGameObject with FactionAffinity) : SourceEntry = {
+ target match {
+ case obj : Player => PlayerSource(obj)
+ case obj : Vehicle => VehicleSource(obj)
+ case _ => ObjectSource(target)
+ }
+ }
+
+ def NameFormat(name : String) : String = {
+ name.replace("_", " ")
+ .split(" ")
+ .map(_.capitalize)
+ .mkString(" ")
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/VehicleSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/VehicleSource.scala
new file mode 100644
index 000000000..ccd7db404
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ballistics/VehicleSource.scala
@@ -0,0 +1,37 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.Vehicle
+import net.psforever.objects.definition.VehicleDefinition
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+
+final case class VehicleSource(obj_def : VehicleDefinition,
+ faction : PlanetSideEmpire.Value,
+ health : Int,
+ shields : Int,
+ position : Vector3,
+ orientation : Vector3,
+ velocity : Option[Vector3] = None) extends SourceEntry {
+ override def Name = SourceEntry.NameFormat(obj_def.Name)
+ override def Faction = faction
+ def Definition : VehicleDefinition = obj_def
+ def Health = health
+ def Shields = shields
+ def Position = position
+ def Orientation = orientation
+ def Velocity = velocity
+}
+
+object VehicleSource {
+ def apply(obj : Vehicle) : VehicleSource = {
+ VehicleSource(
+ obj.Definition,
+ obj.Faction,
+ obj.Health,
+ obj.Shields,
+ obj.Position,
+ obj.Orientation,
+ obj.Velocity
+ )
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index 892e0f21a..043ccb77b 100644
--- a/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -1,14 +1,17 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
-import net.psforever.objects.ballistics.{DamageProfile, DamageType, Projectiles}
+import net.psforever.objects.ballistics.Projectiles
+import net.psforever.objects.vital.DamageType
+import net.psforever.objects.vital.damage.DamageProfile
/**
* 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 DamageProfile {
+class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
+ with DamageProfile {
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
private var damage0 : Int = 0
private var damage1 : Option[Int] = None
@@ -26,6 +29,11 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) wi
private var damageAtEdge : Float = 1f
private var damageRadius : Float = 1f
private var useDamage1Subtract : Boolean = false
+ //derived calculations
+ private var distanceMax : Float = 0f
+ private var distanceFromAcceleration : Float = 0f
+ private var distanceNoDegrade : Float = 0f
+ private var finalVelocity : Float = 0f
Name = "projectile"
def ProjectileType : Projectiles.Value = projectileType
@@ -157,10 +165,43 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) wi
this.damageRadius = damageRadius
DamageRadius
}
+
+ def DistanceMax : Float = distanceMax //accessor only
+
+ def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
+
+ def DistanceNoDegrade : Float = distanceNoDegrade //accessor only
+
+ def FinalVelocity : Float = finalVelocity //accessor only
}
object ProjectileDefinition {
def apply(projectileType : Projectiles.Value) : ProjectileDefinition = {
new ProjectileDefinition(projectileType.id)
}
+
+ def CalculateDerivedFields(pdef : ProjectileDefinition) : Unit = {
+ val (distanceMax, distanceFromAcceleration, finalVelocity) : (Float, Float, Float) = if(pdef.Acceleration == 0f) {
+ (pdef.InitialVelocity * pdef.Lifespan, 0, pdef.InitialVelocity)
+ }
+ else {
+ val distanceFromAcceleration = (pdef.AccelerationUntil * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.AccelerationUntil * pdef.AccelerationUntil)
+ val finalVelocity = pdef.InitialVelocity + pdef.Acceleration * pdef.AccelerationUntil
+ val distanceAfterAcceleration = finalVelocity * (pdef.Lifespan - pdef.AccelerationUntil)
+ (distanceFromAcceleration + distanceAfterAcceleration, distanceFromAcceleration, finalVelocity)
+ }
+ pdef.distanceMax = distanceMax
+ pdef.distanceFromAcceleration = distanceFromAcceleration
+ pdef.finalVelocity = finalVelocity
+
+ pdef.distanceNoDegrade = if(pdef.DegradeDelay == 0f) {
+ pdef.distanceMax
+ }
+ else if(pdef.DegradeDelay < pdef.AccelerationUntil) {
+ (pdef.DegradeDelay * pdef.InitialVelocity) + (0.5f * pdef.Acceleration * pdef.DegradeDelay * pdef.DegradeDelay)
+ }
+ else {
+ pdef.distanceFromAcceleration + pdef.finalVelocity * (pdef.DegradeDelay - pdef.AccelerationUntil)
+ }
+ }
}
diff --git a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index ab3c5da9b..7cc959dca 100644
--- a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -3,17 +3,22 @@ package net.psforever.objects.definition
import net.psforever.objects.definition.converter.VehicleConverter
import net.psforever.objects.inventory.InventoryTile
-import net.psforever.objects.vehicles.UtilityType
+import net.psforever.objects.vehicles.{DestroyedVehicle, UtilityType}
+import net.psforever.objects.vital._
+import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import scala.collection.mutable
import scala.concurrent.duration._
/**
* An object definition system used to construct and retain the parameters of various vehicles.
- * @param objectId the object id the is associated with this sort of `Vehicle`
+ * @param objectId the object id that is associated with this sort of `Vehicle`
*/
-class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
+class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId)
+ with ResistanceProfileMutators
+ with DamageResistanceModel {
private var maxHealth : Int = 100
+ /** vehicle shields offered through amp station facility benefits (generally: 20% of health + 1) */
private var maxShields : Int = 0
/* key - seat index, value - seat object */
private val seats : mutable.HashMap[Int, SeatDefinition] = mutable.HashMap[Int, SeatDefinition]()
@@ -33,8 +38,12 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
private var serverVehicleOverrideSpeeds : (Int, Int) = (0, 0)
private var deconTime : Option[FiniteDuration] = None
private var maximumCapacitor : Int = 0
+ private var destroyedModel : Option[DestroyedVehicle.Value] = None
Name = "vehicle"
Packet = VehicleDefinition.converter
+ Damage = StandardVehicleDamage
+ Resistance = StandardVehicleResistance
+ Model = StandardResolutions.Vehicle
def MaxHealth : Int = maxHealth
@@ -138,6 +147,13 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
maximumCapacitor = maxCapacitor
MaximumCapacitor
}
+
+ def DestroyedModel : Option[DestroyedVehicle.Value] = destroyedModel
+
+ def DestroyedModel_=(model : Option[DestroyedVehicle.Value]) : Option[DestroyedVehicle.Value] = {
+ destroyedModel = model
+ DestroyedModel
+ }
}
object VehicleDefinition {
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/DestroyedVehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/DestroyedVehicleConverter.scala
new file mode 100644
index 000000000..6df53256e
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/DestroyedVehicleConverter.scala
@@ -0,0 +1,25 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.Vehicle
+import net.psforever.packet.game.objectcreate.{DestroyedVehicleData, PlacementData}
+
+import scala.util.{Failure, Success, Try}
+
+class DestroyedVehicleConverter extends ObjectCreateConverter[Vehicle]() {
+ override def DetailedConstructorData(obj : Vehicle) : Try[DestroyedVehicleData] =
+ Failure(new Exception("DestroyedVehicleConverter should not be used to generate detailed DestroyedVehicleData (nothing should)"))
+
+ override def ConstructorData(obj : Vehicle) : Try[DestroyedVehicleData] = {
+ if(obj.Health > 0) {
+ Failure(new Exception("Vehicle used on DestroyedVehicleConverter has not yet been destroyed (Health == 0)"))
+ }
+ else {
+ Success(DestroyedVehicleData(PlacementData(obj.Position, obj.Orientation)))
+ }
+ }
+}
+
+object DestroyedVehicleConverter {
+ final val converter = new DestroyedVehicleConverter
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
index f88c98b38..91b4a7343 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
@@ -4,7 +4,8 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.Vehicle
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.{InventoryItemData, _}
+import net.psforever.packet.game.objectcreate.{InventoryData, InventoryItemData, ObjectClass, PlacementData, SpecificVehicleData, VehicleData, VehicleFormat}
+import net.psforever.types.DriveState
import scala.util.{Failure, Success, Try}
@@ -14,31 +15,57 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
- Success(
- VehicleData(
- PlacementData(obj.Position, obj.Orientation, obj.Velocity),
- obj.Faction,
- bops = false,
- destroyed = health < 3,
- unk1 = 0,
- obj.Jammered,
- unk2 = false,
- obj.Owner match {
- case Some(owner) => owner
- case None => PlanetSideGUID(0)
- },
- unk3 = false,
- health,
- unk4 = false,
- no_mount_points = false,
- obj.DeploymentState,
- unk5 = false,
- unk6 = false,
- obj.Cloaked,
- SpecificFormatData(obj),
- Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj)))
- )(SpecificFormatModifier)
- )
+ if(health > 3) { //active
+ Success(
+ VehicleData(
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
+ obj.Faction,
+ bops = false,
+ destroyed = false,
+ unk1 = 0,
+ obj.Jammered,
+ unk2 = false,
+ obj.Owner match {
+ case Some(owner) => owner
+ case None => PlanetSideGUID(0)
+ },
+ unk3 = false,
+ health,
+ unk4 = false,
+ no_mount_points = false,
+ obj.DeploymentState,
+ unk5 = false,
+ unk6 = false,
+ obj.Cloaked,
+ SpecificFormatData(obj),
+ Some(InventoryData(MakeDriverSeat(obj) ++ MakeUtilities(obj) ++ MakeMountings(obj)))
+ )(SpecificFormatModifier)
+ )
+ }
+ else { //destroyed
+ Success(
+ VehicleData(
+ PlacementData(obj.Position, obj.Orientation),
+ obj.Faction,
+ bops = false,
+ destroyed = true,
+ unk1 = 0,
+ jammered = false,
+ unk2 = false,
+ owner_guid = PlanetSideGUID(0),
+ unk3 = false,
+ health = 0,
+ unk4 = false,
+ no_mount_points = true,
+ driveState = DriveState.Mobile,
+ unk5 = false,
+ unk6 = false,
+ cloak = false,
+ SpecificFormatData(obj),
+ inventory = None
+ )(SpecificFormatModifier)
+ )
+ }
}
private def MakeDriverSeat(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
diff --git a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
index 3b010e48b..a38a1015f 100644
--- a/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.equipment
import net.psforever.objects.Tool
-import net.psforever.objects.ballistics.DamageProfile
+import net.psforever.objects.vital.damage.DamageProfile
import scala.collection.mutable
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
index 4229cc61a..52b1cad29 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
@@ -96,18 +96,16 @@ class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase
}
case VehicleSpawnControl.ProcessControl.Flush =>
- if(!periodicReminder.isCancelled) {
- periodicReminder.cancel
- orders.foreach { VehicleSpawnControl.CancelOrder(_, Continent) }
- orders = Nil
- trackedOrder match {
- case Some(entry) =>
- VehicleSpawnControl.CancelOrder(entry, Continent)
- case None => ;
- }
- trackedOrder = None
- concealPlayer ! akka.actor.Kill //will cause the actor to restart, which will abort any trapped messages
+ periodicReminder.cancel
+ orders.foreach { VehicleSpawnControl.CancelOrder(_, Continent) }
+ orders = Nil
+ trackedOrder match {
+ case Some(entry) =>
+ VehicleSpawnControl.CancelOrder(entry, Continent)
+ case None => ;
}
+ trackedOrder = None
+ concealPlayer ! akka.actor.Kill //will cause the actor to restart, which will abort any trapped messages
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/DestroyedVehicle.scala b/common/src/main/scala/net/psforever/objects/vehicles/DestroyedVehicle.scala
new file mode 100644
index 000000000..4927030c4
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vehicles/DestroyedVehicle.scala
@@ -0,0 +1,33 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vehicles
+
+/**
+ *
+ */
+object DestroyedVehicle extends Enumeration {
+ type Type = Value
+
+ val Ams = Value(47)
+ val Ant = Value(61)
+ val Apc = Value(65)
+ val Dropship = Value(260)
+ val Flail = Value(295)
+ val Liberator = Value(439)
+ val LightGunship = Value(442)
+ val Lightning = Value(447)
+ val Lodestar = Value(460)
+ val Magrider = Value(471)
+ val Mosquito = Value(573)
+ val MediumTransport = Value(533)
+ val Prowler = Value(698)
+ val QuadAssault = Value(708)
+ val QuadStealth = Value(711)
+ val Router = Value(742)
+ val Skyguard = Value(785)
+ val Switchblade = Value(848)
+ val ThreeManHeavyBuggy = Value(863)
+ val TwoManAssaultBuggy = Value(897)
+ val TwoManHeavyBuggy = Value(899)
+ val TwoManHoverBuggy = Value(901)
+ val Vanguard = Value(924)
+}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index dc114e68c..e773696e2 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -3,9 +3,11 @@ package net.psforever.objects.vehicles
import akka.actor.Actor
import net.psforever.objects.Vehicle
+import net.psforever.objects.ballistics.VehicleSource
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
+import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
import net.psforever.types.ExoSuitType
/**
@@ -59,6 +61,22 @@ class VehicleControl(vehicle : Vehicle) extends Actor
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
}
+ case Vitality.Damage(damage_func) =>
+ if(vehicle.Health > 0) {
+ damage_func(vehicle)
+ sender ! Vitality.DamageResolution(vehicle)
+ }
+
+ case Vehicle.ChargeShields(amount) =>
+ val now : Long = System.nanoTime
+ //make certain vehicle doesn't charge shields too quickly
+ if(vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
+ !vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))) {
+ vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
+ vehicle.Shields = vehicle.Shields + amount
+ sender ! Vehicle.UpdateShieldsCharge(vehicle)
+ }
+
case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = vehicle.Faction
if(originalAffinity != (vehicle.Faction = faction)) {
@@ -81,3 +99,24 @@ class VehicleControl(vehicle : Vehicle) extends Actor
case _ => ;
}
}
+
+object VehicleControl {
+ import net.psforever.objects.vital.{DamageFromProjectile, VehicleShieldCharge, VitalsActivity}
+ import scala.concurrent.duration._
+
+ /**
+ * Determine if a given activity entry would invalidate the act of charging vehicle shields this tick.
+ * @param now the current time (in nanoseconds)
+ * @param act a `VitalsActivity` entry to test
+ * @return `true`, if the vehicle took damage in the last five seconds or
+ * charged shields in the last second;
+ * `false`, otherwise
+ */
+ def LastShieldChargeOrDamage(now : Long)(act : VitalsActivity) : Boolean = {
+ act match {
+ case DamageFromProjectile(data) => now - data.hit_time < (5 seconds).toNanos //damage delays next charge by 5s
+ case vsc : VehicleShieldCharge => now - vsc.time < (1 seconds).toNanos //previous charge delays next by 1s
+ case _ => false
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala b/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
new file mode 100644
index 000000000..7d98bc7ff
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
@@ -0,0 +1,80 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
+import net.psforever.objects.vital.damage.DamageSelection
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.objects.vital.resistance.ResistanceSelection
+import net.psforever.objects.vital.resolution.ResolutionCalculations
+
+/**
+ * The functionality that is necessary for interaction of a vital game object with the rest of the game world.
+ *
+ * A vital object can be hurt or damaged or healed or repaired (HDHR).
+ * The actual implementation of how that works is left to the specific object and its interfaces, however.
+ * The more involved values that are applied to the vital object are calculated by a series of functions
+ * that contribute different values, e.g., the value for being damaged.
+ * "Being damaged" is also not the same for all valid targets:
+ * some targets don't utilize the same kinds of values in the same way as another,
+ * and some targets utilize a different assortment of values than either of the first two examples.
+ * The damage model is a common interface for producing those values
+ * and reconciling those values with a valid target object
+ * without much fuss.
+ *
+ * By default, nothing should do anything of substance.
+ * @see `Vitality`
+ */
+trait DamageResistanceModel {
+ /** the functionality that processes damage; required */
+ private var damage : DamageSelection = NoDamageSelection
+
+ /** the functionality that processes resistance; optional */
+ private var resistance : ResistanceSelection = NoResistanceSelection
+
+ /** the functionality that prepares for damage application actions; required */
+ private var model : ResolutionCalculations.Form = NoResolutions.Calculate
+
+ def Damage : DamageSelection = damage
+
+ def Damage_=(selector : DamageSelection) : DamageSelection = {
+ damage = selector
+ Damage
+ }
+
+ def Resistance : ResistanceSelection = resistance
+
+ def Resistance_=(selector : ResistanceSelection) : ResistanceSelection = {
+ resistance = selector
+ Resistance
+ }
+
+ def Model : ResolutionCalculations.Form = model
+
+ def Model_=(selector : ResolutionCalculations.Form) : ResolutionCalculations.Form = {
+ model = selector
+ Model
+ }
+
+ /**
+ * Magic stuff.
+ * @param data the historical `ResolvedProjectile` information
+ * @return a function literal that encapsulates delayed modification instructions for certain objects
+ */
+ def Calculate(data : ResolvedProjectile) : ResolutionCalculations.Output = {
+ val dam : ProjectileCalculations.Form = Damage(data)
+ val res : ProjectileCalculations.Form = Resistance(data)
+ Model(dam, res, data)
+ }
+
+ /**
+ * Magic stuff.
+ * @param data the historical `ResolvedProjectile` information
+ * @param resolution an explicit damage resolution overriding the one in the `ResolvedProjectile` object
+ * @return a function literal that encapsulates delayed modification instructions for certain objects
+ */
+ def Calculate(data : ResolvedProjectile, resolution : ProjectileResolution.Value) : ResolutionCalculations.Output = {
+ val dam : ProjectileCalculations.Form = Damage(resolution)
+ val res : ProjectileCalculations.Form = Resistance(resolution)
+ Model(dam, res, data)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala b/common/src/main/scala/net/psforever/objects/vital/DamageType.scala
similarity index 90%
rename from common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala
rename to common/src/main/scala/net/psforever/objects/vital/DamageType.scala
index 8f2722b06..f0070f21b 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/DamageType.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/DamageType.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.ballistics
+package net.psforever.objects.vital
/**
* An `Enumeration` of the damage type.
diff --git a/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala b/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala
new file mode 100644
index 000000000..9c7ebbaee
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/StandardDamages.scala
@@ -0,0 +1,113 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.vital.damage._
+import net.psforever.objects.vital.damage.DamageCalculations._
+
+/**
+ * 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 InfantryLashDamage extends NoDamageBase {
+ override def Calculate(data : ResolvedProjectile) : Int = (InfantryHitDamage.Calculate(data) * 0.2f).toInt
+}
+
+object MaxLashDamage extends NoDamageBase {
+ override def Calculate(data : ResolvedProjectile) : Int = (MaxHitDamage.Calculate(data) * 0.2f).toInt
+}
+
+object VehicleLashDamage extends NoDamageBase {
+ override def Calculate(data : ResolvedProjectile) : Int = (VehicleHitDamage.Calculate(data) * 0.2f).toInt
+}
+
+object AircraftLashDamage extends NoDamageBase {
+ override def Calculate(data : ResolvedProjectile) : Int = (AircraftHitDamage.Calculate(data) * 0.2f).toInt
+}
+
+object NoDamageSelection extends DamageSelection {
+ def Direct = None
+ def Splash = None
+ def Lash = None
+}
+
+object StandardInfantryDamage extends DamageSelection {
+ def Direct = InfantryHitDamage.Calculate
+ def Splash = InfantrySplashDamage.Calculate
+ def Lash = InfantryLashDamage.Calculate
+}
+
+object StandardMaxDamage extends DamageSelection {
+ def Direct = MaxHitDamage.Calculate
+ def Splash = MaxSplashDamage.Calculate
+ def Lash = MaxLashDamage.Calculate
+}
+
+object StandardVehicleDamage extends DamageSelection {
+ def Direct = VehicleHitDamage.Calculate
+ def Splash = VehicleSplashDamage.Calculate
+ def Lash = VehicleLashDamage.Calculate
+}
+
+object StandardAircraftDamage extends DamageSelection {
+ def Direct = AircraftHitDamage.Calculate
+ def Splash = AircraftSplashDamage.Calculate
+ def Lash = AircraftLashDamage.Calculate
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala b/common/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala
new file mode 100644
index 000000000..d419c02c3
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/StandardResistanceProfile.scala
@@ -0,0 +1,26 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.vital.resistance.ResistanceProfile
+
+/**
+ * The different values for four common methods of modifying incoming damage.
+ * Two of the four resistances are directly paired with forms of incoming damage.
+ * This is for defining pure accessor functions,
+ * based on the assumption that the implementing object's `Definition` is the primary `ResistanceProfile`.
+ */
+trait StandardResistanceProfile extends ResistanceProfile {
+ this : PlanetSideGameObject =>
+ //actually check that this will work for this implementing class
+ assert(Definition.isInstanceOf[ResistanceProfile], s"$this object definition must extend ResistanceProfile")
+ private val resistDef = Definition.asInstanceOf[ResistanceProfile] //cast only once
+
+ def ResistanceDirectHit : Int = resistDef.ResistanceDirectHit
+
+ def ResistanceSplash : Int = resistDef.ResistanceDirectHit
+
+ def ResistanceAggravated : Int = resistDef.ResistanceDirectHit
+
+ def RadiationShielding : Float = resistDef.ResistanceDirectHit
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/StandardResistances.scala b/common/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
new file mode 100644
index 000000000..5aaaa6f70
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
@@ -0,0 +1,72 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.ballistics.{PlayerSource, SourceEntry, VehicleSource}
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.objects.vital.resistance.{ResistanceCalculations, ResistanceSelection}
+
+object NoResistance extends ResistanceCalculations[SourceEntry](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.NoResistExtractor
+)
+
+object InfantryHitResistance extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitDirectExtractor
+)
+
+object InfantrySplashResistance extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitSplashExtractor
+)
+
+object InfantryLashResistance extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.NoResistExtractor
+)
+
+object InfantryAggravatedResistance extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitAggravatedExtractor
+)
+
+object VehicleHitResistance extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleDirectExtractor
+)
+
+object VehicleSplashResistance extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleSplashExtractor
+)
+
+object VehicleLashResistance extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.NoResistExtractor
+)
+
+object VehicleAggravatedResistance extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleAggravatedExtractor
+)
+
+object NoResistanceSelection extends ResistanceSelection {
+ def Direct : ProjectileCalculations.Form = None
+ def Splash : ProjectileCalculations.Form = None
+ def Lash : ProjectileCalculations.Form = None
+ def Aggravated : ProjectileCalculations.Form = None
+}
+
+object StandardInfantryResistance extends ResistanceSelection {
+ def Direct : ProjectileCalculations.Form = InfantryHitResistance.Calculate
+ def Splash : ProjectileCalculations.Form = InfantrySplashResistance.Calculate
+ def Lash : ProjectileCalculations.Form = InfantryLashResistance.Calculate
+ def Aggravated : ProjectileCalculations.Form = InfantryAggravatedResistance.Calculate
+}
+
+object StandardVehicleResistance extends ResistanceSelection {
+ def Direct : ProjectileCalculations.Form = VehicleHitResistance.Calculate
+ def Splash : ProjectileCalculations.Form = VehicleSplashResistance.Calculate
+ def Lash : ProjectileCalculations.Form = VehicleLashResistance.Calculate
+ def Aggravated : ProjectileCalculations.Form = VehicleAggravatedResistance.Calculate
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala b/common/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
new file mode 100644
index 000000000..6a25af3ee
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
@@ -0,0 +1,31 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.vital.resolution._
+
+object NoResolutions extends DamageResistCalculations(
+ ResolutionCalculations.NoDamage,
+ ResolutionCalculations.NoApplication
+)
+
+object InfantryResolutions extends DamageResistCalculations(
+ ResolutionCalculations.InfantryDamageAfterResist,
+ ResolutionCalculations.InfantryApplication
+)
+
+object MaxResolutions extends DamageResistCalculations(
+ ResolutionCalculations.MaxDamageAfterResist,
+ ResolutionCalculations.InfantryApplication
+)
+
+object VehicleResolutions extends DamageResistCalculations(
+ ResolutionCalculations.VehicleDamageAfterResist,
+ ResolutionCalculations.VehicleApplication
+)
+
+object StandardResolutions extends ResolutionSelection {
+ def Infantry : ResolutionCalculations.Form = InfantryResolutions.Calculate
+ def Max : ResolutionCalculations.Form = MaxResolutions.Calculate
+ def Vehicle : ResolutionCalculations.Form = VehicleResolutions.Calculate
+ def Aircraft : ResolutionCalculations.Form = VehicleResolutions.Calculate
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala
new file mode 100644
index 000000000..a90522123
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala
@@ -0,0 +1,110 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital
+
+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.types.{ExoSuitType, ImplantType}
+
+abstract class VitalsActivity(target : SourceEntry) {
+ def Target : SourceEntry = target
+ val t : Long = System.nanoTime //???
+
+ def time : Long = t
+}
+
+abstract class HealingActivity(target : SourceEntry) extends VitalsActivity(target)
+
+abstract class DamagingActivity(target : SourceEntry) extends VitalsActivity(target)
+
+final case class HealFromKit(target : PlayerSource, amount : Int, kit_def : KitDefinition) extends HealingActivity(target)
+
+final case class HealFromTerm(target : PlayerSource, health : Int, armor : Int, term_def : TerminalDefinition) extends HealingActivity(target)
+
+final case class HealFromImplant(target : PlayerSource, amount : Int, implant : ImplantType.Value) extends HealingActivity(target)
+
+final case class HealFromExoSuitChange(target : PlayerSource, exosuit : ExoSuitType.Value) extends HealingActivity(target)
+
+final case class RepairFromTerm(target : VehicleSource, amount : Int, term_def : TerminalDefinition) extends HealingActivity(target)
+
+final case class VehicleShieldCharge(target : VehicleSource, amount : Int) extends HealingActivity(target) //TODO facility
+
+final case class DamageFromProjectile(data : ResolvedProjectile) extends DamagingActivity(data.target)
+
+final case class PlayerSuicide(target : PlayerSource) extends DamagingActivity(target)
+
+/**
+ * A vital object can be hurt or damaged or healed or repaired (HDHR).
+ * The amount of HDHR is controlled by the damage model of this vital object reacting to stimulus.
+ * A history of the previous changes in vital statistics of the underlying object is recorded
+ * in reverse chronological order.
+ * The damage model is also provided.
+ */
+trait Vitality {
+ this : PlanetSideGameObject =>
+
+ /** a reverse-order list of chronological events that have occurred to these vital statistics */
+ private var vitalHistory : List[VitalsActivity] = List.empty[VitalsActivity]
+
+ def History : List[VitalsActivity] = vitalHistory
+
+ /**
+ * A `VitalsActivity` event must be recorded.
+ * Add new entry to the front of the list (for recent activity).
+ * @param action the fully-informed entry
+ * @return the list of previous changes to this object's vital statistics
+ */
+ def History(action : VitalsActivity) : List[VitalsActivity] = {
+ vitalHistory = action +: vitalHistory
+ vitalHistory
+ }
+
+ /**
+ * Very common example of a `VitalsActivity` event involving weapon discharge.
+ * @param projectile the fully-informed entry of discharge of a weapon
+ * @return the list of previous changes to this object's vital statistics
+ */
+ def History(projectile : ResolvedProjectile) : List[VitalsActivity] = {
+ vitalHistory = DamageFromProjectile(projectile) +: vitalHistory
+ vitalHistory
+ }
+
+ /**
+ * Find, specifically, the last instance of a weapon discharge vital statistics change.
+ * @return information about the discharge
+ */
+ def LastShot : Option[ResolvedProjectile] = {
+ vitalHistory.find({p => p.isInstanceOf[DamageFromProjectile]}) match {
+ case Some(entry : DamageFromProjectile) =>
+ Some(entry.data)
+ case _ =>
+ None
+ }
+ }
+
+ def ClearHistory() : List[VitalsActivity] = {
+ val out = vitalHistory
+ vitalHistory = List.empty[VitalsActivity]
+ out
+ }
+
+ def DamageModel : DamageResistanceModel
+}
+
+object Vitality {
+
+ /**
+ * Provide the damage model-generated functionality
+ * that would properly enact the calculated changes of a vital statistics event
+ * upon a given vital object.
+ * @param func a function literal
+ */
+ final case class Damage(func : (Any)=>Unit)
+
+ /**
+ * Report that a vitals object must be updated due to damage.
+ * @param obj the vital object
+ */
+ final case class DamageResolution(obj : Vitality)
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
new file mode 100644
index 000000000..7e48430a5
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/damage/DamageCalculations.scala
@@ -0,0 +1,129 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.damage
+
+import net.psforever.types.Vector3
+import net.psforever.objects.ballistics.{Projectile, ResolvedProjectile}
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+import DamageCalculations._
+
+/**
+ * The base class for function literal description related to calculating damage information.
+ *
+ * Implementing functionality of the children is the product of three user-defined processes
+ * and information for the calculation is extracted from the to-be-provided weapon discharge information.
+ * The specific functions passed into this object typically operate simultaneously normally
+ * and are related to the target and the kind of interaction the weapon discharge had with the target.
+ * @param damages function by which damage is modified by distance
+ * @param extractor function that recovers damage information
+ * @param distanceFunc a function to calculate the distance for scaling the damage, if valid
+ */
+abstract class DamageCalculations(damages : DamagesType,
+ extractor : DamageWithModifiersType,
+ distanceFunc : DistanceType) extends ProjectileCalculations {
+ /**
+ * Combine the damage and distance data extracted from the `ResolvedProjectile` entry.
+ * @param data the historical `ResolvedProjectile` information
+ * @return the damage value
+ */
+ def Calculate(data : ResolvedProjectile) : Int = {
+ val projectile = data.projectile
+ damages(
+ projectile,
+ extractor(projectile.profile, List(projectile.fire_mode.Modifiers)),
+ distanceFunc(data)
+ )
+ }
+}
+
+object DamageCalculations {
+ //types
+ type DamagesType = (Projectile, Int, Float)=>Int
+ type DamageWithModifiersType = (DamageProfile, List[DamageProfile])=>Int
+ type DistanceType = (ResolvedProjectile)=>Float
+
+ //raw damage selectors
+ def NoDamageAgainst(profile : DamageProfile) : Int = 0
+
+ def DamageAgainstExoSuit(profile : DamageProfile) : Int = profile.Damage0
+
+ def DamageAgainstVehicle(profile : DamageProfile) : Int = profile.Damage1
+
+ def DamageAgainstAircraft(profile : DamageProfile) : Int = profile.Damage2
+
+ def DamageAgainstMaxSuit(profile : DamageProfile) : Int = profile.Damage3
+
+ def DamageAgainstUnknown(profile : DamageProfile) : Int = profile.Damage4
+
+ //raw damage selection functions
+ /**
+ * Get damage information from a series of profiles related to the weapon discharge.
+ * @param extractor the function that recovers the damage value
+ * @param base the profile from which primary damage is to be selected
+ * @param modifiers alternate profiles that will modify the base damage value
+ * @return the accumulated damage value
+ */
+ //TODO modifiers come from various sources; expand this part of the calculation model in the future
+ def DamageWithModifiers(extractor : (DamageProfile)=>Int)(base : DamageProfile, modifiers : List[DamageProfile]) : Int = {
+ extractor(base) + modifiers.foldLeft(0)(_ + extractor(_))
+ }
+
+ //damage calculation functions
+ def NoDamage(projectile : Projectile, rawDamage : Int, distance : Float) : Int = 0
+
+ /**
+ * Modify the base damage based on the degrade distance of the projectile type
+ * and its maximum effective distance.
+ * Calls out "direct hit" damage but is recycled for other damage types as well.
+ * @param projectile information about the weapon discharge (itself)
+ * @param rawDamage the accumulated amount of damage
+ * @param distance how far the source was from the target
+ * @return the modified damage value
+ */
+ def DirectHitDamageWithDegrade(projectile : Projectile, rawDamage: Int, distance: Float): Int = {
+ val profile = projectile.profile
+ if(distance <= profile.DistanceMax) {
+ if(profile.DistanceNoDegrade == profile.DistanceMax || distance <= profile.DistanceNoDegrade) {
+ rawDamage
+ }
+ else {
+ rawDamage - ((rawDamage - profile.DegradeMultiplier * rawDamage) * ((distance - profile.DistanceNoDegrade) / (profile.DistanceMax - profile.DistanceNoDegrade))).toInt
+ }
+ }
+ else {
+ 0
+ }
+ }
+
+ /**
+ * Modify the base damage based on the radial distance of the target from the center of an explosion.
+ * Calls out "splash" damage exclusively.
+ * @param projectile information about the weapon discharge (itself)
+ * @param rawDamage the accumulated amount of damage
+ * @param distance how far the origin of the explosion was from the target
+ * @return the modified damage value
+ */
+ def SplashDamageWithRadialDegrade(projectile : Projectile, rawDamage : Int, distance : Float) : Int = {
+ val radius = projectile.profile.DamageRadius
+ if(distance <= radius) {
+ val base : Float = projectile.profile.DamageAtEdge
+ val degrade : Float = (1 - base) * ((radius - distance) / radius) + base
+ rawDamage + (rawDamage * degrade).toInt
+ }
+ else {
+ 0
+ }
+ }
+
+ //distance functions
+ def NoDistance(data : ResolvedProjectile) : Float = 0
+
+ def TooFar(data : ResolvedProjectile) : Float = Float.MaxValue
+
+ def DistanceBetweenTargetandSource(data : ResolvedProjectile) : Float = {
+ Vector3.Distance(data.target.Position, data.projectile.owner.Position)
+ }
+
+ def DistanceFromExplosionToTarget(data : ResolvedProjectile) : Float = {
+ Vector3.Distance(data.target.Position, data.hit_pos)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
similarity index 92%
rename from common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala
rename to common/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
index 1d2e877e1..95f99ffc5 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/DamageProfile.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/damage/DamageProfile.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.ballistics
+package net.psforever.objects.vital.damage
/**
* The different values for five common types of damage that can be dealt, based on target and application.
diff --git a/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala b/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala
new file mode 100644
index 000000000..9ecd9b41c
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/damage/DamageSelection.scala
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.damage
+
+import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
+import net.psforever.objects.vital.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.Calculate
+
+ def Direct : ProjectileCalculations.Form
+ def Splash : ProjectileCalculations.Form
+ def Lash : ProjectileCalculations.Form
+
+ def apply(data : ResolvedProjectile) : ProjectileCalculations.Form = data.resolution match {
+ case ProjectileResolution.Hit => Direct
+ case ProjectileResolution.Splash => Splash
+ case ProjectileResolution.Lash => Lash
+ case _ => None
+ }
+
+ def apply(res : ProjectileResolution.Value) : ProjectileCalculations.Form = res match {
+ case ProjectileResolution.Hit => Direct
+ case ProjectileResolution.Splash => Splash
+ case ProjectileResolution.Lash => Lash
+ case _ => None
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala
new file mode 100644
index 000000000..28f8d0609
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/projectile/ProjectileCalculations.scala
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.projectile
+
+import net.psforever.objects.ballistics.ResolvedProjectile
+
+/**
+ * The base for all projectile-induced damage calculation function literals.
+ */
+trait ProjectileCalculations {
+ /**
+ * The exposed entry for the calculation function literal defined by this base.
+ * @param data the historical `ResolvedProjectile` information
+ * @return the calculated value
+ */
+ def Calculate(data : ResolvedProjectile) : Int
+}
+
+object ProjectileCalculations {
+ type Form = (ResolvedProjectile)=>Int
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala
new file mode 100644
index 000000000..82b08dee5
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceCalculations.scala
@@ -0,0 +1,123 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resistance
+
+import net.psforever.objects.{ExoSuitDefinition, GlobalDefinitions}
+import net.psforever.objects.ballistics._
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+import net.psforever.types.ExoSuitType
+
+import scala.util.{Failure, Success, Try}
+
+/**
+ * The base class for function literal description related to calculating resistance information.
+ *
+ * Implementing functionality of the children is the product of two user-defined processes
+ * and information for the calculation is extracted from the to-be-provided weapon discharge information.
+ * Specifically, the information is found as the `target` object which is a member of the said information.
+ * The specific functions passed into this object typically operate simultaneously normally
+ * and are related to the target and the kind of interaction the weapon discharge had with the target.
+ * @param validate determine if a more generic `target` object is actually an expected type;
+ * cast to and return that type of object
+ * @param extractor recover the resistance values from an approved type of object
+ * @tparam TargetType an internal type that converts between `validate`'s output and `extractor`'s input;
+ * in essence, should match the type of object container to which these resistances belong;
+ * never has to be defined explicitly, but will be checked upon object definition
+ */
+abstract class ResistanceCalculations[TargetType](validate : (ResolvedProjectile)=>Try[TargetType],
+ extractor : (TargetType)=>Int) extends ProjectileCalculations {
+ /**
+ * Get resistance valuess.
+ * @param data the historical `ResolvedProjectile` information
+ * @return the damage value
+ */
+ def Calculate(data : ResolvedProjectile) : Int = {
+ validate(data) match {
+ case Success(target) =>
+ extractor(target)
+ case _ =>
+ 0
+ }
+ }
+}
+
+object ResistanceCalculations {
+ private def failure(typeName : String) = Failure(new Exception(s"can not match expected target $typeName"))
+
+ //target identification
+ def InvalidTarget(data : ResolvedProjectile) : Try[SourceEntry] = failure(s"invalid ${data.target.Definition.Name}")
+
+ def ValidInfantryTarget(data : ResolvedProjectile) : Try[PlayerSource] = {
+ data.target match {
+ case target : PlayerSource =>
+ if(target.ExoSuit != ExoSuitType.MAX) { //max is not counted as an official infantry exo-suit type
+ Success(target)
+ }
+ else {
+ failure("infantry")
+ }
+ case _ =>
+ failure("infantry")
+ }
+ }
+
+ def ValidMaxTarget(data : ResolvedProjectile) : Try[PlayerSource] = {
+ data.target match {
+ case target : PlayerSource =>
+ if(target.ExoSuit == ExoSuitType.MAX) {
+ Success(target)
+ }
+ else {
+ failure("max")
+ }
+ case _ =>
+ failure("max")
+ }
+ }
+
+ def ValidVehicleTarget(data : ResolvedProjectile) : Try[VehicleSource] = {
+ data.target match {
+ case target : VehicleSource =>
+ if(!GlobalDefinitions.isFlightVehicle(target.Definition)) {
+ Success(target)
+ }
+ else {
+ failure("vehicle")
+ }
+ case _ =>
+ failure("vehicle")
+ }
+ }
+
+ def ValidAircraftTarget(data : ResolvedProjectile) : Try[VehicleSource] = {
+ data.target match {
+ case target : VehicleSource =>
+ if(GlobalDefinitions.isFlightVehicle(target.Definition)) {
+ Success(target)
+ }
+ else {
+ failure("aircraft")
+ }
+ case _ =>
+ failure("aircraft")
+ }
+ }
+
+ //extractors
+ def NoResistExtractor(target : SourceEntry) : Int = 0
+
+ def ExoSuitDirectExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceDirectHit
+
+ def ExoSuitSplashExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceSplash
+
+ def ExoSuitAggravatedExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceAggravated
+
+ def ExoSuitRadiationExtractor(target : PlayerSource) : Float = ExoSuitDefinition.Select(target.ExoSuit).RadiationShielding
+
+ def VehicleDirectExtractor(target : VehicleSource) : Int = target.Definition.ResistanceDirectHit
+
+ def VehicleSplashExtractor(target : VehicleSource) : Int = target.Definition.ResistanceSplash
+
+ def VehicleAggravatedExtractor(target : VehicleSource) : Int = target.Definition.ResistanceAggravated
+
+ def VehicleRadiationExtractor(target : VehicleSource) : Float = target.Definition.RadiationShielding
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
new file mode 100644
index 000000000..695b9c033
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceProfile.scala
@@ -0,0 +1,69 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resistance
+
+import net.psforever.objects.vital.DamageType
+
+/**
+ * The different values for four common methods of modifying incoming damage.
+ * Two of the four resistances are directly paired with forms of incoming damage.
+ * This is for defining pure accessor functions.
+ */
+trait ResistanceProfile {
+ def ResistanceDirectHit : Int
+
+ def ResistanceSplash : Int
+
+ def ResistanceAggravated : Int
+
+ def RadiationShielding : Float
+
+ def Resist(dtype : DamageType.Value) : Float = {
+ dtype match {
+ case DamageType.Direct => ResistanceDirectHit
+ case DamageType.Splash => ResistanceSplash
+ case DamageType.Aggravated => ResistanceAggravated
+ case DamageType.Radiation => RadiationShielding
+ case _ => 0
+ }
+ }
+}
+
+/**
+ * The different values for four common methods of modifying incoming damage.
+ * Two of the four resistances are directly paired with forms of incoming damage.
+ * This is for defining both accessor and mutator functions.
+ */
+trait ResistanceProfileMutators extends ResistanceProfile {
+ private var resistanceDirectHit : Int = 0
+ private var resistanceSplash : Int = 0
+ private var resistanceAggravated : Int = 0
+ private var radiationShielding : Float = 0f
+
+ def ResistanceDirectHit : Int = resistanceDirectHit
+
+ def ResistanceDirectHit_=(resist : Int) : Int = {
+ resistanceDirectHit = resist
+ ResistanceDirectHit
+ }
+
+ def ResistanceSplash : Int = resistanceSplash
+
+ def ResistanceSplash_=(resist : Int) : Int = {
+ resistanceSplash = resist
+ ResistanceSplash
+ }
+
+ def ResistanceAggravated : Int = resistanceAggravated
+
+ def ResistanceAggravated_=(resist : Int) : Int = {
+ resistanceAggravated = resist
+ ResistanceAggravated
+ }
+
+ def RadiationShielding : Float = radiationShielding
+
+ def RadiationShielding_=(resist : Float) : Float = {
+ radiationShielding = resist
+ RadiationShielding
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
new file mode 100644
index 000000000..36c9736e5
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
@@ -0,0 +1,33 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resistance
+
+import net.psforever.objects.ballistics._
+import net.psforever.objects.vital.NoResistance
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+
+/**
+ * Maintain information about four primary forms of resistance calculation
+ * and a means to test which calculation is valid in a given situation.
+ */
+trait ResistanceSelection {
+ final def None : ProjectileCalculations.Form = NoResistance.Calculate
+
+ def Direct : ProjectileCalculations.Form
+ def Splash : ProjectileCalculations.Form
+ def Lash : ProjectileCalculations.Form
+ def Aggravated : ProjectileCalculations.Form
+
+ def apply(data : ResolvedProjectile) : ProjectileCalculations.Form = data.resolution match {
+ case ProjectileResolution.Hit => Direct
+ case ProjectileResolution.Splash => Splash
+ case ProjectileResolution.Lash => Lash
+ case _ => None
+ }
+
+ def apply(res : ProjectileResolution.Value) : ProjectileCalculations.Form = res match {
+ case ProjectileResolution.Hit => Direct
+ case ProjectileResolution.Splash => Splash
+ case ProjectileResolution.Lash => Lash
+ case _ => None
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
new file mode 100644
index 000000000..e46fb46aa
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/DamageResistCalculations.scala
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resolution
+
+import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+
+/**
+ * A specific implementation of `ResolutionCalculations` that deals with
+ * the damage value and the resistance value in a specific manner.
+ * (The input type of the function literal output of `calcFunc`.)
+ * @param calcFunc a function literal that retrieves the function
+ * that factors the affects of damage and resistance values
+ * @param applyFunc a function literal that applies the final modified values to a target object
+ * @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),
+ applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
+ extends ResolutionCalculations {
+ def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
+ val dam : Int = damages(data)
+ val res : Int = resistances(data)
+ val mod = calcFunc(data)
+ val modDam = mod(dam, res)
+ applyFunc(modDam, data)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
new file mode 100644
index 000000000..3b143ba09
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
@@ -0,0 +1,160 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resolution
+
+import net.psforever.objects.{Player, Vehicle}
+import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
+import net.psforever.objects.vital.projectile.ProjectileCalculations
+
+/**
+ * The base for the combining step of all projectile-induced damage calculation function literals.
+ */
+trait ResolutionCalculations {
+ /**
+ * The exposed entry for the calculation function literal defined by this base.
+ * @param damages the function literal that accumulates and calculates damages
+ * @param resistances the function literal that collects resistance values
+ * @param data the historical `ResolvedProjectile` information
+ * @return a function literal that encapsulates delayed modification instructions for certain objects
+ */
+ def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output
+}
+
+object ResolutionCalculations {
+ type Output = (Any)=>Unit
+ type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile)=>Output
+
+ def NoDamage(data : ResolvedProjectile)(a : Int, b : Int) : Int = 0
+
+ def InfantryDamageAfterResist(data : ResolvedProjectile) : (Int, Int)=>(Int, Int) = {
+ data.target match {
+ case target : PlayerSource =>
+ InfantryDamageAfterResist(target.health, target.armor)
+ case _ =>
+ InfantryDamageAfterResist(0, 0)
+ }
+ }
+
+ def InfantryDamageAfterResist(currentHP : Int, currentArmor : Int)(damages : Int, resistance : Int) : (Int, Int) = {
+ if(damages > 0 && currentHP > 0) {
+ if(currentArmor <= 0) {
+ (damages, 0) //no armor; health damage
+ }
+ else if(damages > resistance) {
+ val resistedDam = damages - resistance
+ //(resistedDam, resistance)
+ if(resistance <= currentArmor) {
+ (resistedDam, resistance) //armor and health damage
+ }
+ else {
+ (resistedDam + (resistance - currentArmor), currentArmor) //deplete armor; health damage + bonus
+ }
+ }
+ else {
+ (0, damages) //too weak; armor damage (less than resistance)
+ }
+ }
+ else {
+ (0, 0) //no damage
+ }
+ }
+
+ def MaxDamageAfterResist(data : ResolvedProjectile) : (Int, Int)=>(Int, Int) = {
+ data.target match {
+ case target : PlayerSource =>
+ MaxDamageAfterResist(target.health, target.armor)
+ case _ =>
+ MaxDamageAfterResist(0, 0)
+ }
+ }
+
+ def MaxDamageAfterResist(currentHP : Int, currentArmor : Int)(damages : Int, resistance : Int) : (Int, Int) = {
+ val resistedDam = damages - resistance
+ if(resistedDam > 0 && currentHP > 0) {
+ if(currentArmor <= 0) {
+ (resistedDam, 0) //no armor; health damage
+ }
+ else if(resistedDam >= currentArmor) {
+ (resistedDam - currentArmor, currentArmor) //deplete armor; health damage
+ }
+ else {
+ (0, resistedDam) //too weak; armor damage (less than resistance)
+ }
+ }
+ else {
+ (0, 0) //no damage
+ }
+ }
+
+ /**
+ * Unlike with `Infantry*` and with `Max*`'s,
+ * `VehicleDamageAfterResist` does not necessarily need to validate its target object.
+ * The required input is sufficient.
+ * @param data the historical `ResolvedProjectile` information
+ * @return a function literal for dealing with damage values and resistance values together
+ */
+ def VehicleDamageAfterResist(data : ResolvedProjectile) : (Int, Int)=>Int = {
+ VehicleDamageAfterResist
+ }
+
+ def VehicleDamageAfterResist(damages : Int, resistance : Int) : Int = {
+ if(damages > resistance) {
+ damages - resistance
+ }
+ else {
+ damages
+ }
+ }
+
+ def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : Unit = { }
+
+ /**
+ * The expanded `(Any)=>Unit` function for infantry.
+ * Apply the damage values to the health field and personal armor field for an infantry target.
+ * @param damageValues a tuple containing damage values for: health, personal armor
+ * @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 =>
+ val (a, b) = damageValues
+ //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.Armor - b < 0) {
+ player.Health = player.Health - a - (b - player.Armor)
+ player.Armor = 0
+ }
+ else {
+ player.Armor = player.Armor - b
+ player.Health = player.Health - a
+ }
+ }
+ case _ =>
+ }
+
+ /**
+ * The expanded `(Any)=>Unit` function for vehicles.
+ * Apply the damage value to the shield field and then the health field (that order) for a vehicle target.
+ * @param damage the raw damage
+ * @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) {
+ vehicle.History(data)
+ val shields = vehicle.Shields
+ if(shields > damage) {
+ vehicle.Shields = shields - damage
+ }
+ else if(shields > 0) {
+ vehicle.Health = vehicle.Health - (damage - shields)
+ vehicle.Shields = 0
+ }
+ else {
+ vehicle.Health = vehicle.Health - damage
+ }
+ }
+ case _ => ;
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
new file mode 100644
index 000000000..824a9faef
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/vital/resolution/ResolutionSelection.scala
@@ -0,0 +1,12 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.vital.resolution
+
+/**
+ * Maintain information about four target types as the entry points for damage calculation.
+ */
+trait ResolutionSelection {
+ def Infantry : ResolutionCalculations.Form
+ def Max : ResolutionCalculations.Form
+ def Vehicle : ResolutionCalculations.Form
+ def Aircraft : ResolutionCalculations.Form
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala b/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala
index db2fc6eda..f8dc51d83 100644
--- a/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala
@@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game
+import net.psforever.objects.ballistics.SourceEntry
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideEmpire
import scodec.Codec
@@ -15,47 +16,43 @@ import scodec.codecs._
* 3) victim information
* In the case of a player kill, the player's name will be attributed directly.
* In the case of an absentee kill, a description of the method of death will be attributed.
- * In the case of a suicide, the player attributed is the player who was killed (message format displays only the victim).
- * The victim's name is byte-aligned with a 5-bit buffer.
+ * In the case of a suicide, the player attributed is the player who was killed (message format displays only the victim).
*
* The four bytes that follow each name seems to be important to the identification of the associated player.
- * The same value will be seen in every `DestroyDisplayMessage` that includes the player, with respect to whether they are listed as the "killer" or as the "victim."
- * This holds true for every entry within thie same login session, at least.
- * Blanking these values out does not change anything about the format of the event message.
- * In the case of absentee kills, for example, where there is no killer listed, this field has been zero'd (`00000000`).
+ * The same value will be seen in every `DestroyDisplayMessage` that includes that player,
+ * with respect to whether they are listed as the "killer" or as the "victim."
+ * This holds true for every entry within the same login session, at least.
+ * Blanking either of these values out does not change anything about the format of the event message.
+ * If the two ids match, the packet will interpreted as the "suicide" format, even if the names do not match.
+ * In the case of absentee kills where there is no killer listed, this field is zero'd.
*
- * The faction affiliation is different from the normal way `PlanetSideEmpire` values are recorded.
- * The higher nibble will reflect the first part of the `PlanetSideEmpire` value.
- * An extra `20` will be added if the player is in a vehicle or turret at the time.
- * When marked as being in a vehicle or turret, the player's name will be enclosed within square brackets.
- * The length of the player's name found at the start of the wide character string does not reflect whether or not there will be square brackets (fortunately).
- *
- * The two bytes in between the killer section and the victim section are the method of homicide or suicide.
- * The color of the resulting icon is borrowed from the attributed killer's faction affiliation if it can be determined.
- * An unidentified method defaults to a skull and crossbones icon.
- * The exact range of unique and valid icon values for this parameter is currently unknown.
- * It is also unknown what the two bytes preceding `method` specify, as changing them does nothing to the displayed message.
+ * When marked as being in a vehicle or a turret, the player's name will be enclosed within square brackets.
+ * The length of the player's name found at the start of the character string does not reflect
+ * whether or not there will be square brackets (fortunately).
+ * The color of the resulting icon is borrowed from the attributed killer's faction affiliation if it can be determined
+ * and the type of icon is the same as an object id.
+ * An unidentified method or a missing icon defaults to a skull and crossbones.
* @param killer the name of the player who did the killing
- * @param killer_charId Same as CharacterInfoMessage
+ * @param killer_charId same as CharacterInfoMessage
* @param killer_empire the empire affiliation of the killer
- * @param killer_inVehicle true, if the killer was in a vehicle at the time of the kill; false, otherwise
+ * @param killer_in_vehicle true, if the killer was in a vehicle at the time of the kill; false, otherwise
* @param unk na; but does not like being set to 0
* @param method modifies the icon in the message, related to the way the victim was killed
* @param victim the name of the player who was killed
- * @param victim_charId Same as CharacterInfoMessage
+ * @param victim_charId same as CharacterInfoMessage
* @param victim_empire the empire affiliation of the victim
- * @param victim_inVehicle true, if the victim was in a vehicle when he was killed; false, otherwise
+ * @param victim_in_vehicle true, if the victim was in a vehicle when he was killed; false, otherwise
*/
final case class DestroyDisplayMessage(killer : String,
killer_charId : Long,
killer_empire : PlanetSideEmpire.Value,
- killer_inVehicle : Boolean,
+ killer_in_vehicle : Boolean,
unk : Int,
method : Int,
victim : String,
victim_charId : Long,
victim_empire : PlanetSideEmpire.Value,
- victim_inVehicle : Boolean
+ victim_in_vehicle : Boolean
)
extends PlanetSideGamePacket {
type Packet = DestroyDisplayMessage
@@ -68,12 +65,12 @@ object DestroyDisplayMessage extends Marshallable[DestroyDisplayMessage] {
("killer" | PacketHelpers.encodedWideString) ::
("killer_charId" | ulongL(32)) ::
("killer_empire" | PlanetSideEmpire.codec) ::
- ("killer_inVehicle" | bool) ::
+ ("killer_in_vehicle" | bool) ::
("unk" | uint16L) ::
("method" | uint16L) ::
("victim" | PacketHelpers.encodedWideStringAligned(5)) ::
("victim_charId" | ulongL(32)) ::
("victim_empire" | PlanetSideEmpire.codec) ::
- ("victim_inVehicle" | bool)
+ ("victim_in_vehicle" | bool)
).as[DestroyDisplayMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index 1dc999bf2..09b6d289e 100644
--- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -156,6 +156,10 @@ final case class PlanetsideAttributeMessage(player_guid : PlanetSideGUID,
}
object PlanetsideAttributeMessage extends Marshallable[PlanetsideAttributeMessage] {
+ def apply(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Int) : PlanetsideAttributeMessage = {
+ PlanetsideAttributeMessage(player_guid, attribute_type, attribute_value.toLong)
+ }
+
implicit val codec : Codec[PlanetsideAttributeMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
("attribute_type" | uint8L) ::
diff --git a/common/src/main/scala/services/avatar/AvatarAction.scala b/common/src/main/scala/services/avatar/AvatarAction.scala
index 8d3a0ea28..310bad7e6 100644
--- a/common/src/main/scala/services/avatar/AvatarAction.scala
+++ b/common/src/main/scala/services/avatar/AvatarAction.scala
@@ -1,14 +1,15 @@
// Copyright (c) 2017 PSForever
package services.avatar
+import net.psforever.objects.ballistics.SourceEntry
import net.psforever.objects.{PlanetSideGameObject, Player}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.Container
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
-import net.psforever.packet.game.{CargoMountPointStatusMessage, PlanetSideGUID, PlayerStateMessageUpstream}
+import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
-import net.psforever.types.ExoSuitType
+import net.psforever.types.{ExoSuitType, Vector3}
import scala.concurrent.duration.FiniteDuration
@@ -21,9 +22,14 @@ object AvatarAction {
final case class ChangeFireState_Start(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 Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : (Any)=>Unit) 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 DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
+ final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
+ final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
@@ -38,5 +44,4 @@ object AvatarAction {
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
-// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
}
diff --git a/common/src/main/scala/services/avatar/AvatarResponse.scala b/common/src/main/scala/services/avatar/AvatarResponse.scala
index b56d92cf6..03647d5dd 100644
--- a/common/src/main/scala/services/avatar/AvatarResponse.scala
+++ b/common/src/main/scala/services/avatar/AvatarResponse.scala
@@ -2,11 +2,12 @@
package services.avatar
import net.psforever.objects.Player
+import net.psforever.objects.ballistics.SourceEntry
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData
-import net.psforever.types.ExoSuitType
+import net.psforever.types.{ExoSuitType, Vector3}
object AvatarResponse {
trait Response
@@ -17,8 +18,13 @@ object AvatarResponse {
final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
final case class ConcealPlayer() extends Response
- final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
+ final case class DamageResolution(target : Player, resolution_function : (Any)=>Unit) 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
+ final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
+ final case class HitHint(source_guid : PlanetSideGUID) extends Response
+ final case class KilledWhileInVehicle() extends Response
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
final case class ObjectHeld(slot : Int) extends Response
@@ -31,6 +37,4 @@ object AvatarResponse {
final case class SendResponse(msg: PlanetSideGamePacket) extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
- // final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
- // final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
}
diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala
index f74fec1d1..c703b3601 100644
--- a/common/src/main/scala/services/avatar/AvatarService.scala
+++ b/common/src/main/scala/services/avatar/AvatarService.scala
@@ -65,6 +65,18 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
)
+ case AvatarAction.Damage(player_guid, target, resolution_function) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.DamageResolution(target, resolution_function))
+ )
+ case AvatarAction.Destroy(victim, killer, weapon, pos) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos))
+ )
+ case AvatarAction.DestroyDisplay(killer, victim, method, unk) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DestroyDisplay(killer, victim, method, unk))
+ )
case AvatarAction.DropItem(player_guid, item, zone) =>
val definition = item.Definition
val objectData = DroppedItemData(
@@ -86,6 +98,14 @@ class AvatarService extends Actor {
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
)
)
+ case AvatarAction.HitHint(source_guid, player_guid) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
+ )
+ case AvatarAction.KilledWhileInVehicle(player_guid) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.KilledWhileInVehicle())
+ )
case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) =>
val pkt = pdata match {
case Some(data) =>
@@ -169,22 +189,6 @@ class AvatarService extends Actor {
AvatarServiceReply.PlayerStateShift(killer)
))
}
- case AvatarService.DestroyDisplay(killer, victim) =>
- val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(victim)
- if (playerOpt.isDefined) {
- val player: PlayerAvatar = playerOpt.get
- AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, victim,
- AvatarServiceReply.DestroyDisplay(killer)
- ))
- }
- case AvatarService.HitHintReturn(source_guid,victim_guid) =>
- val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(source_guid)
- if (playerOpt.isDefined) {
- val player: PlayerAvatar = playerOpt.get
- AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, victim_guid,
- AvatarServiceReply.DestroyDisplay(source_guid)
- ))
- }
*/
case msg =>
log.warn(s"Unhandled message $msg from $sender")
diff --git a/common/src/main/scala/services/vehicle/VehicleAction.scala b/common/src/main/scala/services/vehicle/VehicleAction.scala
index 604256cf2..1ae95ea81 100644
--- a/common/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/common/src/main/scala/services/vehicle/VehicleAction.scala
@@ -21,7 +21,9 @@ object VehicleAction {
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action
+ final case class ObjectDelete(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
final case class Ownership(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
+ final case class PlanetsideAttribute(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
diff --git a/common/src/main/scala/services/vehicle/VehicleResponse.scala b/common/src/main/scala/services/vehicle/VehicleResponse.scala
index e083bd247..5c972bf63 100644
--- a/common/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/common/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
package services.vehicle
-import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
import net.psforever.packet.PlanetSideGamePacket
@@ -19,12 +18,14 @@ object VehicleResponse {
final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
final case class DismountVehicle(bailType : BailType.Value , unk2 : Boolean) extends Response
final case class EquipmentInSlot(pkt : ObjectCreateMessage) extends Response
+ final case class HitHint(source_guid : PlanetSideGUID) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response
final case class Ownership(vehicle_guid : PlanetSideGUID) extends Response
+ final case class PlanetsideAttribute(vehicle_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Response
final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
diff --git a/common/src/main/scala/services/vehicle/VehicleService.scala b/common/src/main/scala/services/vehicle/VehicleService.scala
index 6f347da79..cacfae0d1 100644
--- a/common/src/main/scala/services/vehicle/VehicleService.scala
+++ b/common/src/main/scala/services/vehicle/VehicleService.scala
@@ -92,6 +92,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid))
)
+ case VehicleAction.PlanetsideAttribute(exclude_guid, target_guid, attribute_type, attribute_value) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", exclude_guid, VehicleResponse.PlanetsideAttribute(target_guid, attribute_type, attribute_value))
+ )
case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission))
diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala
index b863499a2..7021914f6 100644
--- a/common/src/test/scala/objects/ConverterTest.scala
+++ b/common/src/test/scala/objects/ConverterTest.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package objects
-import net.psforever.objects.definition.converter.{ACEConverter, CharacterSelectConverter, REKConverter}
+import net.psforever.objects.definition.converter.{ACEConverter, CharacterSelectConverter, DestroyedVehicleConverter, REKConverter}
import net.psforever.objects._
import net.psforever.objects.definition._
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
@@ -9,6 +9,7 @@ import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
+import net.psforever.objects.vehicles.DestroyedVehicle
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
@@ -368,7 +369,40 @@ class ConverterTest extends Specification {
ams.Utilities(4)().GUID = PlanetSideGUID(417)
ams.Definition.Packet.ConstructorData(ams).isSuccess mustEqual true
- ok //TODO write more of this test
+ }
+
+ "convert to packet (3)" in {
+ val
+ ams = Vehicle(GlobalDefinitions.ams)
+ ams.GUID = PlanetSideGUID(413)
+ ams.Health = 0 //destroyed vehicle
+
+ ams.Definition.Packet.ConstructorData(ams).isSuccess mustEqual true
+ //did not initialize the utilities, but the converter did not fail
+ }
+ }
+
+ "DestroyedVehicle" should {
+ "not convert a working vehicle" in {
+ val ams = Vehicle(GlobalDefinitions.ams)
+ ams.GUID = PlanetSideGUID(413)
+ ams.Health mustEqual 3000 //not destroyed vehicle
+ DestroyedVehicleConverter.converter.ConstructorData(ams).isFailure mustEqual true
+ }
+
+ "convert to packet" in {
+ val ams = Vehicle(GlobalDefinitions.ams)
+ ams.GUID = PlanetSideGUID(413)
+ ams.Health = 0
+ DestroyedVehicleConverter.converter.ConstructorData(ams).isSuccess mustEqual true
+ //did not initialize the utilities, but the converter did not fail
+ }
+
+ "not convert into a detailed packet" in {
+ val ams = Vehicle(GlobalDefinitions.ams)
+ ams.GUID = PlanetSideGUID(413)
+ ams.Health = 0
+ DestroyedVehicleConverter.converter.DetailedConstructorData(ams).isFailure mustEqual true
}
}
}
\ No newline at end of file
diff --git a/common/src/test/scala/objects/DamageModelTests.scala b/common/src/test/scala/objects/DamageModelTests.scala
new file mode 100644
index 000000000..2b4c95b7d
--- /dev/null
+++ b/common/src/test/scala/objects/DamageModelTests.scala
@@ -0,0 +1,416 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects._
+import net.psforever.objects.vital.damage.{DamageCalculations, DamageProfile}
+import DamageCalculations._
+import net.psforever.objects.vital.resistance.ResistanceCalculations
+import ResistanceCalculations._
+import net.psforever.objects.vital.resolution.ResolutionCalculations
+import ResolutionCalculations._
+import net.psforever.objects.ballistics._
+import net.psforever.objects.vital.Vitality
+import net.psforever.types._
+import org.specs2.mutable.Specification
+
+class DamageCalculationsTests extends Specification {
+ "DamageCalculations" should {
+ val wep = GlobalDefinitions.galaxy_gunship_cannon
+ val wep_fmode = Tool(wep).FireMode
+ val wep_prof = wep_fmode.Modifiers.asInstanceOf[DamageProfile]
+ val proj = wep.ProjectileTypes.head
+ val proj_prof = proj.asInstanceOf[DamageProfile]
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val target = Vehicle(GlobalDefinitions.fury)
+ target.Position = Vector3(10, 0, 0)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3(50, 50, 0))
+ "extract no damage numbers" in {
+ NoDamageAgainst(proj_prof) mustEqual 0
+ }
+
+ "extract damage against exosuit target" in {
+ DamageAgainstExoSuit(proj_prof) mustEqual 50
+ }
+
+ "extract damage against MAX target" in {
+ DamageAgainstMaxSuit(proj_prof) mustEqual 75
+ }
+
+ "extract damage against vehicle target" in {
+ DamageAgainstVehicle(proj_prof) mustEqual 82
+ }
+
+ "extract damage against aircraft target" in {
+ DamageAgainstAircraft(proj_prof) mustEqual 82
+ }
+
+ "extract damage against something" in {
+ DamageAgainstUnknown(proj_prof) mustEqual 66
+ }
+
+ "extract a complete damage profile (1)" in {
+ val result = DamageAgainstVehicle(proj_prof) + DamageAgainstVehicle(wep_prof)
+ val func : (DamageProfile, List[DamageProfile]) => Int = DamageWithModifiers(DamageAgainstVehicle)
+ func(proj_prof, List(wep_prof)) mustEqual result
+ }
+
+ "extract a complete damage profile (2)" in {
+ val result = 2 * DamageAgainstVehicle(proj_prof) + DamageAgainstVehicle(wep_prof)
+ val func : (DamageProfile, List[DamageProfile]) => Int = DamageWithModifiers(DamageAgainstVehicle)
+ func(proj_prof, List(proj_prof, wep_prof)) mustEqual result
+ }
+
+ "calculate no distance" in {
+ NoDistance(resprojectile) mustEqual 0
+ }
+
+ "calculate too far distance" in {
+ TooFar(resprojectile) mustEqual Float.MaxValue
+ }
+
+ "calculate distance between target and source" in {
+ DistanceBetweenTargetandSource(resprojectile) mustEqual 10
+ }
+
+ "calculate distance between target and explosion (splash)" in {
+ DistanceFromExplosionToTarget(resprojectile) mustEqual 64.03124f
+ }
+
+ "calculate no damage from components" in {
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ val distance = DistanceFromExplosionToTarget(resprojectile)
+ DamageCalculations.NoDamage(projectile, result, distance) mustEqual 0
+ }
+
+ "calculate degraded damage from components (near)" in {
+ val projectile_alt = Projectile(GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
+ wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ DirectHitDamageWithDegrade(projectile_alt, result, 0) mustEqual 132
+ }
+
+ "calculate degraded damage from components (medium)" in {
+ val projectile_alt = Projectile(GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
+ wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ DirectHitDamageWithDegrade(projectile_alt, result, 250) mustEqual 103
+ }
+
+ "calculate degraded damage from components (far)" in {
+ val projectile_alt = Projectile(GlobalDefinitions.galaxy_gunship_gun_projectile, //need projectile with degrade
+ wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ DirectHitDamageWithDegrade(projectile_alt, result, 1000) mustEqual 0
+ }
+
+ "calculate splash damage from components (near)" in {
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ SplashDamageWithRadialDegrade(projectile, result, 0) mustEqual 264
+ }
+
+ "calculate splash damage from components (medium)" in {
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ SplashDamageWithRadialDegrade(projectile, result, 5) mustEqual 145
+ }
+
+ "calculate splash damage from components (far)" in {
+ val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
+ SplashDamageWithRadialDegrade(projectile, result, 6) mustEqual 0
+ }
+ }
+}
+
+class ResistanceCalculationsTests extends Specification {
+ val wep = GlobalDefinitions.galaxy_gunship_cannon
+ val wep_fmode = Tool(wep).FireMode
+ val proj = wep.ProjectileTypes.head //GlobalDefinitions.heavy_grenade_projectile
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2,2,0), Vector3.Zero)
+
+ "ResistanceCalculations" should {
+ "ignore all targets" in {
+ val target = Vehicle(GlobalDefinitions.fury)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ InvalidTarget(resprojectile).isFailure mustEqual true
+ }
+
+ "discern standard infantry targets" in {
+ val target = player
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ ValidInfantryTarget(resprojectile).isSuccess mustEqual true
+ ValidMaxTarget(resprojectile).isSuccess mustEqual false
+ ValidVehicleTarget(resprojectile).isSuccess mustEqual false
+ ValidAircraftTarget(resprojectile).isSuccess mustEqual false
+ }
+
+ "discern mechanized infantry targets" in {
+ val target = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ target.ExoSuit = ExoSuitType.MAX
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ ValidInfantryTarget(resprojectile).isSuccess mustEqual false
+ ValidMaxTarget(resprojectile).isSuccess mustEqual true
+ ValidVehicleTarget(resprojectile).isSuccess mustEqual false
+ ValidAircraftTarget(resprojectile).isSuccess mustEqual false
+ }
+
+ "discern ground vehicle targets" in {
+ val target = Vehicle(GlobalDefinitions.fury)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ ValidInfantryTarget(resprojectile).isSuccess mustEqual false
+ ValidMaxTarget(resprojectile).isSuccess mustEqual false
+ ValidVehicleTarget(resprojectile).isSuccess mustEqual true
+ ValidAircraftTarget(resprojectile).isSuccess mustEqual false
+ }
+
+ "discern flying vehicle targets" in {
+ val target = Vehicle(GlobalDefinitions.mosquito)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ ValidInfantryTarget(resprojectile).isSuccess mustEqual false
+ ValidMaxTarget(resprojectile).isSuccess mustEqual false
+ ValidVehicleTarget(resprojectile).isSuccess mustEqual false
+ ValidAircraftTarget(resprojectile).isSuccess mustEqual true
+ }
+
+ "extract no resistance values" in {
+ NoResistExtractor(SourceEntry(player)) mustEqual 0
+ }
+
+ "extract resistance values from exo-suit" in {
+ val pSource = PlayerSource(player)
+ ExoSuitDirectExtractor(pSource) mustEqual 4
+ ExoSuitSplashExtractor(pSource) mustEqual 15
+ ExoSuitAggravatedExtractor(pSource) mustEqual 8
+ ExoSuitRadiationExtractor(pSource) mustEqual 0
+ }
+
+ "extract resistance values from vehicle" in {
+ val vSource = VehicleSource(Vehicle(GlobalDefinitions.fury))
+ VehicleDirectExtractor(vSource) mustEqual 0
+ VehicleSplashExtractor(vSource) mustEqual 0
+ VehicleAggravatedExtractor(vSource) mustEqual 0
+ VehicleRadiationExtractor(vSource) mustEqual 0
+ }
+ }
+}
+
+class ResolutionCalculationsTests extends Specification {
+ val wep = GlobalDefinitions.suppressor
+ val wep_fmode = Tool(wep).FireMode
+ val proj = wep.ProjectileTypes.head
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ player.Spawn
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2,2,0), Vector3.Zero)
+
+ "ResolutionCalculations" should {
+ "calculate no damage" in {
+ val target = player
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target), target.DamageModel, Vector3.Zero)
+ ResolutionCalculations.NoDamage(resprojectile)(50,50) mustEqual 0
+ }
+
+ "calculate no infantry damage for vehicles" in {
+ val target1 = Vehicle(GlobalDefinitions.fury) //!
+ val resprojectile1 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target1), target1.DamageModel, Vector3.Zero)
+ InfantryDamageAfterResist(resprojectile1)(50, 10) mustEqual (0,0)
+
+ val target2 = player
+ val resprojectile2 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target2), target2.DamageModel, Vector3.Zero)
+ InfantryDamageAfterResist(resprojectile2)(50, 10) mustEqual (40,10)
+ }
+
+ "calculate health and armor damage for infantry target" in {
+ InfantryDamageAfterResist(100,100)(50, 10) mustEqual (40,10)
+ }
+
+ "calculate health and armor damage, with bonus damage, for infantry target" in {
+ //health = 100, armor = 5 -> resist 10 but only have 5, so rollover extra -> damages (40+5, 5)
+ InfantryDamageAfterResist(100,5)(50, 10) mustEqual (45,5)
+ }
+
+ "calculate health damage for infantry target" in {
+ //health = 100, armor = 0
+ InfantryDamageAfterResist(100,0)(50, 10) mustEqual (50,0)
+ }
+
+ "calculate armor damage for infantry target" in {
+ //resistance > damage
+ InfantryDamageAfterResist(100,100)(50, 60) mustEqual (0,50)
+ }
+
+ val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ player2.ExoSuit = ExoSuitType.MAX
+ player2.Spawn
+ "calculate no max damage for vehicles" in {
+ val target1 = Vehicle(GlobalDefinitions.fury) //!
+ val resprojectile1 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target1), target1.DamageModel, Vector3.Zero)
+ MaxDamageAfterResist(resprojectile1)(50, 10) mustEqual (0,0)
+
+ val target2 = player2
+ val resprojectile2 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target2), target2.DamageModel, Vector3.Zero)
+ MaxDamageAfterResist(resprojectile2)(50, 10) mustEqual (0,40)
+ }
+
+ "calculate health and armor damage for max target" in {
+ MaxDamageAfterResist(100,5)(50, 10) mustEqual (35,5)
+ }
+
+ "calculate health damage for max target" in {
+ //health = 100, armor = 0
+ MaxDamageAfterResist(100,0)(50, 10) mustEqual (40,0)
+ }
+
+ "calculate armor damage for max target" in {
+ //resistance > damage
+ MaxDamageAfterResist(100,100)(50, 10) mustEqual (0,40)
+ }
+
+ "do not care if target is infantry for vehicle calculations" in {
+ val target1 = player
+ val resprojectile1 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target1), target1.DamageModel, Vector3.Zero)
+ VehicleDamageAfterResist(resprojectile1)(50, 10) mustEqual 40
+
+ val target2 = Vehicle(GlobalDefinitions.fury) //!
+ val resprojectile2 = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(target2), target2.DamageModel, Vector3.Zero)
+ VehicleDamageAfterResist(resprojectile2)(50, 10) mustEqual 40
+ }
+ }
+
+ "calculate resisted damage for vehicle target" in {
+ VehicleDamageAfterResist(50, 10) mustEqual 40
+ }
+
+ "calculate un-resisted damage for vehicle target" in {
+ VehicleDamageAfterResist(50, 0) mustEqual 50
+ }
+}
+
+class DamageModelTests extends Specification {
+ val wep = GlobalDefinitions.suppressor
+ val wep_tool = Tool(wep)
+ val wep_fmode = wep_tool.FireMode
+ val proj = wep.ProjectileTypes.head
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ player.Spawn
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2,2,0), Vector3.Zero)
+
+ "DamageModel" should {
+ "be a part of vitality" in {
+ player.isInstanceOf[Vitality] mustEqual true
+ try {
+ player.getClass.getDeclaredMethod("DamageModel").hashCode()
+ }
+ catch {
+ case _ : Exception =>
+ ko //the method doesn't exist
+ }
+
+ wep_tool.isInstanceOf[Vitality] mustEqual false
+ try {
+ wep_tool.getClass.getDeclaredMethod("DamageModel").hashCode()
+ ko
+ }
+ catch {
+ case _ : Exception =>
+ ok //the method doesn't exist
+ }
+ ok
+ }
+
+ "resolve infantry targets" in {
+ val tplayer = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ tplayer.Spawn
+ tplayer.Health mustEqual 100
+ 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)
+
+ func(tplayer)
+ tplayer.Health mustEqual 87
+ tplayer.Armor mustEqual 46
+ }
+
+ "resolve infantry targets in a specific way" in {
+ val tplayer = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ tplayer.Spawn
+ tplayer.Health mustEqual 100
+ 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)
+
+ func(tplayer)
+ tplayer.Health mustEqual 81
+ tplayer.Armor mustEqual 35
+ }
+
+ "resolve infantry targets, with damage overflow" in {
+ val tplayer = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ tplayer.Spawn
+ tplayer.Health mustEqual 100
+ 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)
+ tplayer.Armor = 0
+
+ func(tplayer)
+ tplayer.Health mustEqual 83
+ tplayer.Armor mustEqual 0
+ }
+
+ "resolve vehicle targets" in {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ 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)
+
+ func(vehicle)
+ vehicle.Health mustEqual 641
+ }
+
+ "resolve vehicle targets (with shields)" in {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.Shields = 10
+ vehicle.Health mustEqual 650
+ 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)
+
+ func(vehicle)
+ vehicle.Health mustEqual 650
+ vehicle.Shields mustEqual 1
+ }
+
+ "resolve vehicle targets (losing shields)" in {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.Shields = 10
+ vehicle.Health mustEqual 650
+ 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)
+
+ func(vehicle)
+ vehicle.Health mustEqual 650
+ vehicle.Shields mustEqual 1
+ func(vehicle)
+ vehicle.Health mustEqual 642
+ vehicle.Shields mustEqual 0
+ }
+
+ "resolve vehicle targets in a specific way" in {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ 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)
+
+ func(vehicle)
+ vehicle.Health mustEqual 632
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/ProjectileTest.scala b/common/src/test/scala/objects/ProjectileTest.scala
index d28aba917..26d2a87cc 100644
--- a/common/src/test/scala/objects/ProjectileTest.scala
+++ b/common/src/test/scala/objects/ProjectileTest.scala
@@ -1,19 +1,29 @@
// Copyright (c) 2017 PSForever
package objects
-import net.psforever.objects.{GlobalDefinitions, LocalProjectile, Tool}
-import net.psforever.objects.ballistics.{DamageType, Projectile, ProjectileResolution, Projectiles}
+import net.psforever.objects._
+import net.psforever.objects.ballistics._
import net.psforever.objects.definition.ProjectileDefinition
-import net.psforever.types.Vector3
+import net.psforever.objects.serverobject.mblocker.Locker
+import net.psforever.objects.vital.DamageType
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types._
import org.specs2.mutable.Specification
class ProjectileTest extends Specification {
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val fury = Vehicle(GlobalDefinitions.fury)
+
"LocalProjectile" should {
"construct" in {
val obj = new LocalProjectile() //since they're just placeholders, they only need to construct
obj.Definition.ObjectId mustEqual 0
obj.Definition.Name mustEqual "projectile"
}
+
+ "local projectile range" in {
+ Projectile.BaseUID < Projectile.RangeUID mustEqual true
+ }
}
"ProjectileDefinition" should {
@@ -151,48 +161,156 @@ class ProjectileTest extends Specification {
}
}
- "Projectile" should {
- "construct" in {
- val beamer_wep = Tool(GlobalDefinitions.beamer)
- val projectile = beamer_wep.Projectile
- val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
+ "SourceEntry" should {
+ "construct for players" in {
+ SourceEntry(player) match {
+ case o : PlayerSource =>
+ o.Name mustEqual "TestCharacter"
+ o.Faction mustEqual PlanetSideEmpire.TR
+ o.Seated mustEqual false
+ o.ExoSuit mustEqual ExoSuitType.Standard
+ o.Health mustEqual 0
+ o.Armor mustEqual 0
+ o.Definition mustEqual GlobalDefinitions.avatar
+ o.Position mustEqual Vector3.Zero
+ o.Orientation mustEqual Vector3.Zero
+ o.Velocity mustEqual None
+ case _ =>
+ ko
+ }
+ }
- obj.profile mustEqual beamer_wep.Projectile
- obj.tool_def mustEqual GlobalDefinitions.beamer
+ "construct for vehicles" in {
+ SourceEntry(fury) match {
+ case o : VehicleSource =>
+ o.Name mustEqual "Fury"
+ o.Faction mustEqual PlanetSideEmpire.TR
+ o.Definition mustEqual GlobalDefinitions.fury
+ o.Health mustEqual 650
+ o.Shields mustEqual 0
+ o.Position mustEqual Vector3.Zero
+ o.Orientation mustEqual Vector3.Zero
+ o.Velocity mustEqual None
+ case _ =>
+ ko
+ }
+ }
+
+ "construct for generic object" in {
+ val obj = Locker()
+ SourceEntry(obj) match {
+ case o : ObjectSource =>
+ o.obj mustEqual obj
+ o.Name mustEqual "Mb Locker"
+ o.Faction mustEqual PlanetSideEmpire.NEUTRAL
+ o.Definition mustEqual GlobalDefinitions.mb_locker
+ o.Position mustEqual Vector3.Zero
+ o.Orientation mustEqual Vector3.Zero
+ o.Velocity mustEqual None
+ case _ =>
+ ko
+ }
+ }
+
+ "contain timely information" in {
+ val obj = Player(Avatar("TestCharacter-alt", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ obj.VehicleSeated = Some(PlanetSideGUID(1))
+ obj.Position = Vector3(1.2f, 3.4f, 5.6f)
+ obj.Orientation = Vector3(2.1f, 4.3f, 6.5f)
+ obj.Velocity = Some(Vector3(1.1f, 2.2f, 3.3f))
+ val sobj = SourceEntry(obj)
+ obj.VehicleSeated = None
+ obj.Position = Vector3.Zero
+ obj.Orientation = Vector3.Zero
+ obj.Velocity = None
+ obj.ExoSuit = ExoSuitType.Agile
+
+ sobj match {
+ case o : PlayerSource =>
+ o.Name mustEqual "TestCharacter-alt"
+ o.Faction mustEqual PlanetSideEmpire.TR
+ o.Seated mustEqual true
+ o.ExoSuit mustEqual ExoSuitType.Standard
+ o.Definition mustEqual GlobalDefinitions.avatar
+ o.Position mustEqual Vector3(1.2f, 3.4f, 5.6f)
+ o.Orientation mustEqual Vector3(2.1f, 4.3f, 6.5f)
+ o.Velocity mustEqual Some(Vector3(1.1f, 2.2f, 3.3f))
+ case _ =>
+ ko
+ }
+ }
+ }
+
+ "Projectile" should {
+ val beamer_def = GlobalDefinitions.beamer
+ val beamer_wep = Tool(beamer_def)
+ val firemode = beamer_wep.FireMode
+ val projectile = beamer_wep.Projectile
+
+ "construct" in {
+ val obj = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, PlayerSource(player), beamer_def.ObjectId, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
+ obj.profile mustEqual projectile
+ obj.tool_def mustEqual beamer_def
+ obj.fire_mode mustEqual firemode
+ obj.owner match {
+ case _ : PlayerSource =>
+ ok
+ case _ =>
+ ko
+ }
+ obj.attribute_to mustEqual obj.tool_def.ObjectId
obj.shot_origin mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.shot_angle mustEqual Vector3(0.2f, 0.4f, 0.6f)
- obj.resolution mustEqual ProjectileResolution.Unresolved
obj.fire_time <= System.nanoTime mustEqual true
- obj.hit_time mustEqual 0
+ obj.isResolved mustEqual false
+ }
+
+ "construct (different attribute)" in {
+ val obj1 = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, player, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
+ obj1.attribute_to mustEqual obj1.tool_def.ObjectId
+
+ val obj2 = Projectile(beamer_wep.Projectile, beamer_wep.Definition, beamer_wep.FireMode, PlayerSource(player), 65, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
+ obj2.attribute_to == obj2.tool_def.ObjectId mustEqual false
+ obj2.attribute_to mustEqual 65
}
"resolve" in {
- val beamer_wep = Tool(GlobalDefinitions.beamer)
- val projectile = beamer_wep.Projectile
- val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
- val obj2 = obj.Resolve(ProjectileResolution.MissedShot)
+ val obj = Projectile(projectile, beamer_def, firemode, PlayerSource(player), beamer_def.ObjectId, Vector3.Zero, Vector3.Zero)
+ obj.isResolved mustEqual false
+ obj.isMiss mustEqual false
- obj.resolution mustEqual ProjectileResolution.Unresolved
- obj.fire_time <= System.nanoTime mustEqual true
- obj.hit_time mustEqual 0
- obj2.resolution mustEqual ProjectileResolution.MissedShot
- obj2.fire_time == obj.fire_time mustEqual true
- obj2.hit_time <= System.nanoTime mustEqual true
- obj2.fire_time <= obj2.hit_time mustEqual true
+ obj.Resolve()
+ obj.isResolved mustEqual true
+ obj.isMiss mustEqual false
}
- "resolve, with coordinates" in {
- val beamer_wep = Tool(GlobalDefinitions.beamer)
- val projectile = beamer_wep.Projectile
- val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
- val obj2 = obj.Resolve(Vector3(7.2f, 8.4f, 9.6f), Vector3(1.2f, 1.4f, 1.6f), ProjectileResolution.Resolved)
+ "missed" in {
+ val obj = Projectile(projectile, beamer_def, firemode, PlayerSource(player), beamer_def.ObjectId, Vector3.Zero, Vector3.Zero)
+ obj.isResolved mustEqual false
+ obj.isMiss mustEqual false
- obj.resolution mustEqual ProjectileResolution.Unresolved
- obj.current.Position mustEqual Vector3.Zero
- obj.current.Orientation mustEqual Vector3.Zero
- obj2.resolution mustEqual ProjectileResolution.Resolved
- obj2.current.Position mustEqual Vector3(7.2f, 8.4f, 9.6f)
- obj2.current.Orientation mustEqual Vector3(1.2f, 1.4f, 1.6f)
+ obj.Miss()
+ obj.isResolved mustEqual true
+ obj.isMiss mustEqual true
+ }
+ }
+
+ "ResolvedProjectile" should {
+ val beamer_wep = Tool(GlobalDefinitions.beamer)
+ val p_source = PlayerSource(player)
+ val player2 = Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute))
+ val p2_source = PlayerSource(player2)
+ val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
+ val fury_dm = fury.DamageModel
+
+ "construct" in {
+ val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, PlayerSource(player2), fury_dm, Vector3(1.2f, 3.4f, 5.6f), 123456L)
+ obj.resolution mustEqual ProjectileResolution.Hit
+ obj.projectile mustEqual projectile
+ obj.target mustEqual p2_source
+ obj.damage_model mustEqual fury.DamageModel
+ obj.hit_pos mustEqual Vector3(1.2f, 3.4f, 5.6f)
+ obj.hit_time mustEqual 123456L
}
}
}
diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala
index 94c2878d9..0cb21ef96 100644
--- a/common/src/test/scala/objects/VehicleTest.scala
+++ b/common/src/test/scala/objects/VehicleTest.scala
@@ -4,14 +4,16 @@ package objects
import akka.actor.Props
import base.ActorTest
import net.psforever.objects._
+import net.psforever.objects.ballistics.{PlayerSource, Projectile, ProjectileResolution, ResolvedProjectile}
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles._
+import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.{CharacterVoice, ExoSuitType}
+import net.psforever.types._
import org.specs2.mutable._
-import scala.concurrent.duration.Duration
+import scala.concurrent.duration._
class VehicleTest extends Specification {
import VehicleTest._
@@ -611,6 +613,103 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
}
}
+class VehicleControlShieldsChargingTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ "charge vehicle shields" in {
+ assert(vehicle.Shields == 0)
+ assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ val msg = receiveOne(500 milliseconds)
+ assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge])
+ assert(vehicle.Shields == 15)
+ assert(vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ }
+}
+
+class VehicleControlShieldsNotChargingVehicleDeadTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ "not charge vehicle shields if the vehicle is destroyed" in {
+ assert(vehicle.Health > 0)
+ vehicle.Health = 0
+ assert(vehicle.Health == 0)
+ assert(vehicle.Shields == 0)
+ assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ expectNoMsg(1 seconds)
+ assert(vehicle.Shields == 0)
+ assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ }
+}
+
+class VehicleControlShieldsNotChargingVehicleShieldsFullTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ "not charge vehicle shields if the vehicle is destroyed" in {
+ assert(vehicle.Shields == 0)
+ vehicle.Shields = vehicle.MaxShields
+ assert(vehicle.Shields == vehicle.MaxShields)
+ assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ expectNoMsg(1 seconds)
+ assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
+ }
+}
+
+class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+
+ "charge vehicle shields" in {
+ assert(vehicle.Shields == 0)
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ val msg = receiveOne(200 milliseconds)
+ assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge])
+ assert(vehicle.Shields == 15)
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ expectNoMsg(200 milliseconds)
+ assert(vehicle.Shields == 15)
+ }
+}
+
+class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ vehicle.GUID = PlanetSideGUID(10)
+ vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
+ //
+ val beamer_wep = Tool(GlobalDefinitions.beamer)
+ val p_source = PlayerSource( Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
+ val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
+ val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
+ val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime)
+
+ "charge vehicle shields" in {
+ assert(vehicle.Shields == 0)
+ vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj)})
+
+ val msg = receiveOne(200 milliseconds)
+ assert(msg.isInstanceOf[Vitality.DamageResolution])
+ assert(vehicle.Shields == 0)
+ vehicle.Actor ! Vehicle.ChargeShields(15)
+
+ expectNoMsg(200 milliseconds)
+ assert(vehicle.Shields == 0)
+ }
+}
+
object VehicleTest {
import net.psforever.objects.Avatar
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
diff --git a/common/src/test/scala/objects/VitalityTest.scala b/common/src/test/scala/objects/VitalityTest.scala
new file mode 100644
index 000000000..ca527ea02
--- /dev/null
+++ b/common/src/test/scala/objects/VitalityTest.scala
@@ -0,0 +1,83 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import net.psforever.objects.ballistics._
+import net.psforever.objects._
+import net.psforever.objects.vital._
+import net.psforever.types._
+import org.specs2.mutable.Specification
+
+class VitalityTest extends Specification {
+ "Vitality" should {
+ val wep = GlobalDefinitions.galaxy_gunship_cannon
+ val wep_fmode = Tool(wep).FireMode
+ val proj = wep.ProjectileTypes.head
+ val vehicle = Vehicle(GlobalDefinitions.fury)
+ val vSource = VehicleSource(vehicle)
+
+ "accept a variety of events" in {
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val pSource = PlayerSource(player)
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(player), player.DamageModel, Vector3(50, 50, 0))
+
+ player.History(resprojectile) //ResolvedProjectile, straight-up
+ player.History(DamageFromProjectile(resprojectile))
+ player.History(HealFromKit(pSource, 10, GlobalDefinitions.medkit))
+ player.History(HealFromTerm(pSource, 10, 0, GlobalDefinitions.order_terminal))
+ player.History(HealFromImplant(pSource, 10, ImplantType.AdvancedRegen))
+ player.History(HealFromExoSuitChange(pSource, ExoSuitType.Standard))
+ player.History(RepairFromTerm(vSource, 10, GlobalDefinitions.order_terminal))
+ player.History(VehicleShieldCharge(vSource, 10))
+ player.History(PlayerSuicide(pSource))
+ ok
+ }
+
+ "return and clear the former list of vital activities" in {
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val pSource = PlayerSource(player)
+
+ player.History(HealFromKit(pSource, 10, GlobalDefinitions.medkit))
+ player.History(HealFromTerm(pSource, 10, 0, GlobalDefinitions.order_terminal))
+ player.History(HealFromImplant(pSource, 10, ImplantType.AdvancedRegen))
+ player.History(HealFromExoSuitChange(pSource, ExoSuitType.Standard))
+ player.History(RepairFromTerm(vSource, 10, GlobalDefinitions.order_terminal))
+ player.History(VehicleShieldCharge(vSource, 10))
+ player.History(PlayerSuicide(pSource))
+ player.History.size mustEqual 7
+
+ val list = player.ClearHistory()
+ player.History.size mustEqual 0
+ list.head.isInstanceOf[PlayerSuicide] mustEqual true
+ list(1).isInstanceOf[VehicleShieldCharge] mustEqual true
+ list(2).isInstanceOf[RepairFromTerm] mustEqual true
+ list(3).isInstanceOf[HealFromExoSuitChange] mustEqual true
+ list(4).isInstanceOf[HealFromImplant] mustEqual true
+ list(5).isInstanceOf[HealFromTerm] mustEqual true
+ list(6).isInstanceOf[HealFromKit] mustEqual true
+ }
+
+ "get exactly one entry that was caused by projectile damage" in {
+ val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
+ val pSource = PlayerSource(player)
+ val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
+ val resprojectile = ResolvedProjectile(ProjectileResolution.Splash, projectile, SourceEntry(player), player.DamageModel, Vector3(50, 50, 0))
+
+ player.History(DamageFromProjectile(resprojectile))
+ player.History(HealFromKit(pSource, 10, GlobalDefinitions.medkit))
+ player.History(HealFromTerm(pSource, 10, 0, GlobalDefinitions.order_terminal))
+ player.History(HealFromImplant(pSource, 10, ImplantType.AdvancedRegen))
+ player.History(HealFromExoSuitChange(pSource, ExoSuitType.Standard))
+ player.History(RepairFromTerm(vSource, 10, GlobalDefinitions.order_terminal))
+ player.History(VehicleShieldCharge(vSource, 10))
+ player.History(PlayerSuicide(pSource))
+
+ player.LastShot match {
+ case Some(resolved_projectile) =>
+ resolved_projectile.projectile mustEqual projectile
+ case None =>
+ ko
+ }
+ }
+ }
+}
diff --git a/common/src/test/scala/service/RemoverActorTest.scala b/common/src/test/scala/service/RemoverActorTest.scala
index 73f870549..c0cc62340 100644
--- a/common/src/test/scala/service/RemoverActorTest.scala
+++ b/common/src/test/scala/service/RemoverActorTest.scala
@@ -15,34 +15,34 @@ import services.{RemoverActor, ServiceManager}
import scala.concurrent.duration._
-class StandardRemoverActorTest extends ActorTest {
- ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-
- "RemoverActor" should {
- "handle a simple task" in {
- expectNoMsg(500 milliseconds)
- val probe = TestProbe()
- val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
- remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
-
- val reply1 = probe.receiveOne(200 milliseconds)
- assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
- val reply2 = probe.receiveOne(200 milliseconds)
- assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
- probe.expectNoMsg(1 seconds) //delay
- val reply3 = probe.receiveOne(300 milliseconds)
- assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
- val reply4 = probe.receiveOne(300 milliseconds)
- assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
- val reply5 = probe.receiveOne(300 milliseconds)
- assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
- val reply6 = probe.receiveOne(500 milliseconds)
- assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
- val reply7 = probe.receiveOne(500 milliseconds)
- assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
- }
- }
-}
+//class StandardRemoverActorTest extends ActorTest {
+// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
+//
+// "RemoverActor" should {
+// "handle a simple task" in {
+// expectNoMsg(500 milliseconds)
+// val probe = TestProbe()
+// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
+//
+// val reply1 = probe.receiveOne(200 milliseconds)
+// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
+// val reply2 = probe.receiveOne(200 milliseconds)
+// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
+// probe.expectNoMsg(1 seconds) //delay
+// val reply3 = probe.receiveOne(300 milliseconds)
+// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
+// val reply4 = probe.receiveOne(300 milliseconds)
+// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
+// val reply5 = probe.receiveOne(300 milliseconds)
+// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
+// val reply6 = probe.receiveOne(500 milliseconds)
+// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
+// val reply7 = probe.receiveOne(500 milliseconds)
+// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
+// }
+// }
+//}
//class DelayedRemoverActorTest extends ActorTest {
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 18b55d833..39d10935a 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -13,8 +13,9 @@ import csr.{CSRWarp, CSRZone, Traveler}
import net.psforever.objects.GlobalDefinitions._
import services.ServiceManager.Lookup
import net.psforever.objects._
+import net.psforever.objects.ballistics._
import net.psforever.objects.definition.ToolDefinition
-import net.psforever.objects.definition.converter.CorpseConverter
+import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter}
import net.psforever.objects.equipment._
import net.psforever.objects.loadouts._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
@@ -35,7 +36,9 @@ import net.psforever.objects.serverobject.structures.{Building, StructureType, W
import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.serverobject.tube.SpawnTube
-import net.psforever.objects.vehicles._
+import net.psforever.objects.serverobject.turret.{MannedTurret, TurretUpgrade}
+import net.psforever.objects.vehicles.{AccessPermissionGroup, Cargo, Utility, VehicleLockState, _}
+import net.psforever.objects.vital._
import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
@@ -53,8 +56,6 @@ import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success
import akka.pattern.ask
-import net.psforever.objects.ballistics.{Projectile, ProjectileResolution}
-import net.psforever.objects.serverobject.turret.{MannedTurret, TurretUpgrade}
class WorldSessionActor extends Actor with MDCContextAware {
import WorldSessionActor._
@@ -109,6 +110,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0
override def postStop() = {
+ //TODO normally, player avatar persists a minute or so after disconnect; we are subject to the SessionReaper
clientKeepAlive.cancel
reviveTimer.cancel
respawnTimer.cancel
@@ -133,20 +135,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
})
+ //TODO final character save before doing any of this
+ continent.Population ! Zone.Population.Release(avatar)
if(player.isAlive) {
//actually being alive or manually deconstructing
- DismountVehicleOnLogOut()
- continent.Population ! Zone.Population.Release(avatar)
- player.Position = Vector3.Zero //save character before doing this
- avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectDelete(player_guid, player_guid))
+ player.Position = Vector3.Zero
+ if(player.VehicleSeated.nonEmpty) {
+ //quickly and briefly kill player to avoid disembark animation
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
+ DismountVehicleOnLogOut()
+ }
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid))
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
- //TODO normally, the actual player avatar persists a minute or so after the user disconnects
}
else if(continent.LivePlayers.contains(player) && !continent.Corpses.contains(player)) {
//player disconnected while waiting for a revive
//similar to handling ReleaseAvatarRequestMessage
player.Release
- continent.Population ! Zone.Population.Release(avatar)
player.VehicleSeated match {
case None =>
FriskCorpse(player) //TODO eliminate dead letters
@@ -189,7 +194,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(vehicle : Vehicle) =>
vehicle.Seat(vehicle.PassengerInSeat(player).get).get.Occupant = None
if(vehicle.Seats.values.count(_.isOccupied) == 0) {
- vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent), vehicle.Definition.DeconstructionTime) //start vehicle decay
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, vehicle.Definition.DeconstructionTime)) //start vehicle decay
}
vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
@@ -392,7 +397,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(GenericObjectActionMessage(guid, 36))
}
- case msg @ AvatarResponse.DropItem(pkt) =>
+ case AvatarResponse.DamageResolution(target, resolution_function) =>
+ if(player.isAlive) {
+ resolution_function(target)
+
+ val health = player.Health
+ val armor = player.Armor
+ val playerGUID = player.GUID
+ sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
+ sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 4, armor))
+ if(health == 0 && player.isAlive) {
+ KillPlayer(player)
+ }
+ }
+
+ case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
+ // guid = victim // killer = killer ;)
+ sendResponse(DestroyMessage(victim, killer, weapon, pos))
+
+ case AvatarResponse.DestroyDisplay(killer, victim, method, unk) =>
+ sendResponse(DestroyDisplayMessage(killer, victim, method, unk))
+
+ case AvatarResponse.DropItem(pkt) =>
if(tplayer_guid != guid) {
sendResponse(pkt)
}
@@ -402,6 +430,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(pkt)
}
+ case AvatarResponse.HitHint(source_guid) =>
+ sendResponse(HitHint(source_guid, guid))
+
+ case AvatarResponse.KilledWhileInVehicle() =>
+ if(player.isAlive && player.VehicleSeated.nonEmpty) {
+ continent.GUID(player.VehicleSeated.get) match {
+ case Some(vehicle : Vehicle) =>
+ if(vehicle.Health == 0) {
+ vehicle.LastShot match {
+ case Some(cause) =>
+ player.History(cause)
+ case None => ;
+ }
+ KillPlayer(player)
+ }
+ case _ => ;
+ }
+ }
+
case AvatarResponse.LoadPlayer(pkt) =>
if(tplayer_guid != guid) {
sendResponse(pkt)
@@ -544,7 +591,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) }
reply match {
case VehicleResponse.Ownership(vehicle_guid) =>
- sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid.toLong))
+ sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid))
case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) =>
sendResponse(ObjectAttachMessage(pad_guid, vehicle_guid, 3))
@@ -577,6 +624,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(pkt)
}
+ case VehicleResponse.HitHint(source_guid) =>
+ sendResponse(HitHint(source_guid, player.GUID))
+
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(tplayer_guid != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
@@ -621,6 +671,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
}
+ case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) =>
+ if(tplayer_guid != guid) {
+ sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type, attribute_value))
+ }
+
case VehicleResponse.ResetSpawnPad(pad_guid) =>
sendResponse(GenericObjectActionMessage(pad_guid, 92))
@@ -787,6 +842,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Mountable.CanMount(obj : Vehicle, seat_num) =>
val obj_guid : PlanetSideGUID = obj.GUID
+ val player_guid : PlanetSideGUID = tplayer.GUID
+ log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num")
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
+ PlayerActionsToCancel()
+ sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
+ sendResponse(PlanetsideAttributeMessage(obj_guid, 68, 0)) //shield health
+ sendResponse(PlanetsideAttributeMessage(obj_guid, 113, 0)) //capacitor
if(seat_num == 0) { //simplistic vehicle ownership management
obj.Owner match {
case Some(owner_guid) =>
@@ -807,7 +869,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//update mounted weapon belonging to seat
weapon.AmmoSlots.foreach(slot => { //update the magazine(s) in the weapon, specifically
val magazine = slot.Box
- sendResponse(InventoryStateMessage(magazine.GUID, weapon.GUID, magazine.Capacity.toLong))
+ sendResponse(InventoryStateMessage(magazine.GUID, weapon.GUID, magazine.Capacity))
})
case _ => ; //no weapons to update
}
@@ -859,6 +921,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Terminal.TerminalMessage(tplayer, msg, order) =>
order match {
case Terminal.BuyExosuit(exosuit, subtype) => //refresh armor points
+ tplayer.History(HealFromExoSuitChange(PlayerSource(tplayer), exosuit))
if(tplayer.ExoSuit == exosuit) {
if(exosuit == ExoSuitType.MAX) {
//special MAX case - clear any special state
@@ -1005,6 +1068,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment
log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true))
+ tplayer.History(HealFromExoSuitChange(PlayerSource(tplayer), exosuit))
//ensure arm is down
tplayer.DrawnSlot = Player.HandsDownSlot
sendResponse(ObjectHeldMessage(tplayer.GUID, Player.HandsDownSlot, true))
@@ -1105,7 +1169,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
else {
//accommodate as much of inventory as possible
- //TODO map x,y -> x,y rather than reorganize items
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory) //dropped items can be forgotten
stow
}
@@ -1121,7 +1184,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(!tplayer.Certifications.contains(cert)) {
log.info(s"$tplayer is learning the $cert certification for $cost points")
avatar.Certifications += cert
- sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 24, cert.id.toLong))
+ sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 24, cert.id))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true))
}
else {
@@ -1133,7 +1196,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(tplayer.Certifications.contains(cert)) {
log.info(s"$tplayer is forgetting the $cert certification for $cost points")
avatar.Certifications -= cert
- sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 25, cert.id.toLong))
+ sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 25, cert.id))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Sell, true))
}
else {
@@ -1289,9 +1352,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on
- //sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, 10))//vehicle.Definition.MaxHealth))
- sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) // Shield health
- sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) // Capacitor (EMP)
ReloadVehicleAccessPermissions(vehicle)
ServerVehicleLock(vehicle)
@@ -1551,6 +1611,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil))
sendResponse(FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true, Nil))
+ avatarService ! Service.Join(avatar.name) //will be same as player.Name
cluster ! InterstellarCluster.GetWorld("z6")
case InterstellarCluster.GiveWorld(zoneId, zone) =>
@@ -1750,6 +1811,50 @@ class WorldSessionActor extends Actor with MDCContextAware {
case DelayedProximityUnitStop(terminal) =>
StopUsingProximityUnit(terminal)
+ case Vitality.DamageResolution(target : Vehicle) =>
+ val targetGUID = target.GUID
+ val playerGUID = player.GUID
+ val continentId = continent.Id
+ val players = target.Seats.values.filter(seat => { seat.isOccupied && seat.Occupant.get.isAlive })
+ if(target.Health > 0) {
+ //alert occupants to damage source
+ players.foreach(seat => {
+ val tplayer = seat.Occupant.get
+ avatarService ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(playerGUID, tplayer.GUID))
+ })
+ }
+ else {
+ //alert to vehicle death (hence, occupants' deaths)
+ players.foreach(seat => {
+ val tplayer = seat.Occupant.get
+ val tplayerGUID = tplayer.GUID
+ avatarService ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
+ avatarService ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
+ })
+ //vehicle wreckage has no weapons
+ target.Weapons.values
+ .filter { _.Equipment.nonEmpty }
+ .foreach(slot => {
+ val wep = slot.Equipment.get
+ avatarService ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
+ })
+ if(target.Definition == GlobalDefinitions.ams) {
+ target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
+ ClearCurrentAmsSpawnPoint()
+ }
+ avatarService ! AvatarServiceMessage(continentId, AvatarAction.Destroy(targetGUID, playerGUID, playerGUID, target.Position))
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), continent))
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, continent, Some(1 minute)))
+ }
+ vehicleService ! VehicleServiceMessage(continentId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
+ vehicleService ! VehicleServiceMessage(s"${target.Actor}", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, target.Shields))
+
+ case Vitality.DamageResolution(target : PlanetSideGameObject) =>
+ log.warn(s"Vital target ${target.Definition.Name} damage resolution not supported using this method")
+
+ case Vehicle.UpdateShieldsCharge(vehicle) =>
+ vehicleService ! VehicleServiceMessage(s"${vehicle.Actor}", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields))
+
case ResponseToSelf(pkt) =>
log.info(s"Received a direct message: $pkt")
sendResponse(pkt)
@@ -1780,7 +1885,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO begin temp player character auto-loading; remove later
import net.psforever.objects.GlobalDefinitions._
import net.psforever.types.CertificationType._
- avatar = Avatar("TestCharacter" + sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
+ val avatar = Avatar("TestCharacter" + sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1)
avatar.Certifications += StandardAssault
avatar.Certifications += MediumAssault
avatar.Certifications += StandardExoSuit
@@ -1814,6 +1919,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += AdvancedEngineering
avatar.Certifications += FortificationEngineering
avatar.Certifications += AssaultEngineering
+ this.avatar = avatar
AwardBattleExperiencePoints(avatar, 1000000L)
player = new Player(avatar)
//player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) //home3, HART C
@@ -1992,8 +2098,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.Corpses.foreach {
TurnPlayerIntoCorpse
}
- //load active vehicles in zone
- continent.Vehicles.foreach(vehicle => {
+ //load vehicles in zone
+ val (wreckages, vehicles) = continent.Vehicles.partition(vehicle => { vehicle.Health == 0 && vehicle.Definition.DestroyedModel.nonEmpty })
+ //active vehicles
+ vehicles.foreach(vehicle => {
val vehicle_guid = vehicle.GUID
val vdefinition = vehicle.Definition
sendResponse(ObjectCreateMessage(vdefinition.ObjectId, vehicle_guid, vdefinition.Packet.ConstructorData(vehicle).get))
@@ -2014,9 +2122,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
ReloadVehicleAccessPermissions(vehicle)
})
-
- // Loop over vehicles again to add cargohold occupants after all vehicles have been created on the local client
- continent.Vehicles.foreach(vehicle => {
+ //Loop over vehicles again to add cargohold occupants after all vehicles have been created on the local client
+ vehicles.foreach(vehicle => {
vehicle.CargoHolds.foreach({ case (cargo_num, cargo) => {
cargo.Occupant match {
case Some(cargo_vehicle) =>
@@ -2031,6 +2138,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}})
})
+ //vehicle wreckages
+ wreckages.foreach(vehicle => {
+ sendResponse(
+ ObjectCreateMessage(
+ vehicle.Definition.DestroyedModel.get.id,
+ vehicle.GUID,
+ DestroyedVehicleConverter.converter.ConstructorData(vehicle).get
+ )
+ )
+ })
//implant terminals
continent.Map.TerminalToInterface.foreach({ case ((terminal_guid, interface_guid)) =>
@@ -2198,8 +2315,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case Some(_) =>
- //TODO we do not want to delete the player if he is seated in a vehicle when releasing
- //TODO it is necessary for now until we know how to juggle ownership properly
val player_guid = player.GUID
sendResponse(ObjectDeleteMessage(player_guid, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
@@ -2207,8 +2322,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(50 milliseconds, self, UnregisterCorpseOnVehicleDisembark(player))
- //sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0))
- //sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
}
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
@@ -2289,7 +2402,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
if(messagetype == ChatMessageType.CMT_SUICIDE) {
if(player.isAlive && deadState != DeadState.Release) {
- KillPlayer(player)
+ Suicide(player)
}
}
if(messagetype == ChatMessageType.CMT_DESTROY) {
@@ -2726,10 +2839,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(_ : LocalProjectile) =>
FindProjectileEntry(object_guid) match {
case Some(projectile) =>
- if(projectile.resolution != ProjectileResolution.Unresolved) {
- log.warn(s"RequestDestroy: tried to clean up missed projectile ${object_guid.guid} but it was already resolved")
+ if(projectile.isResolved) {
+ log.warn(s"RequestDestroy: tried to clean up projectile ${object_guid.guid} but it was already resolved")
+ }
+ else {
+ projectile.Miss()
}
- ResolveProjectileEntry(object_guid, ProjectileResolution.MissedShot)
case None =>
log.warn(s"RequestDestroy: projectile ${object_guid.guid} has never been fired")
}
@@ -2901,7 +3016,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
sendResponse(ObjectDeleteMessage(kit.GUID, 0))
taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
- //TODO better health/damage control workflow
+ player.History(HealFromKit(PlayerSource(player), 25, kit.Definition))
player.Health = player.Health + 25
sendResponse(PlanetsideAttributeMessage(avatar_guid, 0, player.Health))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(avatar_guid, 0, player.Health))
@@ -3224,16 +3339,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
else { //shooting
tool.Discharge
val projectileIndex = projectile_guid.guid - Projectile.BaseUID
- val ang = obj match {
- case _ : Player =>
- obj.Orientation //TODO upper body facing
+ val projectilePlace = projectiles(projectileIndex)
+ if(projectilePlace match {
+ case Some(projectile) => !projectile.isResolved
+ case None => false
+ }) {
+ log.warn(s"WeaponFireMessage: former projectile ${projectile_guid.guid} was not resolved properly; overwriting anyway")
+ }
+ val (angle, attribution) = obj match {
+ case p : Player =>
+ (p.Orientation, tool.Definition.ObjectId) //TODO upper body facing
case _ : Vehicle =>
- tool.Orientation //TODO this is too simplistic
+ (tool.Orientation, obj.Definition.ObjectId) //TODO this is too simplistic to find proper angle
case _ =>
- Vector3.Zero
+ (obj.Orientation, obj.Definition.ObjectId)
}
projectiles(projectileIndex) =
- Some(Projectile(tool.Projectile, tool.Definition, shot_origin, ang))
+ Some(Projectile(tool.Projectile, tool.Definition, tool.FireMode, player, attribution, shot_origin, angle))
}
case _ => ;
}
@@ -3243,15 +3365,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
log.info(s"Hit: $msg")
- ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit)
+ (hit_info match {
+ case Some(hitInfo) =>
+ continent.GUID(hitInfo.hitobject_guid.get) match {
+ case Some(obj : Player) =>
+ Some((obj, hitInfo.shot_origin, hitInfo.hit_pos))
+ case Some(obj : Vehicle) =>
+ Some((obj, hitInfo.shot_origin, hitInfo.hit_pos))
+ case _ =>
+ None
+ }
+ case None => ;
+ None
+ }) match {
+ case Some((target, shotOrigin, hitPos)) =>
+ ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit, target, hitPos) match {
+ case Some(projectile) =>
+ HandleDealingDamage(target, projectile)
+ case None => ;
+ }
+ case None => ;
+ }
case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) =>
log.info(s"Splash: $msg")
- ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash)
+ continent.GUID(direct_victim_uid) match {
+ case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
+ ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit, target, explosion_pos) match {
+ case Some(projectile) =>
+ HandleDealingDamage(target, projectile)
+ case None => ;
+ }
+ case _ => ;
+ }
+ targets.foreach(elem => {
+ continent.GUID(elem.uid) match {
+ case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
+ ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash, target, target.Position) match {
+ case Some(projectile) =>
+ HandleDealingDamage(target, projectile)
+ case None => ;
+ }
+ case _ => ;
+ }
+ })
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
log.info(s"Lash: $msg")
- ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash)
+ continent.GUID(victim_guid) match {
+ case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
+ ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash, target, pos) match {
+ case Some(projectile) =>
+ HandleDealingDamage(target, projectile)
+ case None => ;
+ }
+ case _ => ;
+ }
case msg @ AvatarFirstTimeEventMessage(avatar_guid, object_guid, unk1, event_name) =>
log.info("AvatarFirstTimeEvent: " + msg)
@@ -3422,9 +3591,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case msg @ FacilityBenefitShieldChargeRequestMessage(guid) =>
- //log.info(s"ShieldChargeRequest: $msg")
+ player.VehicleSeated match {
+ case Some(vehicleGUID) =>
+ continent.GUID(vehicleGUID) match {
+ case Some(obj : Vehicle) =>
+ if(obj.Health > 0) { //vehicle will try to charge even if destroyed
+ obj.Actor ! Vehicle.ChargeShields(15)
+ }
+ case _ =>
+ log.warn(s"FacilityBenefitShieldChargeRequest: can not find vehicle ${vehicleGUID.guid} in zone ${continent.Id}")
+ }
+ case None =>
+ log.warn(s"FacilityBenefitShieldChargeRequest: player ${player.Name} is not seated in a vehicle")
+ }
- case msg @ BattleplanMessage(char_id, player_name, zonr_id, diagrams) =>
+ case msg @ BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
log.info("Battleplan: "+msg)
case msg @ CreateShortcutMessage(player_guid, slot, unk, add, shortcut) =>
@@ -3433,8 +3614,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ FriendsRequest(action, friend) =>
log.info("FriendsRequest: "+msg)
- case msg @ HitHint(source, player_guid) =>
- log.info("HitHint: "+msg)
+ case msg @ HitHint(source_guid, player_guid) =>
+ log.info(s"HitHint: $msg")
+ continent.GUID(player_guid) match {
+ case Some(obj : Player) =>
+ avatarService ! AvatarServiceMessage(obj.Name, AvatarAction.HitHint(source_guid, player_guid))
+ case _ => ;
+ }
+
case msg @ TargetingImplantRequest(list) =>
log.info("TargetingImplantRequest: "+msg)
@@ -3977,7 +4164,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val vehicle_guid = vehicle.GUID
(0 to 3).foreach(group => {
sendResponse(
- PlanetsideAttributeMessage(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id.toLong)
+ PlanetsideAttributeMessage(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id)
)
})
}
@@ -4869,6 +5056,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
}
+ /**
+ * The player has lost the will to live and must be killed.
+ * @see `Vitality`
+ * `PlayerSuicide`
+ * @param tplayer the player to be killed
+ */
+ def Suicide(tplayer : Player) : Unit = {
+ tplayer.History(PlayerSuicide(PlayerSource(tplayer)))
+ KillPlayer(tplayer)
+ }
+
/**
* The player has lost all his vitality and must be killed.
*
@@ -4898,6 +5096,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.GUID(tplayer.VehicleSeated.get) match {
case Some(obj : Vehicle) =>
TotalDriverVehicleControl(obj)
+ UnAccessContents(obj)
case _ => ;
}
//make player invisible (if not, the cadaver sticks out the side in a seated position)
@@ -4906,6 +5105,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
PlayerActionsToCancel()
CancelAllProximityUnits()
+ //TODO other methods of death?
+ val pentry = PlayerSource(tplayer)
+ (tplayer.History.find({p => p.isInstanceOf[PlayerSuicide]}) match {
+ case Some(PlayerSuicide(_)) =>
+ None
+ case _ =>
+ tplayer.LastShot match {
+ case Some(shot) =>
+ if(System.nanoTime - shot.hit_time < (10 seconds).toNanos) {
+ Some(shot)
+ }
+ else {
+ None //suicide
+ }
+ case None =>
+ None //suicide
+ }
+ }) match {
+ case Some(shot) =>
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.DestroyDisplay(shot.projectile.owner, pentry, shot.projectile.attribute_to))
+ case None =>
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.DestroyDisplay(pentry, pentry, 0))
+ }
import scala.concurrent.ExecutionContext.Implicits.global
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
@@ -5192,12 +5414,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
def ProximityMedicalTerminal(unit : Terminal with ProximityUnit) : Unit = {
val healthFull : Boolean = if(player.Health < player.MaxHealth) {
+ player.History(HealFromTerm(PlayerSource(player), 10, 0, unit.Definition))
HealAction(player)
}
else {
true
}
val armorFull : Boolean = if(player.Armor < player.MaxArmor) {
+ player.History(HealFromTerm(PlayerSource(player), 0, 10, unit.Definition))
ArmorRepairAction(player)
}
else {
@@ -5216,6 +5440,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
def ProximityHealCrystal(unit : Terminal with ProximityUnit) : Unit = {
val healthFull : Boolean = if(player.Health < player.MaxHealth) {
+ player.History(HealFromTerm(PlayerSource(player), 10, 0, unit.Definition))
HealAction(player)
}
else {
@@ -5364,11 +5589,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param resolution the resolution status to promote the projectile
* @return the projectile
*/
- def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value) : Option[Projectile] = {
+ def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
val index = projectile_guid.guid - Projectile.BaseUID
- ResolveProjectileEntry(projectile, index, resolution)
+ ResolveProjectileEntry(projectile, index, resolution, target, pos)
case None =>
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
None
@@ -5385,13 +5610,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param resolution the resolution status to promote the projectile
* @return a copy of the projectile
*/
- def ResolveProjectileEntry(projectile : Projectile, index : Int, resolution : ProjectileResolution.Value) : Option[Projectile] = {
- if(projectiles(index).contains(projectile)) {
- projectiles(index) = Some(projectile.Resolve(ProjectileResolution.Resolved))
- Some(projectile.Resolve(resolution))
+ def ResolveProjectileEntry(projectile : Projectile, index : Int, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
+ if(!projectiles(index).contains(projectile)) {
+ log.error(s"expected projectile could not be found at $index; can not resolve")
+ None
+ }
+ else if(projectile.isMiss) {
+ log.error(s"expected projectile at $index was already counted as a missed shot; can not resolve any further")
+ None
}
else {
- None
+ projectile.Resolve()
+ Some(ResolvedProjectile(resolution, projectile, SourceEntry(target), target.DamageModel, pos))
}
}
@@ -5423,6 +5653,65 @@ class WorldSessionActor extends Actor with MDCContextAware {
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false))
}
+ /**
+ * Calculate the amount of damage to be dealt to an active `target`
+ * using the information reconstructed from a `Resolvedprojectile`
+ * and affect the `target` in a synchronized manner.
+ * The active `target` and the target of the `ResolvedProjectile` do not have be the same.
+ * @see `DamageResistanceModel`
+ * `Vitality`
+ * @param target a valid game object that is known to the server
+ * @param data a projectile that will affect the target
+ */
+ def HandleDealingDamage(target : PlanetSideGameObject with Vitality, data : ResolvedProjectile) : Unit = {
+ val func = data.damage_model.Calculate(data)
+ target match {
+ case obj : Player =>
+ //damage is synchronized on the target player's `WSA` (results distributed from there)
+ avatarService ! AvatarServiceMessage(obj.Name, AvatarAction.Damage(player.GUID, obj, func))
+ case obj : Vehicle =>
+ //damage is synchronized on the vehicle actor (results returned to and distributed from this `WSA`)
+ obj.Actor ! Vitality.Damage(func)
+ case _ => ;
+ }
+ }
+
+ /**
+ * Properly format a `DestroyDisplayMessage` packet
+ * given sufficient information about a target (victim) and an actor (killer).
+ * For the packet, the `*_charId` field is most important to determining distinction between players.
+ * The "char id" is not a currently supported field for different players so a name hash is used instead.
+ * The virtually negligent chance of a name hash collision is covered.
+ * @param killer the killer's entry
+ * @param victim the victim's entry
+ * @param method the manner of death
+ * @param unk na;
+ * defaults to 121, the object id of `avatar`
+ * @return a `DestroyDisplayMessage` packet that is properly formatted
+ */
+ def DestroyDisplayMessage(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) : DestroyDisplayMessage = {
+ //TODO charId should reflect the player more properly
+ val killerCharId = math.abs(killer.Name.hashCode)
+ var victimCharId = math.abs(victim.Name.hashCode)
+ if(killerCharId == victimCharId && killer.Name != victim.Name) {
+ //odds of hash collision in a populated zone should be close to odds of being struck by lightning
+ victimCharId = Int.MaxValue - victimCharId + 1
+ }
+ val killer_seated = killer match {
+ case obj : PlayerSource => obj.Seated
+ case _ => false
+ }
+ val victim_seated = victim match {
+ case obj : PlayerSource => obj.Seated
+ case _ => false
+ }
+ new DestroyDisplayMessage(
+ killer.Name, killerCharId, killer.Faction, killer_seated,
+ unk, method,
+ victim.Name, victimCharId, victim.Faction, victim_seated
+ )
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())