diff --git a/server/src/main/resources/overrides/game_objects0.adb.lst b/server/src/main/resources/overrides/game_objects0.adb.lst
index d476e61a..1ea38dca 100644
--- a/server/src/main/resources/overrides/game_objects0.adb.lst
+++ b/server/src/main/resources/overrides/game_objects0.adb.lst
@@ -23,7 +23,7 @@ add_property boomer_trigger equiptime 500
add_property chainblade equiptime 250
add_property chainblade holstertime 250
add_property colossus_flight requirement_award0 false
-add_property command_detonater allowed false
+add_property command_detonater allowed true
add_property command_detonater equiptime 500
add_property command_detonater holstertime 500
add_property cycler equiptime 600
diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
index c68b0f47..12291c4a 100644
--- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala
+++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
@@ -154,6 +154,10 @@ object AvatarActor {
final case class UpdatePurchaseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now())
extends Command
+ /** rchase time for the use of calculating cooldowns */
+ final case class UpdateCUDTime(action: String, time: LocalDateTime = LocalDateTime.now())
+ extends Command
+
/** Set use time for the use of calculating cooldowns */
final case class UpdateUseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now()) extends Command
@@ -459,7 +463,15 @@ object AvatarActor {
case _ => ()
}
} catch {
- case _: Exception => ()
+ case _: Exception =>
+ val cooldown = LocalDateTime.parse(b)
+ name match {
+ case "orbital_strike" if now.compareTo(cooldown.plusMillis(3.hours.toMillis.toInt)) == -1 =>
+ cooldowns.put(name, cooldown)
+ case "emp_blast" | "reveal_friendlies" | "reveal_enemies" if now.compareTo(cooldown.plusMillis(20.minutes.toMillis.toInt)) == -1 =>
+ cooldowns.put(name, cooldown)
+ case _ => ()
+ }
}
case _ =>
log.warn(s"ignoring invalid cooldown string: '$value'")
@@ -1549,6 +1561,21 @@ class AvatarActor(
}
Behaviors.same
+ case UpdateCUDTime(action, time) =>
+ var theTimes = avatar.cooldowns.purchase
+ var updateTheTimes: Boolean = false
+ Avatar.cudCooldowns.get(action) match {
+ case Some(_) =>
+ //only send for items with cooldowns
+ updateTheTimes = true
+ theTimes = theTimes.updated(action, time)
+ case _ => ()
+ }
+ if (updateTheTimes) {
+ avatarCopy(avatar.copy(cooldowns = avatar.cooldowns.copy(purchase = theTimes)))
+ }
+ Behaviors.same
+
case UpdateUseTime(definition, time) =>
if (!Avatar.useCooldowns.contains(definition)) {
log.warn(s"${avatar.name} is updating a use time for item '${definition.Name}' that has no cooldown")
@@ -2655,6 +2682,43 @@ class AvatarActor(
}
if (keysToDrop.nonEmpty) {
val cdown = avatar.cooldowns
+ val cud = Array("orbital_strike", "emp_blast", "reveal_friendlies", "reveal_enemies")
+ keysToDrop.foreach { key =>
+ if (cud.contains(key)) {
+ avatar
+ .cooldowns
+ .purchase
+ .find { case (name, _) => name.equals(key) }
+ .flatMap { case (name, purchaseTime) =>
+ val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()).getSeconds
+ Avatar
+ .cudCooldowns
+ .find(_._1.equals(name))
+ .collect {
+ case (action, cooldown) =>
+ (action, cooldown.toSeconds - secondsSincePurchase)
+ }
+ .orElse {
+ None
+ }
+ .collect {
+ case (action, remainingTime) if remainingTime > 0 =>
+ val convertTime = remainingTime * 1000
+ keysToDrop = keysToDrop.filterNot(_ == action)
+ action match {
+ case "orbital_strike" =>
+ sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 60, convertTime))
+ case "emp_blast" =>
+ sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 59, convertTime))
+ case "reveal_enemies" =>
+ sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 58, convertTime))
+ case "reveal_friendlies" =>
+ sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 57, convertTime))
+ }
+ }
+ }
+ }
+ }
avatarCopy(avatar.copy(cooldowns = cdown.copy(purchase = cdown.purchase.removedAll(keysToDrop))))
}
}
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index 899d3265..25b5c377 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -517,7 +517,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case packet: WeaponLazeTargetPositionMessage =>
logic.shooting.handleWeaponLazeTargetPosition(packet)
- case _: UplinkRequest => ()
+ case packet: UplinkRequest =>
+ logic.shooting.handleUplinkRequest(packet)
case packet: HitMessage =>
logic.shooting.handleDirectHit(packet)
diff --git a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
index 0d33f5ab..785cb06c 100644
--- a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
@@ -8,8 +8,8 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Player, SpecialEmp, Tool, Vehicle}
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
import net.psforever.objects.zones.{Zone, ZoneProjectile}
-import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
-import net.psforever.types.Vector3
+import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, OrbitalStrikeWaypointMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, TriggerEffectMessage, TriggeredEffectLocation, UplinkRequest, UplinkRequestType, UplinkResponse, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
+import net.psforever.types.{ValidPlanetSideGUID, Vector3}
object WeaponAndProjectileLogic {
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
@@ -50,7 +50,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
}
def handleUplinkRequest(packet: UplinkRequest): Unit = {
- sessionLogic.administrativeKick(player)
+ ops.handleUplinkRequest(packet)
}
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = {
diff --git a/src/main/scala/net/psforever/actors/session/spectator/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/WeaponAndProjectileLogic.scala
index 89a95dac..ab500276 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/WeaponAndProjectileLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/WeaponAndProjectileLogic.scala
@@ -28,18 +28,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
def handleWeaponLazeTargetPosition(pkt: WeaponLazeTargetPositionMessage): Unit = { /* intentionally blank */ }
- def handleUplinkRequest(packet: UplinkRequest): Unit = {
- val UplinkRequest(code, _, _) = packet
- val playerFaction = player.Faction
- //todo this is not correct
- code match {
- case UplinkRequestType.RevealFriendlies =>
- sendResponse(UplinkResponse(code.value, continent.LivePlayers.count(_.Faction == playerFaction)))
- case UplinkRequestType.RevealEnemies =>
- sendResponse(UplinkResponse(code.value, continent.LivePlayers.count(_.Faction != playerFaction)))
- case _ => ()
- }
- }
+ def handleUplinkRequest(packet: UplinkRequest): Unit = { /* intentionally blank */ }
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = { /* intentionally blank */ }
diff --git a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala
index 5ade93f0..8e18811e 100644
--- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala
@@ -3,6 +3,8 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed}
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
+import net.psforever.objects.OrbitalStrike.{cr4_os, cr5_os}
+import net.psforever.objects.SpecialEmp.{cr3_emp, cr4_emp, cr5_emp}
import net.psforever.objects.ballistics.ProjectileQuality
import net.psforever.objects.definition.{ProjectileDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.entity.SimpleWorldEntity
@@ -23,7 +25,9 @@ import net.psforever.objects.vital.etc.OicwLilBuddyReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.exp.ToDatabase
-import net.psforever.types.{ChatMessageType, Vector3}
+import net.psforever.packet.game.UplinkRequest
+import net.psforever.services.local.{LocalAction, LocalServiceMessage}
+import net.psforever.types.{ChatMessageType, PlanetSideEmpire, ValidPlanetSideGUID, Vector3}
import net.psforever.util.Config
import scala.collection.mutable
@@ -89,6 +93,8 @@ class WeaponAndProjectileOperations(
) extends CommonSessionInterfacingFunctionality {
var shooting: mutable.Set[PlanetSideGUID] = mutable.Set.empty //ChangeFireStateMessage_Start
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
+ private[session] var orbitalStrikePos: Option[Vector3] = None
+ private[session] var orbitalStrikeInProgress: Boolean = false
private[session] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
private[session] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
private[session] val shotsFired: mutable.HashMap[Int,Int] = mutable.HashMap[Int,Int]()
@@ -292,6 +298,108 @@ class WeaponAndProjectileOperations(
}
}
+ def handleUplinkRequest(pkt: UplinkRequest): Unit = {
+ val UplinkRequest(code, pos, _) = pkt
+ val playerFaction = player.Faction
+ code match {
+ case UplinkRequestType.RevealFriendlies =>
+ val revealZone = player.Zone.Number
+ sendResponse(UplinkResponse(code.value, 0))
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 57, 1200000))
+ avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies")
+ sendResponse(UplinkPositionEvent(5, Event0(5)))
+ sendResponse(UplinkPositionEvent(3, Event1(3, revealZone)))
+ val friendlies = player.Zone.LivePlayers.filter { friend => friend.Faction == player.Faction }
+ val friendlyVehicles = player.Zone.Vehicles.filter { vehicle => vehicle.Faction == player.Faction && !vehicle.Destroyed }
+ friendlies.foreach { f =>
+ sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(f.Position.x, f.Position.y, 0.0f), 255, revealZone, 0, 1117348721, 300000, 299497, Some(true))))
+ }
+ friendlyVehicles.foreach { v =>
+ sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1127348721, 300000, 299497, Some(true))))
+ }
+ sendResponse(UplinkPositionEvent(6, Event0(6)))
+ case UplinkRequestType.RevealEnemies =>
+ val revealZone = player.Zone.Number
+ sendResponse(UplinkResponse(code.value, 0))
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 58, 1200000))
+ avatarActor ! AvatarActor.UpdateCUDTime("reveal_enemies")
+ sendResponse(UplinkPositionEvent(5, Event0(5)))
+ sendResponse(UplinkPositionEvent(4, Event1(4, revealZone)))
+ val enemies = player.Zone.LivePlayers.filter { enemy => enemy.Faction != player.Faction &&
+ Zone.orbitalStrikeDistanceCheck(player.Position, enemy.Position, 200f)} //reusing distance check
+ val enemyVehicles = player.Zone.Vehicles.filter { vehicle => vehicle.Faction != player.Faction && !vehicle.Destroyed &&
+ Zone.orbitalStrikeDistanceCheck(player.Position, vehicle.Position, 200f)} //reusing distance check
+ enemies.foreach { e =>
+ sendResponse(UplinkPositionEvent(1, Event2(1, Vector3(e.Position.x, e.Position.y, 0.0f), 255, revealZone, 0, 1138938442, 300000, 299080, Some(false))))
+ }
+ enemyVehicles.foreach { v =>
+ sendResponse(UplinkPositionEvent(1, Event2(1, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1148938442, 300000, 299080, Some(false))))
+ }
+ sendResponse(UplinkPositionEvent(6, Event0(6)))
+ case UplinkRequestType.ElectroMagneticPulse =>
+ val cr = player.avatar.cr.value
+ val empSize = cr match {
+ case 3 => cr3_emp
+ case 4 => cr4_emp
+ case 5 => cr5_emp
+ }
+ val empColor = if (playerFaction != PlanetSideEmpire.NEUTRAL) { s"explosion_emp_${playerFaction.toString.toLowerCase}" } else { "explosion_emp_bo" }
+ sendResponse(UplinkResponse(code.value, 0))
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 59, 1200000))
+ avatarActor ! AvatarActor.UpdateCUDTime("emp_blast")
+ player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}",
+ LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), empColor, None, Some(TriggeredEffectLocation(player.Position, Vector3(0, 0, 90))))))
+ context.system.scheduler.scheduleOnce(delay = 1 seconds) {
+ Zone.serverSideDamage(player.Zone, player, empSize, SpecialEmp.createEmpInteraction(empSize, player.Position),
+ ExplosiveDeployableControl.detectionForExplosiveSource(player), Zone.findAllTargets)
+ }
+ case UplinkRequestType.OrbitalStrike =>
+ player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, pos.get.x, pos.get.y)))
+ sendResponse(UplinkResponse(code.value, 0))
+ orbitalStrikePos = pos
+ case UplinkRequestType.Unknown5 =>
+ if (!orbitalStrikeInProgress) {
+ orbitalStrikeInProgress = true
+ val cr = player.avatar.cr.value
+ val strikeType = playerFaction match {
+ case PlanetSideEmpire.NC =>
+ if (cr == 4) {"explosion_bluedeath"} else {"explosion_bluedeath_lrg"}
+ case PlanetSideEmpire.TR =>
+ if (cr == 4) {"explosion_bluedeath_tr"} else {"explosion_bluedeath_tr_lrg"}
+ case PlanetSideEmpire.VS =>
+ if (cr == 4) {"explosion_bluedeath_vs"} else {"explosion_bluedeath_vs_lrg"}
+ case PlanetSideEmpire.NEUTRAL =>
+ if (cr == 4) {"explosion_bluedeath_bo"} else {"explosion_bluedeath_bo_lrg"}
+ }
+ val osSize = cr match {
+ case 4 => cr4_os
+ case 5 => cr5_os
+ }
+ sendResponse(UplinkResponse(code.value, 0))
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 60, 10800000))
+ avatarActor ! AvatarActor.UpdateCUDTime("orbital_strike")
+ context.system.scheduler.scheduleOnce(delay = 5 seconds) {
+ player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}",
+ LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), strikeType, None, Some(TriggeredEffectLocation(orbitalStrikePos.get, Vector3(0, 0, 90))))))
+ player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None)))
+ context.system.scheduler.scheduleOnce(delay = 5 seconds) {
+ val sectorTargets = Zone.findOrbitalStrikeTargets(player.Zone, orbitalStrikePos.get, osSize.DamageRadius, Zone.getOrbitbalStrikeTargets)
+ val withinRange = sectorTargets.filter { target => Zone.orbitalStrikeDistanceCheck(orbitalStrikePos.get, target.Position, osSize.DamageRadius) }
+ withinRange.foreach { target =>
+ target.Actor ! Vitality.Damage(DamageInteraction(SourceEntry(target), OrbitalStrike(PlayerSource(player)), target.Position).calculate())
+ }
+ orbitalStrikePos = None
+ orbitalStrikeInProgress = false
+ }
+ }
+ }
+ else {
+ sendResponse(UplinkResponse(code.value, 0))
+ }
+ case _ => ()
+ }
+ }
+
def handleChangeAmmo(pkt: ChangeAmmoMessage): Unit = {
val ChangeAmmoMessage(item_guid, _) = pkt
val (thing, equipment) = sessionLogic.findContainedEquipment()
diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
index a60ac1ed..9e62d70e 100644
--- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
@@ -2613,7 +2613,6 @@ class ZoningOperations(
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
}
- avatarActor ! AvatarActor.RefreshPurchaseTimes()
setupAvatarFunc = AvatarCreate
//begin looking for conditions to set the avatar
context.system.scheduler.scheduleOnce(delay = 750 millisecond, context.self, SessionActor.SetCurrentAvatar(player, 200))
@@ -3276,6 +3275,7 @@ class ZoningOperations(
ZoningOperations.reportProgressionSystem(context.self)
}
}
+ avatarActor ! AvatarActor.RefreshPurchaseTimes()
}
/**
diff --git a/src/main/scala/net/psforever/objects/OrbitalStrike.scala b/src/main/scala/net/psforever/objects/OrbitalStrike.scala
new file mode 100644
index 00000000..33634d85
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/OrbitalStrike.scala
@@ -0,0 +1,61 @@
+// Copyright (c) 2025 PSForever
+package net.psforever.objects
+
+import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
+import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
+import net.psforever.objects.vital.base.{DamageReason, DamageResolution, DamageType}
+import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.vital.prop.{DamageProperties, DamageWithPosition}
+import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
+
+final case class OrbitalStrike(player: PlayerSource)
+ extends DamageReason {
+ def resolution: DamageResolution.Value = DamageResolution.Hit
+
+ def same(test: DamageReason): Boolean = {
+ test.source eq source
+ }
+
+ def source: DamageProperties = OrbitalStrike.source
+
+ def damageModel: DamageAndResistance = OrbitalStrike.drm
+
+ override def adversary : Option[SourceEntry] = Some(player)
+
+}
+
+object OrbitalStrike {
+
+ final val cr4_os = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 10000
+ DamageAtEdge = 0.1f
+ DamageRadius = 10f
+ }
+
+ final val cr5_os = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 10000
+ DamageAtEdge = 0.1f
+ DamageRadius = 20f
+ }
+
+ private val source = new DamageProperties {
+ Damage0 = 10000
+ Damage1 = 10000
+ Damage2 = 10000
+ Damage3 = 10000
+ Damage4 = 10000
+ DamageToHealthOnly = true
+ DamageToVehicleOnly = true
+ DamageToBattleframeOnly = true
+ }
+
+ private val drm = new DamageResistanceModel {
+ DamageUsing = DamageCalculations.AgainstExoSuit
+ ResistUsing = NoResistanceSelection
+ Model = SimpleResolutions.calculate
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/SpecialEmp.scala b/src/main/scala/net/psforever/objects/SpecialEmp.scala
index 5578690a..b58a0e37 100644
--- a/src/main/scala/net/psforever/objects/SpecialEmp.scala
+++ b/src/main/scala/net/psforever/objects/SpecialEmp.scala
@@ -65,6 +65,108 @@ object SpecialEmp {
innateDamage = emp
}
+ final val cr3_emp = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 0
+ DamageAtEdge = 1.0f
+ DamageRadius = 10f
+ AdditionalEffect = true
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Player,
+ EffectTarget.Validation.Player
+ ) -> 1000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.AMS
+ ) -> 5000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.MotionSensor
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.Spitfire
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Turret,
+ EffectTarget.Validation.Turret
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.VehicleNotAMS
+ ) -> 10000
+ Modifiers = MaxDistanceCutoff
+ }
+
+ final val cr4_emp = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 0
+ DamageAtEdge = 1.0f
+ DamageRadius = 15f
+ AdditionalEffect = true
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Player,
+ EffectTarget.Validation.Player
+ ) -> 1000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.AMS
+ ) -> 5000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.MotionSensor
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.Spitfire
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Turret,
+ EffectTarget.Validation.Turret
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.VehicleNotAMS
+ ) -> 10000
+ Modifiers = MaxDistanceCutoff
+ }
+
+ final val cr5_emp = new DamageWithPosition {
+ CausesDamageType = DamageType.Splash
+ SympatheticExplosion = true
+ Damage0 = 0
+ DamageAtEdge = 1.0f
+ DamageRadius = 20f
+ AdditionalEffect = true
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Player,
+ EffectTarget.Validation.Player
+ ) -> 1000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.AMS
+ ) -> 5000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.MotionSensor
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Deployable,
+ EffectTarget.Validation.Spitfire
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Turret,
+ EffectTarget.Validation.Turret
+ ) -> 30000
+ JammedEffectDuration += TargetValidation(
+ EffectTarget.Category.Vehicle,
+ EffectTarget.Validation.VehicleNotAMS
+ ) -> 10000
+ Modifiers = MaxDistanceCutoff
+ }
+
/**
* Trigger an electromagnetic pulse.
*/
diff --git a/src/main/scala/net/psforever/objects/avatar/Avatar.scala b/src/main/scala/net/psforever/objects/avatar/Avatar.scala
index 4e349ec5..633943e2 100644
--- a/src/main/scala/net/psforever/objects/avatar/Avatar.scala
+++ b/src/main/scala/net/psforever/objects/avatar/Avatar.scala
@@ -68,6 +68,13 @@ object Avatar {
GlobalDefinitions.trhev_pounder -> 5.minutes
)
+ val cudCooldowns: Map[String, FiniteDuration] = Map(
+ "orbital_strike" -> 3.hours,
+ "emp_blast" -> 20.minutes,
+ "reveal_friendlies" -> 20.minutes,
+ "reveal_enemies" -> 20.minutes
+ )
+
val useCooldowns: Map[BasicDefinition, FiniteDuration] = Map(
GlobalDefinitions.medkit -> 5.seconds,
GlobalDefinitions.super_armorkit -> 20.minutes,
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 375f4cec..34d6b19a 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -36,6 +36,7 @@ import net.psforever.objects.vital.collision.CollisionReason
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.packet.PlanetSideGamePacket
+import org.joda.time.{LocalDateTime, Seconds}
import scala.concurrent.duration._
@@ -359,6 +360,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
equipment match {
case Some(holsteredEquipment) =>
log.info(s"${player.Name} has put ${player.Sex.possessive} ${holsteredEquipment.Definition.Name} down")
+ //make sure the player didn't just initialte an orbital strike. If not (the if below is true), make sure waypoint is removed
+ if (holsteredEquipment.Definition == GlobalDefinitions.command_detonater && player.avatar.cr.value > 3 &&
+ !player.avatar.cooldowns.purchase.exists(os => os._1 == "orbital_strike" && Seconds.secondsBetween(os._2, LocalDateTime.now()).getSeconds < 12)) {
+ player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Faction}", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None)))
+ }
case None =>
log.info(s"${player.Name} lowers ${player.Sex.possessive} hand")
}
diff --git a/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala b/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala
index 092d763a..9649420f 100644
--- a/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala
@@ -4,6 +4,7 @@ package net.psforever.objects.definition
abstract class BasicDefinition {
private var name: String = "definition"
private var descriptor: Option[String] = None
+ private var mapRevealId: Int = 0
def Name: String = name
@@ -20,4 +21,11 @@ abstract class BasicDefinition {
descriptor = description
Descriptor
}
+
+ def MapRevealId: Int = mapRevealId //for vehicle IDs used by reveal friendlies and enemies with a CUD
+
+ def MapRevealId_=(mapRevealId: Int): Int = {
+ this.mapRevealId = mapRevealId
+ MapRevealId
+ }
}
diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala
index 5e7ae6da..c2fee38f 100644
--- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala
+++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsVehicle.scala
@@ -83,6 +83,7 @@ object GlobalDefinitionsVehicle {
fury.collision.z = CollisionZData(Array((8f, 1), (24f, 35), (40f, 100), (48f, 175), (52f, 350)))
fury.maxForwardSpeed = 90f
fury.mass = 32.1f
+ fury.MapRevealId = 14
quadassault.Name = "quadassault" // Basilisk
quadassault.MaxHealth = 650
@@ -119,6 +120,7 @@ object GlobalDefinitionsVehicle {
quadassault.collision.z = CollisionZData(Array((8f, 1), (24f, 35), (40f, 100), (48f, 175), (52f, 350)))
quadassault.maxForwardSpeed = 90f
quadassault.mass = 32.1f
+ quadassault.MapRevealId = 30
quadstealth.Name = "quadstealth" // Wraith
quadstealth.MaxHealth = 650
@@ -155,6 +157,7 @@ object GlobalDefinitionsVehicle {
quadstealth.collision.z = CollisionZData(Array((8f, 1), (24f, 35), (40f, 100), (48f, 175), (52f, 350)))
quadstealth.maxForwardSpeed = 90f
quadstealth.mass = 32.1f
+ quadstealth.MapRevealId = 15
two_man_assault_buggy.Name = "two_man_assault_buggy" // Harasser
two_man_assault_buggy.MaxHealth = 1250
@@ -193,6 +196,7 @@ object GlobalDefinitionsVehicle {
two_man_assault_buggy.collision.z = CollisionZData(Array((7f, 1), (21f, 50), (35f, 150), (42f, 300), (45.5f, 600)))
two_man_assault_buggy.maxForwardSpeed = 85f
two_man_assault_buggy.mass = 52.4f
+ two_man_assault_buggy.MapRevealId = 1
skyguard.Name = "skyguard"
skyguard.MaxHealth = 1000
@@ -232,6 +236,7 @@ object GlobalDefinitionsVehicle {
skyguard.collision.z = CollisionZData(Array((7f, 1), (21f, 50), (35f, 150), (42f, 300), (45.4f, 600)))
skyguard.maxForwardSpeed = 90f
skyguard.mass = 78.9f
+ skyguard.MapRevealId = 16
threemanheavybuggy.Name = "threemanheavybuggy" // Marauder
threemanheavybuggy.MaxHealth = 1700
@@ -274,6 +279,7 @@ object GlobalDefinitionsVehicle {
threemanheavybuggy.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 300), (39f, 900)))
threemanheavybuggy.maxForwardSpeed = 80f
threemanheavybuggy.mass = 96.3f
+ threemanheavybuggy.MapRevealId = 18
twomanheavybuggy.Name = "twomanheavybuggy" // Enforcer
twomanheavybuggy.MaxHealth = 1800
@@ -313,6 +319,7 @@ object GlobalDefinitionsVehicle {
twomanheavybuggy.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 300), (39f, 900)))
twomanheavybuggy.maxForwardSpeed = 80f
twomanheavybuggy.mass = 83.2f
+ twomanhoverbuggy.MapRevealId = 20
twomanhoverbuggy.Name = "twomanhoverbuggy" // Thresher
twomanhoverbuggy.MaxHealth = 1600
@@ -355,6 +362,7 @@ object GlobalDefinitionsVehicle {
twomanhoverbuggy.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 300), (39f, 900)))
twomanhoverbuggy.maxForwardSpeed = 85f
twomanhoverbuggy.mass = 55.5f
+ twomanhoverbuggy.MapRevealId = 21
mediumtransport.Name = "mediumtransport" // Deliverer
mediumtransport.MaxHealth = 2500
@@ -401,6 +409,7 @@ object GlobalDefinitionsVehicle {
mediumtransport.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 200), (30f, 750), (32.5f, 2000)))
mediumtransport.maxForwardSpeed = 70f
mediumtransport.mass = 108.5f
+ mediumtransport.MapRevealId = 10
battlewagon.Name = "battlewagon" // Raider
battlewagon.MaxHealth = 2500
@@ -450,6 +459,7 @@ object GlobalDefinitionsVehicle {
battlewagon.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 200), (30f, 750), (32.5f, 2000))) //inherited from mediumtransport
battlewagon.maxForwardSpeed = 65f
battlewagon.mass = 108.5f
+ battlewagon.MapRevealId = 10
thunderer.Name = "thunderer"
thunderer.MaxHealth = 2500
@@ -496,6 +506,7 @@ object GlobalDefinitionsVehicle {
thunderer.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 200), (30f, 750), (32.5f, 2000)))
thunderer.maxForwardSpeed = 65f
thunderer.mass = 108.5f
+ thunderer.MapRevealId = 29
aurora.Name = "aurora"
aurora.MaxHealth = 2500
@@ -542,6 +553,7 @@ object GlobalDefinitionsVehicle {
aurora.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 200), (30f, 750), (32.5f, 2000)))
aurora.maxForwardSpeed = 65f
aurora.mass = 108.5f
+ aurora.MapRevealId = 28
apc_tr.Name = "apc_tr" // Juggernaut
apc_tr.MaxHealth = 6000
@@ -612,6 +624,7 @@ object GlobalDefinitionsVehicle {
apc_tr.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))
apc_tr.maxForwardSpeed = 60f
apc_tr.mass = 128.4f
+ apc_tr.MapRevealId = 32
apc_nc.Name = "apc_nc" // Vindicator
apc_nc.MaxHealth = 6000
@@ -682,6 +695,7 @@ object GlobalDefinitionsVehicle {
apc_nc.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))
apc_nc.maxForwardSpeed = 60f
apc_nc.mass = 128.4f
+ apc_nc.MapRevealId = 33
apc_vs.Name = "apc_vs" // Leviathan
apc_vs.MaxHealth = 6000
@@ -752,6 +766,7 @@ object GlobalDefinitionsVehicle {
apc_vs.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))
apc_vs.maxForwardSpeed = 60f
apc_vs.mass = 128.4f
+ apc_vs.MapRevealId = 41
lightning.Name = "lightning"
lightning.MaxHealth = 2000
@@ -790,6 +805,7 @@ object GlobalDefinitionsVehicle {
lightning.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 300), (39f, 750)))
lightning.maxForwardSpeed = 74f
lightning.mass = 100.2f
+ lightning.MapRevealId = 7
prowler.Name = "prowler"
prowler.MaxHealth = 4800
@@ -833,6 +849,7 @@ object GlobalDefinitionsVehicle {
prowler.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 250), (30f, 600), (32.5f, 1500)))
prowler.maxForwardSpeed = 57f
prowler.mass = 510.5f
+ prowler.MapRevealId = 13
vanguard.Name = "vanguard"
vanguard.MaxHealth = 5400
@@ -872,6 +889,7 @@ object GlobalDefinitionsVehicle {
vanguard.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 100), (30f, 250), (32.5f, 600)))
vanguard.maxForwardSpeed = 60f
vanguard.mass = 460.4f
+ vanguard.MapRevealId = 22
magrider.Name = "magrider"
magrider.MaxHealth = 4200
@@ -913,6 +931,7 @@ object GlobalDefinitionsVehicle {
magrider.collision.z = CollisionZData(Array((5f, 1), (15f, 50), (25f, 250), (30f, 600), (32.5f, 1500)))
magrider.maxForwardSpeed = 65f
magrider.mass = 75.3f
+ magrider.MapRevealId = 8
val utilityConverter = new UtilityVehicleConverter
ant.Name = "ant"
@@ -952,6 +971,7 @@ object GlobalDefinitionsVehicle {
ant.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 250), (12f, 500), (13f, 750)))
ant.maxForwardSpeed = 65f
ant.mass = 80.5f
+ ant.MapRevealId = 1
ams.Name = "ams"
ams.MaxHealth = 3000
@@ -995,6 +1015,7 @@ object GlobalDefinitionsVehicle {
ams.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 250), (12f, 805), (13f, 3000)))
ams.maxForwardSpeed = 70f
ams.mass = 136.8f
+ ams.MapRevealId = 2
val variantConverter = new VariantVehicleConverter
router.Name = "router"
@@ -1039,6 +1060,7 @@ object GlobalDefinitionsVehicle {
router.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 350), (39f, 900)))
router.maxForwardSpeed = 60f
router.mass = 60f
+ router.MapRevealId = 25
switchblade.Name = "switchblade"
switchblade.MaxHealth = 1750
@@ -1085,6 +1107,7 @@ object GlobalDefinitionsVehicle {
switchblade.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 350), (39f, 800)))
switchblade.maxForwardSpeed = 80f
switchblade.mass = 63.9f
+ switchblade.MapRevealId = 26
flail.Name = "flail"
flail.MaxHealth = 2400
@@ -1127,6 +1150,7 @@ object GlobalDefinitionsVehicle {
flail.collision.z = CollisionZData(Array((6f, 1), (18f, 50), (30f, 150), (36f, 350), (39f, 900)))
flail.maxForwardSpeed = 55f
flail.mass = 73.5f
+ flail.MapRevealId = 24
}
/**
@@ -1179,6 +1203,7 @@ object GlobalDefinitionsVehicle {
mosquito.collision.z = CollisionZData(Array((3f, 1), (9f, 25), (15f, 50), (18f, 75), (19.5f, 100)))
mosquito.maxForwardSpeed = 120f
mosquito.mass = 53.6f
+ mosquito.MapRevealId = 11
lightgunship.Name = "lightgunship" // Reaver
lightgunship.MaxHealth = 855 // Temporary - Correct Reaver Health from pre-"Coder Madness 2" Event
@@ -1219,6 +1244,7 @@ object GlobalDefinitionsVehicle {
lightgunship.collision.z = CollisionZData(Array((3f, 1), (9f, 30), (15f, 60), (18f, 90), (19.5f, 125)))
lightgunship.maxForwardSpeed = 104f
lightgunship.mass = 51.1f
+ lightgunship.MapRevealId = 6
wasp.Name = "wasp"
wasp.MaxHealth = 515
@@ -1257,6 +1283,7 @@ object GlobalDefinitionsVehicle {
wasp.collision.z = CollisionZData(Array((3f, 1), (9f, 25), (15f, 50), (18f, 75), (19.5f, 100))) //mosquito numbers
wasp.maxForwardSpeed = 120f
wasp.mass = 53.6f
+ wasp.MapRevealId = 11
liberator.Name = "liberator"
liberator.MaxHealth = 2500
@@ -1305,6 +1332,7 @@ object GlobalDefinitionsVehicle {
liberator.collision.z = CollisionZData(Array((3f, 1), (9f, 30), (15f, 60), (18f, 90), (19.5f, 125)))
liberator.maxForwardSpeed = 90f
liberator.mass = 82f
+ liberator.MapRevealId = 5
vulture.Name = "vulture"
vulture.MaxHealth = 2500
@@ -1354,6 +1382,7 @@ object GlobalDefinitionsVehicle {
vulture.collision.z = CollisionZData(Array((3f, 1), (9f, 30), (15f, 60), (18f, 90), (19.5f, 125)))
vulture.maxForwardSpeed = 97f
vulture.mass = 82f
+ vulture.MapRevealId = 5
dropship.Name = "dropship" // Galaxy
dropship.MaxHealth = 5000
@@ -1430,6 +1459,7 @@ object GlobalDefinitionsVehicle {
dropship.collision.z = CollisionZData(Array((3f, 5), (9f, 125), (15f, 250), (18f, 500), (19.5f, 1000)))
dropship.maxForwardSpeed = 80f
dropship.mass = 133f
+ dropship.MapRevealId = 4
galaxy_gunship.Name = "galaxy_gunship"
galaxy_gunship.MaxHealth = 6000
@@ -1490,6 +1520,7 @@ object GlobalDefinitionsVehicle {
galaxy_gunship.collision.z = CollisionZData(Array((3f, 5), (9f, 125), (15f, 250), (18f, 500), (19.5f, 1000)))
galaxy_gunship.maxForwardSpeed = 85f
galaxy_gunship.mass = 133f
+ galaxy_gunship.MapRevealId = 31
lodestar.Name = "lodestar"
lodestar.MaxHealth = 5000
@@ -1542,6 +1573,7 @@ object GlobalDefinitionsVehicle {
lodestar.collision.z = CollisionZData(Array((3f, 5), (9f, 125), (15f, 250), (18f, 500), (19.5f, 1000)))
lodestar.maxForwardSpeed = 80f
lodestar.mass = 128.2f
+ lodestar.MapRevealId = 34
phantasm.Name = "phantasm"
phantasm.MaxHealth = 2500
@@ -1587,6 +1619,7 @@ object GlobalDefinitionsVehicle {
phantasm.collision.z = CollisionZData(Array((3f, 1), (9f, 30), (15f, 60), (18f, 90), (19.5f, 125)))
phantasm.maxForwardSpeed = 140f
phantasm.mass = 100f
+ phantasm.MapRevealId = 35
droppod.Name = "droppod"
droppod.MaxHealth = 20000
@@ -1724,6 +1757,7 @@ object GlobalDefinitionsVehicle {
aphelion_gunner.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
aphelion_gunner.maxForwardSpeed = 17
aphelion_gunner.mass = 615.1f
+ aphelion_gunner.MapRevealId = 44
colossus_gunner.Name = "colossus_gunner"
colossus_gunner.MaxHealth = 4500
@@ -1775,6 +1809,7 @@ object GlobalDefinitionsVehicle {
colossus_gunner.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
colossus_gunner.maxForwardSpeed = 17
colossus_gunner.mass = 709.7f
+ colossus_gunner.MapRevealId = 48
peregrine_gunner.Name = "peregrine_gunner"
peregrine_gunner.MaxHealth = 4500
@@ -1826,6 +1861,7 @@ object GlobalDefinitionsVehicle {
peregrine_gunner.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
peregrine_gunner.maxForwardSpeed = 17
peregrine_gunner.mass = 713f
+ peregrine_gunner.MapRevealId = 52
val battleFrameFlightConverter = new BattleFrameFlightConverter
aphelion_flight.Name = "aphelion_flight"
@@ -1881,6 +1917,7 @@ object GlobalDefinitionsVehicle {
aphelion_flight.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
aphelion_flight.maxForwardSpeed = 35
aphelion_flight.mass = 615.1f
+ aphelion_flight.MapRevealId = 45
colossus_flight.Name = "colossus_flight"
colossus_flight.MaxHealth = 3500
@@ -1935,6 +1972,7 @@ object GlobalDefinitionsVehicle {
colossus_flight.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
colossus_flight.maxForwardSpeed = 34
colossus_flight.mass = 709.7f
+ colossus_flight.MapRevealId = 49
peregrine_flight.Name = "peregrine_flight"
peregrine_flight.MaxHealth = 3500
@@ -1989,5 +2027,6 @@ object GlobalDefinitionsVehicle {
peregrine_flight.collision.z = CollisionZData(Array((25f, 2), (40f, 4), (60f, 8), (85f, 16), (115f, 32)))
peregrine_flight.maxForwardSpeed = 35
peregrine_flight.mass = 713f
+ peregrine_flight.MapRevealId = 51
}
}
diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala
index 45eb69a1..cbf828d0 100644
--- a/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -1845,6 +1845,53 @@ object Zone {
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
}
+ /**
+ * na
+ * @see `DamageWithPosition`
+ * @see `Zone.blockMap.sector`
+ * @param zone the zone in which the explosion should occur
+ * @param sourcePosition a position that is used as the origin of the explosion
+ * @param radius idistance
+ * @param getTargetsFromSector get this list of entities from a sector
+ * @return a list of affected entities
+ */
+ def findOrbitalStrikeTargets(
+ zone: Zone,
+ sourcePosition: Vector3,
+ radius: Float,
+ getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
+ ): List[PlanetSideServerObject with Vitality] = {
+ getTargetsFromSector(zone.blockMap.sector(sourcePosition.xy, radius))
+ }
+
+ def getOrbitbalStrikeTargets(sector: SectorPopulation): List[PlanetSideServerObject with Vitality] = {
+ //collect all targets that can be damaged
+ //players
+ val playerTargets = sector.livePlayerList.filter { player => player.VehicleSeated.isEmpty && player.WhichSide == Sidedness.OutsideOf }
+ //vehicles
+ val vehicleTargets = sector.vehicleList.filterNot { _.Destroyed }
+ //deployables
+ val deployableTargets = sector.deployableList.filter { obj => !obj.Destroyed && obj.WhichSide == Sidedness.OutsideOf }
+ //amenities
+ val soiTargets = sector.amenityList.collect {
+ case amenity: Vitality if !amenity.Destroyed && amenity.WhichSide == Sidedness.OutsideOf && amenity.CanDamage => amenity }
+ //altogether ...
+ playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
+ }
+
+ /**
+ * Check if targets returned from sector are within range of the imminent Orbital Strike
+ * @param p1 OS position
+ * @param p2 target position
+ * @param maxDistance radius of the Orbital Strike
+ * @return `true`, if the two entities are near enough to each other;
+ * `false`, otherwise
+ */
+ def orbitalStrikeDistanceCheck(p1: Vector3, p2: Vector3, maxDistance: Float): Boolean = {
+ val radius = maxDistance * maxDistance
+ Vector3.DistanceSquared(p1.xy, p2.xy) <= radius
+ }
+
/**
* na
* @param instigation what previous event happened, if any, that caused this explosion
diff --git a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index aaf908d1..87f94fb5 100644
--- a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -162,6 +162,10 @@ import scodec.codecs._
*
* `55 - "Someone is attempting to Heal you". Value is 1`
* `56 - "Someone is attempting to Repair you". Value is 1`
+ * `57 - CUD Reveal Friendlies cooldown timer`
+ * `58 - CUD Reveal Enemies cooldown timer`
+ * `59 - CUD EMP Blast cooldown timer`
+ * `60 - CUD Orbital Strike cooldown timer`
* `64 - ????? related to using router telepads`
* `67 - Enables base shields (from cavern module/lock)`
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`
diff --git a/src/main/scala/net/psforever/packet/game/UplinkRequest.scala b/src/main/scala/net/psforever/packet/game/UplinkRequest.scala
index f4a66e59..2d419726 100644
--- a/src/main/scala/net/psforever/packet/game/UplinkRequest.scala
+++ b/src/main/scala/net/psforever/packet/game/UplinkRequest.scala
@@ -26,15 +26,15 @@ object UplinkRequestType extends IntEnum[UplinkRequestType] {
case object OrbitalStrike extends UplinkRequestType(value = 4)
- case object Unknown5 extends UplinkRequestType(value = 5)
+ case object Unknown5 extends UplinkRequestType(value = 5) // pull trigger to start orbital strike countdown
case object Function6 extends UplinkRequestType(value = 6)
- case object Function7 extends UplinkRequestType(value = 7)
+ case object Function7 extends UplinkRequestType(value = 7) // sent back by client after reveal enemies response
case object Function8 extends UplinkRequestType(value = 8)
- case object Unknown9 extends UplinkRequestType(value = 9)
+ case object Unknown9 extends UplinkRequestType(value = 9) // recall squad to sanc
case object UnknownA extends UplinkRequestType(value = 10)