reload opened and closed doors upon zoning changes; rework ntu silo and ant interaction start; clear hack on proximity terminals; facility turrets stop indicating towards jamming cause when mounted

This commit is contained in:
Fate-JH 2024-07-15 16:03:45 -04:00
parent 955fa8ba0a
commit a699c6c223
9 changed files with 119 additions and 61 deletions

View file

@ -41,7 +41,7 @@ import net.psforever.objects.zones.blockmap.BlockMapEntity
import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, Shortcut, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -1025,13 +1025,18 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
private def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = {
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
(continent.GUID(player.VehicleSeated), equipment) match {
val vehicleOpt = continent.GUID(player.avatar.vehicle)
(vehicleOpt, equipment) match {
case (Some(vehicle: Vehicle), Some(item))
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) &&
GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) =>
resourceSilo.Actor ! CommonMessages.Use(player, equipment)
case _ =>
resourceSilo.Actor ! CommonMessages.Use(player)
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
case (Some(vehicle: Vehicle), _)
if vehicle.Definition == GlobalDefinitions.ant &&
vehicle.DeploymentState == DriveState.Deployed &&
Vector3.DistanceSquared(resourceSilo.Position.xy, vehicle.Position.xy) < math.pow(resourceSilo.Definition.UseRadius, 2) =>
resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle))
case _ => ()
}
}

View file

@ -5,7 +5,6 @@ import akka.actor.ActorContext
import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
@ -69,9 +68,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget =>
val pos = player.Position.xy
val range = if (Sidedness.equals(player.WhichSide, Sidedness.InsideOf)) 100f
else if (sessionLogic.general.canSeeReallyFar) 800f
else 400f
val range = ops.doorLoadRange()
val foundDoor = continent
.blockMap
.sector(pos, range)
@ -79,13 +76,12 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
.collect { case door: Door => door }
.find(_.GUID == doorGuid)
val doorExistsInRange: Boolean = foundDoor.nonEmpty
//lazy val doorReallyClose: Boolean = foundDoor.exists(door => Vector3.DistanceSquared(door.Position.xy, pos) < 10201f)
if (doorExistsInRange) {
sendResponse(GenericObjectStateMsg(doorGuid, state=16))
}
case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(doorGuid, state = 17))
sendResponse(GenericObjectStateMsg(doorGuid, state=17))
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
@ -111,16 +107,14 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
//if active, deactivate
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
//if active, deactivate
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
obj.Destroyed = true
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)

View file

@ -96,16 +96,14 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
//if active, deactivate
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
//if active, deactivate
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
obj.Destroyed = true
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)

View file

@ -5,6 +5,8 @@ import akka.actor.ActorContext
import net.psforever.objects.{Players, TurretDeployable}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.packet.game.GenericObjectActionMessage
import net.psforever.services.local.LocalResponse
import net.psforever.types.PlanetSideGUID
@ -22,7 +24,10 @@ class SessionLocalHandlers(
val sessionLogic: SessionData,
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
def deactivateTelpadDeployableMessages(guid: PlanetSideGUID): Unit = {
sendResponse(GenericObjectActionMessage(guid, code = 29))
sendResponse(GenericObjectActionMessage(guid, code = 30))
}
def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
Players.buildCooldownReset(continent, player.Name, obj)
@ -33,4 +38,13 @@ class SessionLocalHandlers(
Players.buildCooldownReset(continent, player.Name, obj)
TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
}
def doorLoadRange(): Float = {
if (Sidedness.equals(player.WhichSide, Sidedness.InsideOf))
100f
else if (sessionLogic.general.canSeeReallyFar)
800f
else
400f
}
}

View file

