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.InventoryScale = InventoryScale
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.ResistanceSplash = ResistanceSplash
obj.ResistanceAggravated = ResistanceAggravated
@ -166,6 +170,7 @@ object ExoSuitDefinition {
MAX.InventoryOffset = 6
MAX.Holster(0, EquipmentSize.Max)
MAX.Holster(4, EquipmentSize.Melee)
MAX.Subtract.Damage1 = -2
MAX.ResistanceDirectHit = 6
MAX.ResistanceSplash = 35
MAX.ResistanceAggravated = 10

View file

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

View file

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

View file

@ -20,7 +20,7 @@ import scala.annotation.tailrec
class Tool(private val toolDef : ToolDefinition) extends Equipment
with FireModeSwitch[FireModeDefinition] {
/** 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 */
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.ce.ComplexDeployable
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
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 Orientation = orientation
def Velocity = None
def Modifiers = obj_def.asInstanceOf[ResistanceProfile]
}
object ComplexDeployableSource {

View file

@ -4,6 +4,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.ce.Deployable
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
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 Orientation = orientation
def Velocity = None
def Modifiers = obj_def.asInstanceOf[ResistanceProfile]
}
object DeployableSource {

View file

@ -3,19 +3,21 @@ package net.psforever.objects.ballistics
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
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 {
velocity : Option[Vector3]) 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
def Modifiers = new ResistanceProfileMutators { }
}
object ObjectSource {

View file

@ -3,6 +3,7 @@ package net.psforever.objects.ballistics
import net.psforever.objects.Player
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
final case class PlayerSource(name : String,
@ -14,7 +15,8 @@ final case class PlayerSource(name : String,
armor : Int,
position : Vector3,
orientation : Vector3,
velocity : Option[Vector3] = None) extends SourceEntry {
velocity : Option[Vector3],
modifiers : ResistanceProfile) extends SourceEntry {
override def Name = name
override def Faction = faction
def Definition = obj_def
@ -25,11 +27,12 @@ final case class PlayerSource(name : String,
def Position = position
def Orientation = orientation
def Velocity = velocity
def Modifiers = modifiers
}
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)
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.entity.WorldEntity
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
trait SourceEntry extends WorldEntity {
@ -16,6 +17,7 @@ trait SourceEntry extends WorldEntity {
def Position_=(pos : Vector3) = Position
def Orientation_=(pos : Vector3) = Position
def Velocity_=(pos : Option[Vector3]) = Velocity
def Modifiers : ResistanceProfile
}
object SourceEntry {
@ -24,6 +26,7 @@ object SourceEntry {
def Position = Vector3.Zero
def Orientation = Vector3.Zero
def Velocity = Some(Vector3.Zero)
def Modifiers = null
}
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.definition.VehicleDefinition
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{PlanetSideEmpire, Vector3}
final case class VehicleSource(obj_def : VehicleDefinition,
@ -11,7 +12,8 @@ final case class VehicleSource(obj_def : VehicleDefinition,
shields : Int,
position : 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 Faction = faction
def Definition : VehicleDefinition = obj_def
@ -20,6 +22,7 @@ final case class VehicleSource(obj_def : VehicleDefinition,
def Position = position
def Orientation = orientation
def Velocity = velocity
def Modifiers = modifiers
}
object VehicleSource {
@ -31,7 +34,8 @@ object VehicleSource {
obj.Shields,
obj.Position,
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.definition.converter.SmallDeployableConverter
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._
trait BaseDeployableDefinition extends DamageResistanceModel {
trait BaseDeployableDefinition extends DamageResistanceModel
with ResistanceProfileMutators {
private var category : DeployableCategory.Value = DeployableCategory.Boomers
private var deployTime : Long = (1 second).toMillis //ms
private var maxHealth : Int = 1

View file

@ -2,8 +2,7 @@
package net.psforever.objects.definition
import net.psforever.objects.ballistics.Projectiles
import net.psforever.objects.vital.DamageType
import net.psforever.objects.vital.damage.DamageProfile
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
/**
* 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
*/
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
with DamageProfile {
with StandardDamageProfile {
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 accelerationUntil : Float = 0f
private var damageType : DamageType.Value = DamageType.None
@ -45,57 +39,6 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
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_=(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 projectileTypes : mutable.ListBuffer[ProjectileDefinition] = new mutable.ListBuffer[ProjectileDefinition]
private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition]
private var defaultFireModeIndex : Option[Int] = None
Name = "tool"
Packet = ToolDefinition.converter
@ -20,6 +21,15 @@ class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes
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 {

View file

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

View file

@ -226,10 +226,12 @@ class GridInventory extends Container {
else {
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
var curr = actualSlot
val fixedItems = items.toMap
val fixedGrid = grid.toList
for(_ <- 0 until h) {
for(col <- 0 until w) {
if(grid(curr + col) > -1) {
collisions += items(grid(curr + col))
if(fixedGrid(curr + col) > -1) {
collisions += fixedItems(fixedGrid(curr + col))
}
}
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 context a context to allow the object to properly set up `ActorSystem` functionality;
* not necessary for this object, but required by signature
* @return the `Locker` object
* @return the `ResourceSilo` object
*/
def Constructor(id : Int, context : ActorContext) : ResourceSilo = {
val obj = ResourceSilo()
obj.Actor = context.actorOf(Props(classOf[ResourceSiloControl], obj), s"${obj.Definition.Name}_$id")
obj.Actor ! "startup"
obj
}
}

View file

@ -42,7 +42,7 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg))
case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
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))
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
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.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
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 => {
val slot = tool.AmmoSlots(index)
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
case obj : ShorthandAmmoBox =>
MakeAmmoBox(obj.definition, Some(obj.capacity))
MakeAmmoBox(obj.definition) //Some(obj.capacity)
case obj : ShorthandConstructionItem =>
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.types.Vector3
import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
class FacilityTurret(tDef : TurretDefinition) extends Amenity
with WeaponTurret {
with WeaponTurret
with Vitality
with StandardResistanceProfile {
/** some turrets can be updated; they all start without updates */
private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None
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 MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
@ -28,6 +33,8 @@ class FacilityTurret(tDef : TurretDefinition) extends Amenity
Upgrade
}
def DamageModel = Definition.asInstanceOf[DamageResistanceModel]
def Definition : TurretDefinition = tDef
}

View file

@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.turret
import akka.actor.Actor
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
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>
@ -38,6 +39,18 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor
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 _ => ;
}
}

View file

@ -3,6 +3,8 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition}
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
@ -10,7 +12,9 @@ import scala.collection.mutable
* The definition for any `MannedTurret`.
* @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
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
* see `MannedTurret.TurretAmmoBox` for details */
private var hasReserveAmmunition : Boolean = false
Damage = StandardVehicleDamage
Resistance = StandardVehicleResistance
Model = StandardResolutions.FacilityTurrets
def MaxHealth : Int = maxHealth

View file

@ -63,7 +63,16 @@ class VehicleControl(vehicle : Vehicle) extends Actor
case Vitality.Damage(damage_func) =>
if(vehicle.Health > 0) {
val originalHealth = vehicle.Health
val originalShields = vehicle.Shields
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)
}

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

View file

@ -2,6 +2,7 @@
package net.psforever.objects.vital
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.vital.damage.DamageProfile
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")
private val resistDef = Definition.asInstanceOf[ResistanceProfile] //cast only once
def Subtract : DamageProfile = resistDef.Subtract
def ResistanceDirectHit : Int = resistDef.ResistanceDirectHit
def ResistanceSplash : Int = resistDef.ResistanceDirectHit

View file

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

View file

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

View file

@ -27,9 +27,16 @@ abstract class DamageCalculations(damages : DamagesType,
*/
def Calculate(data : ResolvedProjectile) : Int = {
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(
projectile,
extractor(projectile.profile, List(projectile.fire_mode.Modifiers)),
extractor(profile, modifiers),
distanceFunc(data)
)
}
@ -70,6 +77,17 @@ object DamageCalculations {
//damage calculation functions
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
* and its maximum effective distance.
@ -106,8 +124,26 @@ object DamageCalculations {
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
val degrade : Float = (1 - base) * ((radius - distance)/radius) + base
(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 {
0

View file

@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever
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.
@ -9,6 +10,8 @@ import net.psforever.objects.vital.DamageType
* This is for defining pure accessor functions.
*/
trait ResistanceProfile {
def Subtract : DamageProfile
def ResistanceDirectHit : Int
def ResistanceSplash : Int
@ -34,11 +37,25 @@ trait ResistanceProfile {
* This is for defining both accessor and mutator functions.
*/
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 resistanceSplash : Int = 0
private var resistanceAggravated : Int = 0
private var radiationShielding : Float = 0f
def Subtract : DamageProfile = subtract
def ResistanceDirectHit : Int = resistanceDirectHit
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.ballistics.{PlayerSource, ResolvedProjectile}
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.vital.projectile.ProjectileCalculations
/**
@ -159,12 +160,17 @@ object ResolutionCalculations {
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 =>
if(ce.Health > 0) {
ce.Health -= damage
ce.History(data)
}
case turret : FacilityTurret =>
if(turret.Health > 0) {
turret.Health -= damage
turret.History(data)
}
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.source.LimitedNumberSource
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.tube.SpawnTube
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
}
})
//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 = {

View file

@ -2,33 +2,45 @@
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.Vector3
import net.psforever.types.{SpawnGroup, Vector3}
import scodec.Codec
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>
* <br>
* 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>
* <br>
* 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.
* If the logged action is applicable, the matrixing sound effect will be played too.
* Not displaying events is occasionally warranted for aesthetics.
* The game will always note if this is your first time binding.<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>
* The game will always note if this is your first time binding regardless of the state of this flag.<br>
* <br>
* Bind Descriptors:<br>
* `&#64;amp_station`<br>
@ -41,22 +53,28 @@ import scodec.codecs._
* Exploration:<br>
* Find other bind descriptors.
* @param action the purpose of the packet
* @param bindDesc a description of the respawn binding point
* @param unk1 na; usually set `true` if there is more data in the packet ...
* @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 logging true, to report on bind point change visible in the events window;
* false, to render spawn change silent;
* a first time event notification will always show
* @param unk2 na; if a value, it is usually 40 (hex`28`)
* @param unk3 na
* some first time notifications will always display regardless of this flag
* @param spawn_group the kind of spawn request that will be made;
* 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 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,
bindDesc : String,
final case class BindPlayerMessage(action : BindStatus.Value,
bind_desc : String,
unk1 : Boolean,
logging : Boolean,
unk2 : Int,
unk3 : Long,
spawn_group : SpawnGroup.Value,
zone_number : Long,
unk4 : Long,
pos : Vector3)
extends PlanetSideGamePacket {
@ -68,20 +86,18 @@ final case class BindPlayerMessage(action : Int,
object BindPlayerMessage extends Marshallable[BindPlayerMessage] {
/**
* 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] = (
("action" | uint8L) ::
("bindDesc" | PacketHelpers.encodedString) ::
("action" | BindStatus.codec) ::
("bind_desc" | PacketHelpers.encodedString) ::
("unk1" | bool) ::
("logging" | bool) ::
ignore(1) ::
("unk2" | uint4L) ::
ignore(1) ::
("unk3" | uint32L) ::
("spawn_group" | spawnGroupCodec) ::
("zone_number" | uint32L) ::
("unk4" | uint32L) ::
("pos" | Vector3.codec_pos)
).as[BindPlayerMessage]

View file

@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever
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.codecs._
@ -9,20 +10,16 @@ import scodec.codecs._
* na
* @param unk1 when defined, na;
* 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;
* 0 is nothing;
* 2 is ams;
* 6 is towers;
* 7 is facilities
* @param spawn_type the type of spawn point destination
* @param unk3 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,
unk2 : Long,
spawn_type : SpawnGroup.Value,
unk3 : Int,
unk4 : Int,
unk5 : Int)
zone_number : Int)
extends PlanetSideGamePacket {
type Packet = SpawnRequestMessage
def opcode = GamePacketOpcode.SpawnRequestMessage
@ -30,11 +27,13 @@ final case class SpawnRequestMessage(unk1 : Int,
}
object SpawnRequestMessage extends Marshallable[SpawnRequestMessage] {
private val spawnGroupCodec = PacketHelpers.createLongEnumerationCodec(SpawnGroup, uint32L)
implicit val codec : Codec[SpawnRequestMessage] = (
("unk1" | uint16L) ::
("unk2" | uint32L) ::
("spawn_type" | spawnGroupCodec) ::
("unk3" | uint16L) ::
("unk4" | uint16L) ::
("unk5" | uintL(10))
("zone_number" | uintL(10))
).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.GlobalDefinitions
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) )
.map(util => util().asInstanceOf[SpawnTube])
}

View file

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

View file

@ -4,27 +4,42 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.Vector3
import net.psforever.types.{SpawnGroup, Vector3}
import scodec.bits._
class BindPlayerMessageTest extends Specification {
val string_standard = hex"16028004000000000000000000000000000000"
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_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 {
PacketCoding.DecodePacket(string_ams).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 5
bindDesc.length mustEqual 4
action mustEqual BindStatus.Unavailable
bindDesc mustEqual "@ams"
unk1 mustEqual false
logging mustEqual false
unk2 mustEqual 4
unk3 mustEqual 40
unk2 mustEqual SpawnGroup.AMS
unk3 mustEqual 10
unk4 mustEqual 0
pos.x mustEqual 0f
pos.y mustEqual 0f
pos.z mustEqual 0f
pos mustEqual Vector3.Zero
case _ =>
ko
}
@ -33,40 +48,60 @@ class BindPlayerMessageTest extends Specification {
"decode (tech)" in {
PacketCoding.DecodePacket(string_tech).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 1
bindDesc.length mustEqual 11
action mustEqual BindStatus.Bind
bindDesc mustEqual "@tech_plant"
unk1 mustEqual true
logging mustEqual true
unk2 mustEqual 10
unk3 mustEqual 40
unk4 mustEqual 56
pos.x mustEqual 2060.0f
pos.y mustEqual 598.0078f
pos.z mustEqual 274.5f
unk2 mustEqual SpawnGroup.BoundFacility
unk3 mustEqual 10
unk4 mustEqual 14
pos mustEqual Vector3(4610.0f, 6292, 69.625f)
case _ =>
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 {
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
pkt mustEqual string_ams
}
"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
pkt mustEqual string_tech
}
"standard" in {
val msg = BindPlayerMessage.STANDARD
"encode (akkan)" in {
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
pkt mustEqual hex"16028004000000000000000000000000000000"
pkt mustEqual string_akkan
}
}

View file

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

View file

@ -106,12 +106,12 @@ class DamageCalculationsTests extends Specification {
"calculate splash damage from components (near)" in {
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 {
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 {
@ -341,7 +341,7 @@ class DamageModelTests extends Specification {
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
func(tplayer)
tplayer.Health mustEqual 81
tplayer.Health mustEqual 98
tplayer.Armor mustEqual 35
}
@ -410,7 +410,7 @@ class DamageModelTests extends Specification {
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
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.AmmoTypes += GlobalDefinitions.shotgun_shell
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
obj.FireModes += new FireModeDefinition
obj.FireModes.head.AmmoTypeIndices += 0
obj.FireModes.head.AmmoTypeIndices += 1
@ -163,6 +164,29 @@ class EquipmentTest extends Specification {
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 {
//explanation: sample_weapon has two fire modes; each fire mode has a different ammunition type
val obj : Tool = Tool(punisher)

File diff suppressed because it is too large Load diff