diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index f8c14fe1..88e7aaef 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -26,7 +26,7 @@ import net.psforever.objects.ce._
import net.psforever.objects.definition._
import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter}
import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
-import net.psforever.objects.equipment.{EffectTarget, Equipment, FireModeSwitch, JammableUnit}
+import net.psforever.objects.equipment.{ChargeFireModeDefinition, EffectTarget, Equipment, FireModeSwitch, JammableUnit}
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
@@ -127,8 +127,10 @@ object SessionActor {
* must be a positive value
* @param completionAction a finalizing action performed once the progress reaches 100(%)
* @param tickAction an action that is performed for each increase of progress
+ * @param tickTime how long between each `tickAction` (ms);
+ * defaults to 250 milliseconds
*/
- final case class ProgressEvent(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean)
+ final case class ProgressEvent(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean, tickTime: Long = 250)
private final val zoningCountdownMessages: Seq[Int] = Seq(5, 10, 20)
@@ -175,6 +177,8 @@ class SessionActor extends Actor with MDCContextAware {
var progressBarValue: Option[Float] = None
var shooting: Option[PlanetSideGUID] = None //ChangeFireStateMessage_Start
var prefire: Option[PlanetSideGUID] = None //if WeaponFireMessage precedes ChangeFireStateMessage_Start
+ var shootingStart: Long = 0
+ var shootingStop: Long = 0
var shotsWhileDead: Int = 0
var accessedContainer: Option[PlanetSideGameObject with Container] = None
var connectionState: Int = 25
@@ -543,8 +547,8 @@ class SessionActor extends Actor with MDCContextAware {
self ! ProgressEvent(rate, finishedAction, stepAction)
}
- case ProgressEvent(delta, finishedAction, stepAction) =>
- HandleProgressChange(delta, finishedAction, stepAction)
+ case ProgressEvent(delta, finishedAction, stepAction, tick) =>
+ HandleProgressChange(delta, finishedAction, stepAction, tick)
case Door.DoorMessage(tplayer, msg, order) =>
HandleDoorMessage(tplayer, msg, order)
@@ -2986,7 +2990,7 @@ class SessionActor extends Actor with MDCContextAware {
* @param tickAction an optional action is is performed for each tick of progress;
* also performs a continuity check to determine if the process has been disrupted
*/
- def HandleProgressChange(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean): Unit = {
+ def HandleProgressChange(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean, tick: Long): Unit = {
progressBarUpdate.cancel()
progressBarValue match {
case Some(value) =>
@@ -3015,9 +3019,9 @@ class SessionActor extends Actor with MDCContextAware {
progressBarValue = Some(next)
import scala.concurrent.ExecutionContext.Implicits.global
progressBarUpdate = context.system.scheduler.scheduleOnce(
- 250 milliseconds,
+ tick milliseconds,
self,
- ProgressEvent(delta, completionAction, tickAction)
+ ProgressEvent(delta, completionAction, tickAction, tick)
)
} else {
progressBarValue = None
@@ -4051,6 +4055,7 @@ class SessionActor extends Actor with MDCContextAware {
if (tool.Magazine > 0 || prefire.contains(item_guid)) {
prefire = None
shooting = Some(item_guid)
+ shootingStart = System.currentTimeMillis()
//special case - suppress the decimator's alternate fire mode, by projectile
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
continent.AvatarEvents ! AvatarServiceMessage(
@@ -4058,6 +4063,17 @@ class SessionActor extends Actor with MDCContextAware {
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
)
}
+ //charge ammunition drain
+ tool.FireMode match {
+ case mode: ChargeFireModeDefinition =>
+ progressBarValue = Some(0f)
+ progressBarUpdate = context.system.scheduler.scheduleOnce(
+ (mode.Time + mode.DrainInterval) milliseconds,
+ self,
+ ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
+ )
+ case _ => ;
+ }
} else {
log.warn(
s"ChangeFireState_Start: ${tool.Definition.Name} magazine is empty before trying to shoot bullet"
@@ -4067,6 +4083,7 @@ class SessionActor extends Actor with MDCContextAware {
case Some(_) => //permissible, for now
prefire = None
shooting = Some(item_guid)
+ shootingStart = System.currentTimeMillis()
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
@@ -4079,6 +4096,7 @@ class SessionActor extends Actor with MDCContextAware {
case msg @ ChangeFireStateMessage_Stop(item_guid) =>
log.trace("ChangeFireState_Stop: " + msg)
prefire = None
+ shootingStop = System.currentTimeMillis()
val weapon: Option[Equipment] = if (shooting.contains(item_guid)) {
shooting = None
continent.AvatarEvents ! AvatarServiceMessage(
@@ -4099,6 +4117,7 @@ class SessionActor extends Actor with MDCContextAware {
continent.id,
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
)
+ shootingStart = System.currentTimeMillis() - 1L
}
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
@@ -4118,6 +4137,11 @@ class SessionActor extends Actor with MDCContextAware {
}
weapon match {
case Some(tool: Tool) =>
+ tool.FireMode match {
+ case mode : ChargeFireModeDefinition =>
+ sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
+ case _ => ;
+ }
if (tool.Magazine == 0) {
FireCycleCleanup(tool)
}
@@ -4201,7 +4225,7 @@ class SessionActor extends Actor with MDCContextAware {
log.warn(s"ReloadMessage: no ammunition could be found for $item_guid")
case x :: xs =>
val (deleteFunc, modifyFunc): (Equipment => Future[Any], (AmmoBox, Int) => Unit) = obj match {
- case (veh: Vehicle) =>
+ case veh: Vehicle =>
(RemoveOldEquipmentFromInventory(veh, taskResolver), ModifyAmmunitionInVehicle(veh))
case o: PlanetSideServerObject with Container =>
(RemoveOldEquipmentFromInventory(o, taskResolver), ModifyAmmunition(o))
@@ -5244,7 +5268,7 @@ class SessionActor extends Actor with MDCContextAware {
unk6,
unk7
) =>
- //log.info(s"WeaponFire: $msg")
+ log.info(s"WeaponFire: $msg")
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin)
case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
@@ -6811,6 +6835,7 @@ class SessionActor extends Actor with MDCContextAware {
)
prefire = None
shooting = None
+ shootingStop = System.currentTimeMillis()
case None => ;
}
if (session.flying) {
@@ -9345,8 +9370,7 @@ class SessionActor extends Actor with MDCContextAware {
case _ =>
(obj.Orientation, obj.Definition.ObjectId, 300f)
}
- val distanceToOwner =
- Vector3.DistanceSquared(shotOrigin, player.Position)
+ val distanceToOwner = Vector3.DistanceSquared(shotOrigin, player.Position)
if (distanceToOwner <= acceptableDistanceToOwner) {
val projectile_info = tool.Projectile
val projectile =
@@ -9357,9 +9381,15 @@ class SessionActor extends Actor with MDCContextAware {
player,
attribution,
shotOrigin,
- angle
+ angle,
)
- projectiles(projectileIndex) = Some(projectile)
+ val initialQuality = tool.FireMode match {
+ case mode: ChargeFireModeDefinition =>
+ ProjectileQuality.Modified((projectile.fire_time - shootingStart) / mode.Time.toFloat)
+ case _ =>
+ ProjectileQuality.Normal
+ }
+ projectiles(projectileIndex) = Some(projectile.quality(initialQuality))
if (projectile_info.ExistsOnRemoteClients) {
log.trace(
s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile"
@@ -9421,9 +9451,9 @@ class SessionActor extends Actor with MDCContextAware {
avatarActor ! AvatarActor.ConsumeStamina(avatar.stamina)
}
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
+ prefire = shooting.orElse(Some(weaponGUID))
+ tool.Discharge()
}
- prefire = shooting.orElse(Some(weaponGUID))
- tool.Discharge() //always
out
case _ =>
(None, None)
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 8e1d65ff..a1ef9755 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -2,7 +2,7 @@
package net.psforever.objects
import net.psforever.objects.avatar.Certification
-import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, AggravatedTiming, Projectiles}
+import net.psforever.objects.ballistics._
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
import net.psforever.objects.definition._
import net.psforever.objects.definition.converter._
@@ -23,8 +23,9 @@ import net.psforever.objects.serverobject.structures.{BuildingDefinition, WarpGa
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers}
-import net.psforever.objects.vital.{DamageType, StandardResolutions}
+import net.psforever.objects.vital.{DamageType, StandardDamageProfile, StandardResolutions}
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3}
+
import scala.collection.mutable
import scala.concurrent.duration._
@@ -3865,19 +3866,20 @@ object GlobalDefinitions {
sparrow_secondary_projectile.Modifiers = DamageModifiers.RadialDegrade
spiker_projectile.Name = "spiker_projectile"
- // spiker_projectile.Damage0 = 75
- spiker_projectile.Damage0 = 20
- // spiker_projectile.Damage0_min = 20
- // spiker_projectile.Damage1 = 75
- spiker_projectile.Damage1 = 20
- // spiker_projectile.Damage1_min = 20
+ spiker_projectile.Charging = ChargeDamage(4, StandardDamageProfile(damage0 = Some(20), damage1 = Some(20)))
+ spiker_projectile.Damage0 = 75
+ spiker_projectile.Damage1 = 75
spiker_projectile.DamageAtEdge = 0.1f
spiker_projectile.DamageRadius = 5f
- spiker_projectile.DamageRadius = 1f
+ spiker_projectile.DamageRadiusMin = 1f
spiker_projectile.ProjectileDamageType = DamageType.Splash
spiker_projectile.InitialVelocity = 40
spiker_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
+ spiker_projectile.Modifiers = List(
+ DamageModifiers.SpikerChargeDamage,
+ DamageModifiers.RadialDegrade
+ )
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
spitfire_aa_ammo_projectile.Damage0 = 5
@@ -4214,8 +4216,8 @@ object GlobalDefinitions {
isp.FireModes.head.AmmoTypeIndices += 0
isp.FireModes.head.AmmoTypeIndices += 1
isp.FireModes.head.AmmoSlotIndex = 0
+ isp.FireModes.head.Chamber = 6 //8 shells x 6 pellets = 36
isp.FireModes.head.Magazine = 8
- isp.FireModes.head.Chamber = 6 //8 shells x 6 pellets = 48
isp.FireModes.head.Add.Damage0 = 1
isp.FireModes.head.Add.Damage2 = 1
isp.FireModes.head.Add.Damage3 = 1
@@ -4417,7 +4419,7 @@ object GlobalDefinitions {
spiker.Size = EquipmentSize.Pistol
spiker.AmmoTypes += ancient_ammo_combo
spiker.ProjectileTypes += spiker_projectile
- spiker.FireModes += new FireModeDefinition
+ spiker.FireModes += new ChargeFireModeDefinition(time = 1000, drainInterval = 500)
spiker.FireModes.head.AmmoTypeIndices += 0
spiker.FireModes.head.AmmoSlotIndex = 0
spiker.FireModes.head.Magazine = 25
@@ -4682,8 +4684,8 @@ object GlobalDefinitions {
pellet_gun.FireModes += new PelletFireModeDefinition
pellet_gun.FireModes.head.AmmoTypeIndices += 0
pellet_gun.FireModes.head.AmmoSlotIndex = 0
- pellet_gun.FireModes.head.Magazine = 1
- pellet_gun.FireModes.head.Chamber = 8 //1 shells * 8 pellets = 8
+ pellet_gun.FireModes.head.Magazine = 1 //what is this?
+ pellet_gun.FireModes.head.Chamber = 8 //1 shell * 8 pellets = 8
pellet_gun.Tile = InventoryTile.Tile63
six_shooter.Name = "six_shooter"
@@ -4773,17 +4775,17 @@ object GlobalDefinitions {
nchev_scattercannon.FireModes.head.AmmoTypeIndices += 0
nchev_scattercannon.FireModes.head.AmmoSlotIndex = 0
nchev_scattercannon.FireModes.head.Magazine = 40
- nchev_scattercannon.FireModes.head.Chamber = 10
+ nchev_scattercannon.FireModes.head.Chamber = 10 //40 shells * 10 pellets = 400
nchev_scattercannon.FireModes += new PelletFireModeDefinition
nchev_scattercannon.FireModes(1).AmmoTypeIndices += 0
nchev_scattercannon.FireModes(1).AmmoSlotIndex = 0
nchev_scattercannon.FireModes(1).Magazine = 40
- nchev_scattercannon.FireModes(1).Chamber = 10
+ nchev_scattercannon.FireModes(1).Chamber = 10 //40 shells * 10 pellets = 400
nchev_scattercannon.FireModes += new PelletFireModeDefinition
nchev_scattercannon.FireModes(2).AmmoTypeIndices += 0
nchev_scattercannon.FireModes(2).AmmoSlotIndex = 0
nchev_scattercannon.FireModes(2).Magazine = 40
- nchev_scattercannon.FireModes(2).Chamber = 10
+ nchev_scattercannon.FireModes(2).Chamber = 10 //40 shells * 10 pellets = 400
nchev_falcon.Name = "nchev_falcon"
nchev_falcon.Size = EquipmentSize.Max
@@ -5526,7 +5528,7 @@ object GlobalDefinitions {
energy_gun_nc.FireModes.head.AmmoTypeIndices += 0
energy_gun_nc.FireModes.head.AmmoSlotIndex = 0
energy_gun_nc.FireModes.head.Magazine = 35
- energy_gun_nc.FireModes.head.Chamber = 9
+ energy_gun_nc.FireModes.head.Chamber = 8 //35 shots * 8 pellets = 280
energy_gun_tr.Name = "energy_gun_tr"
energy_gun_tr.Size = EquipmentSize.BaseTurretWeapon
diff --git a/src/main/scala/net/psforever/objects/Tool.scala b/src/main/scala/net/psforever/objects/Tool.scala
index 114d08cf..22284143 100644
--- a/src/main/scala/net/psforever/objects/Tool.scala
+++ b/src/main/scala/net/psforever/objects/Tool.scala
@@ -239,3 +239,5 @@ object Tool {
def Definition: FireModeDefinition = fdef
}
}
+
+
diff --git a/src/main/scala/net/psforever/objects/Tools.scala b/src/main/scala/net/psforever/objects/Tools.scala
new file mode 100644
index 00000000..6744550b
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/Tools.scala
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects
+
+import net.psforever.objects.equipment.ChargeFireModeDefinition
+import net.psforever.packet.game.QuantityUpdateMessage
+import net.psforever.services.Service
+import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
+
+object Tools {
+ /**
+ *
+ * @param player the player performing the revive action
+ * @param tool the tool being used to execute the attack;
+ * should have a selected chargeable fire mode
+ * @param progress the current progress value
+ * @see `ChargeFireModeDefinition`
+ * @see `QuantityUpdateMessage`
+ * @return `true`, if the next cycle of progress should occur;
+ * `false`, otherwise
+ */
+ def ChargeFireMode(player: Player, tool: Tool)(progress: Float): Boolean = {
+ tool.FireMode match {
+ case mode: ChargeFireModeDefinition if tool.Magazine > 0 =>
+ val magazine = tool.Magazine -= mode.RoundsPerInterval
+ player.Zone.AvatarEvents ! AvatarServiceMessage(
+ player.Name,
+ AvatarAction.SendResponse(
+ Service.defaultPlayerGUID,
+ QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, magazine)
+ )
+ )
+ player.isAlive
+ case _ =>
+ false
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala b/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala
new file mode 100644
index 00000000..c3633dca
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/ballistics/ChargeDamage.scala
@@ -0,0 +1,9 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.ballistics
+
+import net.psforever.objects.vital.StandardDamageProfile
+
+final case class ChargeDamage(
+ effect_count: Int,
+ min: StandardDamageProfile
+ )
diff --git a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
index a00b79e4..8f000b70 100644
--- a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
@@ -43,7 +43,7 @@ final case class Projectile(
shot_angle: Vector3,
quality: ProjectileQuality = ProjectileQuality.Normal,
id: Long = Projectile.idGenerator.getAndIncrement(),
- fire_time: Long = System.nanoTime
+ fire_time: Long = System.currentTimeMillis()
) extends PlanetSideGameObject {
Position = shot_origin
Orientation = shot_angle
diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index 6c225fb0..f4294e6d 100644
--- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
-import net.psforever.objects.ballistics.{AggravatedDamage, Projectiles}
+import net.psforever.objects.ballistics.{AggravatedDamage, ChargeDamage, Projectiles}
import net.psforever.objects.equipment.JammingUnit
import net.psforever.objects.vital.damage.DamageModifiers
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
@@ -38,8 +38,10 @@ class ProjectileDefinition(objectId: Int)
private var lifespan: Float = 1f
/** for radial damage, how much damage has been lost the further away from the impact point (m) */
private var damageAtEdge: Float = 1f
- /** for radial damage, the radial distance of the explosion effect (m) */
- private var damageRadius: Float = 1f
+ /** for radial damage, the distance of the explosion effect (m) */
+ private var damageRadius: Float = 0f
+ /** for radial damage, the distance before degradation of the explosion effect (m) */
+ private var damageRadiusMin: Float = 1f
/** for lashing damage, how far away a target will be affected by the projectile (m) */
private var lashRadius : Float = 0f
/** use a specific modifier as a part of damage calculations */
@@ -65,9 +67,11 @@ class ProjectileDefinition(objectId: Int)
private var jammerProjectile: Boolean = false
/** projectile takes the form of a type of "grenade";
* grenades arc with gravity rather than travel in a relatively straight path */
- private var grenade_projectile : Boolean = false
+ private var grenade_projectile: Boolean = false
/** projectile tries to confers aggravated damage burn to its target */
- private var aggravated_damage : Option[AggravatedDamage] = None
+ private var aggravated_damage: Option[AggravatedDamage] = None
+ /** */
+ private var charging: Option[ChargeDamage] = None
//derived calculations
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
* typically handled as the projectile no longer performing damage;
@@ -75,7 +79,8 @@ class ProjectileDefinition(objectId: Int)
private var distanceMax: Float = 0f
/** how far the projectile will travel while accelerating (m) */
private var distanceFromAcceleration: Float = 0f
- /** how far the projectile will travel while no degrading (m) */
+ /** how far the projectile will travel while not degrading (m);
+ * this field is not to be used in the place of minimum radial damage */
private var distanceNoDegrade: Float = 0f
/** after acceleration, if any, what is the final speed of the projectile (m/s) */
private var finalVelocity: Float = 0f
@@ -172,6 +177,13 @@ class ProjectileDefinition(objectId: Int)
DamageRadius
}
+ def DamageRadiusMin: Float = damageRadiusMin
+
+ def DamageRadiusMin_=(damageRadius: Float): Float = {
+ this.damageRadiusMin = damageRadius
+ DamageRadiusMin
+ }
+
def LashRadius: Float = lashRadius
def LashRadius_=(radius: Float): Float = {
@@ -239,6 +251,15 @@ class ProjectileDefinition(objectId: Int)
Aggravated
}
+ def Charging : Option[ChargeDamage] = charging
+
+ def Charging_=(damage : ChargeDamage) : Option[ChargeDamage] = Charging_=(Some(damage))
+
+ def Charging_=(damage : Option[ChargeDamage]) : Option[ChargeDamage] = {
+ charging = damage
+ Charging
+ }
+
def DistanceMax : Float = distanceMax //accessor only
def DistanceFromAcceleration: Float = distanceFromAcceleration //accessor only
@@ -253,6 +274,13 @@ object ProjectileDefinition {
new ProjectileDefinition(projectileType.id)
}
+ /**
+ * Calculate the secondary fields of the projectile's damage.
+ * Depending on whether the appropriate fields are defined,
+ * it may calculate for "damage over distance", typically associated with straight-fire direct hit projectiles,
+ * or for "radial damage", typically associated with explosive splash projectiles.
+ * @param pdef the projectile's definition, often called its profile
+ */
def CalculateDerivedFields(pdef: ProjectileDefinition): Unit = {
val (distanceMax, distanceFromAcceleration, finalVelocity): (Float, Float, Float) = if (pdef.Acceleration == 0) {
(pdef.InitialVelocity * pdef.Lifespan, 0, pdef.InitialVelocity.toFloat)
diff --git a/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala b/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
index 2772417d..3fc46bba 100644
--- a/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
+++ b/src/main/scala/net/psforever/objects/equipment/FireModeDefinition.scala
@@ -2,7 +2,8 @@
package net.psforever.objects.equipment
import net.psforever.objects.Tool
-import net.psforever.objects.vital.damage.{DamageModifiers, DamageProfile}
+import net.psforever.objects.vital.SpecificDamageProfile
+import net.psforever.objects.vital.damage.DamageModifiers
import scala.collection.mutable
@@ -41,7 +42,7 @@ class FireModeDefinition extends DamageModifiers {
private var chamber: Int = 1
/** modifiers for each damage type */
- private val modifiers: FireModeDamageModifiers = new FireModeDamageModifiers
+ private val modifiers: SpecificDamageProfile = new SpecificDamageProfile
def AmmoSlotIndex: Int = ammoSlotIndex
@@ -91,7 +92,7 @@ class FireModeDefinition extends DamageModifiers {
Chamber
}
- def Add: FireModeDamageModifiers = modifiers
+ def Add: SpecificDamageProfile = modifiers
/**
* Shoot a weapon, remove an anticipated amount of ammunition.
@@ -108,8 +109,8 @@ class FireModeDefinition extends DamageModifiers {
}
}
-class PelletFireModeDefinition extends FireModeDefinition {
-
+class PelletFireModeDefinition
+ extends FireModeDefinition {
/**
* Shoot a weapon, remove an anticipated amount of ammunition.
*
@@ -132,7 +133,8 @@ class PelletFireModeDefinition extends FireModeDefinition {
}
}
-class InfiniteFireModeDefinition extends FireModeDefinition {
+class InfiniteFireModeDefinition
+ extends FireModeDefinition {
/**
* Shoot a weapon, remove an anticipated amount of ammunition.
@@ -150,45 +152,19 @@ class InfiniteFireModeDefinition extends FireModeDefinition {
override def Discharge(weapon: Tool, rounds: Option[Int] = None): Int = 1
}
-class FireModeDamageModifiers extends DamageProfile {
- private var damage0: Int = 0
- private var damage1: Int = 0
- private var damage2: Int = 0
- private var damage3: Int = 0
- private var damage4: Int = 0
+/**
+ * Shoot a weapon, remove an anticipated amount of ammunition.
+ *
+ * Hold down the fire trigger to create a damage multiplier.
+ * After the multiplier has reach complete/full, expend additional ammunition to sustain it.
+ * @param time the duration until the charge is full (milliseconds)
+ * @param drainInterval the curation between ticks of ammunition depletion after "full charge"
+ */
+class ChargeFireModeDefinition(private val time: Long, private val drainInterval: Long, private val roundsPerInterval: Int = 1)
+ extends FireModeDefinition {
+ def Time: Long = time
- def Damage0: Int = damage0
+ def DrainInterval: Long = drainInterval
- def Damage0_=(damage: Int): Int = {
- damage0 = damage
- Damage0
- }
-
- def Damage1: Int = damage1
-
- def Damage1_=(damage: Int): Int = {
- damage1 = damage
- Damage1
- }
-
- def Damage2: Int = damage2
-
- def Damage2_=(damage: Int): Int = {
- damage2 = damage
- Damage2
- }
-
- def Damage3: Int = damage3
-
- def Damage3_=(damage: Int): Int = {
- damage3 = damage
- Damage3
- }
-
- def Damage4: Int = damage4
-
- def Damage4_=(damage: Int): Int = {
- damage4 = damage
- Damage4
- }
+ def RoundsPerInterval: Int = roundsPerInterval
}
diff --git a/src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala b/src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala
new file mode 100644
index 00000000..33a4c23f
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vital/SpecificDamageProfile.scala
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.vital
+
+import net.psforever.objects.vital.damage.DamageProfile
+
+class SpecificDamageProfile extends DamageProfile {
+ private var damage0: Int = 0
+ private var damage1: Int = 0
+ private var damage2: Int = 0
+ private var damage3: Int = 0
+ private var damage4: Int = 0
+
+ def Damage0: Int = damage0
+
+ def Damage0_=(damage: Int): Int = {
+ damage0 = damage
+ Damage0
+ }
+
+ def Damage1: Int = damage1
+
+ def Damage1_=(damage: Int): Int = {
+ damage1 = damage
+ Damage1
+ }
+
+ def Damage2: Int = damage2
+
+ def Damage2_=(damage: Int): Int = {
+ damage2 = damage
+ Damage2
+ }
+
+ def Damage3: Int = damage3
+
+ def Damage3_=(damage: Int): Int = {
+ damage3 = damage
+ Damage3
+ }
+
+ def Damage4: Int = damage4
+
+ def Damage4_=(damage: Int): Int = {
+ damage4 = damage
+ Damage4
+ }
+}
+
+object SpecificDamageProfile {
+ def apply(
+ damage0: Int = 0,
+ damage1: Int = 0,
+ damage2: Int = 0,
+ damage3: Int = 0,
+ damage4: Int = 0
+ ): SpecificDamageProfile = {
+ val obj = new SpecificDamageProfile
+ obj.Damage0 = damage0
+ obj.Damage1 = damage1
+ obj.Damage2 = damage2
+ obj.Damage3 = damage3
+ obj.Damage4 = damage4
+ obj
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala b/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala
index 62995960..87b0681f 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala
+++ b/src/main/scala/net/psforever/objects/vital/StandardDamageProfile.scala
@@ -61,3 +61,21 @@ trait StandardDamageProfile extends DamageProfile {
Damage4
}
}
+
+object StandardDamageProfile {
+ def apply(
+ damage0: Option[Int] = None,
+ damage1: Option[Int] = None,
+ damage2: Option[Int] = None,
+ damage3: Option[Int] = None,
+ damage4: Option[Int] = None
+ ): StandardDamageProfile = {
+ val obj = new StandardDamageProfile { }
+ obj.Damage0 = damage0
+ obj.Damage1 = damage1
+ obj.Damage2 = damage2
+ obj.Damage3 = damage3
+ obj.Damage4 = damage4
+ obj
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
index 82a874ea..089a41cc 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
@@ -2,6 +2,7 @@
package net.psforever.objects.vital.damage
import net.psforever.objects.ballistics._
+import net.psforever.objects.equipment.ChargeFireModeDefinition
import net.psforever.objects.vital.DamageType
import net.psforever.types.{ExoSuitType, Vector3}
@@ -97,12 +98,17 @@ object DamageModifiers {
def Calculate: DamageModifiers.Format = function
private def function(damage: Int, data: ResolvedProjectile): Int = {
- val profile = data.projectile.profile
- val distance = Vector3.Distance(data.hit_pos, data.target.Position)
- val radius = profile.DamageRadius
- if (distance <= radius) {
- val base: Float = profile.DamageAtEdge
- (damage * ((1 - base) * ((radius - distance) / radius) + base)).toInt
+ val profile = data.projectile.profile
+ val distance = Vector3.Distance(data.hit_pos, data.target.Position)
+ val radius = profile.DamageRadius
+ val radiusMin = profile.DamageRadiusMin
+ if (distance <= radiusMin) {
+ damage
+ } else if (distance <= radius) {
+ //damage - (damage * profile.DamageAtEdge * (distance - radiusMin) / (radius - radiusMin)).toInt
+ val base = profile.DamageAtEdge
+ val radi = radius - radiusMin
+ (damage * ((1 - base) * ((radi - (distance - radiusMin)) / radi) + base)).toInt
} else {
0
}
@@ -418,4 +424,19 @@ object DamageModifiers {
}
}
}
+
+ case object SpikerChargeDamage extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ val projectile = data.projectile
+ (projectile.fire_mode, projectile.profile.Charging) match {
+ case (_: ChargeFireModeDefinition, Some(info: ChargeDamage)) =>
+ val chargeQuality = math.max(0f, math.min(projectile.quality.mod, 1f))
+ data.damage_model.DamageUsing(info.min) + (damage * chargeQuality).toInt
+ case _ =>
+ damage
+ }
+ }
+ }
}
diff --git a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index a3a7256a..ad006ed2 100644
--- a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -18,107 +18,280 @@ object GamePacketOpcode extends Enumeration {
type Type = Value
val
// OPCODES 0x00-0f
- Unknown0, // PPT_NULL in beta client
- LoginMessage, LoginRespMessage, ConnectToWorldRequestMessage, // found by searching for 83 F8 03 89 in IDA
- ConnectToWorldMessage, VNLWorldStatusMessage, UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
- UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
+ Unknown0, // PPT_NULL in beta client
+ LoginMessage,
+ LoginRespMessage,
+ ConnectToWorldRequestMessage, // found by searching for 83 F8 03 89 in IDA
+ ConnectToWorldMessage,
+ VNLWorldStatusMessage,
+ UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
+ UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
// 0x08
- PlayerStateMessage, HitMessage, HitHint, DamageMessage, DestroyMessage, ReloadMessage, MountVehicleMsg,
- DismountVehicleMsg,
+ PlayerStateMessage,
+ HitMessage,
+ HitHint,
+ DamageMessage,
+ DestroyMessage,
+ ReloadMessage,
+ MountVehicleMsg,
+ DismountVehicleMsg,
// OPCODES 0x10-1f
- UseItemMessage, MoveItemMessage, ChatMsg, CharacterNoRecordMessage, CharacterInfoMessage,
- UnknownMessage21, // PPT_DISCONNECT
- BindPlayerMessage, ObjectCreateMessage_Duplicate, // PPT_OBJECTCREATE
+ UseItemMessage,
+ MoveItemMessage,
+ ChatMsg,
+ CharacterNoRecordMessage,
+ CharacterInfoMessage,
+ UnknownMessage21, // PPT_DISCONNECT
+ BindPlayerMessage,
+ ObjectCreateMessage_Duplicate, // PPT_OBJECTCREATE
// 0x18
ObjectCreateMessage, // PPT_OBJECTCREATEDETAILED
- ObjectDeleteMessage, PingMsg, VehicleStateMessage, FrameVehicleStateMessage, GenericObjectStateMsg,
- ChildObjectStateMessage, ActionResultMessage,
+ ObjectDeleteMessage,
+ PingMsg,
+ VehicleStateMessage,
+ FrameVehicleStateMessage,
+ GenericObjectStateMsg,
+ ChildObjectStateMessage,
+ ActionResultMessage,
// OPCODES 0x20-2f
UnknownMessage32, // PPT_ACTIONBEGIN
- ActionProgressMessage, ActionCancelMessage, ActionCancelAcknowledgeMessage, SetEmpireMessage, EmoteMsg,
- UnuseItemMessage, ObjectDetachMessage,
+ ActionProgressMessage,
+ ActionCancelMessage,
+ ActionCancelAcknowledgeMessage,
+ SetEmpireMessage,
+ EmoteMsg,
+ UnuseItemMessage,
+ ObjectDetachMessage,
// 0x28
- CreateShortcutMessage, ChangeShortcutBankMessage, ObjectAttachMessage, UnknownMessage43, // PPT_OBJECTEMPTY
- PlanetsideAttributeMessage, RequestDestroyMessage, UnknownMessage46, // PPT_EQUIPITEM
+ CreateShortcutMessage,
+ ChangeShortcutBankMessage,
+ ObjectAttachMessage,
+ UnknownMessage43, // PPT_OBJECTEMPTY
+ PlanetsideAttributeMessage,
+ RequestDestroyMessage,
+ UnknownMessage46, // PPT_EQUIPITEM
CharacterCreateRequestMessage,
// OPCODES 0x30-3f
- CharacterRequestMessage, LoadMapMessage, SetCurrentAvatarMessage, ObjectHeldMessage, WeaponFireMessage,
- AvatarJumpMessage, PickupItemMessage, DropItemMessage,
+ CharacterRequestMessage,
+ LoadMapMessage,
+ SetCurrentAvatarMessage,
+ ObjectHeldMessage,
+ WeaponFireMessage,
+ AvatarJumpMessage,
+ PickupItemMessage,
+ DropItemMessage,
// 0x38
- InventoryStateMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, UnknownMessage59,
- GenericCollisionMsg, QuantityUpdateMessage, ArmorChangedMessage, ProjectileStateMessage,
+ InventoryStateMessage,
+ ChangeFireStateMessage_Start,
+ ChangeFireStateMessage_Stop,
+ UnknownMessage59,
+ GenericCollisionMsg,
+ QuantityUpdateMessage,
+ ArmorChangedMessage,
+ ProjectileStateMessage,
// OPCODES 0x40-4f
- MountVehicleCargoMsg, DismountVehicleCargoMsg, CargoMountPointStatusMessage, BeginZoningMessage,
- ItemTransactionMessage, ItemTransactionResultMessage, ChangeFireModeMessage, ChangeAmmoMessage,
+ MountVehicleCargoMsg,
+ DismountVehicleCargoMsg,
+ CargoMountPointStatusMessage,
+ BeginZoningMessage,
+ ItemTransactionMessage,
+ ItemTransactionResultMessage,
+ ChangeFireModeMessage,
+ ChangeAmmoMessage,
// 0x48
- TimeOfDayMessage, UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
- SpawnRequestMessage, DeployRequestMessage, UnknownMessage76, // PPT_BUILDINGSTATECHANGED
- RepairMessage, ServerVehicleOverrideMsg, LashMessage,
+ TimeOfDayMessage,
+ UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
+ SpawnRequestMessage,
+ DeployRequestMessage,
+ UnknownMessage76, // PPT_BUILDINGSTATECHANGED
+ RepairMessage,
+ ServerVehicleOverrideMsg,
+ LashMessage,
// OPCODES 0x50-5f
- TargetingInfoMessage, TriggerEffectMessage, WeaponDryFireMessage, DroppodLaunchRequestMessage, HackMessage,
- DroppodLaunchResponseMessage, GenericObjectActionMessage, AvatarVehicleTimerMessage,
+ TargetingInfoMessage,
+ TriggerEffectMessage,
+ WeaponDryFireMessage,
+ DroppodLaunchRequestMessage,
+ HackMessage,
+ DroppodLaunchResponseMessage,
+ GenericObjectActionMessage,
+ AvatarVehicleTimerMessage,
// 0x58
- AvatarImplantMessage, UnknownMessage89, // PPT_SEARCHMESSAGE
- DelayedPathMountMsg, OrbitalShuttleTimeMsg, AIDamage, DeployObjectMessage, FavoritesRequest, FavoritesResponse,
+ AvatarImplantMessage,
+ UnknownMessage89, // PPT_SEARCHMESSAGE
+ DelayedPathMountMsg,
+ OrbitalShuttleTimeMsg,
+ AIDamage,
+ DeployObjectMessage,
+ FavoritesRequest,
+ FavoritesResponse,
// OPCODES 0x60-6f
- FavoritesMessage, ObjectDetectedMessage, SplashHitMessage, SetChatFilterMessage, AvatarSearchCriteriaMessage,
- AvatarSearchResponse, WeaponJammedMessage, LinkDeadAwarenessMsg,
+ FavoritesMessage,
+ ObjectDetectedMessage,
+ SplashHitMessage,
+ SetChatFilterMessage,
+ AvatarSearchCriteriaMessage,
+ AvatarSearchResponse,
+ WeaponJammedMessage,
+ LinkDeadAwarenessMsg,
// 0x68
- DroppodFreefallingMessage, AvatarFirstTimeEventMessage, AggravatedDamageMessage, TriggerSoundMessage, LootItemMessage,
- VehicleSubStateMessage, SquadMembershipRequest, SquadMembershipResponse,
+ DroppodFreefallingMessage,
+ AvatarFirstTimeEventMessage,
+ AggravatedDamageMessage,
+ TriggerSoundMessage,
+ LootItemMessage,
+ VehicleSubStateMessage,
+ SquadMembershipRequest,
+ SquadMembershipResponse,
// OPCODES 0x70-7f
- SquadMemberEvent, PlatoonEvent, FriendsRequest, FriendsResponse, TriggerEnvironmentalDamageMessage,
- TrainingZoneMessage, DeployableObjectsInfoMessage, SquadState,
+ SquadMemberEvent,
+ PlatoonEvent,
+ FriendsRequest,
+ FriendsResponse,
+ TriggerEnvironmentalDamageMessage,
+ TrainingZoneMessage,
+ DeployableObjectsInfoMessage,
+ SquadState,
// 0x78
- OxygenStateMessage, TradeMessage, UnknownMessage122, DamageFeedbackMessage, DismountBuildingMsg,
- UnknownMessage125, // PPT_MOUNTBUILDING
- UnknownMessage126, // PPT_INTENDEDDROPZONE
+ OxygenStateMessage,
+ TradeMessage,
+ UnknownMessage122,
+ DamageFeedbackMessage,
+ DismountBuildingMsg,
+ UnknownMessage125, // PPT_MOUNTBUILDING
+ UnknownMessage126, // PPT_INTENDEDDROPZONE
AvatarStatisticsMessage,
// OPCODES 0x80-8f
- GenericObjectAction2Message, DestroyDisplayMessage, TriggerBotAction, SquadWaypointRequest, SquadWaypointEvent,
- OffshoreVehicleMessage, ObjectDeployedMessage, ObjectDeployedCountMessage,
+ GenericObjectAction2Message,
+ DestroyDisplayMessage,
+ TriggerBotAction,
+ SquadWaypointRequest,
+ SquadWaypointEvent,
+ OffshoreVehicleMessage,
+ ObjectDeployedMessage,
+ ObjectDeployedCountMessage,
// 0x88
- WeaponDelayFireMessage, BugReportMessage, PlayerStasisMessage, UnknownMessage139, OutfitMembershipRequest,
- OutfitMembershipResponse, OutfitRequest, OutfitEvent,
+ WeaponDelayFireMessage,
+ BugReportMessage,
+ PlayerStasisMessage,
+ UnknownMessage139,
+ OutfitMembershipRequest,
+ OutfitMembershipResponse,
+ OutfitRequest,
+ OutfitEvent,
// OPCODES 0x90-9f
- OutfitMemberEvent, OutfitMemberUpdate, PlanetsideStringAttributeMessage, DataChallengeMessage,
- DataChallengeMessageResp, WeatherMessage, SimDataChallenge, SimDataChallengeResp,
+ OutfitMemberEvent,
+ OutfitMemberUpdate,
+ PlanetsideStringAttributeMessage,
+ DataChallengeMessage,
+ DataChallengeMessageResp,
+ WeatherMessage,
+ SimDataChallenge,
+ SimDataChallengeResp,
// 0x98
- OutfitListEvent, EmpireIncentivesMessage, InvalidTerrainMessage, SyncMessage, DebugDrawMessage, SoulMarkMessage,
- UplinkPositionEvent, HotSpotUpdateMessage,
+ OutfitListEvent,
+ EmpireIncentivesMessage,
+ InvalidTerrainMessage,
+ SyncMessage,
+ DebugDrawMessage,
+ SoulMarkMessage,
+ UplinkPositionEvent,
+ HotSpotUpdateMessage,
// OPCODES 0xa0-af
- BuildingInfoUpdateMessage, FireHintMessage, UplinkRequest, UplinkResponse, WarpgateRequest, WarpgateResponse,
- DamageWithPositionMessage, GenericActionMessage,
+ BuildingInfoUpdateMessage,
+ FireHintMessage,
+ UplinkRequest,
+ UplinkResponse,
+ WarpgateRequest,
+ WarpgateResponse,
+ DamageWithPositionMessage,
+ GenericActionMessage,
// 0xa8
- ContinentalLockUpdateMessage, AvatarGrenadeStateMessage, UnknownMessage170, UnknownMessage171,
- ReleaseAvatarRequestMessage, AvatarDeadStateMessage, CSAssistMessage, CSAssistCommentMessage,
+ ContinentalLockUpdateMessage,
+ AvatarGrenadeStateMessage,
+ UnknownMessage170,
+ UnknownMessage171,
+ ReleaseAvatarRequestMessage,
+ AvatarDeadStateMessage,
+ CSAssistMessage,
+ CSAssistCommentMessage,
// OPCODES 0xb0-bf
- VoiceHostRequest, VoiceHostKill, VoiceHostInfo, BattleplanMessage, BattleExperienceMessage, TargetingImplantRequest,
- ZonePopulationUpdateMessage, DisconnectMessage,
+ VoiceHostRequest,
+ VoiceHostKill,
+ VoiceHostInfo,
+ BattleplanMessage,
+ BattleExperienceMessage,
+ TargetingImplantRequest,
+ ZonePopulationUpdateMessage,
+ DisconnectMessage,
// 0xb8
- ExperienceAddedMessage, OrbitalStrikeWaypointMessage, KeepAliveMessage, MapObjectStateBlockMessage, SnoopMsg,
- PlayerStateMessageUpstream, PlayerStateShiftMessage, ZipLineMessage,
+ ExperienceAddedMessage,
+ OrbitalStrikeWaypointMessage,
+ KeepAliveMessage,
+ MapObjectStateBlockMessage,
+ SnoopMsg,
+ PlayerStateMessageUpstream,
+ PlayerStateShiftMessage,
+ ZipLineMessage,
// OPCODES 0xc0-cf
- CaptureFlagUpdateMessage, VanuModuleUpdateMessage, FacilityBenefitShieldChargeRequestMessage,
- ProximityTerminalUseMessage, QuantityDeltaUpdateMessage, ChainLashMessage, ZoneInfoMessage,
- LongRangeProjectileInfoMessage,
+ CaptureFlagUpdateMessage,
+ VanuModuleUpdateMessage,
+ FacilityBenefitShieldChargeRequestMessage,
+ ProximityTerminalUseMessage,
+ QuantityDeltaUpdateMessage,
+ ChainLashMessage,
+ ZoneInfoMessage,
+ LongRangeProjectileInfoMessage,
// 0xc8
- WeaponLazeTargetPositionMessage, ModuleLimitsMessage, OutfitBenefitMessage, EmpireChangeTimeMessage,
- ClockCalibrationMessage, DensityLevelUpdateMessage, ActOfGodMessage, AvatarAwardMessage,
+ WeaponLazeTargetPositionMessage,
+ ModuleLimitsMessage,
+ OutfitBenefitMessage,
+ EmpireChangeTimeMessage,
+ ClockCalibrationMessage,
+ DensityLevelUpdateMessage,
+ ActOfGodMessage,
+ AvatarAwardMessage,
// OPCODES 0xd0-df
- UnknownMessage208, DisplayedAwardMessage, RespawnAMSInfoMessage, ComponentDamageMessage,
- GenericObjectActionAtPositionMessage, PropertyOverrideMessage, WarpgateLinkOverrideMessage, EmpireBenefitsMessage,
+ UnknownMessage208,
+ DisplayedAwardMessage,
+ RespawnAMSInfoMessage,
+ ComponentDamageMessage,
+ GenericObjectActionAtPositionMessage,
+ PropertyOverrideMessage,
+ WarpgateLinkOverrideMessage,
+ EmpireBenefitsMessage,
// 0xd8
- ForceEmpireMessage, BroadcastWarpgateUpdateMessage, UnknownMessage218, SquadMainTerminalMessage,
- SquadMainTerminalResponseMessage, SquadOrderMessage, SquadOrderResponse, ZoneLockInfoMessage,
+ ForceEmpireMessage,
+ BroadcastWarpgateUpdateMessage,
+ UnknownMessage218,
+ SquadMainTerminalMessage,
+ SquadMainTerminalResponseMessage,
+ SquadOrderMessage,
+ SquadOrderResponse,
+ ZoneLockInfoMessage,
// OPCODES 0xe0-ef
- SquadBindInfoMessage, AudioSequenceMessage, SquadFacilityBindInfoMessage, ZoneForcedCavernConnectionsMessage,
- MissionActionMessage, MissionKillTriggerMessage, ReplicationStreamMessage, SquadDefinitionActionMessage,
+ SquadBindInfoMessage,
+ AudioSequenceMessage,
+ SquadFacilityBindInfoMessage,
+ ZoneForcedCavernConnectionsMessage,
+ MissionActionMessage,
+ MissionKillTriggerMessage,
+ ReplicationStreamMessage,
+ SquadDefinitionActionMessage,
// 0xe8
- SquadDetailDefinitionUpdateMessage, TacticsMessage, RabbitUpdateMessage, SquadInvitationRequestMessage,
- CharacterKnowledgeMessage, GameScoreUpdateMessage, UnknownMessage238, OrderTerminalBugMessage,
+ SquadDetailDefinitionUpdateMessage,
+ TacticsMessage,
+ RabbitUpdateMessage,
+ SquadInvitationRequestMessage,
+ CharacterKnowledgeMessage,
+ GameScoreUpdateMessage,
+ UnknownMessage238,
+ OrderTerminalBugMessage,
// OPCODES 0xf0-f3
- QueueTimedHelpMessage, MailMessage, GameVarUpdate, ClientCheatedMessage // last known message type (243, 0xf3)
+ QueueTimedHelpMessage,
+ MailMessage,
+ GameVarUpdate,
+ ClientCheatedMessage // last known message type (243, 0xf3)
= Value
private def noDecoder(opcode: GamePacketOpcode.Type) =
diff --git a/src/main/scala/net/psforever/util/DefinitionUtil.scala b/src/main/scala/net/psforever/util/DefinitionUtil.scala
index 0a3dfa8f..a41a7bae 100644
--- a/src/main/scala/net/psforever/util/DefinitionUtil.scala
+++ b/src/main/scala/net/psforever/util/DefinitionUtil.scala
@@ -3,6 +3,7 @@ package net.psforever.util
import net.psforever.objects.definition.BasicDefinition
import net.psforever.objects.{AmmoBox, GlobalDefinitions, Player, SimpleItem, Tool}
import net.psforever.types.ExoSuitType
+
import scala.reflect.runtime.universe
// TODO definitions should be in an iterable format
@@ -225,10 +226,10 @@ object DefinitionUtil {
def applyDefaultLoadout(player: Player): Unit = {
val faction = player.Faction
player.ExoSuit = ExoSuitType.Standard
- player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(faction))
+ player.Slot(0).Equipment = Tool(GlobalDefinitions.spiker)//StandardPistol(faction))
player.Slot(2).Equipment = Tool(GlobalDefinitions.suppressor)
player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(faction))
- player.Slot(6).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
+ player.Slot(6).Equipment = AmmoBox(GlobalDefinitions.ancient_ammo_combo)
player.Slot(9).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
player.Slot(12).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm)
player.Slot(33).Equipment = AmmoBox(GlobalDefinitions.bullet_9mm_AP)
diff --git a/src/test/scala/objects/FireModeTest.scala b/src/test/scala/objects/FireModeTest.scala
index ca3470cd..04f30755 100644
--- a/src/test/scala/objects/FireModeTest.scala
+++ b/src/test/scala/objects/FireModeTest.scala
@@ -3,12 +3,7 @@ package objects
import net.psforever.objects.definition.ToolDefinition
import net.psforever.objects.{GlobalDefinitions, Tool}
-import net.psforever.objects.equipment.{
- EquipmentSize,
- FireModeDefinition,
- InfiniteFireModeDefinition,
- PelletFireModeDefinition
-}
+import net.psforever.objects.equipment._
import org.specs2.mutable._
class FireModeTest extends Specification {
@@ -129,4 +124,32 @@ class FireModeTest extends Specification {
obj.Magazine mustEqual 1
}
}
+
+ "ChargeFireModeDefinition" should {
+ "construct" in {
+ val obj = new ChargeFireModeDefinition(1000, 500)
+ obj.AmmoTypeIndices mustEqual Nil
+ obj.AmmoSlotIndex mustEqual 0
+ obj.Magazine mustEqual 1
+ obj.RoundsPerShot mustEqual 1
+ obj.Chamber mustEqual 1
+ obj.Time mustEqual 1000L
+ obj.DrainInterval mustEqual 500L
+ }
+
+ "discharge" in {
+ val obj = Tool(GlobalDefinitions.spiker)
+ obj.FireMode.isInstanceOf[ChargeFireModeDefinition] mustEqual true
+ obj.Magazine mustEqual 25
+ obj.FireMode.RoundsPerShot mustEqual 1
+ obj.FireMode.Chamber mustEqual 1
+
+ obj.Magazine mustEqual 25
+ obj.Discharge()
+ obj.Magazine mustEqual 24
+ obj.Discharge()
+ obj.Discharge()
+ obj.Magazine mustEqual 22
+ }
+ }
}
diff --git a/src/test/scala/objects/ProjectileTest.scala b/src/test/scala/objects/ProjectileTest.scala
index 4d96ccd1..17f81493 100644
--- a/src/test/scala/objects/ProjectileTest.scala
+++ b/src/test/scala/objects/ProjectileTest.scala
@@ -46,7 +46,7 @@ class ProjectileTest extends Specification {
obj.InitialVelocity mustEqual 1
obj.Lifespan mustEqual 1f
obj.DamageAtEdge mustEqual 1f
- obj.DamageRadius mustEqual 1f
+ obj.DamageRadius mustEqual 0f
obj.UseDamage1Subtract mustEqual false
}
@@ -269,7 +269,7 @@ class ProjectileTest extends Specification {
obj.attribute_to mustEqual obj.tool_def.ObjectId
obj.shot_origin mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.shot_angle mustEqual Vector3(0.2f, 0.4f, 0.6f)
- obj.fire_time <= System.nanoTime mustEqual true
+ obj.fire_time <= System.currentTimeMillis() mustEqual true
obj.isResolved mustEqual false
}