diff --git a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala index ccd99554..9c03dc32 100644 --- a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala @@ -317,7 +317,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act sendResponse(ChatMsg(ChatMessageType.UNK_224, "@BailingMechanismFailure_Pilot")) case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed) - if { + if GlobalDefinitions.isFlightVehicle(obj.Definition) && { continent .blockMap .sector(obj) diff --git a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala index 3511b4dc..946b68f9 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala @@ -1,10 +1,12 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.hackable +import net.psforever.objects.serverobject.structures.{Building, WarpGate} +import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.{Player, Vehicle} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7} -import net.psforever.types.PlanetSideGUID +import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState} import net.psforever.services.Service import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage} @@ -85,6 +87,8 @@ object GenericHackables { (HackState.Finished, 100) } else if (target.isMoving(test = 1f) || target.Destroyed || !target.HasGUID) { (HackState.Cancelled, 0) + } else if (target.isInstanceOf[CaptureTerminal] && EndHackProgress(target, hacker)) { + (HackState.Cancelled, 0) } else { (HackState.Ongoing, progress.toInt) } @@ -135,4 +139,43 @@ object GenericHackables { log.warn(s"Hack message failed on target: ${target.Definition.Name}@${target.GUID.guid}") } } + + /** + * Check if the state of connected facilities has changed since the hack progress began. It accounts for a friendly facility + * on the other side of a warpgate as well in case there are no friendly facilities in the same zone + * @param target the `Hackable` object that has been hacked + * @param hacker the player performing the action + */ + def EndHackProgress(target: PlanetSideServerObject, hacker: Player): Boolean = { + val building = target.asInstanceOf[CaptureTerminal].Owner.asInstanceOf[Building] + if (building.Faction == PlanetSideEmpire.NEUTRAL) { + false + } else { + val stopHackingCount = building.Neighbours match { + case Some(neighbors) => + neighbors.count { + case wg: WarpGate if wg.Faction == hacker.Faction => + true + case wg: WarpGate => + val friendlyBaseOpt = for { + otherWg <- wg.Neighbours.flatMap(_.find(_.isInstanceOf[WarpGate])) + friendly <- otherWg.Neighbours.flatMap(_.collectFirst { case b: Building if !b.isInstanceOf[WarpGate] => b }) + } yield friendly + friendlyBaseOpt.exists { fb => + fb.Faction == hacker.Faction && + !fb.CaptureTerminalIsHacked && + fb.NtuLevel > 0 && + fb.Generator.forall(_.Condition != PlanetSideGeneratorState.Destroyed) + } + case b => + b.Faction == hacker.Faction && + !b.CaptureTerminalIsHacked && + b.NtuLevel > 0 && + b.Generator.forall(_.Condition != PlanetSideGeneratorState.Destroyed) + } + case None => 0 + } + stopHackingCount == 0 + } + } } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala index 4a4f97b6..d1d2a355 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala @@ -175,6 +175,10 @@ class VehicleControl(vehicle: Vehicle) if vehicle.DeploymentState == DriveState.AutoPilot => sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType)) + case Mountable.TryDismount(user, seat_num, bailType) + if vehicle.isMoving(test = 1f) && bailType == BailType.Normal => + sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType)) + case msg @ Mountable.TryDismount(player, seat_num, _) => dismountBehavior.apply(msg) dismountCleanup(seat_num, player) diff --git a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala index 4e05d85d..29dacff8 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala @@ -5,8 +5,9 @@ import akka.actor.Actor import net.psforever.actors.zone.ZoneActor import net.psforever.objects.definition.{ObjectDefinition, VehicleDefinition} import net.psforever.objects.serverobject.deploy.{Deployment, Interference} +import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.vital.InGameHistory -import net.psforever.objects.{Default, Vehicle} +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} @@ -176,8 +177,17 @@ object ZoneVehicleActor { vehicle: Vehicle, reportedInterferenceList: Seq[ObjectDefinition] ): Boolean = { - if (reportedInterferenceList.nonEmpty) { - reportedInterferenceList + val msgOpt: Option[String] = { + val insideWarpGate = zone.blockMap.sector(vehicle).buildingList.exists { + case wg: WarpGate => + Vector3.DistanceSquared(vehicle.Position, wg.Position) < math.pow(wg.Definition.SOIRadius, 2) + case _ => false + } + if (insideWarpGate && vehicle.Definition == GlobalDefinitions.ams) { + Some("@nodeploy_warpgate") + } + else if (reportedInterferenceList.nonEmpty) { + reportedInterferenceList .find(_.isInstanceOf[VehicleDefinition]) .map { definition => s"@nodeploy_${definition.Name}" } .orElse { @@ -190,15 +200,15 @@ object ZoneVehicleActor { None } } - .foreach { msg => - zone.VehicleEvents ! VehicleServiceMessage( - vehicle.Seats.headOption.flatMap(_._2.occupant).map(_.Name).getOrElse(""), - VehicleAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg)) - ) - } - true - } else { - false + } + else None } + msgOpt.foreach { msg => + zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Seats.headOption.flatMap(_._2.occupant).map(_.Name).getOrElse(""), + VehicleAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg)) + ) + } + msgOpt.isDefined } } diff --git a/src/main/scala/net/psforever/services/teamwork/SquadService.scala b/src/main/scala/net/psforever/services/teamwork/SquadService.scala index afb5c9cf..3c372ee1 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadService.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadService.scala @@ -273,7 +273,7 @@ class SquadService extends Actor { case SquadAction.Membership(SquadRequestType.Cancel, cancellingPlayer, _, _, _) => SquadActionMembershipCancel(cancellingPlayer, tplayer) - case SquadAction.Membership(SquadRequestType.Promote, _, _, _, _) => () + //case SquadAction.Membership(SquadRequestType.Promote, _, _, _, _) => () case SquadAction.Membership(SquadRequestType.Promote, promotingPlayer, Some(_promotedPlayer), promotedName, _) => SquadActionMembershipPromote(promotingPlayer, _promotedPlayer, promotedName, SquadServiceMessage(tplayer, zone, action), sender())