From 95b8b3459473e1f814e01af13c15122bddf7dc74 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Thu, 17 Jul 2025 15:35:10 -0400 Subject: [PATCH 1/6] cud emp and os --- .../resources/overrides/game_objects0.adb.lst | 2 +- .../actors/session/AvatarActor.scala | 66 +++++++++++- .../actors/session/SessionActor.scala | 3 +- .../normal/WeaponAndProjectileLogic.scala | 6 +- .../spectator/WeaponAndProjectileLogic.scala | 13 +-- .../WeaponAndProjectileOperations.scala | 80 +++++++++++++- .../session/support/ZoningOperations.scala | 2 +- .../net/psforever/objects/OrbitalStrike.scala | 61 +++++++++++ .../net/psforever/objects/SpecialEmp.scala | 102 ++++++++++++++++++ .../net/psforever/objects/avatar/Avatar.scala | 7 ++ .../objects/avatar/PlayerControl.scala | 6 ++ .../net/psforever/objects/zones/Zone.scala | 47 ++++++++ .../game/PlanetsideAttributeMessage.scala | 4 + .../psforever/packet/game/UplinkRequest.scala | 6 +- 14 files changed, 382 insertions(+), 23 deletions(-) create mode 100644 src/main/scala/net/psforever/objects/OrbitalStrike.scala diff --git a/server/src/main/resources/overrides/game_objects0.adb.lst b/server/src/main/resources/overrides/game_objects0.adb.lst index d476e61a9..1ea38dca0 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 c68b0f47c..12291c4ac 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 899d32651..25b5c377e 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 0d33f5aba..785cb06c2 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 89a95dac1..ab5002760 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 5ade93f01..eebb56466 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,7 @@ 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 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 +297,79 @@ class WeaponAndProjectileOperations( } } + def handleUplinkRequest(pkt: UplinkRequest): Unit = { + val UplinkRequest(code, pos, _) = pkt + val playerFaction = player.Faction + code match { + case UplinkRequestType.RevealFriendlies => () + /*sendResponse(UplinkResponse(code.value, 0)) + sendResponse(PlanetsideAttributeMessage(player.GUID, 57, 10000)) //1200000 + avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies")*/ + case UplinkRequestType.RevealEnemies => () + /*sendResponse(UplinkResponse(code.value, 0)) + sendResponse(PlanetsideAttributeMessage(player.GUID, 58, 10000)) //1200000 + avatarActor ! AvatarActor.UpdateCUDTime("reveal_enemies") + These are seen in a few logs, but didn't work. Unclear what these do or what '10' in Event1 represents + sendResponse(UplinkPositionEvent(5, Event0(5))) + sendResponse(UplinkPositionEvent(4, Event1(4, 10))) + 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, 10000)) //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 => + val cr = player.avatar.cr.value + val strikeType = playerFaction match { + case PlanetSideEmpire.NC => + if (cr == 4) {"explosion_bluedeath_nc"} else {"explosion_bluedeath_nc_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, 10000)) //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 + } + } + 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 a60ac1edc..9e62d70ee 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 000000000..33634d852 --- /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 5578690af..b58a0e377 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 4e349ec55..8f0a41a16 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" -> 1.minutes, // 3.hours + "emp_blast" -> 1.minutes, // 20.minutes + "reveal_friendlies" -> 1.minutes, // 20.minutes + "reveal_enemies" -> 1.minutes // 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 375f4cec5..4e151f973 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 < 10)) { + 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/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 45eb69a12..cbf828d07 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 aaf908d14..87f94fb5f 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 f4a66e591..2d4197269 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) From c9b13394e9fdd8f980e476edbd1237759633caf1 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Thu, 17 Jul 2025 20:19:49 -0400 Subject: [PATCH 2/6] just one os please --- .../support/WeaponAndProjectileOperations.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 eebb56466..7dba959fc 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -94,6 +94,7 @@ class WeaponAndProjectileOperations( 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]() @@ -335,6 +336,8 @@ class WeaponAndProjectileOperations( 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 => @@ -359,13 +362,18 @@ class WeaponAndProjectileOperations( 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)} + 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 _ => () } } From 1b6e50cf0d06ecdbf07b003ccdbba7ec7bb4cfa0 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Fri, 18 Jul 2025 21:29:42 -0400 Subject: [PATCH 3/6] we can reveal --- .../WeaponAndProjectileOperations.scala | 50 +++++++++++++------ .../net/psforever/objects/avatar/Avatar.scala | 8 +-- .../objects/definition/BasicDefinition.scala | 8 +++ .../global/GlobalDefinitionsVehicle.scala | 39 +++++++++++++++ 4 files changed, 87 insertions(+), 18 deletions(-) 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 7dba959fc..24ba6f335 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -302,18 +302,40 @@ class WeaponAndProjectileOperations( val UplinkRequest(code, pos, _) = pkt val playerFaction = player.Faction code match { - case UplinkRequestType.RevealFriendlies => () - /*sendResponse(UplinkResponse(code.value, 0)) - sendResponse(PlanetsideAttributeMessage(player.GUID, 57, 10000)) //1200000 - avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies")*/ - case UplinkRequestType.RevealEnemies => () - /*sendResponse(UplinkResponse(code.value, 0)) - sendResponse(PlanetsideAttributeMessage(player.GUID, 58, 10000)) //1200000 - avatarActor ! AvatarActor.UpdateCUDTime("reveal_enemies") - These are seen in a few logs, but didn't work. Unclear what these do or what '10' in Event1 represents - sendResponse(UplinkPositionEvent(5, Event0(5))) - sendResponse(UplinkPositionEvent(4, Event1(4, 10))) - sendResponse(UplinkPositionEvent(6, Event0(6)))*/ + 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(4, Event1(4, revealZone))) + sendResponse(UplinkPositionEvent(6, Event0(6))) + 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, 1118938442, 300000, 299080, Some(true)))) + } + friendlyVehicles.foreach { v => + sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1118938442, 300000, 299080, Some(true)))) + } + 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))) + sendResponse(UplinkPositionEvent(6, Event0(6))) + 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, 1118938442, 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, 1118938442, 300000, 299080, Some(false)))) + } case UplinkRequestType.ElectroMagneticPulse => val cr = player.avatar.cr.value val empSize = cr match { @@ -323,7 +345,7 @@ class WeaponAndProjectileOperations( } 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, 10000)) //1200000 + 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)))))) @@ -354,7 +376,7 @@ class WeaponAndProjectileOperations( case 5 => cr5_os } sendResponse(UplinkResponse(code.value, 0)) - sendResponse(PlanetsideAttributeMessage(player.GUID, 60, 10000)) //10800000 + 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}", diff --git a/src/main/scala/net/psforever/objects/avatar/Avatar.scala b/src/main/scala/net/psforever/objects/avatar/Avatar.scala index 8f0a41a16..633943e26 100644 --- a/src/main/scala/net/psforever/objects/avatar/Avatar.scala +++ b/src/main/scala/net/psforever/objects/avatar/Avatar.scala @@ -69,10 +69,10 @@ object Avatar { ) val cudCooldowns: Map[String, FiniteDuration] = Map( - "orbital_strike" -> 1.minutes, // 3.hours - "emp_blast" -> 1.minutes, // 20.minutes - "reveal_friendlies" -> 1.minutes, // 20.minutes - "reveal_enemies" -> 1.minutes // 20.minutes + "orbital_strike" -> 3.hours, + "emp_blast" -> 20.minutes, + "reveal_friendlies" -> 20.minutes, + "reveal_enemies" -> 20.minutes ) val useCooldowns: Map[BasicDefinition, FiniteDuration] = Map( diff --git a/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala b/src/main/scala/net/psforever/objects/definition/BasicDefinition.scala index 092d763a6..9649420f2 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 5e7ae6dad..c2fee38f6 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 } } From 3055f73a68fdcb9ccfff5b5a78160ae0c6e76e14 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Sat, 19 Jul 2025 22:10:07 -0400 Subject: [PATCH 4/6] wrong name --- .../actors/session/support/WeaponAndProjectileOperations.scala | 2 +- src/main/scala/net/psforever/objects/avatar/PlayerControl.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 24ba6f335..cf4c36ad4 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -363,7 +363,7 @@ class WeaponAndProjectileOperations( val cr = player.avatar.cr.value val strikeType = playerFaction match { case PlanetSideEmpire.NC => - if (cr == 4) {"explosion_bluedeath_nc"} else {"explosion_bluedeath_nc_lrg"} + 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 => diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index 4e151f973..34d6b19a0 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -362,7 +362,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm 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 < 10)) { + !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 => From 198f4cebb959ac4c882dfed09bc294743c6c6f07 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Sun, 20 Jul 2025 07:48:13 -0400 Subject: [PATCH 5/6] packet order --- .../session/support/WeaponAndProjectileOperations.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 cf4c36ad4..5be9ab5b6 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -309,15 +309,15 @@ class WeaponAndProjectileOperations( avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies") sendResponse(UplinkPositionEvent(5, Event0(5))) sendResponse(UplinkPositionEvent(4, Event1(4, revealZone))) - sendResponse(UplinkPositionEvent(6, Event0(6))) 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, 1118938442, 300000, 299080, Some(true)))) + sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(f.Position.x, f.Position.y, 0.0f), 255, revealZone, 0, 1127348721, 300000, 298858, Some(true)))) } friendlyVehicles.foreach { v => - sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1118938442, 300000, 299080, Some(true)))) + sendResponse(UplinkPositionEvent(0, Event2(0, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1127348721, 300000, 298858, Some(true)))) } + sendResponse(UplinkPositionEvent(6, Event0(6))) case UplinkRequestType.RevealEnemies => val revealZone = player.Zone.Number sendResponse(UplinkResponse(code.value, 0)) @@ -325,7 +325,6 @@ class WeaponAndProjectileOperations( avatarActor ! AvatarActor.UpdateCUDTime("reveal_enemies") sendResponse(UplinkPositionEvent(5, Event0(5))) sendResponse(UplinkPositionEvent(4, Event1(4, revealZone))) - sendResponse(UplinkPositionEvent(6, Event0(6))) 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 && @@ -336,6 +335,7 @@ class WeaponAndProjectileOperations( enemyVehicles.foreach { v => sendResponse(UplinkPositionEvent(1, Event2(1, Vector3(v.Position.x, v.Position.y, 0.0f), v.Definition.MapRevealId, revealZone, 0, 1118938442, 300000, 299080, Some(false)))) } + sendResponse(UplinkPositionEvent(6, Event0(6))) case UplinkRequestType.ElectroMagneticPulse => val cr = player.avatar.cr.value val empSize = cr match { From 697d5cfba9039758d5a019f0733992ea5a0385d8 Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Mon, 21 Jul 2025 07:51:23 -0400 Subject: [PATCH 6/6] found a friendly --- .../support/WeaponAndProjectileOperations.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 5be9ab5b6..8e18811e3 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -308,14 +308,14 @@ class WeaponAndProjectileOperations( sendResponse(PlanetsideAttributeMessage(player.GUID, 57, 1200000)) avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies") sendResponse(UplinkPositionEvent(5, Event0(5))) - sendResponse(UplinkPositionEvent(4, Event1(4, revealZone))) + 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, 1127348721, 300000, 298858, Some(true)))) + 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, 298858, Some(true)))) + 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 => @@ -330,10 +330,10 @@ class WeaponAndProjectileOperations( 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, 1118938442, 300000, 299080, Some(false)))) + 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, 1118938442, 300000, 299080, Some(false)))) + 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 =>