From 9f4452a36aa2c216f832f94d0bac1b56cde5a50e Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Sun, 25 Aug 2024 15:51:43 -0400 Subject: [PATCH] vehicle pad deconstruction complete message; message when nc max shield deactivates when out of capacitor power or when max opens fire; message when ams can not deploy because it is blocked by other entities; message when facility capture requires ntu --- .../actors/session/normal/GeneralLogic.scala | 11 +-- .../WeaponAndProjectileOperations.scala | 5 +- .../pad/VehicleSpawnControl.scala | 35 +++++--- .../MajorFacilityHackParticipation.scala | 82 ++++++++++++++++++- .../objects/zones/ZoneVehicleActor.scala | 19 ++++- .../local/support/HackCaptureActor.scala | 19 ++++- 6 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala index 1a595f09e..a20031138 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -120,11 +120,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex ops.fallHeightTracker(pos.z) if (isCrouching && !player.Crouching) { //dev stuff goes here - sendResponse(ChatMsg(ChatMessageType.UNK_228, "@login_reposition_to_sanctuary")) - sendResponse(ChatMsg(ChatMessageType.UNK_229, "@PadDeconstruct_Done")) - //sendResponse(ChatMsg(ChatMessageType.UNK_227, "@NoMount_Permission")) - //sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOff")) - //sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOverride")) + //sendResponse(ChatMsg(ChatMessageType.UNK_227, "@fav_light_infantry")) //Light Infantry / Vehicle Driver + //sendResponse(ChatMsg(ChatMessageType.UNK_227, "@fav_heavy_infantry")) //Heavy Infantry } player.Position = pos player.Velocity = vel @@ -1455,6 +1452,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex val drainAmount = player.ExoSuitDef.CapacitorDrainPerSecond.toFloat * timeDiff player.Capacitor -= drainAmount sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt)) + if (player.Capacitor <= 0 && player.UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded) { + ops.toggleMaxSpecialState(enable = false) + sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOff")) + } } else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) { if (player.Faction != PlanetSideEmpire.VS) { ops.toggleMaxSpecialState(enable = false) 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 7741fa86f..55753dff9 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -2,9 +2,11 @@ package net.psforever.actors.session.support import akka.actor.{ActorContext, typed} +import net.psforever.objects.definition.SpecialExoSuitDefinition import net.psforever.objects.zones.Zoning import net.psforever.objects.serverobject.turret.VanuSentry import net.psforever.objects.zones.exp.ToDatabase +import net.psforever.types.ChatMessageType import scala.collection.mutable import scala.concurrent.duration._ @@ -83,9 +85,10 @@ class WeaponAndProjectileOperations( if (player.ZoningRequest != Zoning.Method.None) { sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire") } - if (player.isShielded) { + if (player.UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded) { // Cancel NC MAX shield if it's active sessionLogic.general.toggleMaxSpecialState(enable = false) + sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOverride")) } val (o, tools) = FindContainedWeapon val (_, enabledTools) = FindEnabledWeaponsToHandleWeaponFireAccountability(o, tools) diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala index 06b65013c..375525032 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -349,6 +349,10 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) ) } else if (reminderSeq.size == 1) { //end reminder + standaloneBlockedReminder( + VehicleSpawnPad.VehicleOrder(entry.driver, entry.vehicle, null), + Some("@PadDeconstruct_Done") + ) periodicReminder.cancel() periodicReminder = Default.Cancellable reminderSeq = List() @@ -374,7 +378,10 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) * @param blockedOrder the previous order whose vehicle is blocking the spawn pad from operating * @param recipients all of the other customers who will be receiving the message */ - private def BlockedReminder(blockedOrder: VehicleSpawnControl.Order, recipients: Seq[VehicleSpawnPad.VehicleOrder]): Unit = { + private def BlockedReminder( + blockedOrder: VehicleSpawnControl.Order, + recipients: Seq[VehicleSpawnPad.VehicleOrder] + ): Unit = { //everyone else recursiveBlockedReminder( recipients.iterator, @@ -389,12 +396,8 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) .orElse(pad.Zone.GUID(blockedOrder.vehicle.OwnerGuid)) .orElse(pad.Zone.GUID(blockedOrder.DriverGUID)) collect { case p: Player if p.isAlive => - recursiveBlockedReminder( - Seq(VehicleSpawnPad.VehicleOrder( - blockedOrder.driver, - blockedOrder.vehicle, - null //permissible - )).iterator, + standaloneBlockedReminder( + VehicleSpawnPad.VehicleOrder(blockedOrder.driver, blockedOrder.vehicle, null), Some(s"@PadDeconstruct_secsA^${reminderSeq.head}~") ) } @@ -432,16 +435,22 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) cause: Option[Any] ): Unit = { if (iter.hasNext) { - val recipient = iter.next() - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( - recipient.player.Name, - VehicleSpawnPad.Reminders.Blocked, - cause - ) + standaloneBlockedReminder(iter.next(), cause) recursiveBlockedReminder(iter, cause) } } + private def standaloneBlockedReminder( + entry: VehicleSpawnPad.VehicleOrder, + cause: Option[Any] + ): Unit = { + pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + entry.player.Name, + VehicleSpawnPad.Reminders.Blocked, + cause + ) + } + @tailrec private final def recursiveOrderReminder( iter: Iterator[VehicleSpawnPad.VehicleOrder], size: Int, diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala index 05a01682f..6b7b3f958 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala @@ -5,12 +5,16 @@ import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.sourcing.{PlayerSource, UniquePlayer} import net.psforever.objects.zones.{HotSpotInfo, ZoneHotSpotProjector} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.types.{PlanetSideEmpire, Vector3} +import net.psforever.types.{ChatMessageType, PlanetSideEmpire, Vector3} import net.psforever.util.Config import akka.pattern.ask import akka.util.Timeout +import net.psforever.objects.Player import net.psforever.objects.avatar.scoring.Kill +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.zones.exp.ToDatabase +import net.psforever.packet.game.ChatMsg +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import scala.collection.mutable import scala.concurrent.duration._ @@ -31,6 +35,20 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci updateHotSpotInfoOverTime() updateTime(now) } + building.CaptureTerminal + .map(_.HackedBy) + .collect { + case Some(info@Hackable.HackInfo(_, _, start, length, _)) + if building.NtuLevel == 0 && { + val approximateHackTimeRemaining = math.max(0, start + length - System.currentTimeMillis()) + approximateHackTimeRemaining <= 300.seconds.toMillis && approximateHackTimeRemaining > 295.seconds.toMillis + } => + MajorFacilityHackParticipation.warningMessageForHackOccupiers( + building, + info, + ChatMsg(ChatMessageType.UNK_227, "@FacilityRequiresResourcesForHackCriticalWarning") + ) + } lastInfoRequest = now } @@ -287,8 +305,8 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci if (isResecured) { hackerScore } else { - val flagCarrierScore = flagCarrier.map (p => List((p.CharId, 0L, "llu"))).getOrElse(Nil) - if (playersInSoi.exists(_.CharId == hackerId) && !flagCarrierScore.exists { case (charId, _,_) => charId == hackerId }) { + val flagCarrierScore = flagCarrier.map(p => List((p.CharId, 0L, "llu"))).getOrElse(Nil) + if (playersInSoi.exists(_.CharId == hackerId) && !flagCarrierScore.exists { case (charId, _, _) => charId == hackerId }) { hackerScore ++ flagCarrierScore } else { flagCarrierScore @@ -326,3 +344,61 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci .getOrElse(list) } } + +object MajorFacilityHackParticipation { + /** + * Dispatch a message to clients affected by some change. + * Establish the hack information by referencing the capture terminal. + * @param building building entity + * @param msg message to send to affected clients + */ + def warningMessageForHackOccupiers( + building: Building, + msg: ChatMsg + ): Unit = { + building + .CaptureTerminal + .flatMap(_.HackedBy) + .foreach { hackedInfo => + warningMessageForHackOccupiers(building, hackedInfo, msg) + } + } + + /** + * Dispatch a message to clients affected by some change. + * Select individuals belonging to the hacking faction to be targets for the message. + * @param building building entity + * @param hackedInfo confirmed information about the hack state + * @param msg message to send to affected clients + */ + def warningMessageForHackOccupiers( + building: Building, + hackedInfo: Hackable.HackInfo, + msg: ChatMsg + ): Unit = { + val hackerFaction = hackedInfo.hackerFaction + warningMessageForHackOccupiers( + building, + building.PlayersInSOI.filter(_.Faction == hackerFaction), + msg + ) + } + + /** + * Dispatch a message to clients affected by some change. + * @param building building entity + * @param targets affected clients by player + * @param msg message to send to affected clients + */ + private def warningMessageForHackOccupiers( + building: Building, + targets: Iterable[Player], + msg: ChatMsg + ): Unit = { + val events = building.Zone.LocalEvents + val message = LocalAction.SendResponse(msg) + targets.foreach { player => + events ! LocalServiceMessage(player.Name, message) + } + } +} diff --git a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala index c1b7780be..1ea4b86a4 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala @@ -6,8 +6,11 @@ import net.psforever.actors.zone.ZoneActor import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.serverobject.deploy.{Deployment, Interference} import net.psforever.objects.vital.InGameHistory -import net.psforever.objects.{Default, Vehicle} -import net.psforever.types.{DriveState, PlanetSideEmpire, Vector3} +import net.psforever.objects.{Default, GlobalDefinitions, Vehicle} +import net.psforever.packet.game.ChatMsg +import net.psforever.services.Service +import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.types.{ChatMessageType, DriveState, PlanetSideEmpire, Vector3} import scala.annotation.tailrec import scala.collection.mutable @@ -83,7 +86,17 @@ class ZoneVehicleActor( case Zone.Vehicle.CanNotDespawn(_, _, _) => () - case Zone.Vehicle.CanNotDeploy(_, vehicle, _, reason) => () + case Zone.Vehicle.CanNotDeploy(_, vehicle, toState, _) + if vehicle.Definition == GlobalDefinitions.ams && + toState == DriveState.Deployed => + val pos = vehicle.Position + zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Seats.headOption.flatMap(_._2.occupant).map(_.Name).getOrElse("Driver"), + VehicleAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, "@nodeploy_ams")) + ) + temporaryInterference = temporaryInterference.filterNot(_._1 == pos) + + case Zone.Vehicle.CanNotDeploy(_, vehicle, _, reason) => val pos = vehicle.Position val driverMoniker = vehicle.Seats.headOption.flatMap(_._2.occupant).map(_.Name).getOrElse("Driver") log.warn(s"$driverMoniker's ${vehicle.Definition.Name} can not deploy in ${zone.id} because $reason") diff --git a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala index 154c0c7c7..f616f8e1d 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -6,16 +6,17 @@ import net.psforever.actors.zone.{BuildingActor, ZoneActor} import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.llu.CaptureFlag -import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.zones.Zone import net.psforever.objects.Default -import net.psforever.packet.game.{GenericAction, HackState7, PlanetsideAttributeEnum} +import net.psforever.objects.serverobject.structures.participation.MajorFacilityHackParticipation +import net.psforever.packet.game.{ChatMsg, GenericAction, HackState7, PlanetsideAttributeEnum} import net.psforever.objects.sourcing.PlayerSource import net.psforever.services.Service import net.psforever.services.local.support.HackCaptureActor.GetHackingFaction import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} +import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID} import scala.concurrent.duration.{FiniteDuration, _} import scala.util.Random @@ -234,6 +235,18 @@ class HackCaptureActor extends Actor { val owner = terminal.Owner.asInstanceOf[Building] // Notify parent building that state has changed owner.Actor ! BuildingActor.AmenityStateChange(terminal, Some(isResecured)) + // If major facility, check NTU + owner.CaptureTerminal + .map(_.HackedBy) + .collect { + case Some(info: Hackable.HackInfo) + if owner.BuildingType == StructureType.Facility && owner.NtuLevel == 0 => + MajorFacilityHackParticipation.warningMessageForHackOccupiers( + owner, + info, + ChatMsg(ChatMessageType.UNK_227, "@FacilityRequiresResourcesForHackWarning") + ) + } // Push map update to clients owner.Zone.actor ! ZoneActor.ZoneMapUpdate() }