Damage Fix, and Stuff (#235)

* redid splash damage radial degrade calculations; lasher damage fixed to 6/0 or 2/4 depending on armor; fixed direct hit distance for weapons that act like slash weapons; gave Falcon a valid projectile

* delayed resource silo startup to avoid crash; discovered issue with MAX armor resulting from incorrect manipulation of inventory items during suit transfer

* modifications to consider a specific modifier that exists on some vehicles and armored equipment called Subtract, a DamageProfile that reduces damage due to a set flag; manually controlling damage tick marks rather than relying only on HitHint (from client)

* Tools now have default firemodes (temporary?); ammo gotten from loadouts is always full; loadouts and exo-suits juggle armor values differently on being changed; log messages for damage to players and vehicles

* weapons with one (last) shot need support communicating that one (last) shot

* fixing tests

* correcting the codec for BindPlayerMessage

* changed wording that identifies the damage log entries

* switching exosuit no longer causes butterfingers

* extending exosuit swap armor value changes to infantry loadout changes

* ranges removed from non-vehicle containers

* enabled partial damage functionality to facility turrets

* BindPlayerMessage replacing BattleplanMessage for purposes of depicting the closest AMS spawn point; specific enums in relation to spawning in BPM and SpawnRequestMessage

* rewrote exo-suit switching and loadout switching code, paying extra-special attention for the needs of MAX's

* quick amendment for previous commit

* switching from mount point check to seat checks in VehicleRemover; started refactoring of Cargo functions before realizing how complicated the full effort would be, so these changes are merely wrapping object identification by GUID

* adding a minor server delay to terminal operations
This commit is contained in:
Fate-JH 2018-11-21 20:31:51 -05:00 committed by GitHub
parent 7cec0bb577
commit 4eca226b5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 900 additions and 453 deletions

View file

@ -85,6 +85,10 @@ class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends
obj.MaxArmor = MaxArmor obj.MaxArmor = MaxArmor
obj.InventoryScale = InventoryScale obj.InventoryScale = InventoryScale
obj.InventoryOffset = InventoryOffset obj.InventoryOffset = InventoryOffset
obj.Subtract.Damage0 = Subtract.Damage0
obj.Subtract.Damage1 = Subtract.Damage1
obj.Subtract.Damage2 = Subtract.Damage2
obj.Subtract.Damage3 = Subtract.Damage3
obj.ResistanceDirectHit = ResistanceDirectHit obj.ResistanceDirectHit = ResistanceDirectHit
obj.ResistanceSplash = ResistanceSplash obj.ResistanceSplash = ResistanceSplash
obj.ResistanceAggravated = ResistanceAggravated obj.ResistanceAggravated = ResistanceAggravated
@ -166,6 +170,7 @@ object ExoSuitDefinition {
MAX.InventoryOffset = 6 MAX.InventoryOffset = 6
MAX.Holster(0, EquipmentSize.Max) MAX.Holster(0, EquipmentSize.Max)
MAX.Holster(4, EquipmentSize.Melee) MAX.Holster(4, EquipmentSize.Melee)
MAX.Subtract.Damage1 = -2
MAX.ResistanceDirectHit = 6 MAX.ResistanceDirectHit = 6
MAX.ResistanceSplash = 35 MAX.ResistanceSplash = 35
MAX.ResistanceAggravated = 10 MAX.ResistanceAggravated = 10

View file

@ -555,11 +555,19 @@ object GlobalDefinitions {
val maelstrom = ToolDefinition(ObjectClass.maelstrom) val maelstrom = ToolDefinition(ObjectClass.maelstrom)
val phoenix = ToolDefinition(ObjectClass.phoenix) //decimator val phoenix = new ToolDefinition(ObjectClass.phoenix) {
override def NextFireModeIndex(index : Int) : Int = index
} //decimator
val striker = ToolDefinition(ObjectClass.striker) val striker = new ToolDefinition(ObjectClass.striker) {
override def NextFireModeIndex(index : Int) : Int = index
DefaultFireModeIndex = 1
}
val hunterseeker = ToolDefinition(ObjectClass.hunterseeker) //phoenix val hunterseeker = new ToolDefinition(ObjectClass.hunterseeker) {
override def NextFireModeIndex(index : Int) : Int = index
DefaultFireModeIndex = 1
} //phoenix
val lancer = ToolDefinition(ObjectClass.lancer) val lancer = ToolDefinition(ObjectClass.lancer)
@ -573,7 +581,7 @@ object GlobalDefinitions {
val bolt_driver = ToolDefinition(ObjectClass.bolt_driver) val bolt_driver = ToolDefinition(ObjectClass.bolt_driver)
val oicw = ToolDefinition(ObjectClass.oicw) //scorpion val oicw = ToolDefinition(ObjectClass.oicw)
val flamethrower = ToolDefinition(ObjectClass.flamethrower) val flamethrower = ToolDefinition(ObjectClass.flamethrower)
@ -718,7 +726,9 @@ object GlobalDefinitions {
val lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system) val lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system)
val wasp_weapon_system = ToolDefinition(ObjectClass.wasp_weapon_system) val wasp_weapon_system = new ToolDefinition(ObjectClass.wasp_weapon_system) {
override def NextFireModeIndex(index : Int) : Int = index
}
val liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system) val liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system)
@ -3924,6 +3934,7 @@ object GlobalDefinitions {
nchev_falcon.Name = "nchev_falcon" nchev_falcon.Name = "nchev_falcon"
nchev_falcon.Size = EquipmentSize.Max nchev_falcon.Size = EquipmentSize.Max
nchev_falcon.AmmoTypes += falcon_ammo nchev_falcon.AmmoTypes += falcon_ammo
nchev_falcon.ProjectileTypes += falcon_projectile
nchev_falcon.FireModes += new FireModeDefinition nchev_falcon.FireModes += new FireModeDefinition
nchev_falcon.FireModes.head.AmmoTypeIndices += 0 nchev_falcon.FireModes.head.AmmoTypeIndices += 0
nchev_falcon.FireModes.head.AmmoSlotIndex = 0 nchev_falcon.FireModes.head.AmmoSlotIndex = 0
@ -4763,6 +4774,7 @@ object GlobalDefinitions {
threemanheavybuggy.TrunkOffset = 30 threemanheavybuggy.TrunkOffset = 30
threemanheavybuggy.AutoPilotSpeeds = (22, 8) threemanheavybuggy.AutoPilotSpeeds = (22, 8)
threemanheavybuggy.DestroyedModel = Some(DestroyedVehicle.ThreeManHeavyBuggy) threemanheavybuggy.DestroyedModel = Some(DestroyedVehicle.ThreeManHeavyBuggy)
threemanheavybuggy.Subtract.Damage1 = 5
twomanheavybuggy.Name = "twomanheavybuggy" twomanheavybuggy.Name = "twomanheavybuggy"
twomanheavybuggy.MaxHealth = 1800 twomanheavybuggy.MaxHealth = 1800
@ -4779,6 +4791,7 @@ object GlobalDefinitions {
twomanheavybuggy.TrunkOffset = 30 twomanheavybuggy.TrunkOffset = 30
twomanheavybuggy.AutoPilotSpeeds = (22, 8) twomanheavybuggy.AutoPilotSpeeds = (22, 8)
twomanheavybuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHeavyBuggy) twomanheavybuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHeavyBuggy)
twomanheavybuggy.Subtract.Damage1 = 5
twomanhoverbuggy.Name = "twomanhoverbuggy" twomanhoverbuggy.Name = "twomanhoverbuggy"
twomanhoverbuggy.MaxHealth = 1600 twomanhoverbuggy.MaxHealth = 1600
@ -4795,6 +4808,7 @@ object GlobalDefinitions {
twomanhoverbuggy.TrunkOffset = 30 twomanhoverbuggy.TrunkOffset = 30
twomanhoverbuggy.AutoPilotSpeeds = (22, 10) twomanhoverbuggy.AutoPilotSpeeds = (22, 10)
twomanhoverbuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHoverBuggy) twomanhoverbuggy.DestroyedModel = Some(DestroyedVehicle.TwoManHoverBuggy)
twomanhoverbuggy.Subtract.Damage1 = 5
mediumtransport.Name = "mediumtransport" mediumtransport.Name = "mediumtransport"
mediumtransport.MaxHealth = 2500 mediumtransport.MaxHealth = 2500
@ -4818,6 +4832,7 @@ object GlobalDefinitions {
mediumtransport.TrunkOffset = 30 mediumtransport.TrunkOffset = 30
mediumtransport.AutoPilotSpeeds = (18, 6) mediumtransport.AutoPilotSpeeds = (18, 6)
mediumtransport.DestroyedModel = Some(DestroyedVehicle.MediumTransport) mediumtransport.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
mediumtransport.Subtract.Damage1 = 7
battlewagon.Name = "battlewagon" battlewagon.Name = "battlewagon"
battlewagon.MaxHealth = 2500 battlewagon.MaxHealth = 2500
@ -4868,6 +4883,7 @@ object GlobalDefinitions {
thunderer.TrunkOffset = 30 thunderer.TrunkOffset = 30
thunderer.AutoPilotSpeeds = (18, 6) thunderer.AutoPilotSpeeds = (18, 6)
thunderer.DestroyedModel = Some(DestroyedVehicle.MediumTransport) thunderer.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
thunderer.Subtract.Damage1 = 7
aurora.Name = "aurora" aurora.Name = "aurora"
aurora.MaxHealth = 2500 aurora.MaxHealth = 2500
@ -4891,6 +4907,7 @@ object GlobalDefinitions {
aurora.TrunkOffset = 30 aurora.TrunkOffset = 30
aurora.AutoPilotSpeeds = (18, 6) aurora.AutoPilotSpeeds = (18, 6)
aurora.DestroyedModel = Some(DestroyedVehicle.MediumTransport) aurora.DestroyedModel = Some(DestroyedVehicle.MediumTransport)
aurora.Subtract.Damage1 = 7
apc_tr.Name = "apc_tr" apc_tr.Name = "apc_tr"
apc_tr.MaxHealth = 6000 apc_tr.MaxHealth = 6000
@ -5043,6 +5060,7 @@ object GlobalDefinitions {
lightning.TrunkOffset = 30 lightning.TrunkOffset = 30
lightning.AutoPilotSpeeds = (20, 8) lightning.AutoPilotSpeeds = (20, 8)
lightning.DestroyedModel = Some(DestroyedVehicle.Lightning) lightning.DestroyedModel = Some(DestroyedVehicle.Lightning)
lightning.Subtract.Damage1 = 7
prowler.Name = "prowler" prowler.Name = "prowler"
prowler.MaxHealth = 4800 prowler.MaxHealth = 4800
@ -5062,6 +5080,7 @@ object GlobalDefinitions {
prowler.TrunkOffset = 30 prowler.TrunkOffset = 30
prowler.AutoPilotSpeeds = (14, 6) prowler.AutoPilotSpeeds = (14, 6)
prowler.DestroyedModel = Some(DestroyedVehicle.Prowler) prowler.DestroyedModel = Some(DestroyedVehicle.Prowler)
prowler.Subtract.Damage1 = 9
vanguard.Name = "vanguard" vanguard.Name = "vanguard"
vanguard.MaxHealth = 5400 vanguard.MaxHealth = 5400
@ -5077,6 +5096,7 @@ object GlobalDefinitions {
vanguard.TrunkOffset = 30 vanguard.TrunkOffset = 30
vanguard.AutoPilotSpeeds = (16, 6) vanguard.AutoPilotSpeeds = (16, 6)
vanguard.DestroyedModel = Some(DestroyedVehicle.Vanguard) vanguard.DestroyedModel = Some(DestroyedVehicle.Vanguard)
vanguard.Subtract.Damage1 = 9
magrider.Name = "magrider" magrider.Name = "magrider"
magrider.MaxHealth = 4200 magrider.MaxHealth = 4200
@ -5094,6 +5114,7 @@ object GlobalDefinitions {
magrider.TrunkOffset = 30 magrider.TrunkOffset = 30
magrider.AutoPilotSpeeds = (18, 6) magrider.AutoPilotSpeeds = (18, 6)
magrider.DestroyedModel = Some(DestroyedVehicle.Magrider) magrider.DestroyedModel = Some(DestroyedVehicle.Magrider)
magrider.Subtract.Damage1 = 9
val utilityConverter = new UtilityVehicleConverter val utilityConverter = new UtilityVehicleConverter
ant.Name = "ant" ant.Name = "ant"
@ -5110,6 +5131,7 @@ object GlobalDefinitions {
ant.MaximumCapacitor = 1500 ant.MaximumCapacitor = 1500
ant.Packet = utilityConverter ant.Packet = utilityConverter
ant.DestroyedModel = Some(DestroyedVehicle.Ant) ant.DestroyedModel = Some(DestroyedVehicle.Ant)
ant.Subtract.Damage1 = 5
ams.Name = "ams" ams.Name = "ams"
ams.MaxHealth = 3000 ams.MaxHealth = 3000
@ -5129,6 +5151,7 @@ object GlobalDefinitions {
ams.AutoPilotSpeeds = (18, 6) ams.AutoPilotSpeeds = (18, 6)
ams.Packet = utilityConverter ams.Packet = utilityConverter
ams.DestroyedModel = Some(DestroyedVehicle.Ams) ams.DestroyedModel = Some(DestroyedVehicle.Ams)
ams.Subtract.Damage1 = 10
val variantConverter = new VariantVehicleConverter val variantConverter = new VariantVehicleConverter
router.Name = "router" router.Name = "router"
@ -5147,6 +5170,7 @@ object GlobalDefinitions {
router.AutoPilotSpeeds = (16, 6) router.AutoPilotSpeeds = (16, 6)
router.Packet = variantConverter router.Packet = variantConverter
router.DestroyedModel = Some(DestroyedVehicle.Router) router.DestroyedModel = Some(DestroyedVehicle.Router)
router.Subtract.Damage1 = 5
switchblade.Name = "switchblade" switchblade.Name = "switchblade"
switchblade.MaxHealth = 1750 switchblade.MaxHealth = 1750
@ -5164,6 +5188,8 @@ object GlobalDefinitions {
switchblade.AutoPilotSpeeds = (22, 8) switchblade.AutoPilotSpeeds = (22, 8)
switchblade.Packet = variantConverter switchblade.Packet = variantConverter
switchblade.DestroyedModel = Some(DestroyedVehicle.Switchblade) switchblade.DestroyedModel = Some(DestroyedVehicle.Switchblade)
switchblade.Subtract.Damage0 = 5
switchblade.Subtract.Damage1 = 5
flail.Name = "flail" flail.Name = "flail"
flail.MaxHealth = 2400 flail.MaxHealth = 2400
@ -5180,6 +5206,7 @@ object GlobalDefinitions {
flail.AutoPilotSpeeds = (14, 6) flail.AutoPilotSpeeds = (14, 6)
flail.Packet = variantConverter flail.Packet = variantConverter
flail.DestroyedModel = Some(DestroyedVehicle.Flail) flail.DestroyedModel = Some(DestroyedVehicle.Flail)
flail.Subtract.Damage1 = 7
mosquito.Name = "mosquito" mosquito.Name = "mosquito"
mosquito.MaxHealth = 665 mosquito.MaxHealth = 665
@ -5210,6 +5237,7 @@ object GlobalDefinitions {
lightgunship.AutoPilotSpeeds = (0, 4) lightgunship.AutoPilotSpeeds = (0, 4)
lightgunship.Packet = variantConverter lightgunship.Packet = variantConverter
lightgunship.DestroyedModel = Some(DestroyedVehicle.LightGunship) lightgunship.DestroyedModel = Some(DestroyedVehicle.LightGunship)
lightgunship.Subtract.Damage1 = 3
wasp.Name = "wasp" wasp.Name = "wasp"
wasp.MaxHealth = 515 wasp.MaxHealth = 515
@ -5247,6 +5275,7 @@ object GlobalDefinitions {
liberator.AutoPilotSpeeds = (0, 4) liberator.AutoPilotSpeeds = (0, 4)
liberator.Packet = variantConverter liberator.Packet = variantConverter
liberator.DestroyedModel = Some(DestroyedVehicle.Liberator) liberator.DestroyedModel = Some(DestroyedVehicle.Liberator)
liberator.Subtract.Damage1 = 5
vulture.Name = "vulture" vulture.Name = "vulture"
vulture.MaxHealth = 2500 vulture.MaxHealth = 2500
@ -5269,6 +5298,7 @@ object GlobalDefinitions {
vulture.AutoPilotSpeeds = (0, 4) vulture.AutoPilotSpeeds = (0, 4)
vulture.Packet = variantConverter vulture.Packet = variantConverter
vulture.DestroyedModel = Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed vulture.DestroyedModel = Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
vulture.Subtract.Damage1 = 5
dropship.Name = "dropship" dropship.Name = "dropship"
dropship.MaxHealth = 5000 dropship.MaxHealth = 5000
@ -5323,6 +5353,7 @@ object GlobalDefinitions {
dropship.AutoPilotSpeeds = (0, 4) dropship.AutoPilotSpeeds = (0, 4)
dropship.Packet = variantConverter dropship.Packet = variantConverter
dropship.DestroyedModel = Some(DestroyedVehicle.Dropship) dropship.DestroyedModel = Some(DestroyedVehicle.Dropship)
dropship.Subtract.Damage1 = 7
galaxy_gunship.Name = "galaxy_gunship" galaxy_gunship.Name = "galaxy_gunship"
galaxy_gunship.MaxHealth = 6000 galaxy_gunship.MaxHealth = 6000
@ -5354,6 +5385,7 @@ object GlobalDefinitions {
galaxy_gunship.AutoPilotSpeeds = (0, 4) galaxy_gunship.AutoPilotSpeeds = (0, 4)
galaxy_gunship.Packet = variantConverter galaxy_gunship.Packet = variantConverter
galaxy_gunship.DestroyedModel = Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists galaxy_gunship.DestroyedModel = Some(DestroyedVehicle.Dropship) //the adb calls out a galaxy_gunship_destroyed but no such asset exists
galaxy_gunship.Subtract.Damage1 = 7
lodestar.Name = "lodestar" lodestar.Name = "lodestar"
lodestar.MaxHealth = 5000 lodestar.MaxHealth = 5000
@ -5367,6 +5399,7 @@ object GlobalDefinitions {
lodestar.AutoPilotSpeeds = (0, 4) lodestar.AutoPilotSpeeds = (0, 4)
lodestar.Packet = variantConverter lodestar.Packet = variantConverter
lodestar.DestroyedModel = Some(DestroyedVehicle.Lodestar) lodestar.DestroyedModel = Some(DestroyedVehicle.Lodestar)
lodestar.Subtract.Damage1 = 7
phantasm.Name = "phantasm" phantasm.Name = "phantasm"
phantasm.MaxHealth = 2500 phantasm.MaxHealth = 2500

View file

@ -281,6 +281,8 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
ChangeSpecialAbility() ChangeSpecialAbility()
} }
def Subtract = exosuit.Subtract
def ResistanceDirectHit = exosuit.ResistanceDirectHit def ResistanceDirectHit = exosuit.ResistanceDirectHit
def ResistanceSplash = exosuit.ResistanceSplash def ResistanceSplash = exosuit.ResistanceSplash

View file

@ -20,7 +20,7 @@ import scala.annotation.tailrec
class Tool(private val toolDef : ToolDefinition) extends Equipment class Tool(private val toolDef : ToolDefinition) extends Equipment
with FireModeSwitch[FireModeDefinition] { with FireModeSwitch[FireModeDefinition] {
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */ /** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
private var fireModeIndex : Int = 0 private var fireModeIndex : Int = toolDef.DefaultFireModeIndex
/** current ammunition slot being used by this fire mode */ /** current ammunition slot being used by this fire mode */
private var ammoSlots : List[Tool.FireModeSlot] = List.empty private var ammoSlots : List[Tool.FireModeSlot] = List.empty

View file

@ -4,6 +4,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.TurretDeployable import net.psforever.objects.TurretDeployable
import net.psforever.objects.ce.ComplexDeployable import net.psforever.objects.ce.ComplexDeployable
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition} import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ComplexDeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition, final case class ComplexDeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition,
@ -22,6 +23,7 @@ final case class ComplexDeployableSource(obj_def : ObjectDefinition with BaseDep
def Position = position def Position = position
def Orientation = orientation def Orientation = orientation
def Velocity = None def Velocity = None
def Modifiers = obj_def.asInstanceOf[ResistanceProfile]
} }
object ComplexDeployableSource { object ComplexDeployableSource {

View file

@ -4,6 +4,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.ce.Deployable import net.psforever.objects.ce.Deployable
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition} import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class DeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition, final case class DeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition,
@ -20,6 +21,7 @@ final case class DeployableSource(obj_def : ObjectDefinition with BaseDeployable
def Position = position def Position = position
def Orientation = orientation def Orientation = orientation
def Velocity = None def Velocity = None
def Modifiers = obj_def.asInstanceOf[ResistanceProfile]
} }
object DeployableSource { object DeployableSource {

View file

@ -3,19 +3,21 @@ package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class ObjectSource(obj : PlanetSideGameObject with FactionAffinity, final case class ObjectSource(obj : PlanetSideGameObject with FactionAffinity,
faction : PlanetSideEmpire.Value, faction : PlanetSideEmpire.Value,
position : Vector3, position : Vector3,
orientation : Vector3, orientation : Vector3,
velocity : Option[Vector3] = None) extends SourceEntry { velocity : Option[Vector3]) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj.Definition.Name) override def Name = SourceEntry.NameFormat(obj.Definition.Name)
override def Faction = faction override def Faction = faction
def Definition = obj.Definition def Definition = obj.Definition
def Position = position def Position = position
def Orientation = orientation def Orientation = orientation
def Velocity = velocity def Velocity = velocity
def Modifiers = new ResistanceProfileMutators { }
} }
object ObjectSource { object ObjectSource {

View file

@ -3,6 +3,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.Player import net.psforever.objects.Player
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
final case class PlayerSource(name : String, final case class PlayerSource(name : String,
@ -14,7 +15,8 @@ final case class PlayerSource(name : String,
armor : Int, armor : Int,
position : Vector3, position : Vector3,
orientation : Vector3, orientation : Vector3,
velocity : Option[Vector3] = None) extends SourceEntry { velocity : Option[Vector3],
modifiers : ResistanceProfile) extends SourceEntry {
override def Name = name override def Name = name
override def Faction = faction override def Faction = faction
def Definition = obj_def def Definition = obj_def
@ -25,11 +27,12 @@ final case class PlayerSource(name : String,
def Position = position def Position = position
def Orientation = orientation def Orientation = orientation
def Velocity = velocity def Velocity = velocity
def Modifiers = modifiers
} }
object PlayerSource { object PlayerSource {
def apply(tplayer : Player) : PlayerSource = { def apply(tplayer : Player) : PlayerSource = {
PlayerSource(tplayer.Name, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty, PlayerSource(tplayer.Name, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty,
tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity) tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity, tplayer.asInstanceOf[ResistanceProfile])
} }
} }

View file

@ -6,6 +6,7 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle} import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle}
import net.psforever.objects.entity.WorldEntity import net.psforever.objects.entity.WorldEntity
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
trait SourceEntry extends WorldEntity { trait SourceEntry extends WorldEntity {
@ -16,6 +17,7 @@ trait SourceEntry extends WorldEntity {
def Position_=(pos : Vector3) = Position def Position_=(pos : Vector3) = Position
def Orientation_=(pos : Vector3) = Position def Orientation_=(pos : Vector3) = Position
def Velocity_=(pos : Option[Vector3]) = Velocity def Velocity_=(pos : Option[Vector3]) = Velocity
def Modifiers : ResistanceProfile
} }
object SourceEntry { object SourceEntry {
@ -24,6 +26,7 @@ object SourceEntry {
def Position = Vector3.Zero def Position = Vector3.Zero
def Orientation = Vector3.Zero def Orientation = Vector3.Zero
def Velocity = Some(Vector3.Zero) def Velocity = Some(Vector3.Zero)
def Modifiers = null
} }
def apply(target : PlanetSideGameObject with FactionAffinity) : SourceEntry = { def apply(target : PlanetSideGameObject with FactionAffinity) : SourceEntry = {

View file

@ -3,6 +3,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.Vehicle import net.psforever.objects.Vehicle
import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class VehicleSource(obj_def : VehicleDefinition, final case class VehicleSource(obj_def : VehicleDefinition,
@ -11,7 +12,8 @@ final case class VehicleSource(obj_def : VehicleDefinition,
shields : Int, shields : Int,
position : Vector3, position : Vector3,
orientation : Vector3, orientation : Vector3,
velocity : Option[Vector3] = None) extends SourceEntry { velocity : Option[Vector3],
modifiers : ResistanceProfile) extends SourceEntry {
override def Name = SourceEntry.NameFormat(obj_def.Name) override def Name = SourceEntry.NameFormat(obj_def.Name)
override def Faction = faction override def Faction = faction
def Definition : VehicleDefinition = obj_def def Definition : VehicleDefinition = obj_def
@ -20,6 +22,7 @@ final case class VehicleSource(obj_def : VehicleDefinition,
def Position = position def Position = position
def Orientation = orientation def Orientation = orientation
def Velocity = velocity def Velocity = velocity
def Modifiers = modifiers
} }
object VehicleSource { object VehicleSource {
@ -31,7 +34,8 @@ object VehicleSource {
obj.Shields, obj.Shields,
obj.Position, obj.Position,
obj.Orientation, obj.Orientation,
obj.Velocity obj.Velocity,
obj.Definition.asInstanceOf[ResistanceProfile]
) )
} }
} }

View file

@ -6,11 +6,13 @@ import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem} import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
import net.psforever.objects.definition.converter.SmallDeployableConverter import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage} import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage, StandardResistanceProfile}
import scala.concurrent.duration._ import scala.concurrent.duration._
trait BaseDeployableDefinition extends DamageResistanceModel { trait BaseDeployableDefinition extends DamageResistanceModel
with ResistanceProfileMutators {
private var category : DeployableCategory.Value = DeployableCategory.Boomers private var category : DeployableCategory.Value = DeployableCategory.Boomers
private var deployTime : Long = (1 second).toMillis //ms private var deployTime : Long = (1 second).toMillis //ms
private var maxHealth : Int = 1 private var maxHealth : Int = 1

View file

@ -2,8 +2,7 @@
package net.psforever.objects.definition package net.psforever.objects.definition
import net.psforever.objects.ballistics.Projectiles import net.psforever.objects.ballistics.Projectiles
import net.psforever.objects.vital.DamageType import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
import net.psforever.objects.vital.damage.DamageProfile
/** /**
* The definition that outlines the damage-dealing characteristics of any projectile. * The definition that outlines the damage-dealing characteristics of any projectile.
@ -11,13 +10,8 @@ import net.psforever.objects.vital.damage.DamageProfile
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
with DamageProfile { with StandardDamageProfile {
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
private var damage0 : Int = 0
private var damage1 : Option[Int] = None
private var damage2 : Option[Int] = None
private var damage3 : Option[Int] = None
private var damage4 : Option[Int] = None
private var acceleration : Int = 0 private var acceleration : Int = 0
private var accelerationUntil : Float = 0f private var accelerationUntil : Float = 0f
private var damageType : DamageType.Value = DamageType.None private var damageType : DamageType.Value = DamageType.None
@ -45,57 +39,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
UseDamage1Subtract UseDamage1Subtract
} }
def Damage0 : Int = damage0
def Damage0_=(damage : Int) : Int = {
damage0 = damage
damage0
}
def Damage0_=(damage : Option[Int]) : Int = {
damage0 = damage match {
case Some(value) => value
case None => 0 //can not be set to None
}
Damage0
}
def Damage1 : Int = damage1.getOrElse(Damage0)
def Damage1_=(damage : Int) : Int = Damage1_=(Some(damage))
def Damage1_=(damage : Option[Int]) : Int = {
this.damage1 = damage
Damage1
}
def Damage2 : Int = damage2.getOrElse(Damage1)
def Damage2_=(damage : Int) : Int = Damage2_=(Some(damage))
def Damage2_=(damage : Option[Int]) : Int = {
this.damage2 = damage
Damage2
}
def Damage3 : Int = damage3.getOrElse(Damage2)
def Damage3_=(damage : Int) : Int = Damage3_=(Some(damage))
def Damage3_=(damage : Option[Int]) : Int = {
this.damage3 = damage
Damage3
}
def Damage4 : Int = damage4.getOrElse(Damage3)
def Damage4_=(damage : Int) : Int = Damage4_=(Some(damage))
def Damage4_=(damage : Option[Int]) : Int = {
this.damage4 = damage
Damage4
}
def Acceleration : Int = acceleration def Acceleration : Int = acceleration
def Acceleration_=(accel : Int) : Int = { def Acceleration_=(accel : Int) : Int = {

View file

@ -10,6 +10,7 @@ class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
private val ammoTypes : mutable.ListBuffer[AmmoBoxDefinition] = new mutable.ListBuffer[AmmoBoxDefinition] private val ammoTypes : mutable.ListBuffer[AmmoBoxDefinition] = new mutable.ListBuffer[AmmoBoxDefinition]
private val projectileTypes : mutable.ListBuffer[ProjectileDefinition] = new mutable.ListBuffer[ProjectileDefinition] private val projectileTypes : mutable.ListBuffer[ProjectileDefinition] = new mutable.ListBuffer[ProjectileDefinition]
private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition] private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition]
private var defaultFireModeIndex : Option[Int] = None
Name = "tool" Name = "tool"
Packet = ToolDefinition.converter Packet = ToolDefinition.converter
@ -20,6 +21,15 @@ class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes
def NextFireModeIndex(index : Int) : Int = index + 1 def NextFireModeIndex(index : Int) : Int = index + 1
def DefaultFireModeIndex : Int = defaultFireModeIndex.getOrElse(0)
def DefaultFireModeIndex_=(index : Int) : Int = DefaultFireModeIndex_=(Some(index))
def DefaultFireModeIndex_=(index : Option[Int]) : Int = {
defaultFireModeIndex = index
DefaultFireModeIndex
}
} }
object ToolDefinition { object ToolDefinition {

View file

@ -60,6 +60,7 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId)
} }
def Seats : mutable.HashMap[Int, SeatDefinition] = seats def Seats : mutable.HashMap[Int, SeatDefinition] = seats
def Cargo : mutable.HashMap[Int, CargoDefinition] = cargo def Cargo : mutable.HashMap[Int, CargoDefinition] = cargo
def MountPoints : mutable.HashMap[Int, Int] = mountPoints def MountPoints : mutable.HashMap[Int, Int] = mountPoints

View file

@ -226,10 +226,12 @@ class GridInventory extends Container {
else { else {
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]() val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
var curr = actualSlot var curr = actualSlot
val fixedItems = items.toMap
val fixedGrid = grid.toList
for(_ <- 0 until h) { for(_ <- 0 until h) {
for(col <- 0 until w) { for(col <- 0 until w) {
if(grid(curr + col) > -1) { if(fixedGrid(curr + col) > -1) {
collisions += items(grid(curr + col)) collisions += fixedItems(fixedGrid(curr + col))
} }
} }
curr += width curr += width

View file

@ -71,16 +71,15 @@ object ResourceSilo {
} }
/** /**
* Instantiate an configure a `Resource Silo` object * Instantiate and configure a `Resource Silo` object
* @param id the unique id that will be assigned to this entity * @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality; * @param context a context to allow the object to properly set up `ActorSystem` functionality;
* not necessary for this object, but required by signature * not necessary for this object, but required by signature
* @return the `Locker` object * @return the `ResourceSilo` object
*/ */
def Constructor(id : Int, context : ActorContext) : ResourceSilo = { def Constructor(id : Int, context : ActorContext) : ResourceSilo = {
val obj = ResourceSilo() val obj = ResourceSilo()
obj.Actor = context.actorOf(Props(classOf[ResourceSiloControl], obj), s"${obj.Definition.Name}_$id") obj.Actor = context.actorOf(Props(classOf[ResourceSiloControl], obj), s"${obj.Definition.Name}_$id")
obj.Actor ! "startup"
obj obj
} }
} }

View file

@ -42,7 +42,7 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg)) sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg))
case ResourceSilo.LowNtuWarning(enabled: Boolean) => case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
resourceSilo.LowNtuWarningOn = enabled resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to ${enabled}") log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 47, if(resourceSilo.LowNtuWarningOn) 1 else 0)) avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 47, if(resourceSilo.LowNtuWarningOn) 1 else 0))
case ResourceSilo.UpdateChargeLevel(amount: Int) => case ResourceSilo.UpdateChargeLevel(amount: Int) =>
@ -55,10 +55,10 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
} }
val ntuBarLevel = scala.math.round((resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat) * 10).toInt val ntuBarLevel = scala.math.round((resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat) * 10)
// Only send updated capacitor display value to all clients if it has actually changed // Only send updated capacitor display value to all clients if it has actually changed
if(resourceSilo.CapacitorDisplay != ntuBarLevel) { if(resourceSilo.CapacitorDisplay != ntuBarLevel) {
log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from ${resourceSilo.CapacitorDisplay} to ${ntuBarLevel}") log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from ${resourceSilo.CapacitorDisplay} to $ntuBarLevel")
resourceSilo.CapacitorDisplay = ntuBarLevel resourceSilo.CapacitorDisplay = ntuBarLevel
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true) resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)) avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay))

View file

@ -366,12 +366,12 @@ object EquipmentTerminalDefinition {
(0 until tool.MaxAmmoSlot).foreach(index => { (0 until tool.MaxAmmoSlot).foreach(index => {
val slot = tool.AmmoSlots(index) val slot = tool.AmmoSlots(index)
slot.AmmoTypeIndex += obj.ammo(index).ammoIndex slot.AmmoTypeIndex += obj.ammo(index).ammoIndex
slot.Box = MakeAmmoBox(ammo(index), Some(obj.ammo(index).ammo.capacity)) slot.Box = MakeAmmoBox(ammo(index), Some(slot.Definition.Magazine)) //Some(obj.ammo(index).ammo.capacity)
}) })
tool tool
case obj : ShorthandAmmoBox => case obj : ShorthandAmmoBox =>
MakeAmmoBox(obj.definition, Some(obj.capacity)) MakeAmmoBox(obj.definition) //Some(obj.capacity)
case obj : ShorthandConstructionItem => case obj : ShorthandConstructionItem =>
MakeConstructionItem(obj.definition) MakeConstructionItem(obj.definition)

View file

@ -3,14 +3,19 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.types.Vector3 import net.psforever.types.Vector3
import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
class FacilityTurret(tDef : TurretDefinition) extends Amenity class FacilityTurret(tDef : TurretDefinition) extends Amenity
with WeaponTurret { with WeaponTurret
with Vitality
with StandardResistanceProfile {
/** some turrets can be updated; they all start without updates */ /** some turrets can be updated; they all start without updates */
private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None
WeaponTurret.LoadDefinition(this) WeaponTurret.LoadDefinition(this)
override def Health_=(toHealth : Int) = super.Health_=(math.max(1, toHealth)) //TODO properly handle destroyed facility turrets
def MaxHealth : Int = Definition.MaxHealth def MaxHealth : Int = Definition.MaxHealth
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
@ -28,6 +33,8 @@ class FacilityTurret(tDef : TurretDefinition) extends Amenity
Upgrade Upgrade
} }
def DamageModel = Definition.asInstanceOf[DamageResistanceModel]
def Definition : TurretDefinition = tDef def Definition : TurretDefinition = tDef
} }

View file

@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.turret
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.vital.Vitality
/** /**
* An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br> * An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br>
@ -38,6 +39,18 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(turret, seat_num)) sender ! Mountable.MountMessages(user, Mountable.CanNotMount(turret, seat_num))
} }
case Vitality.Damage(damage_func) =>
if(turret.Health > 0) {
val originalHealth = turret.Health
damage_func(turret)
val health = turret.Health
val damageToHealth = originalHealth - health
val name = turret.Actor.toString
val slashPoint = name.lastIndexOf("/")
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
sender ! Vitality.DamageResolution(turret)
}
case _ => ; case _ => ;
} }
} }

View file

@ -3,6 +3,8 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition} import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition}
import net.psforever.objects.vehicles.Turrets import net.psforever.objects.vehicles.Turrets
import net.psforever.objects.vital.{DamageResistanceModel, StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import scala.collection.mutable import scala.collection.mutable
@ -10,7 +12,9 @@ import scala.collection.mutable
* The definition for any `MannedTurret`. * The definition for any `MannedTurret`.
* @param objectId the object's identifier number * @param objectId the object's identifier number
*/ */
class TurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId) { class TurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
with ResistanceProfileMutators
with DamageResistanceModel {
Turrets(objectId) //let throw NoSuchElementException Turrets(objectId) //let throw NoSuchElementException
private var maxHealth : Int = 100 private var maxHealth : Int = 100
@ -25,6 +29,9 @@ class TurretDefinition(private val objectId : Int) extends ObjectDefinition(obje
/** creates internal ammunition reserves that can not become depleted /** creates internal ammunition reserves that can not become depleted
* see `MannedTurret.TurretAmmoBox` for details */ * see `MannedTurret.TurretAmmoBox` for details */
private var hasReserveAmmunition : Boolean = false private var hasReserveAmmunition : Boolean = false
Damage = StandardVehicleDamage
Resistance = StandardVehicleResistance
Model = StandardResolutions.FacilityTurrets
def MaxHealth : Int = maxHealth def MaxHealth : Int = maxHealth

View file

@ -63,7 +63,16 @@ class VehicleControl(vehicle : Vehicle) extends Actor
case Vitality.Damage(damage_func) => case Vitality.Damage(damage_func) =>
if(vehicle.Health > 0) { if(vehicle.Health > 0) {
val originalHealth = vehicle.Health
val originalShields = vehicle.Shields
damage_func(vehicle) damage_func(vehicle)
val health = vehicle.Health
val shields = vehicle.Shields
val damageToHealth = originalHealth - health
val damageToShields = originalShields - shields
val name = vehicle.Actor.toString
val slashPoint = name.lastIndexOf("/")
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields")
sender ! Vitality.DamageResolution(vehicle) sender ! Vitality.DamageResolution(vehicle)
} }

View file

@ -0,0 +1,63 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital
import net.psforever.objects.vital.damage.DamageProfile
trait StandardDamageProfile extends DamageProfile {
private var damage0 : Int = 0
private var damage1 : Option[Int] = None
private var damage2 : Option[Int] = None
private var damage3 : Option[Int] = None
private var damage4 : Option[Int] = None
def Damage0 : Int = damage0
def Damage0_=(damage : Int) : Int = {
damage0 = damage
damage0
}
def Damage0_=(damage : Option[Int]) : Int = {
damage0 = damage match {
case Some(value) => value
case None => 0 //can not be set to None
}
Damage0
}
def Damage1 : Int = damage1.getOrElse(Damage0)
def Damage1_=(damage : Int) : Int = Damage1_=(Some(damage))
def Damage1_=(damage : Option[Int]) : Int = {
this.damage1 = damage
Damage1
}
def Damage2 : Int = damage2.getOrElse(Damage1)
def Damage2_=(damage : Int) : Int = Damage2_=(Some(damage))
def Damage2_=(damage : Option[Int]) : Int = {
this.damage2 = damage
Damage2
}
def Damage3 : Int = damage3.getOrElse(Damage2)
def Damage3_=(damage : Int) : Int = Damage3_=(Some(damage))
def Damage3_=(damage : Option[Int]) : Int = {
this.damage3 = damage
Damage3
}
def Damage4 : Int = damage4.getOrElse(Damage3)
def Damage4_=(damage : Int) : Int = Damage4_=(Some(damage))
def Damage4_=(damage : Option[Int]) : Int = {
this.damage4 = damage
Damage4
}
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.vital package net.psforever.objects.vital
import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.vital.damage._ import net.psforever.objects.vital.damage._
import net.psforever.objects.vital.damage.DamageCalculations._ import net.psforever.objects.vital.damage.DamageCalculations._
@ -66,21 +65,35 @@ object AircraftSplashDamage extends DamageCalculations(
DistanceFromExplosionToTarget DistanceFromExplosionToTarget
) )
object InfantryLashDamage extends NoDamageBase { object InfantrySplashDamageDirect extends DamageCalculations(
override def Calculate(data : ResolvedProjectile) : Int = (InfantryHitDamage.Calculate(data) * 0.2f).toInt SplashDamageWithRadialDegrade,
} DamageWithModifiers(DamageAgainstAircraft),
NoDistance
)
object MaxLashDamage extends NoDamageBase { object InfantryLashDamage extends DamageCalculations(
override def Calculate(data : ResolvedProjectile) : Int = (MaxHitDamage.Calculate(data) * 0.2f).toInt LashDamage,
} DamageWithModifiers(DamageAgainstExoSuit),
DistanceBetweenTargetandSource
)
object VehicleLashDamage extends NoDamageBase { object MaxLashDamage extends DamageCalculations(
override def Calculate(data : ResolvedProjectile) : Int = (VehicleHitDamage.Calculate(data) * 0.2f).toInt LashDamage,
} DamageWithModifiers(DamageAgainstMaxSuit),
DistanceBetweenTargetandSource
)
object AircraftLashDamage extends NoDamageBase { object VehicleLashDamage extends DamageCalculations(
override def Calculate(data : ResolvedProjectile) : Int = (AircraftHitDamage.Calculate(data) * 0.2f).toInt LashDamage,
} DamageWithModifiers(DamageAgainstVehicle),
DistanceBetweenTargetandSource
)
object AircraftLashDamage extends DamageCalculations(
LashDamage,
DamageWithModifiers(DamageAgainstAircraft),
DistanceBetweenTargetandSource
)
object NoDamageSelection extends DamageSelection { object NoDamageSelection extends DamageSelection {
def Direct = None def Direct = None

View file

@ -2,6 +2,7 @@
package net.psforever.objects.vital package net.psforever.objects.vital
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.vital.damage.DamageProfile
import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.objects.vital.resistance.ResistanceProfile
/** /**
@ -16,6 +17,8 @@ trait StandardResistanceProfile extends ResistanceProfile {
assert(Definition.isInstanceOf[ResistanceProfile], s"$this object definition must extend ResistanceProfile") assert(Definition.isInstanceOf[ResistanceProfile], s"$this object definition must extend ResistanceProfile")
private val resistDef = Definition.asInstanceOf[ResistanceProfile] //cast only once private val resistDef = Definition.asInstanceOf[ResistanceProfile] //cast only once
def Subtract : DamageProfile = resistDef.Subtract
def ResistanceDirectHit : Int = resistDef.ResistanceDirectHit def ResistanceDirectHit : Int = resistDef.ResistanceDirectHit
def ResistanceSplash : Int = resistDef.ResistanceDirectHit def ResistanceSplash : Int = resistDef.ResistanceDirectHit

View file

@ -22,7 +22,7 @@ object InfantrySplashResistance extends ResistanceCalculations[PlayerSource](
object InfantryLashResistance extends ResistanceCalculations[PlayerSource]( object InfantryLashResistance extends ResistanceCalculations[PlayerSource](
ResistanceCalculations.ValidInfantryTarget, ResistanceCalculations.ValidInfantryTarget,
ResistanceCalculations.NoResistExtractor ResistanceCalculations.ExoSuitDirectExtractor
) )
object InfantryAggravatedResistance extends ResistanceCalculations[PlayerSource]( object InfantryAggravatedResistance extends ResistanceCalculations[PlayerSource](

View file

@ -23,9 +23,9 @@ object VehicleResolutions extends DamageResistCalculations(
ResolutionCalculations.VehicleApplication ResolutionCalculations.VehicleApplication
) )
object SimpleDeployableResolutions extends DamageResistCalculations( object SimpleResolutions extends DamageResistCalculations(
ResolutionCalculations.VehicleDamageAfterResist, ResolutionCalculations.VehicleDamageAfterResist,
ResolutionCalculations.SimpleDeployableApplication ResolutionCalculations.SimpleApplication
) )
object ComplexDeployableResolutions extends DamageResistCalculations( object ComplexDeployableResolutions extends DamageResistCalculations(
@ -38,6 +38,7 @@ object StandardResolutions extends ResolutionSelection {
def Max : ResolutionCalculations.Form = MaxResolutions.Calculate def Max : ResolutionCalculations.Form = MaxResolutions.Calculate
def Vehicle : ResolutionCalculations.Form = VehicleResolutions.Calculate def Vehicle : ResolutionCalculations.Form = VehicleResolutions.Calculate
def Aircraft : ResolutionCalculations.Form = VehicleResolutions.Calculate def Aircraft : ResolutionCalculations.Form = VehicleResolutions.Calculate
def SimpleDeployables : ResolutionCalculations.Form = SimpleDeployableResolutions.Calculate def SimpleDeployables : ResolutionCalculations.Form = SimpleResolutions.Calculate
def ComplexDeployables : ResolutionCalculations.Form = ComplexDeployableResolutions.Calculate def ComplexDeployables : ResolutionCalculations.Form = ComplexDeployableResolutions.Calculate
def FacilityTurrets : ResolutionCalculations.Form = SimpleResolutions.Calculate
} }

View file

@ -27,9 +27,16 @@ abstract class DamageCalculations(damages : DamagesType,
*/ */
def Calculate(data : ResolvedProjectile) : Int = { def Calculate(data : ResolvedProjectile) : Int = {
val projectile = data.projectile val projectile = data.projectile
val profile = projectile.profile
val modifiers = if(profile.UseDamage1Subtract) {
List(projectile.fire_mode.Modifiers, data.target.Modifiers.Subtract)
}
else {
List(projectile.fire_mode.Modifiers)
}
damages( damages(
projectile, projectile,
extractor(projectile.profile, List(projectile.fire_mode.Modifiers)), extractor(profile, modifiers),
distanceFunc(data) distanceFunc(data)
) )
} }
@ -70,6 +77,17 @@ object DamageCalculations {
//damage calculation functions //damage calculation functions
def NoDamage(projectile : Projectile, rawDamage : Int, distance : Float) : Int = 0 def NoDamage(projectile : Projectile, rawDamage : Int, distance : Float) : Int = 0
/**
* Use an unmodified damage value.
* @param projectile information about the weapon discharge (itself);
* unused
* @param rawDamage the accumulated amount of damage
* @param distance how far the source was from the target;
* unused
* @return the rawDamage value
*/
def SameHit(projectile : Projectile, rawDamage : Int, distance : Float) : Int = rawDamage
/** /**
* Modify the base damage based on the degrade distance of the projectile type * Modify the base damage based on the degrade distance of the projectile type
* and its maximum effective distance. * and its maximum effective distance.
@ -106,8 +124,26 @@ object DamageCalculations {
val radius = projectile.profile.DamageRadius val radius = projectile.profile.DamageRadius
if(distance <= radius) { if(distance <= radius) {
val base : Float = projectile.profile.DamageAtEdge val base : Float = projectile.profile.DamageAtEdge
val degrade : Float = (1 - base) * ((radius - distance) / radius) + base val degrade : Float = (1 - base) * ((radius - distance)/radius) + base
rawDamage + (rawDamage * degrade).toInt (rawDamage * degrade).toInt
}
else {
0
}
}
/**
* Calculate a flat lash damage value.
* The target needs to be more than five meters away.
* @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 LashDamage(projectile : Projectile, rawDamage : Int, distance : Float) : Int = {
val profile = projectile.profile
if(distance > 5 || distance <= profile.DistanceMax) {
(rawDamage * 0.2f) toInt
} }
else { else {
0 0

View file

@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.vital.resistance package net.psforever.objects.vital.resistance
import net.psforever.objects.vital.DamageType import net.psforever.objects.vital.damage.DamageProfile
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
/** /**
* The different values for four common methods of modifying incoming damage. * The different values for four common methods of modifying incoming damage.
@ -9,6 +10,8 @@ import net.psforever.objects.vital.DamageType
* This is for defining pure accessor functions. * This is for defining pure accessor functions.
*/ */
trait ResistanceProfile { trait ResistanceProfile {
def Subtract : DamageProfile
def ResistanceDirectHit : Int def ResistanceDirectHit : Int
def ResistanceSplash : Int def ResistanceSplash : Int
@ -34,11 +37,25 @@ trait ResistanceProfile {
* This is for defining both accessor and mutator functions. * This is for defining both accessor and mutator functions.
*/ */
trait ResistanceProfileMutators extends ResistanceProfile { trait ResistanceProfileMutators extends ResistanceProfile {
private var subtract : DamageProfile = new StandardDamageProfile {
//subtract numbers are always negative modifiers
override def Damage0_=(damage : Int) : Int = super.Damage0_=( if(damage < 1) damage else -damage)
override def Damage1_=(damage : Int) : Int = super.Damage1_=( if(damage < 1) damage else -damage)
override def Damage2_=(damage : Int) : Int = super.Damage2_=( if(damage < 1) damage else -damage)
override def Damage3_=(damage : Int) : Int = super.Damage3_=( if(damage < 1) damage else -damage)
override def Damage4_=(damage : Int) : Int = super.Damage4_=( if(damage < 1) damage else -damage)
}
private var resistanceDirectHit : Int = 0 private var resistanceDirectHit : Int = 0
private var resistanceSplash : Int = 0 private var resistanceSplash : Int = 0
private var resistanceAggravated : Int = 0 private var resistanceAggravated : Int = 0
private var radiationShielding : Float = 0f private var radiationShielding : Float = 0f
def Subtract : DamageProfile = subtract
def ResistanceDirectHit : Int = resistanceDirectHit def ResistanceDirectHit : Int = resistanceDirectHit
def ResistanceDirectHit_=(resist : Int) : Int = { def ResistanceDirectHit_=(resist : Int) : Int = {

View file

@ -4,6 +4,7 @@ package net.psforever.objects.vital.resolution
import net.psforever.objects.{Player, TurretDeployable, Vehicle} import net.psforever.objects.{Player, TurretDeployable, Vehicle}
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile} import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable} import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.vital.projectile.ProjectileCalculations import net.psforever.objects.vital.projectile.ProjectileCalculations
/** /**
@ -159,12 +160,17 @@ object ResolutionCalculations {
case _ => ; case _ => ;
} }
def SimpleDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match { def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
case ce : SimpleDeployable => case ce : SimpleDeployable =>
if(ce.Health > 0) { if(ce.Health > 0) {
ce.Health -= damage ce.Health -= damage
ce.History(data) ce.History(data)
} }
case turret : FacilityTurret =>
if(turret.Health > 0) {
turret.Health -= damage
turret.History(data)
}
case _ => case _ =>
} }

View file

@ -13,6 +13,7 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.inventory.Container import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects.serverobject.turret.FacilityTurret
@ -358,6 +359,13 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
case (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error case (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error
} }
}) })
//ntu management (eventually move to a generic building startup function)
buildings.values
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
.collect {
case silo : ResourceSilo =>
silo.Actor ! "startup"
}
} }
private def CreateSpawnGroups() : Unit = { private def CreateSpawnGroups() : Unit = {

View file

@ -2,33 +2,45 @@
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.Vector3 import net.psforever.types.{SpawnGroup, Vector3}
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/**
* The purpose of the `BindPlayerMessage` packet.<br>
* <br>
* `Bind` and `Unbind` are generally manual actions performed by the player.
* `Available` is applied to automatic Advanced Mobile Spawn points and other "Bound" points at the time of redeployment.
* `Lost` and `Unavailable` remove the status of being bound and have slightly different connotations.
* Each generates a different a events chat message if logging it turned on.
*/
object BindStatus extends Enumeration(1) {
type Type = Value
val
Bind,
Unbind,
Lost,
Available,
Unavailable
= Value
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8)
}
/** /**
* A packet dispatched to maintain a manually-set respawn location.<br> * A packet dispatched to maintain a manually-set respawn location.<br>
* <br> * <br>
* The packet establishes the player's ability to spawn in an arbitrary location that is not a normal local option. * The packet establishes the player's ability to spawn in an arbitrary location that is not a normal local option.
* This process is called "binding." * This process is called "binding one's matrix."
* In addition to player establishing the binding, the packet updates as conditions of the respawn location changes.<br> * In addition to player establishing the binding, the packet updates as conditions of the respawn location changes.<br>
* <br> * <br>
* If `logging` is turned on, packets will display different messages depending on context. * If `logging` is turned on, packets will display different messages depending on context.
* The bind descriptor will be used to flavor the events chat message.
* As long as the event is marked to be logged, when the packet is received, a message is displayed in the events window. * As long as the event is marked to be logged, when the packet is received, a message is displayed in the events window.
* If the logged action is applicable, the matrixing sound effect will be played too. * If the logged action is applicable, the matrixing sound effect will be played too.
* Not displaying events is occasionally warranted for aesthetics. * Not displaying events is occasionally warranted for aesthetics.
* The game will always note if this is your first time binding.<br> * The game will always note if this is your first time binding regardless of the state of this flag.<br>
* <br>
* One common occurrence of this packet is during zone transport.
* Specifically, a packet is dispatched after unloading the current zone but before beginning loading in the new zone.
* It is preceded by all of the `ObjectDeleteMessage` packets and itself precedes the `LoadMapMessage` packet.<br>
* <br>
* Actions:<br>
* `1` - bound to respawn point<br>
* `2` - general unbound / unbinding from respawn point<br>
* `3` - respawn point lost<br>
* `4` - bound spawn point became available<br>
* `5` - bound spawn point became unavailable (different from 3)<br>
* <br> * <br>
* Bind Descriptors:<br> * Bind Descriptors:<br>
* `&#64;amp_station`<br> * `&#64;amp_station`<br>
@ -41,22 +53,28 @@ import scodec.codecs._
* Exploration:<br> * Exploration:<br>
* Find other bind descriptors. * Find other bind descriptors.
* @param action the purpose of the packet * @param action the purpose of the packet
* @param bindDesc a description of the respawn binding point * @param bind_desc a text description of the respawn binding point
* @param unk1 na; usually set `true` if there is more data in the packet ... * @param unk1 na;
* usually set `true` if there is more data in the packet ...
* @param logging true, to report on bind point change visible in the events window; * @param logging true, to report on bind point change visible in the events window;
* false, to render spawn change silent; * false, to render spawn change silent;
* a first time event notification will always show * some first time notifications will always display regardless of this flag
* @param unk2 na; if a value, it is usually 40 (hex`28`) * @param spawn_group the kind of spawn request that will be made;
* @param unk3 na * affects the type of icon displayed;
* will coincide with the value of `unk2` in `SpawnRequestMessage` when the spawn option is selected
* @param zone_number the number of the zone in which to display this spawn option;
* if `zone_number` is not the current zone, and the action is positive,
* a small map of the alternate zone with selectable spawn point will become visible
* @param unk4 na * @param unk4 na
* @param pos a position associated with the binding * @param pos coordinates for any displayed deployment map icon;
* `x` and `y` determine the position
*/ */
final case class BindPlayerMessage(action : Int, final case class BindPlayerMessage(action : BindStatus.Value,
bindDesc : String, bind_desc : String,
unk1 : Boolean, unk1 : Boolean,
logging : Boolean, logging : Boolean,
unk2 : Int, spawn_group : SpawnGroup.Value,
unk3 : Long, zone_number : Long,
unk4 : Long, unk4 : Long,
pos : Vector3) pos : Vector3)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
@ -68,20 +86,18 @@ final case class BindPlayerMessage(action : Int,
object BindPlayerMessage extends Marshallable[BindPlayerMessage] { object BindPlayerMessage extends Marshallable[BindPlayerMessage] {
/** /**
* A common variant of this packet. * A common variant of this packet.
* `16028004000000000000000000000000000000`
*/ */
val STANDARD = BindPlayerMessage(2, "", false, false, 2, 0, 0, Vector3(0, 0, 0)) val Standard = BindPlayerMessage(BindStatus.Unbind, "", false, false, SpawnGroup.BoundAMS, 0, 0, Vector3.Zero)
private val spawnGroupCodec = PacketHelpers.createEnumerationCodec(SpawnGroup, uint4)
//TODO: there are two ignore(1) in this packet; are they in a good position?
implicit val codec : Codec[BindPlayerMessage] = ( implicit val codec : Codec[BindPlayerMessage] = (
("action" | uint8L) :: ("action" | BindStatus.codec) ::
("bindDesc" | PacketHelpers.encodedString) :: ("bind_desc" | PacketHelpers.encodedString) ::
("unk1" | bool) :: ("unk1" | bool) ::
("logging" | bool) :: ("logging" | bool) ::
ignore(1) :: ("spawn_group" | spawnGroupCodec) ::
("unk2" | uint4L) :: ("zone_number" | uint32L) ::
ignore(1) ::
("unk3" | uint32L) ::
("unk4" | uint32L) :: ("unk4" | uint32L) ::
("pos" | Vector3.codec_pos) ("pos" | Vector3.codec_pos)
).as[BindPlayerMessage] ).as[BindPlayerMessage]

View file

@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.SpawnGroup
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
@ -9,20 +10,16 @@ import scodec.codecs._
* na * na
* @param unk1 when defined, na; * @param unk1 when defined, na;
* non-zero when selecting the sanctuary option from a non-sanctuary continent deployment map * non-zero when selecting the sanctuary option from a non-sanctuary continent deployment map
* @param unk2 when defined, indicates type of spawn point by destination; * @param spawn_type the type of spawn point destination
* 0 is nothing;
* 2 is ams;
* 6 is towers;
* 7 is facilities
* @param unk3 na * @param unk3 na
* @param unk4 na * @param unk4 na
* @param unk5 when defined, the continent number * @param zone_number when defined, the continent number
*/ */
final case class SpawnRequestMessage(unk1 : Int, final case class SpawnRequestMessage(unk1 : Int,
unk2 : Long, spawn_type : SpawnGroup.Value,
unk3 : Int, unk3 : Int,
unk4 : Int, unk4 : Int,
unk5 : Int) zone_number : Int)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = SpawnRequestMessage type Packet = SpawnRequestMessage
def opcode = GamePacketOpcode.SpawnRequestMessage def opcode = GamePacketOpcode.SpawnRequestMessage
@ -30,11 +27,13 @@ final case class SpawnRequestMessage(unk1 : Int,
} }
object SpawnRequestMessage extends Marshallable[SpawnRequestMessage] { object SpawnRequestMessage extends Marshallable[SpawnRequestMessage] {
private val spawnGroupCodec = PacketHelpers.createLongEnumerationCodec(SpawnGroup, uint32L)
implicit val codec : Codec[SpawnRequestMessage] = ( implicit val codec : Codec[SpawnRequestMessage] = (
("unk1" | uint16L) :: ("unk1" | uint16L) ::
("unk2" | uint32L) :: ("spawn_type" | spawnGroupCodec) ::
("unk3" | uint16L) :: ("unk3" | uint16L) ::
("unk4" | uint16L) :: ("unk4" | uint16L) ::
("unk5" | uintL(10)) ("zone_number" | uintL(10))
).as[SpawnRequestMessage] ).as[SpawnRequestMessage]
} }

View file

@ -0,0 +1,39 @@
// Copyright (c) 2017 PSForever
package net.psforever.types
/**
* The spawn group.<br>
* <br>
* The groups `Sanctuary`, `Tower`, and ,`Facility` are typically hard-defined by the client.
* The groups `AMS` and the `Bound*` spawns can only be displayed on the deployment map
* by sending a manual `BindPlayerMessage` packet to the client,
* and the designated spawn group identifier is returned to the server if the spawn point that is created is selected.
* The sanctuary spawn is also used as a fallback for an unknown spawn point
* as going back to one's own sanctuary counts as a "safe spawn."<br>
* <br>
* The `Sanctuary` spawn is commonly accessible on a smaller map (of the sanctuary continent)
* off to one side of the greater deployment map.
* It does not generate an icon when manually set.
* The icons produced by the normal and the bound tower and facility groups are not detailed.
* The ones that are not designated as "bound" also do not display icons when manually set.
* The AMS spawn group icons have an overhead AMS glyph and are smaller in radius, identical otherwise.
*/
object SpawnGroup extends Enumeration {
type Type = Value
val
Sanctuary,
BoundAMS,
AMS,
Unknown3,
BoundTower, //unused?
BoundFacility,
Tower,
Facility,
Unknown8,
Unknown9,
Unknown10,
Unknown11,
Unknown12
= Value
}

View file

@ -198,7 +198,7 @@ class VehicleService extends Actor {
import net.psforever.objects.vehicles.UtilityType import net.psforever.objects.vehicles.UtilityType
import net.psforever.objects.GlobalDefinitions import net.psforever.objects.GlobalDefinitions
zone.Vehicles zone.Vehicles
.filter(veh => veh.Definition == GlobalDefinitions.ams && veh.DeploymentState == DriveState.Deployed) .filter(veh => veh.Health > 0 && veh.Definition == GlobalDefinitions.ams && veh.DeploymentState == DriveState.Deployed)
.flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube) ) .flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube) )
.map(util => util().asInstanceOf[SpawnTube]) .map(util => util().asInstanceOf[SpawnTube])
} }

View file

@ -27,8 +27,7 @@ class VehicleRemover extends RemoverActor {
val zoneId = entry.zone.Id val zoneId = entry.zone.Id
vehicle.Actor ! Vehicle.PrepareForDeletion vehicle.Actor ! Vehicle.PrepareForDeletion
//kick out all passengers //kick out all passengers
vehicle.Definition.MountPoints.values.foreach(mount => { vehicle.Seats.values.foreach(seat => {
val seat = vehicle.Seat(mount).get
seat.Occupant match { seat.Occupant match {
case Some(tplayer) => case Some(tplayer) =>
seat.Occupant = None seat.Occupant = None

View file

@ -4,27 +4,42 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.Vector3 import net.psforever.types.{SpawnGroup, Vector3}
import scodec.bits._ import scodec.bits._
class BindPlayerMessageTest extends Specification { class BindPlayerMessageTest extends Specification {
val string_standard = hex"16028004000000000000000000000000000000"
val string_ams = hex"16 05 8440616D73 08 28000000 00000000 00000 00000 0000" val string_ams = hex"16 05 8440616D73 08 28000000 00000000 00000 00000 0000"
val string_tech = hex"16 01 8b40746563685f706c616e74 d4 28000000 38000000 00064 012b1 a044" val string_tech = hex"16 01 8b40746563685f706c616e74 d4 28000000 38000000 00064 012b1 a044"
val string_akkan = hex"16048440616d7388100000001400000214e171a8e33024"
"decode (standard)" in {
PacketCoding.DecodePacket(string_standard).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual BindStatus.Unbind
bindDesc mustEqual ""
unk1 mustEqual false
logging mustEqual false
unk2 mustEqual SpawnGroup.BoundAMS
unk3 mustEqual 0
unk4 mustEqual 0
pos mustEqual Vector3.Zero
case _ =>
ko
}
}
"decode (ams)" in { "decode (ams)" in {
PacketCoding.DecodePacket(string_ams).require match { PacketCoding.DecodePacket(string_ams).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) => case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 5 action mustEqual BindStatus.Unavailable
bindDesc.length mustEqual 4
bindDesc mustEqual "@ams" bindDesc mustEqual "@ams"
unk1 mustEqual false unk1 mustEqual false
logging mustEqual false logging mustEqual false
unk2 mustEqual 4 unk2 mustEqual SpawnGroup.AMS
unk3 mustEqual 40 unk3 mustEqual 10
unk4 mustEqual 0 unk4 mustEqual 0
pos.x mustEqual 0f pos mustEqual Vector3.Zero
pos.y mustEqual 0f
pos.z mustEqual 0f
case _ => case _ =>
ko ko
} }
@ -33,40 +48,60 @@ class BindPlayerMessageTest extends Specification {
"decode (tech)" in { "decode (tech)" in {
PacketCoding.DecodePacket(string_tech).require match { PacketCoding.DecodePacket(string_tech).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) => case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 1 action mustEqual BindStatus.Bind
bindDesc.length mustEqual 11
bindDesc mustEqual "@tech_plant" bindDesc mustEqual "@tech_plant"
unk1 mustEqual true unk1 mustEqual true
logging mustEqual true logging mustEqual true
unk2 mustEqual 10 unk2 mustEqual SpawnGroup.BoundFacility
unk3 mustEqual 40 unk3 mustEqual 10
unk4 mustEqual 56 unk4 mustEqual 14
pos.x mustEqual 2060.0f pos mustEqual Vector3(4610.0f, 6292, 69.625f)
pos.y mustEqual 598.0078f
pos.z mustEqual 274.5f
case _ => case _ =>
ko ko
} }
} }
"decode (akkan)" in {
PacketCoding.DecodePacket(string_akkan).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual BindStatus.Available
bindDesc mustEqual "@ams"
unk1 mustEqual true
logging mustEqual false
unk2 mustEqual SpawnGroup.AMS
unk3 mustEqual 4
unk4 mustEqual 5
pos mustEqual Vector3(2673.039f, 4423.547f, 39.1875f)
case _ =>
ko
}
}
"encode (standard)" in {
val msg = BindPlayerMessage.Standard
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_standard
}
"encode (ams)" in { "encode (ams)" in {
val msg = BindPlayerMessage(5, "@ams", false, false, 4, 40, 0, Vector3(0, 0, 0)) val msg = BindPlayerMessage(BindStatus.Unavailable, "@ams", false, false, SpawnGroup.AMS, 10, 0, Vector3.Zero)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_ams pkt mustEqual string_ams
} }
"encode (tech)" in { "encode (tech)" in {
val msg = BindPlayerMessage(1, "@tech_plant", true, true, 10, 40, 56, Vector3(2060.0f, 598.0078f, 274.5f)) val msg = BindPlayerMessage(BindStatus.Bind, "@tech_plant", true, true, SpawnGroup.BoundFacility, 10, 14, Vector3(4610.0f, 6292, 69.625f))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_tech pkt mustEqual string_tech
} }
"standard" in { "encode (akkan)" in {
val msg = BindPlayerMessage.STANDARD val msg = BindPlayerMessage(BindStatus.Available, "@ams", true, false, SpawnGroup.AMS, 4, 5, Vector3(2673.039f, 4423.547f, 39.1875f))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual hex"16028004000000000000000000000000000000" pkt mustEqual string_akkan
} }
} }

View file

@ -4,6 +4,7 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.SpawnGroup
import scodec.bits._ import scodec.bits._
class SpawnRequestMessageTest extends Specification { class SpawnRequestMessageTest extends Specification {
@ -13,7 +14,7 @@ class SpawnRequestMessageTest extends Specification {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case SpawnRequestMessage(unk1,unk2,unk3,unk4,unk5) => case SpawnRequestMessage(unk1,unk2,unk3,unk4,unk5) =>
unk1 mustEqual 0 unk1 mustEqual 0
unk2 mustEqual 7 unk2 mustEqual SpawnGroup.Facility
unk3 mustEqual 0 unk3 mustEqual 0
unk4 mustEqual 0 unk4 mustEqual 0
unk5 mustEqual 2 unk5 mustEqual 2
@ -23,7 +24,7 @@ class SpawnRequestMessageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = SpawnRequestMessage(0, 7, 0, 0, 2) val msg = SpawnRequestMessage(0, SpawnGroup.Facility, 0, 0, 2)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -106,12 +106,12 @@ class DamageCalculationsTests extends Specification {
"calculate splash damage from components (near)" in { "calculate splash damage from components (near)" in {
val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof)) val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
SplashDamageWithRadialDegrade(projectile, result, 0) mustEqual 264 SplashDamageWithRadialDegrade(projectile, result, 0) mustEqual 132
} }
"calculate splash damage from components (medium)" in { "calculate splash damage from components (medium)" in {
val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof)) val result = DamageWithModifiers(DamageAgainstVehicle)(proj_prof, List(wep_prof))
SplashDamageWithRadialDegrade(projectile, result, 5) mustEqual 145 SplashDamageWithRadialDegrade(projectile, result, 5) mustEqual 13
} }
"calculate splash damage from components (far)" in { "calculate splash damage from components (far)" in {
@ -341,7 +341,7 @@ class DamageModelTests extends Specification {
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash) val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
func(tplayer) func(tplayer)
tplayer.Health mustEqual 81 tplayer.Health mustEqual 98
tplayer.Armor mustEqual 35 tplayer.Armor mustEqual 35
} }
@ -410,7 +410,7 @@ class DamageModelTests extends Specification {
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash) val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
func(vehicle) func(vehicle)
vehicle.Health mustEqual 632 vehicle.Health mustEqual 641
} }
} }
} }

View file

@ -111,6 +111,7 @@ class EquipmentTest extends Specification {
obj.Size = EquipmentSize.Rifle obj.Size = EquipmentSize.Rifle
obj.AmmoTypes += GlobalDefinitions.shotgun_shell obj.AmmoTypes += GlobalDefinitions.shotgun_shell
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
obj.FireModes += new FireModeDefinition obj.FireModes += new FireModeDefinition
obj.FireModes.head.AmmoTypeIndices += 0 obj.FireModes.head.AmmoTypeIndices += 0
obj.FireModes.head.AmmoTypeIndices += 1 obj.FireModes.head.AmmoTypeIndices += 1
@ -163,6 +164,29 @@ class EquipmentTest extends Specification {
obj.AmmoType mustEqual Ammo.hellfire_ammo obj.AmmoType mustEqual Ammo.hellfire_ammo
} }
"default fire mode (dual magazine feed)" in {
val obj = ToolDefinition(1076)
obj.AmmoTypes += GlobalDefinitions.shotgun_shell
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
obj.ProjectileTypes += GlobalDefinitions.shotgun_shell_projectile
obj.ProjectileTypes += GlobalDefinitions.shotgun_shell_AP_projectile
obj.FireModes += new FireModeDefinition
obj.FireModes.head.AmmoTypeIndices += 0
obj.FireModes.head.AmmoSlotIndex = 0
obj.FireModes += new FireModeDefinition
obj.FireModes(1).AmmoTypeIndices += 1
obj.FireModes(1).AmmoSlotIndex = 1
val tool0 = Tool(obj)
tool0.FireModeIndex mustEqual 0
tool0.ProjectileType mustEqual GlobalDefinitions.shotgun_shell_projectile.ProjectileType
obj.DefaultFireModeIndex = 1
val tool1 = Tool(obj)
tool1.FireModeIndex mustEqual 1
tool1.ProjectileType mustEqual GlobalDefinitions.shotgun_shell_AP_projectile.ProjectileType
}
"multiple fire modes" in { "multiple fire modes" in {
//explanation: sample_weapon has two fire modes; each fire mode has a different ammunition type //explanation: sample_weapon has two fire modes; each fire mode has a different ammunition type
val obj : Tool = Tool(punisher) val obj : Tool = Tool(punisher)

File diff suppressed because it is too large Load diff