@ -2227,15 +2227,28 @@ class ZoningOperations(
sessionLogic.actionsToCancel()
continent.GUID(player.VehicleSeated) match {
case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty =>
vehicle.PassengerInSeat(player) match {
case Some(0) =>
deadState = DeadState.Release // cancel movement updates
vehicle.Position = position
LoadZonePhysicalSpawnPoint(continent.id, position, Vector3.z(vehicle.Orientation.z), 0 seconds, None)
case _ => // not seated as the driver, in which case we can't move
vehicle
.PassengerInSeat(player)
.collect {
case 0 => //driver of the vehicle carries the vehicle and its passengers
deadState = DeadState.Release //cancel movement updates
vehicle.Position = position
doorsThatShouldBeClosedOrBeOpenedByRange(
player.Position,
sessionLogic.localResponse.doorLoadRange(),
position,
openRange = 100f
)
LoadZonePhysicalSpawnPoint(continent.id, position, Vector3.z(vehicle.Orientation.z), 0 seconds, None)
}
case None =>
deadState = DeadState.Release // cancel movement updates
doorsThatShouldBeClosedOrBeOpenedByRange(
player.Position,
sessionLogic.localResponse.doorLoadRange(),
position,
openRange = 100f
)
player.Position = position
sendResponse(PlayerStateShiftMessage(ShiftState(0, position, player.Orientation.z, None)))
deadState = DeadState.Alive // must be set here
@ -3105,6 +3118,7 @@ class ZoningOperations(
}
})
}
doorsThatShouldBeOpenInRange(pos, range = 100f)
setAvatar = true
player.allowInteraction = true
upstreamMessageCount = 0
@ -3622,6 +3636,32 @@ class ZoningOperations(
}
}
def doorsThatShouldBeClosedOrBeOpenedByRange(
closedPosition: Vector3,
closedRange: Float,
openPosition: Vector3,
openRange: Float
): Unit = {
continent
.blockMap
.sector(closedPosition, closedRange)
.amenityList
.collect { case door: Door if door.isOpen =>
sendResponse(GenericObjectStateMsg(door.GUID, state=17))
}
doorsThatShouldBeOpenInRange(openPosition, openRange)
}
def doorsThatShouldBeOpenInRange(position: Vector3, range: Float): Unit = {
continent
.blockMap
.sector(position.xy, range)
.amenityList
.collect { case door: Door if door.isOpen =>
sendResponse(GenericObjectStateMsg(door.GUID, state=16))
}
}
override protected[session] def stop(): Unit = {
zoningTimer.cancel()
spawn.respawnTimer.cancel()

View file

@ -698,6 +698,7 @@ object GlobalDefinitionsMiscellaneous {
spawn_tube_door_coffin.Damageable = true
resource_silo.Name = "resource_silo"
resource_silo.UseRadius = 22 //20
resource_silo.Damageable = false
resource_silo.Repairable = false
resource_silo.MaxNtuCapacitor = 1000

View file

@ -1,7 +1,7 @@
// Copyright (c) 2019 PSForever
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{Actor, ActorRef}
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
@ -53,37 +53,31 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
checkBehavior
.orElse(storageBehavior)
.orElse {
case CommonMessages.Use(player, _) =>
case CommonMessages.Use(_, Some(vehicle: Vehicle))
if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) =>
val siloFaction = resourceSilo.Faction
val playerFaction = player.Faction
resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match {
case Some(vehicle) =>
(if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
//bfr's discharge into friendly silos and charge from enemy and neutral silos
if (siloFaction == playerFaction) {
Some(TransferBehavior.Discharging(Ntu.Nanites))
} else if (resourceSilo.MaxNtuCapacitor * 0.4f < resourceSilo.NtuCapacitor) {
//the bfr never drains below 40%
Some(TransferBehavior.Charging(Ntu.Nanites))
} else {
None
}
} else if(siloFaction == PlanetSideEmpire.NEUTRAL || siloFaction == playerFaction) {
//ants discharge into neutral and friendly silos
Some(TransferBehavior.Discharging(Ntu.Nanites))
} else {
None
}) match {
case Some(msg) =>
context.system.scheduler.scheduleOnce(
delay = 1000 milliseconds,
vehicle.Actor,
msg
)
case None => ;
}
case _ => ;
val vehicleFaction = vehicle.Faction
val msgOpt = if (siloFaction == vehicleFaction) {
Some(TransferBehavior.Discharging(Ntu.Nanites))
} else if (resourceSilo.MaxNtuCapacitor * 0.4f < resourceSilo.NtuCapacitor) {
//the bfr never drains below 40%
Some(TransferBehavior.Charging(Ntu.Nanites))
} else {
None
}
msgOpt.collect { SendNtuMessage(vehicle.Actor, _) }
case CommonMessages.Use(_, Some(vehicle: Vehicle))
if vehicle.Definition == GlobalDefinitions.ant =>
val siloFaction = resourceSilo.Faction
val vehicleFaction = vehicle.Faction
val msgOpt = if (siloFaction == PlanetSideEmpire.NEUTRAL || siloFaction == vehicleFaction) {
//ants discharge into neutral and friendly silos
Some(TransferBehavior.Discharging(Ntu.Nanites))
} else {
None
}
msgOpt.collect { SendNtuMessage(vehicle.Actor, _) }
case ResourceSilo.LowNtuWarning(enabled: Boolean) =>
LowNtuWarning(enabled)
@ -91,9 +85,13 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
case ResourceSilo.UpdateChargeLevel(amount: Float) =>
UpdateChargeLevel(amount)
case _ => ;
case _ => ()
}
def SendNtuMessage(replyTo: ActorRef, msg: Any): Cancellable = {
context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, replyTo, msg)
}
def LowNtuWarning(enabled: Boolean): Unit = {
resourceSilo.LowNtuWarningOn = enabled
log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled")
@ -147,6 +145,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
* The silo will agree to offers until its nanite capacitor is completely full.
*/
def HandleNtuOffer(sender: ActorRef, src: NtuContainer): Unit = {
//todo if I want to test conditions towards continuing ntu transfers, do those here
sender ! (if (resourceSilo.NtuCapacitor < resourceSilo.MaxNtuCapacitor) {
Ntu.Request(0, resourceSilo.MaxNtuCapacitor - resourceSilo.NtuCapacitor)
} else {

View file

@ -5,6 +5,7 @@ import akka.actor.{ActorRef, Cancellable}
import net.psforever.objects.sourcing.AmenitySource
import org.log4s.Logger
import scala.annotation.unused
import scala.collection.mutable
import scala.concurrent.duration._
//
@ -121,6 +122,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
}
def unpoweredStateLogic : Receive = commonBehavior
.orElse(clearHackBehavior)
.orElse {
case CommonMessages.Use(_, _) =>
log.warn(s"unexpected format for CommonMessages.Use in this context")
@ -145,7 +147,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
isPowered && super.tryAutoRepair()
}
def Use(target: PlanetSideGameObject, zone: String, callback: ActorRef): Unit = {
def Use(target: PlanetSideGameObject, @unused zone: String, callback: ActorRef): Unit = {
val hadNoUsers = term.NumberUsers == 0
if (term.AddUser(target)) {
log.trace(s"ProximityTerminal.Use: unit ${term.Definition.Name}@${term.GUID.guid} will act on $target")
@ -169,7 +171,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
}
}
def Unuse(target: PlanetSideGameObject, zone: String): Unit = {
def Unuse(target: PlanetSideGameObject, @unused zone: String): Unit = {
val whereTarget = term.Targets.indexWhere(_ eq target)
val previousUsers = term.NumberUsers
val hadUsers = previousUsers > 0
@ -223,7 +225,7 @@ object ProximityTerminalControl {
* @param target the object being affected by the unit
*/
def selectAndTryProximityUnitBehavior(
callback: ActorRef,
@unused callback: ActorRef,
terminal: Terminal with ProximityUnit,
target: PlanetSideGameObject
): Boolean = {

View file

@ -104,9 +104,14 @@ class FacilityTurretControl(turret: FacilityTurret)
}
override protected def tryMount(obj: PlanetSideServerObject with Mountable, seatNumber: Int, player: Player): Boolean = {
val originalAutoState = AutomaticOperation
AutomaticOperation = false //turn off
if (JammableObject.Jammed) {
val zone = TurretObject.Zone
AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID) //can not recover lost jamming aggro
}
if (!super.tryMount(obj, seatNumber, player)) {
AutomaticOperation = true //revert?
AutomaticOperation = originalAutoState //revert
false
} else {
true