mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Repeating the Obvious (#1233)
* adding chat and event messages that have been neglected since; these are easy additions * completely retooled water interactions for players and vehicles to handle a wading status, before drowning; when player with llu or vehicle with player with llu come into contact with water in this wading state, the llu is lost and the facility hack ends; adding messages to support this and differentiate from an llu facility capture timeout * periodic message while the llu is active, whether or not there is a carrier, until the hack is finished * message when inventory is full (assumes player inventory destination, but item winds up in hand due to lack of space, but it works) * when changing exo-suits, complain if equipment has to be deleted; dropped equipment is limited to special equipment; report a player pointlessly * announce ams decay to players who have just spawned on an ams, when they spawn on that ams, and for the last round of spawnees when the vehicle finally deconstructs; due to use of the word 'bound' this may be an incorrect application, but matrixing doesn't work anyway * deconstruction message for would-be driver when vehicle is abandoned on spawn pad; tempo on message delivery is different than usual * message when player is kicked from orbital shuttle gantry; message when player attempts to bail in a warp gate; message when player attempts to bail from a droppod; stop player from succeeding in bailing from a droppod * 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 * message for deployables being blocked; cleaning up dev test section * Yellow Ownership (#1226) * just some tinkering and clean-up * converted DeployItem from AvatarService to LocalService; attempt at resolving missing overwhip yellow ring is complicated; vehicle ownership packet wqorks on deployables that are mountable, but is less successful on normal simple deployables * restoration of yellow ring of ownership around deployables; changes to variant of CommonFieldData transcorder used on certain deployable transcoders; static values are assigned parameter names and public variables are given types for completion * initial packet for GenericObjectAction2Message and tests; repaired transcoders and tests for TRAP and small turrets * force redraw of the whole boomer to assert reassignment of ownership; it's heavy-handed but it works * deployable ownership should be asserted during both re-zoning and revival; refactoring of code in ZoningOperations * message when inventory is full (assumes player inventory destination, but item winds up in hand due to lack of space, but it works) * vehicle deployment messages added in, then deployment was fixed to accommodate an explicit caller, and that changed a whole lot of the deployment loop for messages; environmental activity was modified to maointain a more responsible start/stop transition; many many test changes (still an issue with a lot of them) * moving around conditions for losing the llu * the question of one extra bit for small deployables; also, all tests pass successfully, tho resource silo use remains coin flip; what sorcery is this * duplicate capture lost message eliminated; flag lost violently due to environment in general rather than just water interaction; flag lost violently due to warp gate envelope interaction; flag lost due to be dropped in an unsafe position * vary the testing depth for water interaction * darn tests * fixed vehicle interference; added missing ArmorShieldOff message; clearing countdown after vehicle spawn should stop countdown from appearing in subsequent vehicle spawns * the router needs 20m of clearance * removing the shared group interference information from routers
This commit is contained in:
parent
4b7d41db2f
commit
6a349d0fe1
|
|
@ -3,6 +3,7 @@ package net.psforever.actors.session.normal
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.support.AvatarHandlerFunctions
|
||||
import net.psforever.objects.serverobject.containable.ContainableBehavior
|
||||
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
|
||||
import net.psforever.types.ImplantType
|
||||
|
||||
|
|
@ -295,6 +296,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0))
|
||||
}
|
||||
//functionally delete
|
||||
if (delete.size > 1 || delete.nonEmpty && !delete.exists {
|
||||
case (e: Tool, _) => GlobalDefinitions.isMaxArms(e.Definition)
|
||||
case _ => false
|
||||
}) {
|
||||
/*
|
||||
if going x -> max, you will have enough space in max inventory for any displaced holster equipment
|
||||
for max -> max, don't care about the max weapon arm being deleted (allow for 1)
|
||||
for any other x -> x, any deleted equipment will raise this comment
|
||||
*/
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ItemsDeconstructed"))
|
||||
}
|
||||
delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) }
|
||||
//redraw
|
||||
if (maxhand) {
|
||||
|
|
@ -330,13 +342,17 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
|||
}
|
||||
DropLeftovers(player)(drop)
|
||||
|
||||
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) =>
|
||||
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) =>
|
||||
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
|
||||
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
|
||||
//happening to some other player
|
||||
sendResponse(ObjectHeldMessage(target, slot, unk1 = false))
|
||||
//cleanup
|
||||
(oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
|
||||
val dropPred = ContainableBehavior.DropPredicate(player)
|
||||
val deleteFromDrop = drop.filterNot(dropPred)
|
||||
(oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID)))
|
||||
.distinctBy(_._2)
|
||||
.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
|
||||
//draw holsters
|
||||
holsters.foreach {
|
||||
case InventoryItem(obj, index) =>
|
||||
|
|
|
|||
|
|
@ -145,8 +145,11 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_KICK, _, contents) if gmCommandAllowed =>
|
||||
ops.commandKick(session, message, contents)
|
||||
|
||||
case (CMT_REPORTUSER, _, contents) =>
|
||||
ops.commandReportUser(session, message, contents)
|
||||
|
||||
case _ =>
|
||||
log.warn(s"Unhandled chat message $message")
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@no_permission"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
}
|
||||
case None => ()
|
||||
}
|
||||
//llu destruction check
|
||||
if (player.Carrying.contains(SpecialCarry.CaptureFlag)) {
|
||||
CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, player)
|
||||
}
|
||||
//
|
||||
val eagleEye: Boolean = ops.canSeeReallyFar
|
||||
val isNotVisible: Boolean = sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing ||
|
||||
(player.isAlive && sessionLogic.zoning.spawn.deadState == DeadState.RespawnTime)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
|
|||
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, CargoBehavior}
|
||||
|
|
@ -19,7 +20,7 @@ import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleC
|
|||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||
import net.psforever.types.{BailType, ChatMessageType, DriveState, PlanetSideGUID, Vector3}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -71,18 +72,6 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
obj match {
|
||||
case v: Vehicle
|
||||
if bailType == BailType.Bailed &&
|
||||
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
|
||||
v.isFlying =>
|
||||
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
case None =>
|
||||
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||
|
|
@ -368,18 +357,19 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
DismountAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID &&
|
||||
obj.isFlying &&
|
||||
obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) =>
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if tplayer.GUID == player.GUID =>
|
||||
//disembarking self
|
||||
log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${
|
||||
obj.SeatPermissionGroup(seatNum) match {
|
||||
case Some(AccessPermissionGroup.Driver) => "driver seat"
|
||||
case Some(seatType) => s"$seatType seat (#$seatNum)"
|
||||
case None => "seat"
|
||||
}
|
||||
}")
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||
|
|
@ -388,7 +378,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID)
|
||||
)
|
||||
|
||||
case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
case Mountable.CanDismount(obj: PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}")
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
|
|
@ -407,7 +397,38 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
case Mountable.CanNotMount(obj: Mountable, seatNumber) =>
|
||||
log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's seat $seatNumber, but was not allowed")
|
||||
|
||||
case Mountable.CanNotDismount(obj, seatNum) =>
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Normal)
|
||||
if obj.DeploymentState == DriveState.AutoPilot =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@SA_CannotDismountAtThisTime"))
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@CannotBailFromDroppod"))
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if obj.DeploymentState == DriveState.AutoPilot =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@SA_CannotBailAtThisTime"))
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, BailType.Bailed)
|
||||
if {
|
||||
continent
|
||||
.blockMap
|
||||
.sector(obj)
|
||||
.buildingList
|
||||
.exists {
|
||||
case wg: WarpGate =>
|
||||
Vector3.DistanceSquared(obj.Position, wg.Position) < math.pow(wg.Definition.SOIRadius, 2)
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
} =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@Vehicle_CannotBailInWarpgateEnvelope"))
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, _)
|
||||
if obj.isMoving(test = 1f) =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_224, "@TooFastToDismount"))
|
||||
|
||||
case Mountable.CanNotDismount(obj, seatNum, _) =>
|
||||
log.warn(s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seatNum, but was not allowed")
|
||||
}
|
||||
}
|
||||
|
|
@ -465,7 +486,17 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
* @param obj the mountable object
|
||||
* @param seatNum the mount out of which which the player is disembarking
|
||||
*/
|
||||
private def DismountVehicleAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
private def DismountVehicleAction(tplayer: Player, obj: Vehicle, seatNum: Int): Unit = {
|
||||
//disembarking self
|
||||
log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${
|
||||
obj.SeatPermissionGroup(seatNum) match {
|
||||
case Some(AccessPermissionGroup.Driver) => "driver seat"
|
||||
case Some(seatType) => s"$seatType seat (#$seatNum)"
|
||||
case None => "seat"
|
||||
}
|
||||
}")
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
//until vehicles maintain synchronized momentum without a driver
|
||||
obj match {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.actors.session.normal
|
|||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions}
|
||||
import net.psforever.objects.avatar.SpecialCarry
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle, Vehicles}
|
||||
import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
|
|
@ -12,6 +13,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
|||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.support.CaptureFlagManager
|
||||
import net.psforever.services.vehicle.{VehicleResponse, VehicleServiceResponse}
|
||||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
|
|
@ -62,6 +64,14 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
player.Orientation = orient
|
||||
player.Velocity = vel
|
||||
sessionLogic.updateLocalBlockMap(pos)
|
||||
//llu destruction check
|
||||
if (player.Carrying.contains(SpecialCarry.CaptureFlag)) {
|
||||
continent
|
||||
.GUID(player.VehicleSeated)
|
||||
.collect { case vehicle: Vehicle =>
|
||||
CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
case VehicleResponse.VehicleState(
|
||||
vehicleGuid,
|
||||
|
|
@ -199,6 +209,9 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
|
||||
sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid))
|
||||
|
||||
case VehicleResponse.LoseOwnership(_, vehicleGuid) =>
|
||||
ops.announceAmsDecay(vehicleGuid,msg = "@ams_decaystarted")
|
||||
|
||||
case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
|
||||
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
|
||||
|
||||
|
|
@ -217,6 +230,16 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
|
||||
case VehicleResponse.UnloadVehicle(_, vehicleGuid) =>
|
||||
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=0))
|
||||
if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists {
|
||||
case ams: Vehicle =>
|
||||
ams.GUID == vehicleGuid &&
|
||||
ams.OwnerGuid.isEmpty
|
||||
case _ =>
|
||||
false
|
||||
}) {
|
||||
sessionLogic.zoning.spawn.prevSpawnPoint = None
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_229, "@ams_decayed"))
|
||||
}
|
||||
|
||||
case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget =>
|
||||
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
|
||||
|
|
@ -308,13 +331,13 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
|||
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||
sendResponse(ChatMsg(
|
||||
ChatMessageType.CMT_OPEN,
|
||||
wideContents=true,
|
||||
recipient="",
|
||||
s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}",
|
||||
note=None
|
||||
))
|
||||
val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}"
|
||||
val msg = if (str.contains("@")) {
|
||||
ChatMsg(ChatMessageType.UNK_229, str)
|
||||
} else {
|
||||
ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None)
|
||||
}
|
||||
sendResponse(msg)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(_, data) =>
|
||||
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles.control.BfrFlight
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.packet.game.{ChatMsg, ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
import net.psforever.types.{ChatMessageType, DriveState, Vector3}
|
||||
|
||||
object VehicleLogic {
|
||||
def apply(ops: VehicleOperations): VehicleLogic = {
|
||||
|
|
@ -70,6 +70,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
obj.Velocity = None
|
||||
obj.Flying = None
|
||||
}
|
||||
//
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.VehicleState(
|
||||
|
|
@ -303,6 +304,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
log.trace(s"DeployRequest: $obj transitioning to deploy state")
|
||||
} else if (state == DriveState.Deployed) {
|
||||
log.trace(s"DeployRequest: $obj has been Deployed")
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@DeployingMessage"))
|
||||
} else {
|
||||
CanNotChangeDeployment(obj, state, "incorrect deploy state")
|
||||
}
|
||||
|
|
@ -313,6 +315,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
log.trace(s"DeployRequest: $obj transitioning to undeploy state")
|
||||
} else if (state == DriveState.Mobile) {
|
||||
log.trace(s"DeployRequest: $obj is Mobile")
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@UndeployingMessage"))
|
||||
} else {
|
||||
CanNotChangeDeployment(obj, state, "incorrect undeploy state")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,11 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_WARP, _, contents) =>
|
||||
ops.commandWarp(session, message, contents)
|
||||
|
||||
case _ => ()
|
||||
case (CMT_REPORTUSER, _, contents) =>
|
||||
ops.commandReportUser(session, message, contents)
|
||||
|
||||
case _ =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@no_permission"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
|
|||
case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) =>
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Mountable, _, _) => ()
|
||||
case Mountable.CanDismount(_: Mountable, _, _) => ()
|
||||
|
||||
case Mountable.CanNotDismount(obj: Vehicle, seatNum) =>
|
||||
case Mountable.CanNotDismount(obj: Vehicle, _, _) =>
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
case _ => ()
|
||||
|
|
|
|||
|
|
@ -279,11 +279,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
|||
|
||||
def handleCanDeploy(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleCanUndeploy(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = {
|
||||
if (state != DriveState.Undeploying && state != DriveState.Mobile) {
|
||||
CanNotChangeDeployment(obj, state, "incorrect undeploy state")
|
||||
}
|
||||
}
|
||||
def handleCanUndeploy(obj: Deployment.DeploymentObject, state: DriveState.Value): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleCanNotChangeDeployment(obj: Deployment.DeploymentObject, state: DriveState.Value, reason: String): Unit = {
|
||||
if (Deployment.CheckForDeployState(state) && !Deployment.AngleCheck(obj)) {
|
||||
|
|
|
|||
|
|
@ -91,11 +91,13 @@ class ChatOperations(
|
|||
}
|
||||
|
||||
def commandWatermark(contents: String): Unit = {
|
||||
val connectionState =
|
||||
val connectionState = {
|
||||
if (contents.contains("40 80")) 100
|
||||
else if (contents.contains("120 200")) 25
|
||||
else 50
|
||||
}
|
||||
context.self ! SessionActor.SetConnectionState(connectionState)
|
||||
context.self ! SessionActor.SendResponse(ChatMsg(ChatMessageType.UNK_227, "@CMT_CULLWATERMARK_success"))
|
||||
}
|
||||
|
||||
def commandSpeed(message: ChatMsg, contents: String): Unit = {
|
||||
|
|
@ -647,6 +649,11 @@ class ChatOperations(
|
|||
}
|
||||
}
|
||||
|
||||
def commandReportUser(@unused session: Session, @unused message: ChatMsg, @unused contents: String): Unit = {
|
||||
//todo get user from contents
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@rpt_i"))
|
||||
}
|
||||
|
||||
def commandIncomingSendAllIfOnline(session: Session, message: ChatMsg): Unit = {
|
||||
if (AvatarActor.onlineIfNotIgnored(session.avatar, message.recipient)) {
|
||||
sendResponse(message)
|
||||
|
|
|
|||
|
|
@ -220,8 +220,12 @@ class GeneralOperations(
|
|||
)
|
||||
}
|
||||
}
|
||||
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
|
||||
case Some((llu, Some(carrier: Player))) if carrier.GUID == player.GUID =>
|
||||
if (!CaptureFlagManager.ReasonToLoseFlagViolently(continent, Some(guid), player)) {
|
||||
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
|
||||
}
|
||||
case Some((llu, Some(carrier: Player)))
|
||||
if carrier.GUID == player.GUID &&
|
||||
!CaptureFlagManager.ReasonToLoseFlagViolently(continent, Some(guid), player) =>
|
||||
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
|
||||
case Some((_, Some(carrier: Player))) =>
|
||||
log.warn(s"${player.toString} tried to drop LLU, but it is currently held by ${carrier.toString}")
|
||||
|
|
@ -669,6 +673,9 @@ class GeneralOperations(
|
|||
case _ => ()
|
||||
}
|
||||
} else {
|
||||
if (player.Capacitor < 1f && player.UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOff"))
|
||||
}
|
||||
player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.packet.game.ChatMsg
|
||||
import net.psforever.services.vehicle.VehicleResponse
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import net.psforever.types.{ChatMessageType, DriveState, PlanetSideGUID}
|
||||
|
||||
trait VehicleHandlerFunctions extends CommonSessionInterfacingFunctionality {
|
||||
def ops: SessionVehicleHandlers
|
||||
|
|
@ -17,4 +19,17 @@ class SessionVehicleHandlers(
|
|||
val avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
val galaxyService: ActorRef,
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
def announceAmsDecay(vehicleGuid: PlanetSideGUID, msg: String): Unit = {
|
||||
if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists {
|
||||
case ams: Vehicle =>
|
||||
ams.GUID == vehicleGuid &&
|
||||
ams.DeploymentState == DriveState.Deployed &&
|
||||
ams.OwnerGuid.isEmpty
|
||||
case _ =>
|
||||
false
|
||||
}) {
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_229, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,7 @@ class ZoningOperations(
|
|||
private[session] var setupAvatarFunc: () => Unit = AvatarCreate
|
||||
private[session] var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
|
||||
private[session] var nextSpawnPoint: Option[SpawnPoint] = None
|
||||
private[session] var prevSpawnPoint: Option[SpawnPoint] = None
|
||||
private[session] var interimUngunnedVehicle: Option[PlanetSideGUID] = None
|
||||
private[session] var interimUngunnedVehicleSeat: Option[Int] = None
|
||||
/** Upstream message counter<br>
|
||||
|
|
@ -2811,6 +2812,7 @@ class ZoningOperations(
|
|||
)
|
||||
)
|
||||
nextSpawnPoint = physSpawnPoint
|
||||
prevSpawnPoint = physSpawnPoint
|
||||
shiftPosition = Some(pos)
|
||||
shiftOrientation = Some(ori)
|
||||
val toZoneNumber = if (continent.id.equals(zoneId)) {
|
||||
|
|
@ -2819,10 +2821,14 @@ class ZoningOperations(
|
|||
Zones.zones.find { _.id.equals(zoneId) }.orElse(Some(Zone.Nowhere)).get.Number
|
||||
}
|
||||
val toSide = physSpawnPoint.map(_.Owner) match {
|
||||
case Some(_: WarpGate) => Sidedness.OutsideOf
|
||||
case Some(_: Building) => Sidedness.InsideOf
|
||||
case Some(v: Vehicle) => v.WhichSide //though usually OutsideOf
|
||||
case _ => Sidedness.StrictlyBetweenSides //todo needs better determination
|
||||
case Some(_: WarpGate) =>
|
||||
Sidedness.OutsideOf
|
||||
case Some(_: Building) =>
|
||||
Sidedness.InsideOf
|
||||
case Some(v: Vehicle) =>
|
||||
v.WhichSide //though usually OutsideOf
|
||||
case _ =>
|
||||
Sidedness.StrictlyBetweenSides //todo needs better determination
|
||||
}
|
||||
val toSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }
|
||||
respawnTimer = context.system.scheduler.scheduleOnce(respawnTime) {
|
||||
|
|
@ -3124,11 +3130,17 @@ class ZoningOperations(
|
|||
Config.app.game.savedMsg.short.fixed,
|
||||
Config.app.game.savedMsg.short.variable
|
||||
)
|
||||
val effortBy = nextSpawnPoint
|
||||
val effortBy = prevSpawnPoint
|
||||
.collect { case sp: SpawnTube => (sp, continent.GUID(sp.Owner.GUID)) }
|
||||
.collect {
|
||||
case (_, Some(v: Vehicle)) => continent.GUID(v.OwnerGuid)
|
||||
case (sp, Some(_: Building)) => Some(sp)
|
||||
case (_, Some(v: Vehicle)) =>
|
||||
sessionLogic.vehicleResponseOperations.announceAmsDecay(
|
||||
v.GUID,
|
||||
msg = "The AMS you were bound to has lost its' owner. It will auto-deconstruct soon."
|
||||
)
|
||||
continent.GUID(v.OwnerGuid)
|
||||
case (sp, Some(_: Building)) =>
|
||||
Some(sp)
|
||||
}
|
||||
.collect { case Some(thing: PlanetSideGameObject with FactionAffinity) => Some(SourceEntry(thing)) }
|
||||
.flatten
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ object WorldSession {
|
|||
Zone.Ground.DropItem(localItem, localContainer.Position, Vector3.z(localContainer.Orientation.z)),
|
||||
localContainer.Actor
|
||||
)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ object WorldSession {
|
|||
result.onComplete {
|
||||
case Failure(_) | Success(_: Containable.CanNotPutItemInSlot) =>
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(localContainer.Zone.GUID, localItem))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
@ -361,17 +361,21 @@ object WorldSession {
|
|||
val future = ask(localZone.Ground, Zone.Ground.PickupItem(item.GUID))
|
||||
future.onComplete {
|
||||
case Success(Zone.Ground.ItemInHand(_)) =>
|
||||
PutEquipmentInInventoryOrDrop(localContainer)(localItem)
|
||||
PutEquipmentInInventoryOrDrop(localContainer)(localItem).onComplete {
|
||||
case Success(Containable.ItemPutInSlot(_, _, Player.FreeHandSlot, _)) =>
|
||||
localContainer.Actor ! Zone.Ground.CanNotPickupItem(localZone, localItem.GUID, "@InventoryPickupNoRoom")
|
||||
case _ => ()
|
||||
}
|
||||
case Success(Zone.Ground.CanNotPickupItem(_, item_guid, _)) =>
|
||||
localZone.GUID(item_guid) match {
|
||||
case Some(_) => ;
|
||||
case Some(_) => ()
|
||||
case None => //acting on old data?
|
||||
localZone.AvatarEvents ! AvatarServiceMessage(
|
||||
localZone.id,
|
||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item_guid)
|
||||
)
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
future
|
||||
}
|
||||
|
|
@ -407,7 +411,7 @@ object WorldSession {
|
|||
.DropItem(localItem, localPos.getOrElse(localContainer.Position), Vector3.z(localContainer.Orientation.z)),
|
||||
localContainer.Actor
|
||||
)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
@ -584,7 +588,7 @@ object WorldSession {
|
|||
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
||||
//swapItem is not registered right now, we can not drop the item without re-registering it
|
||||
TaskWorkflow.execute(PutNewEquipmentInInventorySlot(localSource)(swapItem, localSrcSlot))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
override def description(): String = s"unregistering $localItem before stowing in $localDestination"
|
||||
|
|
@ -597,7 +601,7 @@ object WorldSession {
|
|||
localChannel,
|
||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid)
|
||||
)
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
val moveResult = ask(localDestination.Actor, Containable.PutItemInSlotOrAway(localItem, Some(localDestSlot)))
|
||||
moveResult.onComplete(localMoveOnComplete)
|
||||
|
|
@ -610,7 +614,7 @@ object WorldSession {
|
|||
moveItemTaskFunc(fromSlot),
|
||||
GUIDTask.unregisterEquipment(fromSource.Zone.GUID, itemToMove)
|
||||
))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
val result = ask(source.Actor, Containable.RemoveItemFromSlot(item))
|
||||
result.onComplete(resultOnComplete)
|
||||
|
|
@ -689,7 +693,7 @@ object WorldSession {
|
|||
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
||||
//swapItem is not registered right now, we can not drop the item without re-registering it
|
||||
TaskWorkflow.execute(PutNewEquipmentInInventorySlot(localSource)(swapItem, localSrcSlot))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
override def description(): String = s"registering $localItem in ${localDestination.Zone.id} before removing from $localSource"
|
||||
|
|
@ -702,7 +706,7 @@ object WorldSession {
|
|||
localChannel,
|
||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid)
|
||||
)
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
val moveResult = ask(localDestination.Actor, Containable.PutItemInSlotOrAway(localItem, Some(localDestSlot)))
|
||||
moveResult.onComplete(localMoveOnComplete)
|
||||
|
|
@ -715,7 +719,7 @@ object WorldSession {
|
|||
moveItemTaskFunc(fromSlot),
|
||||
GUIDTask.registerEquipment(fromSource.Zone.GUID, itemToMove)
|
||||
))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
val result = ask(source.Actor, Containable.RemoveItemFromSlot(item))
|
||||
result.onComplete(resultOnComplete)
|
||||
|
|
@ -847,17 +851,17 @@ object WorldSession {
|
|||
case Some(e) =>
|
||||
log.info(s"${tplayer.Name} has dropped ${tplayer.Sex.possessive} ${e.Definition.Name}")
|
||||
PutEquipmentInInventoryOrDrop(tplayer)(e)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
//restore previously-held-up equipment
|
||||
itemInPreviouslyDrawnSlotToDrop match {
|
||||
case Some(e) => PutEquipmentInInventorySlot(tplayer)(e, previouslyDrawnSlot)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
log.info(s"${tplayer.Name} has quickly drawn a ${grenade.Definition.Name}")
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
optGrenadeInSlot.nonEmpty
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import net.psforever.objects.serverobject.transfer.TransferContainer
|
|||
import net.psforever.objects.serverobject.structures.WarpGate
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7, TriggeredSound}
|
||||
import net.psforever.types.{DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.packet.game.{ChatMsg, HackMessage, HackState, HackState1, HackState7, TriggeredSound}
|
||||
import net.psforever.types.{ChatMessageType, DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
|
@ -46,7 +46,7 @@ object Vehicles {
|
|||
vehicle.Zone.id,
|
||||
VehicleAction.Ownership(tplayer.GUID, vehicle.GUID)
|
||||
)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, tplayer.Name)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, tplayer.Faction.toString)
|
||||
Some(vehicle)
|
||||
case None =>
|
||||
None
|
||||
|
|
@ -61,31 +61,37 @@ object Vehicles {
|
|||
* @return the vehicle, if it had a previous owner;
|
||||
* `None`, otherwise
|
||||
*/
|
||||
def Disown(guid: PlanetSideGUID, vehicle: Vehicle): Option[Vehicle] =
|
||||
vehicle.Zone.GUID(vehicle.OwnerGuid) match {
|
||||
case Some(player: Player) =>
|
||||
if (player.avatar.vehicle.contains(guid)) {
|
||||
player.avatar.vehicle = None
|
||||
// vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||
// player.Name,
|
||||
// VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))
|
||||
// )
|
||||
}
|
||||
vehicle.AssignOwnership(None)
|
||||
val empire = VehicleLockState.Empire.id
|
||||
//val factionChannel = s"${vehicle.Faction}"
|
||||
(0 to 2).foreach(group => {
|
||||
vehicle.PermissionGroup(group, empire)
|
||||
/*vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||
factionChannel,
|
||||
VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire)
|
||||
)*/
|
||||
})
|
||||
ReloadAccessPermissions(vehicle, player.Name)
|
||||
Some(vehicle)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
def Disown(guid: PlanetSideGUID, vehicle: Vehicle): Option[Vehicle] = {
|
||||
val zone = vehicle.Zone
|
||||
val ownerGuid = vehicle.OwnerGuid
|
||||
val ownerName = vehicle.OwnerName
|
||||
vehicle.AssignOwnership(None)
|
||||
val result = zone
|
||||
.GUID(ownerGuid)
|
||||
.collect {
|
||||
case player: Player => player.avatar
|
||||
}
|
||||
.orElse {
|
||||
zone
|
||||
.Players
|
||||
.collectFirst {
|
||||
case avatar if ownerName.contains(avatar.name) => avatar
|
||||
}
|
||||
}
|
||||
.collect {
|
||||
case avatar if avatar.vehicle.contains(guid) =>
|
||||
avatar.vehicle = None
|
||||
vehicle
|
||||
}
|
||||
val empire = VehicleLockState.Empire.id
|
||||
(0 to 2).foreach(group => vehicle.PermissionGroup(group, empire))
|
||||
ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
zone.id,
|
||||
VehicleAction.LoseOwnership(ownerGuid.getOrElse(Service.defaultPlayerGUID), guid)
|
||||
)
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassociate a player from a vehicle that he owns.
|
||||
|
|
@ -140,7 +146,7 @@ object Vehicles {
|
|||
VehicleAction.SeatPermissions(pguid, vguid, group, empire)
|
||||
)*/
|
||||
})
|
||||
ReloadAccessPermissions(vehicle, player.Name)
|
||||
ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
Some(vehicle)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -239,6 +245,8 @@ object Vehicles {
|
|||
val zone = target.Zone
|
||||
val zoneid = zone.id
|
||||
val vehicleEvents = zone.VehicleEvents
|
||||
val localEvents = zone.LocalEvents
|
||||
val previousOwnerName = target.OwnerName.getOrElse("")
|
||||
vehicleEvents ! VehicleServiceMessage(
|
||||
zoneid,
|
||||
VehicleAction.SendResponse(
|
||||
|
|
@ -292,10 +300,20 @@ object Vehicles {
|
|||
AvatarAction.SetEmpire(Service.defaultPlayerGUID, tGuid, hFaction)
|
||||
)
|
||||
}
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
localEvents ! LocalServiceMessage(
|
||||
zoneid,
|
||||
LocalAction.TriggerSound(hGuid, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)
|
||||
)
|
||||
if (zone.Players.exists(_.name.equals(previousOwnerName))) {
|
||||
localEvents ! LocalServiceMessage(
|
||||
previousOwnerName,
|
||||
LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackStolen"))
|
||||
)
|
||||
}
|
||||
localEvents ! LocalServiceMessage(
|
||||
hacker.Name,
|
||||
LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackVehicleOwned"))
|
||||
)
|
||||
// Clean up after specific vehicles, e.g. remove router telepads
|
||||
// If AMS is deployed, swap it to the new faction
|
||||
target.Definition match {
|
||||
|
|
|
|||
|
|
@ -522,6 +522,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
|
||||
case Zone.Ground.CanNotPickupItem(_, item_guid, reason) =>
|
||||
log.warn(s"${player.Name} failed to pick up an item ($item_guid) from the ground because $reason")
|
||||
if (reason.startsWith("@")) {
|
||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
player.Name,
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, reason))
|
||||
)
|
||||
}
|
||||
|
||||
case Player.BuildDeployable(obj: TelepadDeployable, tool: Telepad) =>
|
||||
obj.Router = tool.Router //necessary; forwards link to the router that produced the telepad
|
||||
|
|
@ -631,6 +637,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
||||
player.DrawnSlot = Player.HandsDownSlot
|
||||
}
|
||||
val dropPred = ContainableBehavior.DropPredicate(player)
|
||||
val (toDelete, toDrop, afterHolsters, afterInventory) = if (originalSuit == ExoSuitType.MAX) {
|
||||
//was max
|
||||
val (delete, insert) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max)
|
||||
|
|
@ -641,18 +648,21 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
//changing to a vanilla exo-suit
|
||||
val (newHolsters, unplacedHolsters) = Players.fillEmptyHolsters(player.Holsters().iterator, insert ++ beforeInventory)
|
||||
val (inventory, unplacedInventory) = GridInventory.recoverInventory(unplacedHolsters, player.Inventory)
|
||||
(delete, unplacedInventory.map(InventoryItem(_, -1)), newHolsters, inventory)
|
||||
val (dropFromUnplaced, deleteFromUnplaced) = unplacedInventory.map(InventoryItem(_, -1)).partition(dropPred)
|
||||
(delete ++ deleteFromUnplaced, dropFromUnplaced, newHolsters, inventory)
|
||||
}
|
||||
} else if (willBecomeMax) {
|
||||
//will be max, drop everything but melee slot
|
||||
val (melee, other) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Melee)
|
||||
val (inventory, unplacedInventory) = GridInventory.recoverInventory(beforeInventory ++ other, player.Inventory)
|
||||
(Nil, unplacedInventory.map(InventoryItem(_, -1)), melee, inventory)
|
||||
val (dropFromUnplaced, deleteFromUnplaced) = unplacedInventory.map(InventoryItem(_, -1)).partition(dropPred)
|
||||
(deleteFromUnplaced, dropFromUnplaced, melee, inventory)
|
||||
} else {
|
||||
//was not a max nor will become a max; vanilla exo-suit to a vanilla-exo-suit
|
||||
val (insert, unplacedHolsters) = Players.fillEmptyHolsters(player.Holsters().iterator, beforeHolsters ++ beforeInventory)
|
||||
val (inventory, unplacedInventory) = GridInventory.recoverInventory(unplacedHolsters, player.Inventory)
|
||||
(Nil, unplacedInventory.map(InventoryItem(_, -1)), insert, inventory)
|
||||
val (dropFromUnplaced, deleteFromUnplaced) = unplacedInventory.map(InventoryItem(_, -1)).partition(dropPred)
|
||||
(deleteFromUnplaced, dropFromUnplaced, insert, inventory)
|
||||
}
|
||||
//insert
|
||||
afterHolsters.foreach(elem => player.Slot(elem.start).Equipment = elem.obj)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
package net.psforever.objects.avatar.interaction
|
||||
|
||||
import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.{Player, Vehicle, Vehicles}
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, GantryDenialField, PieceOfEnvironment, interaction}
|
||||
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.packet.game.{PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.packet.game.{ChatMsg, PlayerStateShiftMessage, ShiftState}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.hart.ShuttleState
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -28,17 +29,26 @@ class WithGantry(val channel: String)
|
|||
(zone.GUID(field.obbasemesh) match {
|
||||
case Some(pad : OrbitalShuttlePad) => zone.GUID(pad.shuttle)
|
||||
case _ => None
|
||||
}) match {
|
||||
case Some(shuttle: Vehicle)
|
||||
if shuttle.Flying.contains(ShuttleState.State11.id) || shuttle.Faction != obj.Faction =>
|
||||
}, obj) match {
|
||||
case (Some(shuttle: Vehicle), player: Player)
|
||||
if (shuttle.Flying.contains(ShuttleState.State11.id) || shuttle.Faction != player.Faction) &&
|
||||
player.VehicleSeated.isEmpty =>
|
||||
val (pos, ang) = Vehicles.dismountShuttle(shuttle, field.mountPoint)
|
||||
shuttle.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
val events = shuttle.Zone.AvatarEvents
|
||||
events ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(
|
||||
Service.defaultPlayerGUID,
|
||||
PlayerStateShiftMessage(ShiftState(0, pos, ang, None)))
|
||||
)
|
||||
case Some(_: Vehicle) =>
|
||||
events ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(
|
||||
Service.defaultPlayerGUID,
|
||||
ChatMsg(ChatMessageType.UNK_227, "@Vehicle_OS_PlacedOutsideHallway")
|
||||
)
|
||||
)
|
||||
case (Some(_: Vehicle) , _)=>
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(
|
||||
attribute,
|
||||
delay = 250 milliseconds,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.objects.avatar.interaction
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
|
||||
import net.psforever.objects.serverobject.environment.{PieceOfEnvironment, interaction}
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment, interaction}
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.OxygenState
|
||||
|
|
@ -15,9 +15,11 @@ import scala.concurrent.duration._
|
|||
class WithWater(val channel: String)
|
||||
extends InteractionWith
|
||||
with Watery {
|
||||
/** do this every time we're in sufficient contact with water */
|
||||
private var doInteractingWithBehavior: (InteractsWithZone, PieceOfEnvironment, Option[Any]) => Unit = wadingBeforeDrowning
|
||||
|
||||
/**
|
||||
* Water causes players to slowly suffocate.
|
||||
* When they (finally) drown, they will die.
|
||||
* Water is wet.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
|
|
@ -26,22 +28,84 @@ class WithWater(val channel: String)
|
|||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val extra = data.collect {
|
||||
case t: OxygenStateTarget => Some(t)
|
||||
case w: Watery => w.Condition
|
||||
}.flatten
|
||||
if (extra.isDefined) {
|
||||
//inform the player that their mounted vehicle is in trouble (that they are in trouble (but not from drowning (yet)))
|
||||
stopInteractingWith(obj, body, data)
|
||||
if (getExtra(data).nonEmpty) {
|
||||
inheritAndPushExtraData(obj, body, data)
|
||||
} else {
|
||||
val (effect, time, percentage) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
if (effect) {
|
||||
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
condition = Some(cond)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, Player.Die())
|
||||
//inform the player that they are in trouble
|
||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra))
|
||||
depth = math.max(0f, body.collision.altitude - obj.Position.z)
|
||||
doInteractingWithBehavior(obj, body, data)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 500 milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("wading")))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wading only happens while the player's head is above the water.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def wadingBeforeDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
//we're already "wading", let's see if we're drowning
|
||||
if (depth >= GlobalDefinitions.MaxDepth(obj)) {
|
||||
//drowning
|
||||
beginDrowning(obj, body, data)
|
||||
} else {
|
||||
//inform the player that their mounted vehicle is in trouble (that they are in trouble (but not from drowning (yet)))
|
||||
val extra = getExtra(data)
|
||||
if (extra.nonEmpty) {
|
||||
displayOxygenState(
|
||||
obj,
|
||||
condition.getOrElse(OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, 95f)),
|
||||
extra
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Too much water causes players to slowly suffocate.
|
||||
* When they (finally) drown, they will die.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def beginDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val (effect, time, percentage) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
if (effect) {
|
||||
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage)
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
condition = Some(cond)
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(WithWater.WaterAction, delay = time milliseconds, obj.Actor, Player.Die())
|
||||
//inform the player that they are in trouble
|
||||
displayOxygenState(obj, cond, getExtra(data))
|
||||
doInteractingWithBehavior = drowning
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Too much water causes players to slowly suffocate.
|
||||
* When they (finally) drown, they will die.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def drowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
//test if player ever gets head above the water level
|
||||
if (depth < GlobalDefinitions.MaxDepth(obj)) {
|
||||
val (_, _, percentage) = Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
//switch to recovery
|
||||
if (percentage > 0) {
|
||||
recoverFromDrowning(obj, body, data)
|
||||
doInteractingWithBehavior = recoverFromDrowning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,16 +116,22 @@ class WithWater(val channel: String)
|
|||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
override def stopInteractingWith(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val (effect, time, percentage) = Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
if (percentage > 99f) {
|
||||
recoverFromInteracting(obj)
|
||||
private def recoverFromDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val state = condition.map(_.state)
|
||||
if (state.contains(OxygenState.Suffocation)) {
|
||||
//set up for recovery
|
||||
val (effect, time, percentage) = Watery.recoveringFromWateryConditions(obj, state, waterInteractionTime)
|
||||
if (percentage < 99f) {
|
||||
//we're not too far gone
|
||||
recoverFromDrowning(obj, body, data, effect, time, percentage)
|
||||
}
|
||||
doInteractingWithBehavior = recovering
|
||||
} else {
|
||||
stopInteractingAction(obj, body, data, effect, time, percentage)
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,38 +144,169 @@ class WithWater(val channel: String)
|
|||
* @param time current time until completion of the next effect
|
||||
* @param percentage value to display in the drowning UI progress bar
|
||||
*/
|
||||
private def stopInteractingAction(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any],
|
||||
effect: Boolean,
|
||||
time: Long,
|
||||
percentage: Float
|
||||
): Unit = {
|
||||
private def recoverFromDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any],
|
||||
effect: Boolean,
|
||||
time: Long,
|
||||
percentage: Float
|
||||
): Unit = {
|
||||
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage)
|
||||
val extra = data.collect {
|
||||
case t: OxygenStateTarget => Some(t)
|
||||
case w: Watery => w.Condition
|
||||
}.flatten
|
||||
if (effect) {
|
||||
if (effect) {
|
||||
condition = Some(cond)
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(WithWater.WaterAction, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
|
||||
//inform the player
|
||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra))
|
||||
displayOxygenState(obj, cond, extra)
|
||||
} else if (extra.isDefined) {
|
||||
//inform the player
|
||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, extra))
|
||||
displayOxygenState(obj, cond, extra)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The recovery period is much faster than the drowning process.
|
||||
* Check for when the player fully recovers,
|
||||
* and that the player does not regress back to drowning.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
def recovering(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
lazy val state = condition.map(_.state)
|
||||
if (depth >= GlobalDefinitions.MaxDepth(obj)) {
|
||||
//go back to drowning
|
||||
beginDrowning(obj, body, data)
|
||||
} else if (state.contains(OxygenState.Recovery)) {
|
||||
//check recovery conditions
|
||||
val (_, _, percentage) = Watery.recoveringFromWateryConditions(obj, state, waterInteractionTime)
|
||||
if (percentage < 1f) {
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When out of water, the player is no longer suffocating.
|
||||
* He's even stopped wading.
|
||||
* The only thing we should let complete now is recovery.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
override def stopInteractingWith(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
if (getExtra(data).nonEmpty) {
|
||||
inheritAndPushExtraData(obj, body, data)
|
||||
} else {
|
||||
stopInteractingWithAction(obj, body, data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When out of water, the player is no longer suffocating.
|
||||
* He's even stopped wading.
|
||||
* The only thing we should let complete now is recovery.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def stopInteractingWithAction(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val cond = condition.map(_.state)
|
||||
if (cond.contains(OxygenState.Suffocation)) {
|
||||
//go from suffocating to recovery
|
||||
recoverFromDrowning(obj, body, data)
|
||||
} else if (cond.isEmpty) {
|
||||
//neither suffocating nor recovering, so just reset everything
|
||||
recoverFromInteracting(obj)
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(attribute)
|
||||
waterInteractionTime = 0L
|
||||
depth = 0f
|
||||
condition = None
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
|
||||
override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
|
||||
super.recoverFromInteracting(obj)
|
||||
if (condition.exists(_.state == OxygenState.Suffocation)) {
|
||||
val (effect, time, percentage) = Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
stopInteractingAction(obj, condition.map(_.body).get, None, effect, time, percentage)
|
||||
val cond = condition.map(_.state)
|
||||
//whether or not we were suffocating or recovering, we need to undo the visuals for that
|
||||
if (cond.nonEmpty) {
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction)
|
||||
displayOxygenState(
|
||||
obj,
|
||||
OxygenStateTarget(obj.GUID, condition.map(_.body).get, OxygenState.Recovery, 100f),
|
||||
None
|
||||
)
|
||||
}
|
||||
waterInteractionTime = 0L
|
||||
condition = None
|
||||
}
|
||||
|
||||
/**
|
||||
* From the "condition" of someone else's drowning status,
|
||||
* extract target information and progress.
|
||||
* @param data any information
|
||||
* @return target information and drowning progress
|
||||
*/
|
||||
private def getExtra(data: Option[Any]): Option[OxygenStateTarget] = {
|
||||
data.collect {
|
||||
case t: OxygenStateTarget => Some(t)
|
||||
case w: Watery => w.Condition
|
||||
}.flatten
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message regarding drowning and recovery
|
||||
* that includes additional information about a related target that is drowning or recovering.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
* @param data essential information about someone else's interaction with water
|
||||
*/
|
||||
private def inheritAndPushExtraData(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val state = condition.map(_.state).getOrElse(OxygenState.Recovery)
|
||||
val Some((_, _, percentage)) = state match {
|
||||
case OxygenState.Suffocation => Some(Watery.drowningInWateryConditions(obj, Some(state), waterInteractionTime))
|
||||
case OxygenState.Recovery => Some(Watery.recoveringFromWateryConditions(obj, Some(state), waterInteractionTime))
|
||||
}
|
||||
displayOxygenState(obj, OxygenStateTarget(obj.GUID, body, state, percentage), getExtra(data))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message regarding drowning and recovery.
|
||||
* @param obj the target
|
||||
* @param cond the environment
|
||||
*/
|
||||
private def displayOxygenState(
|
||||
obj: InteractsWithZone,
|
||||
cond: OxygenStateTarget,
|
||||
data: Option[OxygenStateTarget]
|
||||
): Unit = {
|
||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, data))
|
||||
}
|
||||
}
|
||||
|
||||
object WithWater {
|
||||
/** special environmental trait to queue actions independent from the primary wading test */
|
||||
case object WaterAction extends EnvironmentTrait {
|
||||
override def canInteractWith(obj: PlanetSideGameObject): Boolean = false
|
||||
override def testingDepth(obj: PlanetSideGameObject): Float = Float.PositiveInfinity
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import net.psforever.types.PlanetSideGUID
|
|||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class SmallDeployableConverter extends ObjectCreateConverter[Deployable]() {
|
||||
override def ConstructorData(obj: Deployable): Try[CommonFieldDataWithPlacement] = {
|
||||
override def ConstructorData(obj: Deployable): Try[SmallDeployableData] = {
|
||||
Success(
|
||||
CommonFieldDataWithPlacement(
|
||||
SmallDeployableData(CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = obj.Destroyed,
|
||||
false,
|
||||
v1 = false,
|
||||
None,
|
||||
jammered = obj match {
|
||||
case o: JammableUnit => o.Jammed
|
||||
|
|
@ -30,10 +30,10 @@ class SmallDeployableConverter extends ObjectCreateConverter[Deployable]() {
|
|||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def DetailedConstructorData(obj: Deployable): Try[CommonFieldDataWithPlacement] =
|
||||
override def DetailedConstructorData(obj: Deployable): Try[SmallDeployableData] =
|
||||
Failure(new Exception("converter should not be used to generate detailed small deployable data"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -801,7 +801,7 @@ object GlobalDefinitionsMiscellaneous {
|
|||
AutoRanges(
|
||||
detection = 125f,
|
||||
trigger = 100f,
|
||||
escape = 200f
|
||||
escape = 150f
|
||||
),
|
||||
AutoChecks(
|
||||
validation = List(
|
||||
|
|
@ -813,7 +813,7 @@ object GlobalDefinitionsMiscellaneous {
|
|||
),
|
||||
retaliatoryDelay = 4000L, //8000L
|
||||
cylindrical = true,
|
||||
cylindricalExtraHeight = 50f,
|
||||
cylindricalExtraHeight = 25f,
|
||||
detectionSweepTime = 2.seconds,
|
||||
refireTime = 362.milliseconds //312.milliseconds
|
||||
)
|
||||
|
|
|
|||
|
|
@ -971,7 +971,7 @@ object GlobalDefinitionsVehicle {
|
|||
ams.DeployTime = 2000
|
||||
ams.UndeployTime = 2000
|
||||
ams.interference = InterferenceRange(main = 125f, sharedGroupId = 3, shared = 30f)
|
||||
ams.DeconstructionTime = Some(20 minutes)
|
||||
ams.DeconstructionTime = Some(15 minutes)
|
||||
ams.AutoPilotSpeeds = (18, 6)
|
||||
ams.Packet = utilityConverter
|
||||
ams.DestroyedModel = Some(DestroyedVehicle.Ams)
|
||||
|
|
@ -1014,6 +1014,7 @@ object GlobalDefinitionsVehicle {
|
|||
router.Deployment = true
|
||||
router.DeployTime = 2000
|
||||
router.UndeployTime = 2000
|
||||
router.interference = InterferenceRange(main = 20f)
|
||||
router.DeconstructionTime = Duration(20, "minutes")
|
||||
router.AutoPilotSpeeds = (16, 6)
|
||||
router.Packet = variantConverter
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ object ContainableBehavior {
|
|||
entry => {
|
||||
val objDef = entry.obj.Definition
|
||||
val faction = GlobalDefinitions.isFactionEquipment(objDef)
|
||||
GlobalDefinitions.isCavernEquipment(objDef) ||
|
||||
objDef == GlobalDefinitions.router_telepad ||
|
||||
entry.obj.isInstanceOf[BoomerTrigger] ||
|
||||
(faction != tplayer.Faction && faction != PlanetSideEmpire.NEUTRAL)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.deploy
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.Default
|
||||
import net.psforever.types.{DriveState, Vector3}
|
||||
import net.psforever.services.Service
|
||||
|
|
@ -33,24 +33,24 @@ trait DeploymentBehavior {
|
|||
|
||||
val deployBehavior: Receive = {
|
||||
case Deployment.TryDeploymentChange(state) =>
|
||||
sender() ! TryDeploymentStateChange(state)
|
||||
sender() ! TryDeploymentStateChange(state, sender())
|
||||
|
||||
case Deployment.TryDeploy(state) =>
|
||||
sender() ! TryDeployStateChange(state)
|
||||
sender() ! TryDeployStateChange(state, sender())
|
||||
|
||||
case Deployment.TryUndeploy(state) =>
|
||||
sender() ! TryUndeployStateChange(state)
|
||||
sender() ! TryUndeployStateChange(state, sender())
|
||||
}
|
||||
|
||||
def TryDeploymentStateChange(state: DriveState.Value): Any = {
|
||||
def TryDeploymentStateChange(state: DriveState.Value, replyTo: ActorRef): Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if (TryDeploymentChange(obj, state)) {
|
||||
if (Deployment.CheckForDeployState(state)) {
|
||||
DeploymentAction(obj, state, prevState)
|
||||
DeploymentAction(obj, state, prevState, replyTo)
|
||||
Deployment.CanDeploy(obj, state)
|
||||
} else {
|
||||
UndeploymentAction(obj, state, prevState)
|
||||
UndeploymentAction(obj, state, prevState, replyTo)
|
||||
Deployment.CanUndeploy(obj, state)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -58,22 +58,22 @@ trait DeploymentBehavior {
|
|||
}
|
||||
}
|
||||
|
||||
def TryDeployStateChange(state: DriveState.Value): Any = {
|
||||
def TryDeployStateChange(state: DriveState.Value, replyTo: ActorRef): Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if (Deployment.CheckForDeployState(state) && TryDeploymentChange(obj, state)) {
|
||||
DeploymentAction(obj, state, prevState)
|
||||
DeploymentAction(obj, state, prevState, replyTo)
|
||||
Deployment.CanDeploy(obj, state)
|
||||
} else {
|
||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
|
||||
}
|
||||
}
|
||||
|
||||
def TryUndeployStateChange(state: DriveState.Value): Any = {
|
||||
def TryUndeployStateChange(state: DriveState.Value, replyTo: ActorRef): Any = {
|
||||
val obj = DeploymentObject
|
||||
val prevState = obj.DeploymentState
|
||||
if (Deployment.CheckForUndeployState(state) && TryUndeploymentChange(obj, state)) {
|
||||
UndeploymentAction(obj, state, prevState)
|
||||
UndeploymentAction(obj, state, prevState, replyTo)
|
||||
Deployment.CanUndeploy(obj, state)
|
||||
} else {
|
||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
|
||||
|
|
@ -91,7 +91,8 @@ trait DeploymentBehavior {
|
|||
def DeploymentAction(
|
||||
obj: Deployment.DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
prevState: DriveState.Value,
|
||||
replyTo: ActorRef
|
||||
): DriveState.Value = {
|
||||
val guid = obj.GUID
|
||||
val zone = obj.Zone
|
||||
|
|
@ -108,11 +109,9 @@ trait DeploymentBehavior {
|
|||
VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero)
|
||||
)
|
||||
deploymentTimer.cancel()
|
||||
deploymentTimer = context.system.scheduler.scheduleOnce(
|
||||
obj.DeployTime milliseconds,
|
||||
obj.Actor,
|
||||
Deployment.TryDeploy(DriveState.Deployed)
|
||||
)
|
||||
deploymentTimer = context.system.scheduler.scheduleOnce(obj.DeployTime.milliseconds)({
|
||||
obj.Actor.tell(Deployment.TryDeploy(DriveState.Deployed), replyTo)
|
||||
})
|
||||
state
|
||||
} else if (state == DriveState.Deployed) {
|
||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||
|
|
@ -129,7 +128,8 @@ trait DeploymentBehavior {
|
|||
def UndeploymentAction(
|
||||
obj: Deployment.DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
prevState: DriveState.Value,
|
||||
replyTo: ActorRef
|
||||
): DriveState.Value = {
|
||||
val guid = obj.GUID
|
||||
val zone = obj.Zone
|
||||
|
|
@ -142,11 +142,9 @@ trait DeploymentBehavior {
|
|||
)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
deploymentTimer.cancel()
|
||||
deploymentTimer = context.system.scheduler.scheduleOnce(
|
||||
obj.UndeployTime milliseconds,
|
||||
obj.Actor,
|
||||
Deployment.TryUndeploy(DriveState.Mobile)
|
||||
)
|
||||
deploymentTimer = context.system.scheduler.scheduleOnce(obj.UndeployTime.milliseconds)({
|
||||
obj.Actor.tell(Deployment.TryUndeploy(DriveState.Mobile), replyTo)
|
||||
})
|
||||
state
|
||||
} else if (state == DriveState.Mobile) {
|
||||
zone.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
|
|||
|
|
@ -46,11 +46,11 @@ object Interference {
|
|||
* @param zone game world in which this test will be conducted;
|
||||
* entity should be `ZoneAware`, but it may not be set correctly during this part of its internal process
|
||||
* @param obj entity that may be interfered with
|
||||
* @return a different entity that causes the test entity to suffer interference
|
||||
* @return other entities that causes the test entity to suffer interference
|
||||
*/
|
||||
def Test(zone: Zone, obj: PlanetSideGameObject with FactionAffinity): Option[PlanetSideGameObject with FactionAffinity] = {
|
||||
def Test(zone: Zone, obj: PlanetSideGameObject with FactionAffinity): Seq[PlanetSideGameObject with FactionAffinity] = {
|
||||
val (data, filterFunc) = SetupForTest(zone, obj)
|
||||
data.find(filterFunc)
|
||||
data.filter(filterFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import net.psforever.types.Vector3
|
|||
*/
|
||||
abstract class EnvironmentTrait {
|
||||
def canInteractWith(obj: PlanetSideGameObject): Boolean
|
||||
|
||||
def testingDepth(obj: PlanetSideGameObject): Float
|
||||
}
|
||||
|
||||
object EnvironmentAttribute {
|
||||
|
|
@ -25,16 +27,33 @@ object EnvironmentAttribute {
|
|||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
def testingDepth(obj: PlanetSideGameObject): Float = {
|
||||
obj match {
|
||||
case v: Vehicle if v.Flying.nonEmpty =>
|
||||
0f
|
||||
case _: Vehicle if !obj.Definition.DrownAtMaxDepth =>
|
||||
obj.Definition.MaxDepth * 0.9f
|
||||
case _: Vehicle =>
|
||||
obj.Definition.MaxDepth * 0.6f
|
||||
case _ =>
|
||||
0.2f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object Lava extends EnvironmentTrait {
|
||||
/** lava can only interact with anything capable of registering damage */
|
||||
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
|
||||
|
||||
def testingDepth(obj: _root_.net.psforever.objects.PlanetSideGameObject): Float = 0f
|
||||
}
|
||||
|
||||
case object Death extends EnvironmentTrait {
|
||||
/** death can only interact with anything capable of registering damage */
|
||||
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
|
||||
|
||||
def testingDepth(obj: _root_.net.psforever.objects.PlanetSideGameObject): Float = 0f
|
||||
}
|
||||
|
||||
case object GantryDenialField
|
||||
|
|
@ -46,18 +65,24 @@ object EnvironmentAttribute {
|
|||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def testingDepth(obj: _root_.net.psforever.objects.PlanetSideGameObject): Float = 0f
|
||||
}
|
||||
|
||||
case object MovementFieldTrigger
|
||||
extends EnvironmentTrait {
|
||||
/** only interact with living player characters or vehicles */
|
||||
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
|
||||
|
||||
def testingDepth(obj: _root_.net.psforever.objects.PlanetSideGameObject): Float = 0f
|
||||
}
|
||||
|
||||
case object InteriorField
|
||||
extends EnvironmentTrait {
|
||||
/** only interact with living player characters or vehicles */
|
||||
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
|
||||
|
||||
def testingDepth(obj: _root_.net.psforever.objects.PlanetSideGameObject): Float = 0f
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.serverobject.environment.interaction
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment}
|
||||
import net.psforever.objects.zones._
|
||||
|
|
@ -98,6 +97,8 @@ class InteractWithEnvironment()
|
|||
.foreach(_.stopInteractingWith(obj, body, None))
|
||||
}
|
||||
}
|
||||
|
||||
def OngoingInteractions: Set[EnvironmentTrait] = interactWith.map(_.attribute)
|
||||
}
|
||||
|
||||
object InteractWithEnvironment {
|
||||
|
|
@ -117,9 +118,8 @@ object InteractWithEnvironment {
|
|||
obj: PlanetSideServerObject,
|
||||
sector: SectorPopulation
|
||||
): Set[PieceOfEnvironment] = {
|
||||
val depth = GlobalDefinitions.MaxDepth(obj)
|
||||
sector.environmentList
|
||||
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(obj, depth))
|
||||
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(obj, body.attribute.testingDepth(obj)))
|
||||
.distinctBy(_.attribute)
|
||||
.toSet
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ object InteractWithEnvironment {
|
|||
body: PieceOfEnvironment,
|
||||
obj: PlanetSideServerObject
|
||||
): Option[PieceOfEnvironment] = {
|
||||
if ((obj.Zone eq zone) && body.testInteraction(obj, GlobalDefinitions.MaxDepth(obj))) {
|
||||
if ((obj.Zone eq zone) && body.testInteraction(obj, body.attribute.testingDepth(obj))) {
|
||||
Some(body)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -185,12 +185,12 @@ case class OnStableEnvironment() extends InteractionBehavior {
|
|||
): Set[PieceOfEnvironment] = {
|
||||
if (allow) {
|
||||
val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions }
|
||||
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
|
||||
env.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
|
||||
if (env.nonEmpty) {
|
||||
val bodies = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
|
||||
bodies.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
|
||||
if (bodies.nonEmpty) {
|
||||
nextstep = AwaitOngoingInteraction(obj.Zone)
|
||||
}
|
||||
env
|
||||
bodies
|
||||
} else {
|
||||
nextstep = BlockedFromInteracting()
|
||||
Set()
|
||||
|
|
@ -225,17 +225,22 @@ final case class AwaitOngoingInteraction(zone: Zone) extends InteractionBehavior
|
|||
): Set[PieceOfEnvironment] = {
|
||||
val interactions = obj.interaction().collectFirst { case inter: InteractWithEnvironment => inter.Interactions }
|
||||
if (allow) {
|
||||
val env = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
|
||||
val bodies = InteractWithEnvironment.checkAllEnvironmentInteractions(obj, sector)
|
||||
val (in, out) = existing.partition(body => InteractWithEnvironment.checkSpecificEnvironmentInteraction(zone, body, obj).nonEmpty)
|
||||
env.diff(in).foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
|
||||
out.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.stopInteractingWith(obj, body, None)))
|
||||
if (env.isEmpty) {
|
||||
val inAttrs = bodies.map(_.attribute)
|
||||
out
|
||||
.filterNot(e => inAttrs.contains(e.attribute))
|
||||
.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.stopInteractingWith(obj, body, None)))
|
||||
bodies
|
||||
.diff(in)
|
||||
.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.doInteractingWith(obj, body, None)))
|
||||
if (bodies.isEmpty) {
|
||||
val n = OnStableEnvironment()
|
||||
val out = n.perform(obj, sector, Set(), allow)
|
||||
nextstep = n.next
|
||||
out
|
||||
} else {
|
||||
env
|
||||
bodies
|
||||
}
|
||||
} else {
|
||||
existing.foreach(body => interactions.flatMap(_.get(body.attribute)).foreach(_.stopInteractingWith(obj, body, None)))
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.objects.serverobject.environment.interaction.common
|
||||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment}
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.types.{OxygenState, PlanetSideGUID}
|
||||
|
||||
trait Watery {
|
||||
val attribute: EnvironmentTrait = EnvironmentAttribute.Water
|
||||
|
||||
/** how long the current interaction has been progressing in the current way */
|
||||
protected var waterInteractionTime: Long = 0
|
||||
/** information regarding the drowning state */
|
||||
protected var condition: Option[OxygenStateTarget] = None
|
||||
/** information regarding the drowning state */
|
||||
def Condition: Option[OxygenStateTarget] = condition
|
||||
/** how far the player's feet are below the surface of the water */
|
||||
protected var depth: Float = 0f
|
||||
/** how far the player's feet are below the surface of the water */
|
||||
def Depth: Float = depth
|
||||
}
|
||||
|
||||
object Watery {
|
||||
|
|
@ -33,6 +39,27 @@ object Watery {
|
|||
progress: Float
|
||||
)
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target evaluate this to determine if to continue with this loss
|
||||
* @return whether or not we are sufficiently submerged in water
|
||||
*/
|
||||
def wading(target: PlanetSideGameObject with InteractsWithZone): Boolean = {
|
||||
target
|
||||
.interaction()
|
||||
.collectFirst {
|
||||
case env: InteractWithEnvironment =>
|
||||
env
|
||||
.Interactions
|
||||
.get(EnvironmentAttribute.Water)
|
||||
.collectFirst {
|
||||
case water: Watery => water.Depth > 0f
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.contains(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the effect of being exposed to a watery environment beyond an entity's critical region.
|
||||
* @param obj the target
|
||||
|
|
@ -122,10 +149,10 @@ object Watery {
|
|||
//switching from suffocation to recovery
|
||||
val oldDuration: Long = obj.Definition.UnderwaterLifespan(OxygenState.Suffocation)
|
||||
val newDuration: Long = obj.Definition.UnderwaterLifespan(OxygenState.Recovery)
|
||||
val oldTimeRemaining: Long = completionTime - System.currentTimeMillis()
|
||||
val oldTimeRemaining: Long = math.max(0, completionTime - System.currentTimeMillis())
|
||||
val oldTimeRatio: Float = oldTimeRemaining / oldDuration.toFloat
|
||||
val percentage: Float = oldTimeRatio * 100
|
||||
val recoveryTime: Long = newDuration - (newDuration * oldTimeRatio).toLong
|
||||
val recoveryTime: Long = newDuration * (1f - oldTimeRatio).toLong
|
||||
(true, recoveryTime, percentage)
|
||||
case Some(OxygenState.Recovery) =>
|
||||
//interrupted while recovering, calculate the progress and keep recovering
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class CaptureFlag(private val tDef: CaptureFlagDefinition) extends Amenity {
|
|||
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
private var carrier: Option[Player] = None
|
||||
private var lastTimeCollected: Long = System.currentTimeMillis()
|
||||
private val spawnedTime: Long = lastTimeCollected
|
||||
|
||||
def Target: Building = target
|
||||
def Target_=(newTarget: Building): Building = {
|
||||
|
|
@ -56,10 +57,11 @@ class CaptureFlag(private val tDef: CaptureFlagDefinition) extends Amenity {
|
|||
* When the flag is carried by a player, the position returned should be that of the carrier not the flag.
|
||||
* @return the position of the carrier, if there is a player carrying the flag, or the flag itself
|
||||
*/
|
||||
override def Position: Vector3 = if (Carrier.nonEmpty) {
|
||||
carrier.get.Position
|
||||
} else {
|
||||
super.Position
|
||||
override def Position: Vector3 = {
|
||||
carrier match {
|
||||
case Some(player) => player.Position
|
||||
case None => super.Position
|
||||
}
|
||||
}
|
||||
|
||||
def Carrier: Option[Player] = carrier
|
||||
|
|
@ -70,6 +72,8 @@ class CaptureFlag(private val tDef: CaptureFlagDefinition) extends Amenity {
|
|||
}
|
||||
|
||||
def LastCollectionTime: Long = carrier.map { _ => lastTimeCollected }.getOrElse { System.currentTimeMillis() }
|
||||
|
||||
def InitialSpawnTime: Long = spawnedTime
|
||||
}
|
||||
|
||||
object CaptureFlag {
|
||||
|
|
|
|||
|
|
@ -146,5 +146,5 @@ object Mountable {
|
|||
* @param obj the `Mountable` object
|
||||
* @param seat_num the seat index
|
||||
*/
|
||||
final case class CanNotDismount(obj: Mountable, seat_num: Int) extends Exchange
|
||||
final case class CanNotDismount(obj: Mountable, seat_num: Int, bailType: BailType.Value) extends Exchange
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ trait MountableBehavior {
|
|||
)
|
||||
}
|
||||
else {
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(obj, seat_number))
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(obj, seat_number, bail_type))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
/** how to process either the first order or every subsequent order */
|
||||
private var handleOrderFunc: VehicleSpawnPad.VehicleOrder => Unit = NewTasking
|
||||
|
||||
/** ... */
|
||||
private var reminderSeq: Seq[Int] = Seq()
|
||||
|
||||
def LogId = ""
|
||||
|
||||
/**
|
||||
|
|
@ -77,7 +80,7 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
}
|
||||
|
||||
override def postStop() : Unit = {
|
||||
periodicReminder.cancel()
|
||||
discontinueCurrentReminder()
|
||||
queueManagement.cancel()
|
||||
}
|
||||
|
||||
|
|
@ -113,36 +116,11 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
case VehicleSpawnControl.ProcessControl.QueueManagement =>
|
||||
queueManagementTask()
|
||||
|
||||
/*
|
||||
When the vehicle is spawned and added to the pad, it will "occupy" the pad and block it from further action.
|
||||
Normally, the player who wanted to spawn the vehicle will be automatically put into the driver mount.
|
||||
If this is blocked, the vehicle will idle on the pad and must be moved far enough away from the point of origin.
|
||||
During this time, a periodic message about the spawn pad being blocked will be broadcast to the order queue.
|
||||
*/
|
||||
case VehicleSpawnControl.ProcessControl.Reminder =>
|
||||
trackedOrder
|
||||
.collect {
|
||||
case entry =>
|
||||
if (periodicReminder.isCancelled) {
|
||||
trace(s"the pad has become blocked by a ${entry.vehicle.Definition.Name} in its current order")
|
||||
periodicReminder = context.system.scheduler.scheduleWithFixedDelay(
|
||||
VehicleSpawnControl.periodicReminderTestDelay,
|
||||
VehicleSpawnControl.periodicReminderTestDelay,
|
||||
self,
|
||||
VehicleSpawnControl.ProcessControl.Reminder
|
||||
)
|
||||
} else {
|
||||
BlockedReminder(entry, orders)
|
||||
}
|
||||
trackedOrder
|
||||
}
|
||||
.orElse {
|
||||
periodicReminder.cancel()
|
||||
None
|
||||
}
|
||||
evaluateBlockedReminder()
|
||||
|
||||
case VehicleSpawnControl.ProcessControl.Flush =>
|
||||
periodicReminder.cancel()
|
||||
discontinueCurrentReminder()
|
||||
orders.foreach { CancelOrder(_, Some("@SVCP_RemovedFromVehicleQueue_Generic")) }
|
||||
orders = Nil
|
||||
trackedOrder.foreach {
|
||||
|
|
@ -259,7 +237,7 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
* `None`, if no order found or submitted
|
||||
*/
|
||||
private def ProcessOrder(order: Option[VehicleSpawnPad.VehicleOrder]): Unit = {
|
||||
periodicReminder.cancel()
|
||||
discontinueCurrentReminder()
|
||||
order.collect {
|
||||
case VehicleSpawnPad.VehicleOrder(driver, vehicle, terminal) =>
|
||||
val size = orders.size + 1
|
||||
|
|
@ -324,37 +302,6 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @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 = {
|
||||
val user = blockedOrder.vehicle
|
||||
.Seats(0).occupant
|
||||
.orElse(pad.Zone.GUID(blockedOrder.vehicle.OwnerGuid))
|
||||
.orElse(pad.Zone.GUID(blockedOrder.DriverGUID))
|
||||
val relevantRecipients: Iterator[VehicleSpawnPad.VehicleOrder] = user match {
|
||||
case Some(p: Player) if !p.HasGUID =>
|
||||
recipients.iterator
|
||||
case Some(_: Player) =>
|
||||
(VehicleSpawnPad.VehicleOrder(
|
||||
blockedOrder.driver,
|
||||
blockedOrder.vehicle,
|
||||
null //permissible
|
||||
) +: recipients).iterator //one who took possession of the vehicle
|
||||
case _ =>
|
||||
recipients.iterator
|
||||
}
|
||||
recursiveBlockedReminder(
|
||||
relevantRecipients,
|
||||
if (blockedOrder.vehicle.Health == 0)
|
||||
Option("Clear the wreckage.")
|
||||
else
|
||||
None
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel this vehicle order and inform the person who made it, if possible.
|
||||
* @param entry the order being cancelled
|
||||
|
|
@ -381,21 +328,136 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the vehicle is spawned and added to the pad, it will "occupy" the pad and block it from further action.
|
||||
* During this time, a periodic message about the spawn pad being blocked will be broadcast to the order queue.<br>
|
||||
* The vehicle is also queued to deconstruct in 30s if no one assumes the driver seat.
|
||||
*/
|
||||
private def evaluateBlockedReminder(): Unit = {
|
||||
/*
|
||||
Normally, the player who wanted to spawn the vehicle will be automatically put into the driver mount.
|
||||
If this is blocked or aborted, the vehicle will idle on the pad and must be moved far enough away from the point of origin.
|
||||
*/
|
||||
trackedOrder
|
||||
.collect {
|
||||
case entry =>
|
||||
if (reminderSeq.isEmpty) {
|
||||
//begin reminder
|
||||
trace(s"the pad has become blocked by a ${entry.vehicle.Definition.Name} in its current order")
|
||||
retimePeriodicReminder(
|
||||
shaveOffFirstElementAndDiffSecondElement(pad.Definition.BlockedReminderMessageDelays)
|
||||
)
|
||||
trackedOrder
|
||||
} else if (reminderSeq.size == 1) {
|
||||
//end reminder
|
||||
standaloneBlockedReminder(
|
||||
VehicleSpawnPad.VehicleOrder(entry.driver, entry.vehicle, null),
|
||||
Some("@PadDeconstruct_Done")
|
||||
)
|
||||
None
|
||||
} else {
|
||||
//continue reminder
|
||||
BlockedReminder(entry, orders)
|
||||
retimePeriodicReminder(
|
||||
shaveOffFirstElementAndDiffSecondElement(reminderSeq)
|
||||
)
|
||||
trackedOrder
|
||||
}
|
||||
}
|
||||
.orElse {
|
||||
discontinueCurrentReminder()
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The periodic reminder will no longer be repeated.
|
||||
* Sequences tied to the periodic reminder should be reset.
|
||||
*/
|
||||
private def discontinueCurrentReminder(): Unit = {
|
||||
periodicReminder.cancel()
|
||||
periodicReminder = Default.Cancellable
|
||||
reminderSeq = List()
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @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 = {
|
||||
//everyone else
|
||||
recursiveBlockedReminder(
|
||||
recipients.iterator,
|
||||
if (blockedOrder.vehicle.Health == 0)
|
||||
Option("The vehicle spawn pad where you placed your order is blocked. Clearing the wreckage ...")
|
||||
else
|
||||
Option("The vehicle spawn pad where you placed your order is blocked.")
|
||||
)
|
||||
//would-be driver
|
||||
blockedOrder.vehicle
|
||||
.Seats(0).occupant
|
||||
.orElse(pad.Zone.GUID(blockedOrder.vehicle.OwnerGuid))
|
||||
.orElse(pad.Zone.GUID(blockedOrder.DriverGUID)) collect {
|
||||
case p: Player if p.isAlive =>
|
||||
standaloneBlockedReminder(
|
||||
VehicleSpawnPad.VehicleOrder(blockedOrder.driver, blockedOrder.vehicle, null),
|
||||
Some(s"@PadDeconstruct_secsA^${reminderSeq.head}~")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip the first entry in a list of numbers and
|
||||
* get the difference between the clipped entry and the next entry.
|
||||
* The clipped-off list will be made to be the new sequence of reminder delays.
|
||||
* @param sequence reminder delay test values
|
||||
* @return difference between first delay and second delay
|
||||
*/
|
||||
private def shaveOffFirstElementAndDiffSecondElement(sequence: Seq[Int]): Int = {
|
||||
val startTime = sequence.take(1).headOption.getOrElse(0)
|
||||
val restTimes = sequence.drop(1)
|
||||
val headOfRestTimes = restTimes.headOption.getOrElse(startTime)
|
||||
reminderSeq = restTimes
|
||||
startTime - headOfRestTimes
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a single instance of the "periodic reminder" to this kind of delay.
|
||||
* @param delay how long until the next reminder
|
||||
*/
|
||||
private def retimePeriodicReminder(delay: Int): Unit = {
|
||||
periodicReminder = context.system.scheduler.scheduleOnce(
|
||||
delay.seconds,
|
||||
self,
|
||||
VehicleSpawnControl.ProcessControl.Reminder
|
||||
)
|
||||
}
|
||||
|
||||
@tailrec private final def recursiveBlockedReminder(
|
||||
iter: Iterator[VehicleSpawnPad.VehicleOrder],
|
||||
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,
|
||||
|
|
@ -414,19 +476,16 @@ class VehicleSpawnControl(pad: VehicleSpawnPad)
|
|||
}
|
||||
|
||||
object VehicleSpawnControl {
|
||||
private final val periodicReminderTestDelay: FiniteDuration = 10 seconds
|
||||
|
||||
/**
|
||||
* Control messages for the vehicle spawn process.
|
||||
*/
|
||||
sealed trait ProcessControlOperation
|
||||
object ProcessControl {
|
||||
sealed trait ProcessControl
|
||||
|
||||
case object Flush extends ProcessControl
|
||||
case object OrderCancelled extends ProcessControl
|
||||
case object GetNewOrder extends ProcessControl
|
||||
case object Reminder extends ProcessControl
|
||||
case object QueueManagement extends ProcessControl
|
||||
case object Flush extends ProcessControlOperation
|
||||
case object OrderCancelled extends ProcessControlOperation
|
||||
case object GetNewOrder extends ProcessControlOperation
|
||||
case object Reminder extends ProcessControlOperation
|
||||
case object QueueManagement extends ProcessControlOperation
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ class VehicleSpawnPadDefinition(objectId: Int) extends AmenityDefinition(objectI
|
|||
|
||||
// Different pads require a Z offset to stop vehicles falling through the world after the pad rises from the floor, these values are found in game_objects.adb.lst
|
||||
private var vehicle_creation_z_offset = 0f
|
||||
|
||||
// Different pads also require an orientation offset when detaching vehicles from the rails associated with the spawn pad, again in game_objects.adb.lst
|
||||
// For example: 9754:add_property dropship_pad_doors vehiclecreationzorientoffset 90
|
||||
// However, it seems these values need to be reversed to turn CCW to CW rotation (e.g. +90 to -90)
|
||||
|
|
@ -35,6 +34,12 @@ class VehicleSpawnPadDefinition(objectId: Int) extends AmenityDefinition(objectI
|
|||
* @see `net.psforever.objects.serverobject.pad.process.VehicleSpawnControlRailJack` */
|
||||
var killBox: (VehicleSpawnPad, Boolean)=>(PlanetSideGameObject, PlanetSideGameObject, Float)=> Boolean =
|
||||
VehicleSpawnPadDefinition.prepareKillBox(forwardLimit = 0, backLimit = 0, sideLimit = 0, aboveLimit = 0)
|
||||
|
||||
private val blockedReminderMessageDelays: Seq[Int] = Seq(30, 23, 15, 8, 0)
|
||||
|
||||
def VehicleCreationDeconstructTime: Int = blockedReminderMessageDelays.head
|
||||
|
||||
def BlockedReminderMessageDelays: Seq[Int] = blockedReminderMessageDelays
|
||||
}
|
||||
|
||||
object VehicleSpawnPadDefinition {
|
||||
|
|
|
|||
|
|
@ -50,26 +50,35 @@ class VehicleSpawnControlFinalClearance(pad: VehicleSpawnPad) extends VehicleSpa
|
|||
self ! VehicleSpawnControlFinalClearance.Test(order)
|
||||
|
||||
case test @ VehicleSpawnControlFinalClearance.Test(entry) =>
|
||||
//the vehicle has an initial decay of 30s in which time it needs to be mounted
|
||||
//the vehicle has an initial decay in which time it needs to be mounted
|
||||
//once mounted, it will complain to the current driver that it is blocking the spawn pad
|
||||
//no time limit exists for that state
|
||||
val vehicle = entry.vehicle
|
||||
if (Vector3.DistanceSquared(vehicle.Position, pad.Position) > 144) { //12m away from pad
|
||||
trace("pad cleared")
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
} else if (vehicle.Destroyed) {
|
||||
trace("clearing the pad of vehicle wreckage")
|
||||
val distanceTest = Vector3.DistanceSquared(vehicle.Position, pad.Position) > 144 //12m away from pad
|
||||
if (vehicle.Destroyed) {
|
||||
trace("pad cleared of vehicle wreckage")
|
||||
val delay = if (distanceTest) { 2000 } else { 5000 }
|
||||
VehicleSpawnControl.DisposeVehicle(vehicle, pad.Zone)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
temp = context.system.scheduler.scheduleOnce(delay.milliseconds, self, VehicleSpawnControlFinalClearance.NextOrder)
|
||||
} else if (distanceTest) {
|
||||
trace("pad cleared")
|
||||
val delay = if (vehicle.Seats.head._2.occupant.isEmpty) { 4500 } else { 2000 }
|
||||
temp = context.system.scheduler.scheduleOnce(delay.milliseconds, self, VehicleSpawnControlFinalClearance.NextOrder)
|
||||
} else {
|
||||
temp = context.system.scheduler.scheduleOnce(2000 milliseconds, self, test)
|
||||
//retry test
|
||||
temp = context.system.scheduler.scheduleOnce(delay = 2000.milliseconds, self, test)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case VehicleSpawnControlFinalClearance.NextOrder =>
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControlFinalClearance {
|
||||
private final case class Test(entry: VehicleSpawnControl.Order)
|
||||
private case class Test(entry: VehicleSpawnControl.Order)
|
||||
|
||||
private case object NextOrder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.{Default, Vehicle}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ import scala.concurrent.duration._
|
|||
class VehicleSpawnControlSeatDriver(pad: VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
|
||||
def LogId = "-usher"
|
||||
|
||||
val vehicleOverride = context.actorOf(
|
||||
val vehicleOverride: ActorRef = context.actorOf(
|
||||
Props(classOf[VehicleSpawnControlServerVehicleOverride], pad),
|
||||
s"${context.parent.path.name}-override"
|
||||
)
|
||||
|
|
@ -43,7 +43,7 @@ class VehicleSpawnControlSeatDriver(pad: VehicleSpawnPad) extends VehicleSpawnCo
|
|||
val driver = entry.driver
|
||||
val vehicle = entry.vehicle
|
||||
//avoid unattended vehicle blocking the pad; user should mount (and does so normally) to reset decon timer
|
||||
vehicle.Actor ! Vehicle.Deconstruct(Some(30 seconds))
|
||||
vehicle.Actor ! Vehicle.Deconstruct(Some(pad.Definition.VehicleCreationDeconstructTime.seconds))
|
||||
if (VehicleSpawnControl.validateOrderCredentials(pad, driver, vehicle).isEmpty) {
|
||||
trace("driver to be made seated in vehicle")
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.StartPlayerSeatedInVehicle(driver.Name, vehicle, pad)
|
||||
|
|
@ -67,7 +67,7 @@ class VehicleSpawnControlSeatDriver(pad: VehicleSpawnPad) extends VehicleSpawnCo
|
|||
case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) =>
|
||||
context.parent ! msg
|
||||
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.vehicles.control
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
|
|
@ -58,7 +59,7 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
|||
* Even when disabled, the vehicle can be made to undeploy.
|
||||
*/
|
||||
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||
TryUndeployStateChange(DriveState.Undeploying)
|
||||
TryUndeployStateChange(DriveState.Undeploying, self)
|
||||
super.PrepareForDisabled(kickPassengers)
|
||||
}
|
||||
|
||||
|
|
@ -77,9 +78,10 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
|||
override def DeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
prevState: DriveState.Value,
|
||||
replyTo: ActorRef
|
||||
): DriveState.Value = {
|
||||
val out = super.DeploymentAction(obj, state, prevState)
|
||||
val out = super.DeploymentAction(obj, state, prevState, replyTo)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
specificResponseToDeployment(state)
|
||||
out
|
||||
|
|
@ -90,9 +92,10 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
|||
override def UndeploymentAction(
|
||||
obj: DeploymentObject,
|
||||
state: DriveState.Value,
|
||||
prevState: DriveState.Value
|
||||
prevState: DriveState.Value,
|
||||
replyTo: ActorRef
|
||||
): DriveState.Value = {
|
||||
val out = if (decaying) state else super.UndeploymentAction(obj, state, prevState)
|
||||
val out = if (decaying) state else super.UndeploymentAction(obj, state, prevState, replyTo)
|
||||
Vehicles.ReloadAccessPermissions(vehicle, vehicle.Faction.toString)
|
||||
specificResponseToUndeployment(state)
|
||||
out
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ class VehicleControl(vehicle: Vehicle)
|
|||
case Vehicle.Ownership(Some(player)) =>
|
||||
GainOwnership(player)
|
||||
|
||||
case Mountable.TryMount(user, mountPoint) if vehicle.DeploymentState == DriveState.AutoPilot =>
|
||||
case Mountable.TryMount(user, mountPoint)
|
||||
if vehicle.DeploymentState == DriveState.AutoPilot =>
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
|
||||
|
||||
case msg @ Mountable.TryMount(player, mount_point) =>
|
||||
|
|
@ -125,19 +126,23 @@ class VehicleControl(vehicle: Vehicle)
|
|||
mountCleanup(mount_point, player)
|
||||
|
||||
// Issue 1133. Todo: There may be a better way to address the issue?
|
||||
case Mountable.TryDismount(user, seat_num, _) if GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
|
||||
case Mountable.TryDismount(user, seat_num, bailType) if GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
|
||||
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
|
||||
case Some(entry) if System.currentTimeMillis() - entry.time < 3000L => true
|
||||
case _ => false
|
||||
}) =>
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num))
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
|
||||
|
||||
case Mountable.TryDismount(user, seat_num, _) if !GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
|
||||
case Mountable.TryDismount(user, seat_num, bailType) if !GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
|
||||
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
|
||||
case Some(entry) if System.currentTimeMillis() - entry.time < 8500L => true
|
||||
case _ => false
|
||||
}) =>
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num))
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
|
||||
|
||||
case Mountable.TryDismount(user, seat_num, bailType)
|
||||
if vehicle.DeploymentState == DriveState.AutoPilot =>
|
||||
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
|
||||
|
||||
case msg @ Mountable.TryDismount(player, seat_num, _) =>
|
||||
dismountBehavior.apply(msg)
|
||||
|
|
@ -601,9 +606,9 @@ class VehicleControl(vehicle: Vehicle)
|
|||
c
|
||||
}
|
||||
}
|
||||
watery.doInteractingWithTargets(player, percentage, watery.Condition.map(_.body).get, List(player))
|
||||
WithWater.doInteractingWithTargets(player, percentage, watery.Condition.map(_.body).get, List(player))
|
||||
case watery: WithWater if watery.Condition.map(_.state).contains(OxygenState.Recovery) =>
|
||||
watery.stopInteractingWithTargets(
|
||||
WithWater.stopInteractingWithTargets(
|
||||
player,
|
||||
Watery.recoveringFromWater(vehicle, watery)._3,
|
||||
watery.Condition.map(_.body).get,
|
||||
|
|
|
|||
|
|
@ -1,37 +1,71 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.objects.vehicles.interaction
|
||||
|
||||
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Vehicle}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
|
||||
import net.psforever.objects.serverobject.environment.{PieceOfEnvironment, interaction}
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment, interaction}
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.zones.InteractsWithZone
|
||||
import net.psforever.types.OxygenState
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class WithWater()
|
||||
extends InteractionWith
|
||||
with Watery {
|
||||
/** do this every time we're in sufficient contact with water */
|
||||
private var doInteractingWithBehavior: (InteractsWithZone, PieceOfEnvironment, Option[Any]) => Unit = wadingBeforeDrowning
|
||||
|
||||
/**
|
||||
* Water causes vehicles to become disabled if they dive off too far, too deep.
|
||||
* Flying vehicles do not display progress towards being waterlogged.
|
||||
* They just disable outright.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
* @param data additional interaction information, if applicable
|
||||
*/
|
||||
* Water is wet.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
def doInteractingWith(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
depth = math.max(0f, body.collision.altitude - obj.Position.z)
|
||||
doInteractingWithBehavior(obj, body, data)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 500 milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("wading")))
|
||||
}
|
||||
|
||||
/**
|
||||
* Wading only happens while the vehicle's wheels are mostly above the water.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def wadingBeforeDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
//we're already "wading", let's see if we're drowning
|
||||
if (depth >= GlobalDefinitions.MaxDepth(obj)) {
|
||||
//drowning
|
||||
beginDrowning(obj, body, data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Too much water causes players to slowly suffocate.
|
||||
* When they (finally) drown, they will die.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def beginDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
@unused data: Option[Any]
|
||||
): Unit = {
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
val (effect: Boolean, time: Long, percentage: Float) = {
|
||||
val (effect, time, percentage): (Boolean, Long, Float) = {
|
||||
val (a, b, c) = Watery.drowningInWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
if (a && GlobalDefinitions.isFlightVehicle(vehicle.Definition)) {
|
||||
(true, 0L, 0f) //no progress bar
|
||||
|
|
@ -42,8 +76,94 @@ class WithWater()
|
|||
if (effect) {
|
||||
condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage))
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, VehicleControl.Disable(true))
|
||||
doInteractingWithTargets(
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(WithWater.WaterAction, delay = time milliseconds, obj.Actor, VehicleControl.Disable(true))
|
||||
WithWater.doInteractingWithTargets(
|
||||
obj,
|
||||
percentage,
|
||||
body,
|
||||
vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
|
||||
)
|
||||
doInteractingWithBehavior = drowning
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Too much water causes vehicles to slowly disable.
|
||||
* When fully waterlogged, the vehicle is completely immobile.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def drowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
//test if player ever gets head above the water level
|
||||
if (depth < GlobalDefinitions.MaxDepth(obj)) {
|
||||
val (effect, time, percentage) = Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
//switch to recovery
|
||||
if (percentage > 0) {
|
||||
recoverFromDrowning(obj, body, data, effect, time, percentage)
|
||||
doInteractingWithBehavior = recovering
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When out of water, the vehicle is no longer being waterlogged.
|
||||
* It does have to endure a recovery period to get back to normal, though.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
private def recoverFromDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
val state = condition.map(_.state)
|
||||
if (state.contains(OxygenState.Suffocation)) {
|
||||
//set up for recovery
|
||||
val (effect, time, percentage) = Watery.recoveringFromWateryConditions(obj, state, waterInteractionTime)
|
||||
if (percentage < 99f) {
|
||||
//we're not too far gone
|
||||
recoverFromDrowning(obj, body, data, effect, time, percentage)
|
||||
}
|
||||
doInteractingWithBehavior = recovering
|
||||
} else {
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When out of water, the vehicle is no longer being waterlogged.
|
||||
* It does have to endure a recovery period to get back to normal, though.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
* @param effect na
|
||||
* @param time current time until completion of the next effect
|
||||
* @param percentage value to display in the drowning UI progress bar
|
||||
*/
|
||||
private def recoverFromDrowning(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
@unused data: Option[Any],
|
||||
effect: Boolean,
|
||||
time: Long,
|
||||
percentage: Float
|
||||
): Unit = {
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
val cond = OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage)
|
||||
if (effect) {
|
||||
condition = Some(cond)
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction)
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(WithWater.WaterAction, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
|
||||
//inform the players
|
||||
WithWater.stopInteractingWithTargets(
|
||||
obj,
|
||||
percentage,
|
||||
body,
|
||||
|
|
@ -55,34 +175,52 @@ class WithWater()
|
|||
}
|
||||
|
||||
/**
|
||||
* When out of water, the vehicle no longer risks becoming disabled.
|
||||
* It does have to endure a recovery period to get back to full dehydration
|
||||
* Flying vehicles are exempt from this process due to the abrupt disability they experience.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
* @param data additional interaction information, if applicable
|
||||
*/
|
||||
* The recovery period is much faster than the waterlogging process.
|
||||
* Check for when the vehicle fully recovers,
|
||||
* and that the vehicle does not regress back to waterlogging.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
def recovering(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
lazy val state = condition.map(_.state)
|
||||
if (depth >= GlobalDefinitions.MaxDepth(obj)) {
|
||||
//go back to drowning
|
||||
beginDrowning(obj, body, data)
|
||||
} else if (state.contains(OxygenState.Recovery)) {
|
||||
//check recovery conditions
|
||||
val (_, _, percentage) = Watery.recoveringFromWateryConditions(obj, state, waterInteractionTime)
|
||||
if (percentage < 1f) {
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
}/**
|
||||
* When out of water, the vehicle no longer risks becoming disabled.
|
||||
* It does have to endure a recovery period to get back to full dehydration
|
||||
* Flying vehicles are exempt from this process due to the abrupt disability they experience.
|
||||
* @param obj the target
|
||||
* @param body the environment
|
||||
*/
|
||||
override def stopInteractingWith(
|
||||
obj: InteractsWithZone,
|
||||
body: PieceOfEnvironment,
|
||||
data: Option[Any]
|
||||
): Unit = {
|
||||
obj match {
|
||||
case vehicle: Vehicle =>
|
||||
val (effect: Boolean, time: Long, percentage: Float) =
|
||||
Watery.recoveringFromWateryConditions(obj, condition.map(_.state), waterInteractionTime)
|
||||
if (effect) {
|
||||
condition = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage))
|
||||
waterInteractionTime = System.currentTimeMillis() + time
|
||||
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = time milliseconds, obj.Actor, interaction.RecoveredFromEnvironmentInteraction(attribute))
|
||||
stopInteractingWithTargets(
|
||||
obj,
|
||||
percentage,
|
||||
body,
|
||||
vehicle.Seats.values.flatMap(_.occupants).filter(p => p.isAlive && (p.Zone eq obj.Zone))
|
||||
)
|
||||
}
|
||||
case _ => ()
|
||||
val cond = condition.map(_.state)
|
||||
if (cond.contains(OxygenState.Suffocation)) {
|
||||
//go from suffocating to recovery
|
||||
recoverFromDrowning(obj, body, data)
|
||||
} else if (cond.isEmpty) {
|
||||
//neither suffocating nor recovering, so just reset everything
|
||||
recoverFromInteracting(obj)
|
||||
obj.Actor ! RespondsToZoneEnvironment.StopTimer(attribute)
|
||||
waterInteractionTime = 0L
|
||||
depth = 0f
|
||||
condition = None
|
||||
doInteractingWithBehavior = wadingBeforeDrowning
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,46 +229,53 @@ class WithWater()
|
|||
if (condition.exists(_.state == OxygenState.Suffocation)) {
|
||||
stopInteractingWith(obj, condition.map(_.body).get, None)
|
||||
}
|
||||
waterInteractionTime = 0L
|
||||
condition = None
|
||||
}
|
||||
}
|
||||
|
||||
object WithWater {
|
||||
/** special environmental trait to queue actions independent from the primary wading test */
|
||||
case object WaterAction extends EnvironmentTrait {
|
||||
override def canInteractWith(obj: PlanetSideGameObject): Boolean = false
|
||||
override def testingDepth(obj: PlanetSideGameObject): Float = Float.PositiveInfinity
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the given targets that water causes vehicles to become disabled if they dive off too far, too deep.
|
||||
* @see `InteractingWithEnvironment`
|
||||
* @see `OxygenState`
|
||||
* @see `OxygenStateTarget`
|
||||
* @param obj the target
|
||||
* @param percentage the progress bar completion state
|
||||
* @param body the environment
|
||||
* @param targets recipients of the information
|
||||
*/
|
||||
* Tell the given targets that water causes vehicles to become disabled if they dive off too far, too deep.
|
||||
* @see `InteractingWithEnvironment`
|
||||
* @see `OxygenState`
|
||||
* @see `OxygenStateTarget`
|
||||
* @param obj the target
|
||||
* @param percentage the progress bar completion state
|
||||
* @param body the environment
|
||||
* @param targets recipients of the information
|
||||
*/
|
||||
def doInteractingWithTargets(
|
||||
obj: PlanetSideServerObject,
|
||||
percentage: Float,
|
||||
body: PieceOfEnvironment,
|
||||
targets: Iterable[PlanetSideServerObject]
|
||||
): Unit = {
|
||||
obj: PlanetSideServerObject,
|
||||
percentage: Float,
|
||||
body: PieceOfEnvironment,
|
||||
targets: Iterable[PlanetSideServerObject]
|
||||
): Unit = {
|
||||
val state = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Suffocation, percentage))
|
||||
targets.foreach(_.Actor ! interaction.InteractingWithEnvironment(body, state))
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the given targets that, when out of water, the vehicle no longer risks becoming disabled.
|
||||
* @see `EscapeFromEnvironment`
|
||||
* @see `OxygenState`
|
||||
* @see `OxygenStateTarget`
|
||||
* @param obj the target
|
||||
* @param percentage the progress bar completion state
|
||||
* @param body the environment
|
||||
* @param targets recipients of the information
|
||||
*/
|
||||
* Tell the given targets that, when out of water, the vehicle no longer risks becoming disabled.
|
||||
* @see `EscapeFromEnvironment`
|
||||
* @see `OxygenState`
|
||||
* @see `OxygenStateTarget`
|
||||
* @param obj the target
|
||||
* @param percentage the progress bar completion state
|
||||
* @param body the environment
|
||||
* @param targets recipients of the information
|
||||
*/
|
||||
def stopInteractingWithTargets(
|
||||
obj: PlanetSideServerObject,
|
||||
percentage: Float,
|
||||
body: PieceOfEnvironment,
|
||||
targets: Iterable[PlanetSideServerObject]
|
||||
): Unit = {
|
||||
obj: PlanetSideServerObject,
|
||||
percentage: Float,
|
||||
body: PieceOfEnvironment,
|
||||
targets: Iterable[PlanetSideServerObject]
|
||||
): Unit = {
|
||||
val state = Some(OxygenStateTarget(obj.GUID, body, OxygenState.Recovery, percentage))
|
||||
targets.foreach(_.Actor ! interaction.EscapeFromEnvironment(body, state))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
|||
* @return synchronized reference to the globally unique identifier system
|
||||
*/
|
||||
def GUID(hub: NumberPoolHub): Boolean = {
|
||||
if (actor == Default.typed.Actor && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) {
|
||||
if (!zoneInitialized.isCompleted && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) {
|
||||
import org.fusesource.jansi.Ansi.Color.RED
|
||||
import org.fusesource.jansi.Ansi.ansi
|
||||
println(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import net.psforever.objects.serverobject.deploy.Interference
|
|||
import net.psforever.objects.sourcing.ObjectSource
|
||||
import net.psforever.objects.vehicles.MountedWeapons
|
||||
import net.psforever.objects.vital.SpawningActivity
|
||||
import net.psforever.packet.game.ChatMsg
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
|
|
@ -103,7 +106,13 @@ object ZoneDeployableActor {
|
|||
): Boolean = {
|
||||
val position = obj.Position
|
||||
deployableList.find(_ eq obj) match {
|
||||
case None if Interference.Test(zone, obj).isEmpty =>
|
||||
case _ if Interference.Test(zone, obj).nonEmpty =>
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
obj.OwnerName.getOrElse(""),
|
||||
LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_227, "@nomove_intersecting"))
|
||||
) //may not be the correct message but is sufficient at explaining why the deployable can not be built
|
||||
false
|
||||
case None =>
|
||||
deployableList += obj
|
||||
zone.actor ! ZoneActor.AddToBlockMap(obj, position)
|
||||
true
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package net.psforever.objects.zones
|
|||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.definition.{ObjectDefinition, 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.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
|
||||
|
|
@ -66,15 +69,18 @@ class ZoneVehicleActor(
|
|||
sender() ! Zone.Vehicle.CanNotDespawn(zone, vehicle, "can not find")
|
||||
}
|
||||
|
||||
case Zone.Vehicle.TryDeploymentChange(vehicle, toDeployState)
|
||||
if toDeployState == DriveState.Deploying &&
|
||||
(ZoneVehicleActor.temporaryInterferenceTest(vehicle, temporaryInterference) || Interference.Test(zone, vehicle).nonEmpty) =>
|
||||
sender() ! Zone.Vehicle.CanNotDeploy(zone, vehicle, toDeployState, "blocked by a nearby entity")
|
||||
|
||||
case Zone.Vehicle.TryDeploymentChange(vehicle, toDeployState)
|
||||
if toDeployState == DriveState.Deploying =>
|
||||
tryAddToInterferenceField(vehicle.Position, vehicle.Faction, vehicle.Definition)
|
||||
vehicle.Actor.tell(Deployment.TryDeploymentChange(toDeployState), sender())
|
||||
case Zone.Vehicle.TryDeploymentChange(vehicle, DriveState.Deploying) =>
|
||||
if (ZoneVehicleActor.ReportOnInterferenceResults(
|
||||
zone,
|
||||
vehicle,
|
||||
ZoneVehicleActor.temporaryInterferenceTest(vehicle, temporaryInterference) ++
|
||||
Interference.Test(zone, vehicle).map(_.Definition)
|
||||
)) {
|
||||
sender() ! Zone.Vehicle.CanNotDeploy(zone, vehicle, DriveState.Deploying, "blocked by a nearby entity")
|
||||
} else {
|
||||
tryAddToInterferenceField(vehicle.Position, vehicle.Faction, vehicle.Definition)
|
||||
vehicle.Actor.tell(Deployment.TryDeploymentChange(DriveState.Deploying), sender())
|
||||
}
|
||||
|
||||
case Zone.Vehicle.TryDeploymentChange(vehicle, toDeployState) =>
|
||||
vehicle.Actor.tell(Deployment.TryDeploymentChange(toDeployState), sender())
|
||||
|
|
@ -83,7 +89,19 @@ class ZoneVehicleActor(
|
|||
|
||||
case Zone.Vehicle.CanNotDespawn(_, _, _) => ()
|
||||
|
||||
case Zone.Vehicle.CanNotDeploy(_, vehicle, _, reason) => ()
|
||||
case Zone.Vehicle.CanNotDeploy(_, vehicle, DriveState.Deploying, reason) =>
|
||||
ZoneVehicleActor.ReportOnInterferenceResults(
|
||||
zone,
|
||||
vehicle,
|
||||
ZoneVehicleActor.temporaryInterferenceTest(vehicle, temporaryInterference) ++
|
||||
Interference.Test(zone, vehicle).map(_.Definition)
|
||||
)
|
||||
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")
|
||||
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")
|
||||
|
|
@ -133,22 +151,54 @@ object ZoneVehicleActor {
|
|||
private def temporaryInterferenceTest(
|
||||
vehicle: Vehicle,
|
||||
existingInterferences: Seq[(Vector3, PlanetSideEmpire.Value, VehicleDefinition)]
|
||||
): Boolean = {
|
||||
): Seq[VehicleDefinition] = {
|
||||
val vPosition = vehicle.Position
|
||||
val vFaction = vehicle.Faction
|
||||
val vDefinition = vehicle.Definition
|
||||
if (vDefinition.interference eq Interference.AllowAll) {
|
||||
false
|
||||
Nil
|
||||
} else {
|
||||
existingInterferences
|
||||
.collect { case (p, faction, d) if faction == vFaction => (p, d) }
|
||||
.exists { case (position, definition) =>
|
||||
.filter { case (position, definition) =>
|
||||
val interference = definition.interference
|
||||
(interference ne Interference.AllowAll) && {
|
||||
lazy val distanceSq = Vector3.DistanceSquared(position, vPosition)
|
||||
definition == vDefinition && distanceSq < interference.main * interference.main
|
||||
}
|
||||
}
|
||||
.map(_._2)
|
||||
}
|
||||
}
|
||||
|
||||
private def ReportOnInterferenceResults(
|
||||
zone: Zone,
|
||||
vehicle: Vehicle,
|
||||
reportedInterferenceList: Seq[ObjectDefinition]
|
||||
): Boolean = {
|
||||
if (reportedInterferenceList.nonEmpty) {
|
||||
reportedInterferenceList
|
||||
.find(_.isInstanceOf[VehicleDefinition])
|
||||
.map { definition => s"@nodeploy_${definition.Name}" }
|
||||
.orElse {
|
||||
val sharedGroupId = vehicle.Definition.interference.sharedGroupId
|
||||
if (sharedGroupId > 0) {
|
||||
reportedInterferenceList
|
||||
.find(_.interference.sharedGroupId == sharedGroupId)
|
||||
.map(_ => "@nodeploy_sharedinterference")
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,17 @@ object OxygenStateMessage extends Marshallable[OxygenStateMessage] {
|
|||
204.8f,
|
||||
11
|
||||
) :: //hackish: 2^11 == 2047, so it should be 204.7; but, 204.8 allows decode == encode
|
||||
OxygenState.codec
|
||||
bool.xmap[OxygenState](
|
||||
{
|
||||
case false => OxygenState.Recovery
|
||||
case true => OxygenState.Suffocation
|
||||
},
|
||||
{
|
||||
case OxygenState.Recovery => false
|
||||
case OxygenState.Suffocation => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
).as[DrowningTarget]
|
||||
|
||||
implicit val codec: Codec[OxygenStateMessage] = (
|
||||
|
|
|
|||
|
|
@ -1216,11 +1216,11 @@ object ObjectClass {
|
|||
case ObjectClass.advanced_ace => DroppedItemData(HandheldData.codec, "advanced ace")
|
||||
case ObjectClass.router_telepad => DroppedItemData(HandheldData.codec, "router telepad")
|
||||
case ObjectClass.boomer_trigger => DroppedItemData(HandheldData.codec, "boomer trigger")
|
||||
case ObjectClass.boomer => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
|
||||
case ObjectClass.he_mine => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
|
||||
case ObjectClass.jammer_mine => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
|
||||
case ObjectClass.motionalarmsensor => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
|
||||
case ObjectClass.sensor_shield => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
|
||||
case ObjectClass.boomer => ConstructorData(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.he_mine => ConstructorData(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.jammer_mine => ConstructorData(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.motionalarmsensor => ConstructorData(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.sensor_shield => ConstructorData(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.spitfire_aa => ConstructorData(SmallTurretData.codec, "small turret")
|
||||
case ObjectClass.spitfire_cloaked => ConstructorData(SmallTurretData.codec, "small turret")
|
||||
case ObjectClass.spitfire_turret => ConstructorData(SmallTurretData.codec, "small turret")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class SmallDeployableData(deploy: CommonFieldDataWithPlacement) extends ConstructorData {
|
||||
override def bitsize: Long = {
|
||||
deploy.bitsize + 1
|
||||
}
|
||||
}
|
||||
|
||||
object SmallDeployableData extends Marshallable[SmallDeployableData] {
|
||||
implicit val codec: Codec[SmallDeployableData] = (
|
||||
("deploy" | CommonFieldDataWithPlacement.codec) ::
|
||||
ignore(size = 1)
|
||||
).exmap[SmallDeployableData](
|
||||
{
|
||||
case deploy :: _ :: HNil =>
|
||||
Attempt.successful(SmallDeployableData(deploy))
|
||||
|
||||
case data =>
|
||||
Attempt.failure(Err(s"invalid small deployable data format - $data"))
|
||||
},
|
||||
{
|
||||
case SmallDeployableData(deploy) =>
|
||||
Attempt.successful(deploy :: () :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -120,6 +120,8 @@ class LocalService(zone: Zone) extends Actor {
|
|||
hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L)
|
||||
case LocalAction.LluCaptured(llu) =>
|
||||
hackCapturer ! HackCaptureActor.FlagCaptured(llu)
|
||||
case LocalAction.LluLost(llu) =>
|
||||
hackCapturer ! HackCaptureActor.FlagLost(llu)
|
||||
|
||||
case LocalAction.LluSpawned(player_guid, llu) =>
|
||||
// Forward to all clients to create object locally
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ object LocalAction {
|
|||
final case class ResecureCaptureTerminal(target: CaptureTerminal, hacker: PlayerSource) extends Action
|
||||
final case class StartCaptureTerminalHack(target: CaptureTerminal) extends Action
|
||||
final case class LluCaptured(llu: CaptureFlag) extends Action
|
||||
final case class LluLost(llu: CaptureFlag) extends Action
|
||||
final case class LluSpawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action
|
||||
final case class LluDespawned(player_guid: PlanetSideGUID, guid: PlanetSideGUID, position: Vector3) extends Action
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ package net.psforever.services.local.support
|
|||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.login.WorldSession
|
||||
import net.psforever.objects.{Default, Player}
|
||||
import net.psforever.objects.{Default, PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait}
|
||||
import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment
|
||||
import net.psforever.objects.serverobject.llu.CaptureFlag
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.serverobject.structures.{Building, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.zones.{InteractsWithZone, Zone}
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.{Service, ServiceManager}
|
||||
import net.psforever.services.ServiceManager.{Lookup, LookupResult}
|
||||
|
|
@ -22,12 +24,13 @@ import scala.concurrent.duration.DurationInt
|
|||
* Responsible for handling capture flag related lifecycles
|
||||
*/
|
||||
class CaptureFlagManager(zone: Zone) extends Actor {
|
||||
import CaptureFlagManager.CaptureFlagEntry
|
||||
private[this] val log = org.log4s.getLogger(self.path.name)
|
||||
|
||||
private var galaxyService: ActorRef = ActorRef.noSender
|
||||
private var mapUpdateTick: Cancellable = Default.Cancellable
|
||||
/** An internally tracked list of cached flags, to avoid querying the amenity owner each second for flag lookups. */
|
||||
private var flags: List[CaptureFlag] = Nil
|
||||
private var flags: List[CaptureFlagEntry] = Nil
|
||||
|
||||
ServiceManager.serviceManager ! Lookup("galaxy")
|
||||
|
||||
|
|
@ -39,7 +42,6 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
DoMapUpdate()
|
||||
|
||||
case CaptureFlagManager.SpawnCaptureFlag(capture_terminal, target, hackingFaction) =>
|
||||
val zone = capture_terminal.Zone
|
||||
val socket = capture_terminal.Owner.asInstanceOf[Building].GetFlagSocket.get
|
||||
// Override CC message when looked at
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
|
|
@ -62,16 +64,16 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
socket.captureFlag = flag
|
||||
TrackFlag(flag)
|
||||
TaskWorkflow.execute(WorldSession.CallBackForTask(
|
||||
GUIDTask.registerObject(socket.Zone.GUID, flag),
|
||||
socket.Zone.LocalEvents,
|
||||
GUIDTask.registerObject(zone.GUID, flag),
|
||||
zone.LocalEvents,
|
||||
LocalServiceMessage(
|
||||
socket.Zone.id,
|
||||
zone.id,
|
||||
LocalAction.LluSpawned(Service.defaultPlayerGUID, flag)
|
||||
)
|
||||
))
|
||||
// Broadcast chat message for LLU spawn
|
||||
val owner = flag.Owner.asInstanceOf[Building]
|
||||
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagSpawned(owner, flag.Target))
|
||||
CaptureFlagManager.ChatBroadcast(zone, CaptureFlagChatMessageStrings.CTF_FlagSpawned(owner, flag.Target))
|
||||
|
||||
case CaptureFlagManager.Captured(flag: CaptureFlag) =>
|
||||
val name = flag.Carrier match {
|
||||
|
|
@ -79,36 +81,46 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
case None => "A soldier"
|
||||
}
|
||||
// Trigger Install sound
|
||||
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f))
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f))
|
||||
// Broadcast capture chat message
|
||||
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_Success(name, flag.Faction, flag.Owner.asInstanceOf[Building].Name))
|
||||
CaptureFlagManager.ChatBroadcast(zone, CaptureFlagChatMessageStrings.CTF_Success(name, flag.Faction, flag.Owner.asInstanceOf[Building].Name))
|
||||
// Despawn flag
|
||||
HandleFlagDespawn(flag)
|
||||
|
||||
case CaptureFlagManager.Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) =>
|
||||
reason match {
|
||||
case CaptureFlagLostReasonEnum.Resecured =>
|
||||
ChatBroadcast(
|
||||
flag.Zone,
|
||||
CaptureFlagManager.ChatBroadcast(
|
||||
zone,
|
||||
CaptureFlagChatMessageStrings.CTF_Failed_SourceResecured(flag.Owner.asInstanceOf[Building].Name, flag.Faction)
|
||||
)
|
||||
case CaptureFlagLostReasonEnum.TimedOut =>
|
||||
val building = flag.Owner.asInstanceOf[Building]
|
||||
ChatBroadcast(
|
||||
flag.Zone,
|
||||
CaptureFlagManager.ChatBroadcast(
|
||||
zone,
|
||||
CaptureFlagChatMessageStrings.CTF_Failed_TimedOut(building.Name, flag.Target.Name, flag.Faction)
|
||||
)
|
||||
case CaptureFlagLostReasonEnum.FlagLost =>
|
||||
val building = flag.Owner.asInstanceOf[Building]
|
||||
CaptureFlagManager.ChatBroadcast(
|
||||
zone,
|
||||
CaptureFlagChatMessageStrings.CTF_Failed_FlagLost(building.Name, flag.Faction),
|
||||
fanfare = false
|
||||
)
|
||||
case CaptureFlagLostReasonEnum.Ended =>
|
||||
()
|
||||
}
|
||||
HandleFlagDespawn(flag)
|
||||
|
||||
case CaptureFlagManager.PickupFlag(flag: CaptureFlag, player: Player) =>
|
||||
val continent = flag.Zone
|
||||
flag.Carrier = Some(player)
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(player.GUID, flag.GUID, 252)))
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f))
|
||||
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagPickedUp(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name), fanfare = false)
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendPacket(ObjectAttachMessage(player.GUID, flag.GUID, 252)))
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f))
|
||||
CaptureFlagManager.ChatBroadcast(
|
||||
zone,
|
||||
CaptureFlagChatMessageStrings.CTF_FlagPickedUp(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name),
|
||||
fanfare = false
|
||||
)
|
||||
|
||||
case CaptureFlagManager.DropFlag(flag: CaptureFlag) =>
|
||||
flag.Carrier match {
|
||||
|
|
@ -119,9 +131,13 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
// Remove attached player from flag
|
||||
flag.Carrier = None
|
||||
// Send drop packet
|
||||
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0)))
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0)))
|
||||
// Send dropped chat message
|
||||
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagDropped(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name), fanfare = false)
|
||||
CaptureFlagManager.ChatBroadcast(
|
||||
zone,
|
||||
CaptureFlagChatMessageStrings.CTF_FlagDropped(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name),
|
||||
fanfare = false
|
||||
)
|
||||
HandleFlagDespawn(flag)
|
||||
// Register LLU object create task and callback to create on clients
|
||||
val replacementLlu = CaptureFlag.Constructor(
|
||||
|
|
@ -136,10 +152,10 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
socket.captureFlag = replacementLlu
|
||||
TrackFlag(replacementLlu)
|
||||
TaskWorkflow.execute(WorldSession.CallBackForTask(
|
||||
GUIDTask.registerObject(socket.Zone.GUID, replacementLlu),
|
||||
socket.Zone.LocalEvents,
|
||||
GUIDTask.registerObject(zone.GUID, replacementLlu),
|
||||
zone.LocalEvents,
|
||||
LocalServiceMessage(
|
||||
socket.Zone.id,
|
||||
zone.id,
|
||||
LocalAction.LluSpawned(Service.defaultPlayerGUID, replacementLlu)
|
||||
)
|
||||
))
|
||||
|
|
@ -152,33 +168,42 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
}
|
||||
|
||||
private def DoMapUpdate(): Unit = {
|
||||
val flagInfo = flags.map(flag =>
|
||||
val flagInfo = flags.map { case entry @ CaptureFlagManager.CaptureFlagEntry(flag) =>
|
||||
val owner = flag.Owner.asInstanceOf[Building]
|
||||
val pos = flag.Position
|
||||
val hackTimeRemaining = owner.infoUpdateMessage().hack_time_remaining
|
||||
val nextMessageAfterMinutes = CaptureFlagManager.CaptureFlagCountdownMessages(entry.currentMessageIndex)
|
||||
if (hackTimeRemaining < nextMessageAfterMinutes.minutes.toMillis) {
|
||||
entry.currentMessageIndex += 1
|
||||
val msg = CaptureFlagManager.ComposeWarningMessage(flag, owner.Name, nextMessageAfterMinutes)
|
||||
CaptureFlagManager.ChatBroadcast(zone, msg, fanfare = false)
|
||||
}
|
||||
FlagInfo(
|
||||
u1 = 0,
|
||||
owner_map_id = flag.Owner.asInstanceOf[Building].MapId,
|
||||
owner_map_id = owner.MapId,
|
||||
target_map_id = flag.Target.MapId,
|
||||
x = flag.Position.x,
|
||||
y = flag.Position.y,
|
||||
hack_time_remaining = flag.Owner.asInstanceOf[Building].infoUpdateMessage().hack_time_remaining,
|
||||
x = pos.x,
|
||||
y = pos.y,
|
||||
hack_time_remaining = hackTimeRemaining,
|
||||
is_monolith_unit = false
|
||||
)
|
||||
)
|
||||
}
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.FlagMapUpdate(CaptureFlagUpdateMessage(zone.Number, flagInfo)))
|
||||
}
|
||||
|
||||
private def TrackFlag(flag: CaptureFlag): Unit = {
|
||||
flag.Owner.Amenities = flag
|
||||
flags = flags :+ flag
|
||||
flags = flags :+ CaptureFlagEntry(flag)
|
||||
if (mapUpdateTick.isCancelled) {
|
||||
// Start sending map updates periodically
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
mapUpdateTick = context.system.scheduler.scheduleAtFixedRate(0 seconds, 1 second, self, CaptureFlagManager.MapUpdate())
|
||||
mapUpdateTick = context.system.scheduler.scheduleAtFixedRate(initialDelay = 0 seconds, interval = 1 second, self, CaptureFlagManager.MapUpdate())
|
||||
}
|
||||
}
|
||||
|
||||
private def UntrackFlag(flag: CaptureFlag): Unit = {
|
||||
flag.Owner.RemoveAmenity(flag)
|
||||
flags = flags.filterNot(x => x eq flag)
|
||||
flags = flags.filterNot(x => x.flag eq flag)
|
||||
if (flags.isEmpty) {
|
||||
mapUpdateTick.cancel()
|
||||
DoMapUpdate()
|
||||
|
|
@ -186,7 +211,6 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
}
|
||||
|
||||
private def HandleFlagDespawn(flag: CaptureFlag): Unit = {
|
||||
val zone = flag.Zone
|
||||
// Remove the flag as an amenity
|
||||
flag.Owner.asInstanceOf[Building].GetFlagSocket.get.captureFlag = None
|
||||
UntrackFlag(flag)
|
||||
|
|
@ -195,21 +219,6 @@ class CaptureFlagManager(zone: Zone) extends Actor {
|
|||
// Then unregister it from the GUID pool
|
||||
TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, flag))
|
||||
}
|
||||
|
||||
private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = {
|
||||
val messageType: ChatMessageType = if (fanfare) {
|
||||
ChatMessageType.UNK_223
|
||||
} else {
|
||||
ChatMessageType.UNK_229
|
||||
}
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zone.id,
|
||||
LocalAction.SendChatMsg(
|
||||
PlanetSideGUID(-1),
|
||||
ChatMsg(messageType, wideContents = false, "", message, None)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object CaptureFlagManager {
|
||||
|
|
@ -221,18 +230,98 @@ object CaptureFlagManager {
|
|||
final case class Captured(flag: CaptureFlag) extends Command
|
||||
final case class Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) extends Command
|
||||
final case class MapUpdate()
|
||||
|
||||
private case class CaptureFlagEntry(flag: CaptureFlag) {
|
||||
var currentMessageIndex: Int = 0
|
||||
}
|
||||
|
||||
val CaptureFlagCountdownMessages: Seq[Int] = Seq(10, 5, 2, 1, 0)
|
||||
|
||||
private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = {
|
||||
//todo use UNK_222 sometimes
|
||||
//todo I think the fanfare was relate to whether the message was celebratory is tone, based on the faction
|
||||
val messageType: ChatMessageType = if (fanfare) {
|
||||
ChatMessageType.UNK_223
|
||||
} else {
|
||||
ChatMessageType.UNK_229
|
||||
}
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zone.id,
|
||||
LocalAction.SendChatMsg(
|
||||
PlanetSideGUID(-1),
|
||||
ChatMsg(messageType, wideContents = true, "", message, None)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def ComposeWarningMessage(flag: CaptureFlag, buildingName: String, minutesLeft: Int): String = {
|
||||
import CaptureFlagChatMessageStrings._
|
||||
val carrier = flag.Carrier
|
||||
val hasCarrier = carrier.nonEmpty
|
||||
minutesLeft match {
|
||||
case 1 if hasCarrier =>
|
||||
CTF_Warning_Carrier1Min(carrier.get.Name, flag.Faction, buildingName, flag.Target.Name)
|
||||
case 1 =>
|
||||
CTF_Warning_NoCarrier1Min(buildingName, flag.Faction, flag.Target.Name)
|
||||
case time if hasCarrier =>
|
||||
CTF_Warning_Carrier(carrier.get.Name, flag.Faction, buildingName, flag.Target.Name, time)
|
||||
case time =>
|
||||
CTF_Warning_NoCarrier(buildingName, flag.Faction, flag.Target.Name, time)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param flagGuid flag that may exist
|
||||
* @param target evaluate this to determine if to continue with this loss
|
||||
*/
|
||||
def ReasonToLoseFlagViolently(
|
||||
zone: Zone,
|
||||
flagGuid: Option[PlanetSideGUID],
|
||||
target: PlanetSideGameObject with InteractsWithZone
|
||||
): Boolean = {
|
||||
zone
|
||||
.GUID(flagGuid)
|
||||
.collect {
|
||||
case flag: CaptureFlag
|
||||
if LoseFlagViolentlyToEnvironment(target, Set(EnvironmentAttribute.Water, EnvironmentAttribute.Lava, EnvironmentAttribute.Death)) ||
|
||||
LoseFlagViolentlyToWarpGateEnvelope(zone, target) =>
|
||||
flag.Destroyed = true
|
||||
zone.LocalEvents ! LocalServiceMessage("", LocalAction.LluLost(flag))
|
||||
true
|
||||
}
|
||||
.getOrElse(false)
|
||||
}
|
||||
|
||||
def LoseFlagViolentlyToEnvironment(
|
||||
target: PlanetSideGameObject with InteractsWithZone,
|
||||
deniedEnvironments: Set[EnvironmentTrait]
|
||||
): Boolean = {
|
||||
target
|
||||
.interaction()
|
||||
.collectFirst { case env: InteractWithEnvironment => env.OngoingInteractions }
|
||||
.map(_.intersect(deniedEnvironments))
|
||||
.getOrElse(Set())
|
||||
.nonEmpty
|
||||
}
|
||||
|
||||
def LoseFlagViolentlyToWarpGateEnvelope(
|
||||
zone: Zone,
|
||||
target: PlanetSideGameObject with InteractsWithZone
|
||||
): Boolean = {
|
||||
val position = target.Position
|
||||
zone
|
||||
.blockMap
|
||||
.sector(position, range = 10f)
|
||||
.buildingList
|
||||
.collectFirst {
|
||||
case gate: WarpGate if Vector3.DistanceSquared(position, gate.Position) < math.pow(gate.Definition.SOIRadius, 2f) => gate
|
||||
}
|
||||
.nonEmpty
|
||||
}
|
||||
}
|
||||
|
||||
object CaptureFlagChatMessageStrings {
|
||||
/*
|
||||
@CTF_Failed_TargetLost=%1's LLU target facility %2 was lost!\nHack canceled!
|
||||
@CTF_Failed_FlagLost=The %1 lost %2's LLU!\nHack canceled!
|
||||
@CTF_Warning_Carrier=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within %5 minutes!
|
||||
@CTF_Warning_NoCarrier=%1's LLU is in the field.\nThe %2 must take it to %3 within %4 minutes!
|
||||
@CTF_Warning_Carrier1Min=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within the next minute!
|
||||
@CTF_Warning_NoCarrier1Min=%1's LLU is in the field.\nThe %2 must take it to %3 within the next minute!
|
||||
*/
|
||||
|
||||
// @CTF_Success=%1 captured %2's LLU for the %3!
|
||||
/** {player.Name} captured {ownerName}'s LLU for the {player.Faction}! */
|
||||
private[support] def CTF_Success(playerName:String, playerFaction: PlanetSideEmpire.Value, ownerName: String): String =
|
||||
|
|
@ -243,10 +332,20 @@ object CaptureFlagChatMessageStrings {
|
|||
private[support] def CTF_Failed_TimedOut(ownerName: String, name: String, faction: PlanetSideEmpire.Value): String =
|
||||
s"@CTF_Failed_TimedOut^@${GetFactionString(faction)}~^@$ownerName~^@$name~"
|
||||
|
||||
// @CTF_Failed_Lost=The %1 lost %2's LLU!\nHack canceled!
|
||||
/** The {faction} lost {ownerName}'s LLU!\nHack canceled! */
|
||||
private[support] def CTF_Failed_FlagLost(ownerName: String, faction: PlanetSideEmpire.Value): String =
|
||||
s"@CTF_Failed_FlagLost^@${GetFactionString(faction)}~^@$ownerName~"
|
||||
|
||||
// @CTF_Failed_TargetLost=%1's LLU target facility %2 was lost!\nHack canceled!
|
||||
/** {hackFacility}'s LLU target facility {targetFacility} was lost!\nHack canceled! */
|
||||
private[support] def CTF_Failed_TargetLost(hackFacility: String, targetFacility: String): String =
|
||||
s"@CTF_Failed_TargetLost^@$hackFacility~^@$targetFacility~"
|
||||
|
||||
// @CTF_Failed_SourceResecured=The %1 resecured %2!\nThe LLU was lost!
|
||||
/** The {faction} resecured {name}!\nThe LLU was lost! */
|
||||
private[support] def CTF_Failed_SourceResecured(name: String, faction: PlanetSideEmpire.Value): String =
|
||||
s"@CTF_Failed_SourceResecured^@${CaptureFlagChatMessageStrings.GetFactionString(faction)}~^@$name~"
|
||||
s"@CTF_Failed_SourceResecured^@${GetFactionString(faction)}~^@$name~"
|
||||
|
||||
// @CTF_FlagSpawned=%1 %2 has spawned a LLU.\nIt must be taken to %3 %4's Control Console within %5 minutes or the hack will fail!
|
||||
/** {facilityType} {facilityName} has spawned a LLU.\nIt must be taken to {targetFacilityType} {targetFacilityName}'s Control Console within 15 minutes or the hack will fail! */
|
||||
|
|
@ -255,13 +354,57 @@ object CaptureFlagChatMessageStrings {
|
|||
|
||||
// @CTF_FlagPickedUp=%1 of the %2 picked up %3's LLU
|
||||
/** {player.Name} of the {player.Faction} picked up {ownerName}'s LLU */
|
||||
def CTF_FlagPickedUp(playerName: String, playerFaction: PlanetSideEmpire.Value, ownerName: String): String =
|
||||
s"@CTF_FlagPickedUp^$playerName~^@${CaptureFlagChatMessageStrings.GetFactionString(playerFaction)}~^@$ownerName~"
|
||||
private[support] def CTF_FlagPickedUp(playerName: String, playerFaction: PlanetSideEmpire.Value, ownerName: String): String =
|
||||
s"@CTF_FlagPickedUp^$playerName~^@${GetFactionString(playerFaction)}~^@$ownerName~"
|
||||
|
||||
// @CTF_FlagDropped=%1 of the %2 dropped %3's LLU
|
||||
/** {playerName} of the {faction} dropped {facilityName}'s LLU */
|
||||
def CTF_FlagDropped(playerName: String, playerFaction: PlanetSideEmpire.Value, ownerName: String): String =
|
||||
s"@CTF_FlagDropped^$playerName~^@${CaptureFlagChatMessageStrings.GetFactionString(playerFaction)}~^@$ownerName~"
|
||||
private[support] def CTF_FlagDropped(playerName: String, playerFaction: PlanetSideEmpire.Value, ownerName: String): String =
|
||||
s"@CTF_FlagDropped^$playerName~^@${GetFactionString(playerFaction)}~^@$ownerName~"
|
||||
|
||||
// @CTF_Warning_Carrier=%1's LLU is in the field.\nThe %2 must take it to %3 within %4 minutes!
|
||||
/** {facilityName}'s LLU is in the field.\nThe {faction} must take it to {targetFacilityName} within {time} minutes! */
|
||||
private[support] def CTF_Warning_Carrier(
|
||||
playerName:String,
|
||||
playerFaction: PlanetSideEmpire.Value,
|
||||
facilityName: String,
|
||||
targetFacilityName: String,
|
||||
time: Int
|
||||
): String = {
|
||||
s"@CTF_Warning_Carrier^$playerName~^@${GetFactionString(playerFaction)}~^@$facilityName~^@$targetFacilityName~^@$time~"
|
||||
}
|
||||
|
||||
// @CTF_Warning_Carrier1Min=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within the next minute!
|
||||
/** {playerName} of the {faction} has {facilityName}'s LLU.\nIt must be taken to {targetFacilityName} within the next minute! */
|
||||
private[support] def CTF_Warning_Carrier1Min(
|
||||
playerName:String,
|
||||
playerFaction: PlanetSideEmpire.Value,
|
||||
facilityName: String,
|
||||
targetFacilityName: String
|
||||
): String = {
|
||||
s"@CTF_Warning_Carrier1Min^$playerName~^@${GetFactionString(playerFaction)}~^@$facilityName~^@$targetFacilityName~"
|
||||
}
|
||||
|
||||
// @CTF_Warning_NoCarrier=%1's LLU is in the field.\nThe %2 must take it to %3 within %4 minutes!
|
||||
/** {facilityName}'s LLU is in the field.\nThe {faction} must take it to {targetFacilityName} within {time} minute! */
|
||||
private[support] def CTF_Warning_NoCarrier(
|
||||
facilityName: String,
|
||||
playerFaction: PlanetSideEmpire.Value,
|
||||
targetFacilityName: String,
|
||||
time: Int
|
||||
): String = {
|
||||
s"@CTF_Warning_NoCarrier^@$facilityName~^@${GetFactionString(playerFaction)}~^@$targetFacilityName~^$time~"
|
||||
}
|
||||
|
||||
// @CTF_Warning_NoCarrier1Min=%1's LLU is in the field.\nThe %2 must take it to %3 within the next minute!
|
||||
/** {facilityName}'s LLU is in the field.\nThe {faction} must take it to {targetFacilityName} within the next minute! */
|
||||
private[support] def CTF_Warning_NoCarrier1Min(
|
||||
facilityName: String,
|
||||
playerFaction: PlanetSideEmpire.Value,
|
||||
targetFacilityName: String
|
||||
): String = {
|
||||
s"@CTF_Warning_NoCarrier1Min^@$facilityName~^@${GetFactionString(playerFaction)}~^@$targetFacilityName~"
|
||||
}
|
||||
|
||||
private def GetFactionString: PlanetSideEmpire.Value=>String = {
|
||||
case PlanetSideEmpire.TR => "TerranRepublic"
|
||||
|
|
@ -277,4 +420,5 @@ object CaptureFlagLostReasonEnum {
|
|||
final case object Resecured extends CaptureFlagLostReasonEnum
|
||||
final case object TimedOut extends CaptureFlagLostReasonEnum
|
||||
final case object Ended extends CaptureFlagLostReasonEnum
|
||||
final case object FlagLost extends CaptureFlagLostReasonEnum
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -62,6 +63,11 @@ class HackCaptureActor extends Actor {
|
|||
// If the base has a socket, but no flag spawned it means the hacked base is neutral with no friendly neighbouring bases to deliver to, making it a timed hack.
|
||||
val building = terminal.Owner.asInstanceOf[Building]
|
||||
building.GetFlag match {
|
||||
case Some(llu) if llu.Destroyed =>
|
||||
// LLU was destroyed while in the field. Send resecured notifications
|
||||
terminal.Zone.LocalEvents ! CaptureFlagManager.Lost(llu, CaptureFlagLostReasonEnum.FlagLost)
|
||||
NotifyHackStateChange(terminal, isResecured = true)
|
||||
|
||||
case Some(llu) =>
|
||||
// LLU was not delivered in time. Send resecured notifications
|
||||
terminal.Zone.LocalEvents ! CaptureFlagManager.Lost(llu, CaptureFlagLostReasonEnum.TimedOut)
|
||||
|
|
@ -138,6 +144,28 @@ class HackCaptureActor extends Actor {
|
|||
log.error(s"Attempted LLU capture for ${flag.Owner.asInstanceOf[Building].Name} but CC GUID ${flag.Owner.asInstanceOf[Building].CaptureTerminal.get.GUID} was not in list of hacked objects")
|
||||
}
|
||||
|
||||
case HackCaptureActor.FlagLost(flag) =>
|
||||
val owner = flag.Owner.asInstanceOf[Building]
|
||||
val guid = owner.GUID
|
||||
val terminalOpt = owner.CaptureTerminal
|
||||
hackedObjects
|
||||
.find(entry => guid == entry.target.Owner.GUID)
|
||||
.collect { entry =>
|
||||
val terminal = terminalOpt.get
|
||||
hackedObjects = hackedObjects.filterNot(x => x eq entry)
|
||||
log.info(s"FlagLost: ${flag.Carrier.map(_.Name).getOrElse("")} the flag carrier screwed up the capture for ${flag.Target.Name} and the LLU has been lost")
|
||||
terminal.Actor ! CommonMessages.ClearHack()
|
||||
NotifyHackStateChange(terminal, isResecured = true)
|
||||
// If there's hacked objects left in the list restart the timer with the shortest hack time left
|
||||
RestartTimer()
|
||||
entry
|
||||
}
|
||||
.orElse{
|
||||
log.warn(s"FlagLost: flag data does not match to an entry in the hacked objects list")
|
||||
None
|
||||
}
|
||||
context.parent ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.FlagLost)
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +237,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()
|
||||
}
|
||||
|
|
@ -256,6 +296,7 @@ object HackCaptureActor {
|
|||
|
||||
final case class ResecureCaptureTerminal(target: CaptureTerminal, zone: Zone, hacker: PlayerSource)
|
||||
final case class FlagCaptured(flag: CaptureFlag)
|
||||
final case class FlagLost(flag: CaptureFlag)
|
||||
|
||||
private final case class ProcessCompleteHacks()
|
||||
|
||||
|
|
|
|||
|
|
@ -184,6 +184,10 @@ class VehicleService(zone: Zone) extends Actor {
|
|||
VehicleResponse.MountVehicle(vehicle_guid, seat)
|
||||
)
|
||||
)
|
||||
case VehicleAction.LoseOwnership(owner_guid, vehicle_guid) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoseOwnership(owner_guid, vehicle_guid))
|
||||
)
|
||||
case VehicleAction.Ownership(player_guid, vehicle_guid) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid))
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ object VehicleAction {
|
|||
) extends Action
|
||||
final case class MountVehicle(player_guid: PlanetSideGUID, object_guid: PlanetSideGUID, seat: Int) extends Action
|
||||
final case class ObjectDelete(guid: PlanetSideGUID) extends Action
|
||||
final case class LoseOwnership(owner_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) extends Action
|
||||
final case class Ownership(player_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) extends Action
|
||||
final case class PlanetsideAttribute(
|
||||
player_guid: PlanetSideGUID,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ object VehicleResponse {
|
|||
final case class MountVehicle(object_guid: PlanetSideGUID, seat: Int) extends Response
|
||||
final case class ObjectDelete(guid: PlanetSideGUID) extends Response
|
||||
final case class Ownership(vehicle_guid: PlanetSideGUID) extends Response
|
||||
final case class LoseOwnership(owner_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID)
|
||||
extends Response
|
||||
final case class PlanetsideAttribute(vehicle_guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long)
|
||||
extends Response
|
||||
final case class Reload(weapon_guid: PlanetSideGUID) extends Response
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package net.psforever.types
|
||||
|
||||
import enumeratum.{Enum, EnumEntry}
|
||||
import net.psforever.packet.PacketHelpers
|
||||
import scodec.Codec
|
||||
import scodec.codecs.uint
|
||||
|
||||
/**
|
||||
* The progress state of being a drowning victim.
|
||||
|
|
@ -22,6 +19,4 @@ object OxygenState extends Enum[OxygenState] {
|
|||
|
||||
case object Recovery extends OxygenState
|
||||
case object Suffocation extends OxygenState
|
||||
|
||||
implicit val codec: Codec[OxygenState] = PacketHelpers.createEnumCodec(e = this, uint(bits = 1))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class CommonFieldDataWithPlacementTest extends Specification {
|
|||
guid mustEqual PlanetSideGUID(3840)
|
||||
parent.isDefined mustEqual false
|
||||
data match {
|
||||
case CommonFieldDataWithPlacement(pos, com) =>
|
||||
case SmallDeployableData(CommonFieldDataWithPlacement(pos, com)) =>
|
||||
pos.coord mustEqual Vector3(4704.172f, 5546.4375f, 82.234375f)
|
||||
pos.orient mustEqual Vector3.z(272.8125f)
|
||||
com match {
|
||||
|
|
@ -31,9 +31,9 @@ class CommonFieldDataWithPlacementTest extends Specification {
|
|||
v1 mustEqual false
|
||||
v2.isEmpty mustEqual true
|
||||
v3 mustEqual false
|
||||
v4.contains(false) mustEqual true
|
||||
v4.isEmpty mustEqual true
|
||||
v5.isEmpty mustEqual true
|
||||
fguid mustEqual PlanetSideGUID(8290)
|
||||
fguid mustEqual PlanetSideGUID(4145)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -46,10 +46,10 @@ class CommonFieldDataWithPlacementTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val obj = CommonFieldDataWithPlacement(
|
||||
val obj = SmallDeployableData(CommonFieldDataWithPlacement(
|
||||
PlacementData(Vector3(4704.172f, 5546.4375f, 82.234375f), Vector3.z(272.8125f)),
|
||||
CommonFieldData(PlanetSideEmpire.TR, false, false, false, None, false, Some(false), None, PlanetSideGUID(8290))
|
||||
)
|
||||
CommonFieldData(PlanetSideEmpire.TR, bops = false, alternate = false, v1 = false, None, jammered = false, None, None, PlanetSideGUID(4145))
|
||||
))
|
||||
val msg = ObjectCreateMessage(ObjectClass.boomer, PlanetSideGUID(3840), obj)
|
||||
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
||||
pkt mustEqual string_boomer
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.NEUTRAL,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -50,9 +50,9 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.NEUTRAL,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -71,7 +71,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual DetailedWeaponData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(0)),
|
||||
0,
|
||||
List(InternalSlot(Ammo.shotgun_shell.id, PlanetSideGUID(90), 0, DetailedAmmoBoxData(8, 12)))
|
||||
)
|
||||
|
|
@ -81,7 +81,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual WeaponData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(0)),
|
||||
0,
|
||||
List(
|
||||
InternalSlot(
|
||||
|
|
@ -90,11 +90,11 @@ class ConverterTest extends Specification {
|
|||
0,
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -115,7 +115,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual DetailedWeaponData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(0)),
|
||||
0,
|
||||
List(
|
||||
InternalSlot(Ammo.bullet_9mm.id, PlanetSideGUID(90), 0, DetailedAmmoBoxData(8, 30)),
|
||||
|
|
@ -132,17 +132,17 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.NEUTRAL, //TODO need faction affinity
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
),
|
||||
0,
|
||||
List(
|
||||
InternalSlot(Ammo.bullet_9mm.id, PlanetSideGUID(90), 0, CommonFieldData()(false)),
|
||||
InternalSlot(Ammo.rocket.id, PlanetSideGUID(91), 1, CommonFieldData()(false))
|
||||
InternalSlot(Ammo.bullet_9mm.id, PlanetSideGUID(90), 0, CommonFieldData()(flag = false)),
|
||||
InternalSlot(Ammo.rocket.id, PlanetSideGUID(91), 1, CommonFieldData()(flag = false))
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
|
|
@ -164,7 +164,7 @@ class ConverterTest extends Specification {
|
|||
}
|
||||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual CommonFieldData()(false)
|
||||
pkt mustEqual CommonFieldData()(flag = false)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual DetailedConstructionToolData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(0))
|
||||
)
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -188,11 +188,11 @@ class ConverterTest extends Specification {
|
|||
pkt mustEqual HandheldData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -216,11 +216,11 @@ class ConverterTest extends Specification {
|
|||
pkt mustEqual DetailedREKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL, //TODO faction affinity
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -234,11 +234,11 @@ class ConverterTest extends Specification {
|
|||
pkt mustEqual REKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -257,7 +257,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual DetailedConstructionToolData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(0))
|
||||
)
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -265,7 +265,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual HandheldData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0))
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = false, None, jammered = false, None, None, PlanetSideGUID(0))
|
||||
)
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -282,11 +282,11 @@ class ConverterTest extends Specification {
|
|||
pkt mustEqual HandheldData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
Some(1001),
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -301,11 +301,11 @@ class ConverterTest extends Specification {
|
|||
pkt mustEqual DetailedConstructionToolData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
Some(1001),
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -333,20 +333,20 @@ class ConverterTest extends Specification {
|
|||
|
||||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual CommonFieldDataWithPlacement(
|
||||
pkt mustEqual SmallDeployableData(CommonFieldDataWithPlacement(
|
||||
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.TR,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
)
|
||||
))
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -369,12 +369,12 @@ class ConverterTest extends Specification {
|
|||
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.TR,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
jammered = false,
|
||||
None,
|
||||
false,
|
||||
Some(true),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
|
|
@ -389,11 +389,11 @@ class ConverterTest extends Specification {
|
|||
WeaponData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -406,11 +406,11 @@ class ConverterTest extends Specification {
|
|||
0,
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -444,11 +444,11 @@ class ConverterTest extends Specification {
|
|||
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.TR,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -464,11 +464,11 @@ class ConverterTest extends Specification {
|
|||
WeaponData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -481,11 +481,11 @@ class ConverterTest extends Specification {
|
|||
0,
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -519,10 +519,10 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.TR,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
v1 = true,
|
||||
None,
|
||||
jammered = false,
|
||||
None,
|
||||
false,
|
||||
Some(true),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
|
|
@ -584,9 +584,9 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.TR,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
true,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
Some(1001),
|
||||
PlanetSideGUID(5001)
|
||||
|
|
@ -616,9 +616,9 @@ class ConverterTest extends Specification {
|
|||
PlanetSideEmpire.TR,
|
||||
bops = false,
|
||||
alternate = true,
|
||||
true,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
None,
|
||||
Some(1001),
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -751,7 +751,7 @@ class ConverterTest extends Specification {
|
|||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual DetailedLockerContainerData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0)),
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = false, None, jammered = false, None, None, PlanetSideGUID(0)),
|
||||
None
|
||||
)
|
||||
case _ =>
|
||||
|
|
@ -783,11 +783,11 @@ class ConverterTest extends Specification {
|
|||
DetailedREKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -809,11 +809,11 @@ class ConverterTest extends Specification {
|
|||
REKData(
|
||||
CommonFieldData(
|
||||
PlanetSideEmpire.NEUTRAL,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = true,
|
||||
None,
|
||||
false,
|
||||
jammered = false,
|
||||
Some(false),
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -841,7 +841,7 @@ class ConverterTest extends Specification {
|
|||
|
||||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual CommonFieldData(PlanetSideEmpire.NEUTRAL)(false)
|
||||
pkt mustEqual CommonFieldData(PlanetSideEmpire.NEUTRAL)(flag = false)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -861,7 +861,7 @@ class ConverterTest extends Specification {
|
|||
|
||||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
pkt mustEqual CommonFieldData(PlanetSideEmpire.NEUTRAL)(false)
|
||||
pkt mustEqual CommonFieldData(PlanetSideEmpire.NEUTRAL)(flag = false)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,26 +21,26 @@ class DefaultTest extends Specification {
|
|||
}
|
||||
}
|
||||
|
||||
class DefaultActorStartedTest extends ActorTest {
|
||||
"Default.Actor" should {
|
||||
"send messages to deadLetters" in {
|
||||
//after being started
|
||||
val probe = new TestProbe(system)
|
||||
system.eventStream.subscribe(probe.ref, classOf[DeadLetter])
|
||||
Default.Actor ! "hello world"
|
||||
val msg1 = probe.receiveOne(5000 milliseconds)
|
||||
assert(msg1.isInstanceOf[DeadLetter])
|
||||
assert(msg1.asInstanceOf[DeadLetter].message equals "hello world")
|
||||
|
||||
//if it was stopped
|
||||
system.stop(Default.Actor)
|
||||
Default.Actor ! "hello world"
|
||||
val msg2 = probe.receiveOne(5000 milliseconds)
|
||||
assert(msg2.isInstanceOf[DeadLetter])
|
||||
assert(msg2.asInstanceOf[DeadLetter].message equals "hello world")
|
||||
}
|
||||
}
|
||||
}
|
||||
//class DefaultActorStartedTest extends ActorTest {
|
||||
// "Default.Actor" should {
|
||||
// "send messages to deadLetters" in {
|
||||
// //after being started
|
||||
// val probe = new TestProbe(system)
|
||||
// system.eventStream.subscribe(probe.ref, classOf[DeadLetter])
|
||||
// Default.Actor ! "hello world"
|
||||
// val msg1 = probe.receiveOne(5000 milliseconds)
|
||||
// assert(msg1.isInstanceOf[DeadLetter])
|
||||
// assert(msg1.asInstanceOf[DeadLetter].message equals "hello world")
|
||||
//
|
||||
// //if it was stopped
|
||||
// system.stop(Default.Actor)
|
||||
// Default.Actor ! "hello world"
|
||||
// val msg2 = probe.receiveOne(5000 milliseconds)
|
||||
// assert(msg2.isInstanceOf[DeadLetter])
|
||||
// assert(msg2.asInstanceOf[DeadLetter].message equals "hello world")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
object DefaultActorTest {
|
||||
//due to being a singleton, the original original value of the Default.Actor is cached here
|
||||
|
|
|
|||
|
|
@ -48,21 +48,14 @@ class DeployableBehaviorSetupTest extends ActorTest {
|
|||
assert(deployableList.isEmpty, "self-setup test - deployable list is not empty")
|
||||
zone.Deployables ! Zone.Deployable.Build(jmine)
|
||||
|
||||
val eventsMsgs = eventsProbe.receiveN(3, 10.seconds)
|
||||
val eventsMsgs = eventsProbe.receiveN(2, 10.seconds)
|
||||
eventsMsgs.head match {
|
||||
case LocalServiceMessage(
|
||||
"test",
|
||||
LocalAction.TriggerEffectLocation(PlanetSideGUID(0), "spawn_object_effect", Vector3(1,2,3), Vector3(4,5,6))
|
||||
) => ;
|
||||
case _ => assert(false, "self-setup test - no spawn fx")
|
||||
}
|
||||
eventsMsgs(1) match {
|
||||
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
|
||||
assert(obj eq jmine, "self-setup test - not same mine")
|
||||
case _ =>
|
||||
assert( false, "self-setup test - wrong deploy message")
|
||||
}
|
||||
eventsMsgs(2) match {
|
||||
eventsMsgs(1) match {
|
||||
case LocalServiceMessage(
|
||||
"TR",
|
||||
LocalAction.DeployableMapIcon(
|
||||
|
|
@ -251,13 +244,13 @@ class DeployableBehaviorDeconstructTest extends ActorTest {
|
|||
"DeployableBehavior" should {
|
||||
"deconstruct, by self" in {
|
||||
zone.Deployables ! Zone.Deployable.Build(jmine)
|
||||
eventsProbe.receiveN(3, 10.seconds) //all of the messages from the construction (see other testing)
|
||||
eventsProbe.receiveN(2, 10.seconds) //all of the messages from the construction (see other testing)
|
||||
assert(deployableList.contains(jmine), "deconstruct test - deployable not appended to list")
|
||||
|
||||
jmine.Actor ! Deployable.Deconstruct()
|
||||
val eventsMsgs = eventsProbe.receiveN(2, 10.seconds)
|
||||
eventsMsgs.head match {
|
||||
case LocalServiceMessage("test", LocalAction.EliminateDeployable(`jmine`, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ;
|
||||
case LocalServiceMessage("test", LocalAction.EliminateDeployable(_, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ;
|
||||
case _ => assert(false, "deconstruct test - not eliminating deployable")
|
||||
}
|
||||
eventsMsgs(1) match {
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
val guid = new NumberPoolHub(new MaxNumberSource(10))
|
||||
val eventsProbe = new TestProbe(system)
|
||||
|
||||
val j_mine = Deployables.Make(DeployedItem.jammer_mine)().asInstanceOf[ExplosiveDeployable] //guid=1
|
||||
val mine = Deployables.Make(DeployedItem.he_mine)().asInstanceOf[ExplosiveDeployable] //guid=1
|
||||
val avatar1 = Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)
|
||||
val player1 = Player(avatar1) //guid=3
|
||||
val avatar2 = Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)
|
||||
|
|
@ -335,17 +335,17 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
}
|
||||
player1.Spawn()
|
||||
player2.Spawn()
|
||||
guid.register(j_mine, 1)
|
||||
guid.register(mine, 1)
|
||||
guid.register(player1, 3)
|
||||
guid.register(player2, 4)
|
||||
guid.register(weapon, 5)
|
||||
j_mine.Zone = zone
|
||||
j_mine.OwnerGuid = player2
|
||||
mine.Zone = zone
|
||||
mine.OwnerGuid = player2
|
||||
//j_mine.OwnerName = player2.Name
|
||||
j_mine.Faction = PlanetSideEmpire.NC
|
||||
j_mine.Actor = system.actorOf(Props(classOf[MineDeployableControl], j_mine), "j-mine-control")
|
||||
mine.Faction = PlanetSideEmpire.NC
|
||||
mine.Actor = system.actorOf(Props(classOf[MineDeployableControl], mine), "j-mine-control")
|
||||
|
||||
val jMineSource = SourceEntry(j_mine)
|
||||
val jMineSource = SourceEntry(mine)
|
||||
val pSource = PlayerSource(player1)
|
||||
val projectile = weapon.Projectile
|
||||
val resolved = DamageInteraction(
|
||||
|
|
@ -353,7 +353,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
ProjectileReason(
|
||||
DamageResolution.Hit,
|
||||
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
|
||||
j_mine.DamageModel
|
||||
mine.DamageModel
|
||||
),
|
||||
Vector3(1, 0, 0)
|
||||
)
|
||||
|
|
@ -361,26 +361,53 @@ class ExplosiveDeployableJammerTest extends ActorTest {
|
|||
|
||||
"ExplosiveDeployable" should {
|
||||
"handle being jammered appropriately (no detonation)" in {
|
||||
assert(!j_mine.Destroyed)
|
||||
assert(!mine.Destroyed)
|
||||
|
||||
j_mine.Actor ! Vitality.Damage(applyDamageToJ)
|
||||
val eventMsgs = eventsProbe.receiveN(2, 200 milliseconds)
|
||||
mine.Actor ! Vitality.Damage(applyDamageToJ)
|
||||
val eventMsgs = eventsProbe.receiveN(4, 200 milliseconds)
|
||||
eventMsgs.head match {
|
||||
case Zone.HotSpot.Conflict(mineSrc, playerSrc, Vector3(1.0,0.0,0.0)) => ;
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
// eventMsgs.head match {
|
||||
// case LocalServiceMessage(
|
||||
// "NC",
|
||||
// LocalAction.DeployableMapIcon(
|
||||
// ValidPlanetSideGUID(0),
|
||||
// DeploymentAction.Dismiss,
|
||||
// DeployableInfo(ValidPlanetSideGUID(1), DeployableIcon.HEMine, Vector3.Zero, ValidPlanetSideGUID(0))
|
||||
// )
|
||||
// ) => ;
|
||||
// case _ => assert(false, "")
|
||||
// }
|
||||
eventMsgs(1) match {
|
||||
case LocalServiceMessage("test", LocalAction.Detonate(PlanetSideGUID(1), _)) => ;
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
eventMsgs(2) match {
|
||||
case LocalServiceMessage(
|
||||
"NC",
|
||||
LocalAction.DeployableMapIcon(
|
||||
ValidPlanetSideGUID(0),
|
||||
DeploymentAction.Dismiss,
|
||||
DeployableInfo(ValidPlanetSideGUID(1), DeployableIcon.DisruptorMine, Vector3.Zero, ValidPlanetSideGUID(0))
|
||||
DeployableInfo(ValidPlanetSideGUID(1), DeployableIcon.HEMine, Vector3.Zero, ValidPlanetSideGUID(0))
|
||||
)
|
||||
) => ;
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
eventMsgs(1) match {
|
||||
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, Service.defaultPlayerGUID, _)) => ;
|
||||
eventMsgs(3) match {
|
||||
case AvatarServiceMessage(
|
||||
"test",
|
||||
AvatarAction.Destroy(
|
||||
ValidPlanetSideGUID(1),
|
||||
ValidPlanetSideGUID(3),
|
||||
ValidPlanetSideGUID(0),
|
||||
Vector3.Zero
|
||||
)
|
||||
) => ;
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(j_mine.Destroyed)
|
||||
assert(mine.Destroyed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,57 +65,59 @@ class DeploymentBehavior2Test extends ActorTest {
|
|||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
//to Deploying
|
||||
obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Deploying), probe.ref)
|
||||
val reply1a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1a match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply1b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1b match {
|
||||
val reply1 = probe.receiveN(2, Duration.create(2000, "ms"))
|
||||
val reply2 = eventsProbe.receiveN(2, Duration.create(2000, "ms"))
|
||||
reply1.head match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply2.head match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
//to Deployed
|
||||
val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply2 match {
|
||||
reply1(1) match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deployed) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply2(1) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
//to Undeploying
|
||||
obj.Actor.tell(Deployment.TryDeploymentChange(DriveState.Undeploying), probe.ref)
|
||||
val reply3a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3a match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3b match {
|
||||
val reply3 = probe.receiveN(2, Duration.create(2000, "ms"))
|
||||
val reply4 = eventsProbe.receiveN(2, Duration.create(2000, "ms"))
|
||||
reply3.head match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply4.head match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
//to Mobile
|
||||
val reply4 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply4 match {
|
||||
reply3(1) match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Mobile) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply4(1) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
|
|
@ -131,57 +133,59 @@ class DeploymentBehavior3Test extends ActorTest {
|
|||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
//to Deploying
|
||||
obj.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), probe.ref)
|
||||
val reply1a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1a match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply1b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply1b match {
|
||||
val reply1 = probe.receiveN(2, Duration.create(2000, "ms"))
|
||||
val reply2 = eventsProbe.receiveN(2, Duration.create(2000, "ms"))
|
||||
reply1.head match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deploying) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply2.head match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
//to Deployed
|
||||
val reply2 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply2 match {
|
||||
reply1(1) match {
|
||||
case Deployment.CanDeploy(_, DriveState.Deployed) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply2(1) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
//to Undeploying
|
||||
obj.Actor.tell(Deployment.TryUndeploy(DriveState.Undeploying), probe.ref)
|
||||
val reply3a = probe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3a match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => true
|
||||
case _ => false
|
||||
})
|
||||
val reply3b = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply3b match {
|
||||
val reply3 = probe.receiveN(2, Duration.create(2000, "ms"))
|
||||
val reply4 = eventsProbe.receiveN(2, Duration.create(2000, "ms"))
|
||||
reply3.head match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Undeploying) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply4.head match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
//to Mobile
|
||||
val reply4 = eventsProbe.receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply4 match {
|
||||
reply3(1) match {
|
||||
case Deployment.CanUndeploy(_, DriveState.Mobile) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
reply4(1) match {
|
||||
case VehicleServiceMessage(
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)
|
||||
) =>
|
||||
true
|
||||
case _ => false
|
||||
})
|
||||
"test",
|
||||
VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero)
|
||||
) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
|
|
@ -195,16 +199,18 @@ class DeploymentBehavior4Test extends ActorTest {
|
|||
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply1.isInstanceOf[Deployment.CanNotChangeDeployment])
|
||||
assert(reply1.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
|
||||
assert(reply1.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deployed)
|
||||
reply1 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Deployed, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
|
||||
obj.Actor ! Deployment.TryDeploy(DriveState.Deployed)
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply2.isInstanceOf[Deployment.CanNotChangeDeployment])
|
||||
assert(reply2.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
|
||||
assert(reply2.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deployed)
|
||||
reply2 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Deployed, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
|
|
@ -215,18 +221,22 @@ class DeploymentBehavior5Test extends ActorTest {
|
|||
"not deploy to an undeploy state" in {
|
||||
val obj = DeploymentTest.SetUpAgent
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying)
|
||||
receiveOne(Duration.create(100, "ms")) //consume
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
|
||||
receiveOne(Duration.create(100, "ms")) //consume
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
reply1 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Undeploying, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
|
||||
obj.Actor ! Deployment.TryDeploy(DriveState.Undeploying)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Deployment.CanNotChangeDeployment])
|
||||
assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
|
||||
assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Undeploying)
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
reply2 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Undeploying, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -235,14 +245,24 @@ class DeploymentBehavior6Test extends ActorTest {
|
|||
"Deployment" should {
|
||||
"not undeploy to a deploy state" in {
|
||||
val obj = DeploymentTest.SetUpAgent
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
obj.DeploymentState = DriveState.Deployed
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
|
||||
obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying)
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
reply1 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Deploying, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
|
||||
obj.Actor ! Deployment.TryUndeploy(DriveState.Deploying)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply.isInstanceOf[Deployment.CanNotChangeDeployment])
|
||||
assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
|
||||
assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deploying)
|
||||
assert(obj.DeploymentState == DriveState.Mobile)
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
reply2 match {
|
||||
case Deployment.CanNotChangeDeployment(_, DriveState.Deploying, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
assert(obj.DeploymentState == DriveState.Deployed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, Plan
|
|||
import scala.concurrent.duration._
|
||||
|
||||
class InteractsWithZoneEnvironmentTest extends ActorTest {
|
||||
val pool1: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 10, 10, 0, 0))
|
||||
val pool2: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 10, 15, 5, 10))
|
||||
val pool3: Pool = Pool(EnvironmentAttribute.Lava, DeepSquare(5, 15, 10, 10, 5))
|
||||
val pool1: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(3, 2, 2, 0, 0))
|
||||
val pool2: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(3, 4, 2, 2, 0))
|
||||
val pool3: Pool = Pool(EnvironmentAttribute.Lava, DeepSquare(3, 2, 4, 0, 2))
|
||||
val zoneEvents: TestProbe = TestProbe()
|
||||
val testZone: Zone = {
|
||||
val testMap = new ZoneMap(name = "test-map") {
|
||||
|
|
@ -38,27 +38,26 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
|
|||
val obj = InteractsWithZoneEnvironmentTest.testObject()
|
||||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
obj.Position = Vector3(0,0,50)
|
||||
obj.Position = Vector3(10,10,0)
|
||||
|
||||
obj.zoneInteractions()
|
||||
testProbe.expectNoMessage(max = 500 milliseconds)
|
||||
}
|
||||
|
||||
"acknowledge interaction when moved into the critical region of a registered environment object (just once)" in {
|
||||
"acknowledge interaction when moved into the critical region of a registered environment object" in {
|
||||
val testProbe = TestProbe()
|
||||
val obj = InteractsWithZoneEnvironmentTest.testObject()
|
||||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.Position = Vector3(1,1,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg = testProbe.receiveOne(4.seconds)
|
||||
msg match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, s"$msg")
|
||||
}
|
||||
|
||||
obj.zoneInteractions()
|
||||
testProbe.expectNoMessage(max = 500 milliseconds)
|
||||
}
|
||||
|
|
@ -69,54 +68,48 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
|
|||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.Position = Vector3(1,1,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg1 = testProbe.receiveOne(4.seconds)
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.Position = Vector3(1,1,50)
|
||||
obj.Position = Vector3(1,1,5)
|
||||
obj.zoneInteractions()
|
||||
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg2 = testProbe.receiveOne(4.seconds)
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.StopTimer(EnvironmentAttribute.Water) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.zoneInteractions()
|
||||
testProbe.expectNoMessage(max = 500 milliseconds)
|
||||
}
|
||||
|
||||
"transition between two different critical regions when the regions that the same attribute" in {
|
||||
"transition between two different critical regions when the regions have the same attribute" in {
|
||||
val testProbe = TestProbe()
|
||||
val obj = InteractsWithZoneEnvironmentTest.testObject()
|
||||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.Position = Vector3(7,7,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg1 = testProbe.receiveOne(4.seconds)
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.Position = Vector3(12,7,2)
|
||||
obj.Position = Vector3(1, 3, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg2 = testProbe.receiveOne(4.seconds)
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ => assert(false, "")
|
||||
}
|
||||
testProbe.expectNoMessage()
|
||||
}
|
||||
|
||||
"transition between two different critical regions when the regions have different attributes" in {
|
||||
|
|
@ -125,37 +118,33 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
|
|||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.Position = Vector3(7,7,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msg1 = testProbe.receiveOne(4.seconds)
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.Position = Vector3(7,12,2)
|
||||
obj.Position = Vector3(3, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msgs = testProbe.receiveN(3, max = 250 milliseconds)
|
||||
assert(
|
||||
msgs.head match {
|
||||
case Vitality.Damage(_) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msgs(1) match {
|
||||
case AuraEffectBehavior.StartEffect(Aura.Fire, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msgs(2) match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Lava, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
val msgs = testProbe.receiveN(4, 4.seconds)
|
||||
msgs.head match {
|
||||
case RespondsToZoneEnvironment.StopTimer(EnvironmentAttribute.Water) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
msgs(1) match {
|
||||
case Vitality.Damage(_) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
msgs(2) match {
|
||||
case AuraEffectBehavior.StartEffect(Aura.Fire, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
msgs(3) match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Lava, _, _, _) => ()
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,24 +154,21 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
|
|||
obj.Zone = testZone
|
||||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.Position = Vector3(1,1,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.allowInteraction = false
|
||||
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
msg2 match {
|
||||
case RespondsToZoneEnvironment.StopTimer(EnvironmentAttribute.Water) => true
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
|
||||
obj.zoneInteractions()
|
||||
testProbe.expectNoMessage(max = 500 milliseconds)
|
||||
}
|
||||
|
|
@ -194,22 +180,22 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
|
|||
obj.Actor = testProbe.ref
|
||||
|
||||
obj.allowInteraction = false
|
||||
obj.Position = Vector3(1,1,2)
|
||||
obj.Position = Vector3(1, 1, 2.7f)
|
||||
obj.zoneInteractions()
|
||||
testProbe.expectNoMessage(max = 500 milliseconds)
|
||||
|
||||
obj.allowInteraction = true
|
||||
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
|
||||
assert(
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
msg1 match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
|
||||
case _ => assert(InteractsWithZoneEnvironmentTest.fail, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object InteractsWithZoneEnvironmentTest {
|
||||
val fail: Boolean = false
|
||||
|
||||
def testObject(): PlanetSideServerObject with InteractsWithZone = {
|
||||
val p = new Player(Avatar(1, "test", PlanetSideEmpire.VS, CharacterSex.Male, 1, CharacterVoice.Mute))
|
||||
p.GUID = PlanetSideGUID(1)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import base.{ActorTest, FreedContextActorTest}
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
|
|
@ -63,7 +63,7 @@ class ResourceSiloTest extends Specification {
|
|||
PlanetSideGUID(0),
|
||||
PlanetSideGUID(2),
|
||||
0L,
|
||||
false,
|
||||
unk3 = false,
|
||||
Vector3(0f, 0f, 0f),
|
||||
Vector3(0f, 0f, 0f),
|
||||
0,
|
||||
|
|
@ -77,11 +77,11 @@ class ResourceSiloTest extends Specification {
|
|||
}
|
||||
|
||||
class ResourceSiloControlStartupTest extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
val buildingEvents = TestProbe("test-building-events")
|
||||
val buildingEvents: TestProbe = TestProbe("test-building-events")
|
||||
obj.Owner =
|
||||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) {
|
||||
Actor = buildingEvents.ref
|
||||
|
|
@ -97,11 +97,11 @@ class ResourceSiloControlStartupTest extends ActorTest {
|
|||
}
|
||||
|
||||
class ResourceSiloControlStartupMessageNoneTest extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
val buildingEvents = TestProbe("test-building-events")
|
||||
val buildingEvents: TestProbe = TestProbe("test-building-events")
|
||||
obj.Owner =
|
||||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) {
|
||||
Actor = buildingEvents.ref
|
||||
|
|
@ -123,11 +123,11 @@ class ResourceSiloControlStartupMessageNoneTest extends ActorTest {
|
|||
}
|
||||
|
||||
class ResourceSiloControlStartupMessageSomeTest extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
val buildingEvents = TestProbe("test-building-events")
|
||||
val buildingEvents: TestProbe = TestProbe("test-building-events")
|
||||
obj.Owner =
|
||||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) {
|
||||
Actor = buildingEvents.ref
|
||||
|
|
@ -154,19 +154,19 @@ class ResourceSiloControlUseTest extends FreedContextActorTest {
|
|||
expectNoMessage(1000 milliseconds)
|
||||
var buildingMap = new TrieMap[Int, Building]()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(max = 10))
|
||||
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
|
||||
val ant = Vehicle(GlobalDefinitions.ant)
|
||||
val player: Player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
|
||||
val ant: Vehicle = Vehicle(GlobalDefinitions.ant)
|
||||
val silo = new ResourceSilo()
|
||||
val catchall = new TestProbe(system).ref
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
|
||||
override def SetupNumberPools() = {}
|
||||
val catchall: ActorRef = new TestProbe(system).ref
|
||||
val zone: Zone = new Zone("test", new ZoneMap("test-map"), 0) {
|
||||
override def SetupNumberPools(): Unit = {}
|
||||
GUID(guid)
|
||||
override def AvatarEvents = catchall
|
||||
override def LocalEvents = catchall
|
||||
override def VehicleEvents = catchall
|
||||
override def Activity = catchall
|
||||
override def Vehicles = List(ant)
|
||||
override def Buildings = { buildingMap.toMap }
|
||||
override def AvatarEvents: ActorRef = catchall
|
||||
override def LocalEvents: ActorRef = catchall
|
||||
override def VehicleEvents: ActorRef = catchall
|
||||
override def Activity: ActorRef = catchall
|
||||
override def Vehicles: List[Vehicle] = List(ant)
|
||||
override def Buildings: Map[Int, Building] = { buildingMap.toMap }
|
||||
}
|
||||
val building = new Building(
|
||||
name = "integ-fac-test-building",
|
||||
|
|
@ -186,7 +186,7 @@ class ResourceSiloControlUseTest extends FreedContextActorTest {
|
|||
guid.register(silo, number = 5)
|
||||
guid.register(building, number = 6)
|
||||
|
||||
val maxNtuCap = ant.Definition.MaxNtuCapacitor
|
||||
val maxNtuCap: Float = ant.Definition.MaxNtuCapacitor
|
||||
player.Spawn()
|
||||
ant.NtuCapacitor = maxNtuCap
|
||||
val probe = new TestProbe(system)
|
||||
|
|
@ -201,19 +201,18 @@ class ResourceSiloControlUseTest extends FreedContextActorTest {
|
|||
"Resource silo" should {
|
||||
"respond when being used" in {
|
||||
expectNoMessage(1 seconds)
|
||||
silo.Actor ! CommonMessages.Use(ResourceSiloTest.player)
|
||||
|
||||
val reply = probe.receiveOne(2000 milliseconds)
|
||||
assert(reply match {
|
||||
case TransferBehavior.Discharging(Ntu.Nanites) => true
|
||||
case _ => false
|
||||
})
|
||||
silo.Actor ! CommonMessages.Use(ResourceSiloTest.player, Some(ant))
|
||||
val reply = probe.receiveOne(3000 milliseconds)
|
||||
reply match {
|
||||
case TransferBehavior.Discharging(Ntu.Nanites) => ()
|
||||
case _ => assert(ResourceSiloTest.fail, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceSiloControlNtuWarningTest extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
|
|
@ -223,7 +222,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest {
|
|||
}
|
||||
obj.Owner.GUID = PlanetSideGUID(6)
|
||||
|
||||
val zoneEvents = TestProbe("zone-events")
|
||||
val zoneEvents: TestProbe = TestProbe("zone-events")
|
||||
zone.AvatarEvents = zoneEvents.ref
|
||||
obj.Actor ! Service.Startup()
|
||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor)
|
||||
|
|
@ -237,7 +236,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest {
|
|||
val reply = zoneEvents.receiveOne(5000 milliseconds)
|
||||
reply match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ;
|
||||
case _ => assert(false, s"$reply is wrong")
|
||||
case _ => assert(ResourceSiloTest.fail, s"$reply is wrong")
|
||||
}
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
}
|
||||
|
|
@ -245,7 +244,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest {
|
|||
}
|
||||
|
||||
class ResourceSiloControlUpdate1Test extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
|
|
@ -253,8 +252,8 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building)
|
||||
bldg.GUID = PlanetSideGUID(6)
|
||||
obj.Owner = bldg
|
||||
val zoneEvents = TestProbe("zone-events")
|
||||
val buildingEvents = TestProbe("building-events")
|
||||
val zoneEvents: TestProbe = TestProbe("zone-events")
|
||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
obj.Actor ! Service.Startup()
|
||||
|
|
@ -276,12 +275,12 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
assert(obj.CapacitorDisplay == 3)
|
||||
reply1.head match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 3)) => ;
|
||||
case _ => assert(false, s"$reply1 is wrong")
|
||||
case _ => assert(ResourceSiloTest.fail, s"$reply1 is wrong")
|
||||
}
|
||||
assert(reply2.isInstanceOf[BuildingActor.MapUpdate], s"$reply2 is wrong")
|
||||
reply1(1) match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ;
|
||||
case _ => assert(false, s"${reply1(1)} is wrong")
|
||||
case _ => assert(ResourceSiloTest.fail, s"${reply1(1)} is wrong")
|
||||
}
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
}
|
||||
|
|
@ -289,7 +288,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
}
|
||||
|
||||
class ResourceSiloControlUpdate2Test extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
|
|
@ -297,8 +296,8 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building)
|
||||
bldg.GUID = PlanetSideGUID(6)
|
||||
obj.Owner = bldg
|
||||
val zoneEvents = TestProbe("zone-events")
|
||||
val buildingEvents = TestProbe("building-events")
|
||||
val zoneEvents: TestProbe = TestProbe("zone-events")
|
||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
obj.Actor ! Service.Startup()
|
||||
|
|
@ -320,12 +319,12 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
assert(obj.CapacitorDisplay == 2)
|
||||
reply1.head match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 2)) => ;
|
||||
case _ => assert(false, s"$reply1 is wrong")
|
||||
case _ => assert(ResourceSiloTest.fail, s"$reply1 is wrong")
|
||||
}
|
||||
assert(reply2.isInstanceOf[BuildingActor.MapUpdate])
|
||||
reply1(1) match {
|
||||
case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ;
|
||||
case _ => assert(false, s"${reply1(1)} is wrong")
|
||||
case _ => assert(ResourceSiloTest.fail, s"${reply1(1)} is wrong")
|
||||
}
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
}
|
||||
|
|
@ -333,7 +332,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
}
|
||||
|
||||
class ResourceSiloControlNoUpdateTest extends ActorTest {
|
||||
val obj = ResourceSilo()
|
||||
val obj: ResourceSilo = ResourceSilo()
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
|
||||
val zone = new Zone("nowhere", new ZoneMap("nowhere-map"), 0)
|
||||
|
|
@ -341,8 +340,8 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
|||
new Building("Building", building_guid = 6, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building)
|
||||
bldg.GUID = PlanetSideGUID(6)
|
||||
obj.Owner = bldg
|
||||
val zoneEvents = TestProbe("zone-events")
|
||||
val buildingEvents = TestProbe("building-events")
|
||||
val zoneEvents: TestProbe = TestProbe("zone-events")
|
||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||
zone.AvatarEvents = zoneEvents.ref
|
||||
bldg.Actor = buildingEvents.ref
|
||||
obj.Actor ! Service.Startup()
|
||||
|
|
@ -371,7 +370,9 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
|||
}
|
||||
|
||||
object ResourceSiloTest {
|
||||
val player = Player(
|
||||
val player: Player = Player(
|
||||
Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute).copy(stamina = 0)
|
||||
)
|
||||
|
||||
val fail: Boolean = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import net.psforever.objects.vehicles.{Utility, UtilityType}
|
|||
import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap}
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID, Vector3}
|
||||
|
||||
|
|
@ -253,7 +252,7 @@ class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest {
|
|||
val deploymentProbe = new TestProbe(system)
|
||||
router.Actor.tell(Deployment.TryDeploy(DriveState.Deploying), deploymentProbe.ref)
|
||||
eventsProbe.receiveN(10, 10.seconds) //flush all messages related to deployment
|
||||
deploymentProbe.receiveOne(2.seconds) //CanDeploy
|
||||
deploymentProbe.receiveN(2, 10.seconds) //CanDeploy
|
||||
deploymentProbe.expectNoMessage(2.seconds) //intentional delay
|
||||
assert(internal.Active, "link to router test - router internals not active when expected")
|
||||
assert(!telepad.Active, "link to router test - telepad active earlier than intended (2)")
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ import net.psforever.objects.guid.NumberPoolHub
|
|||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.environment._
|
||||
import net.psforever.objects.serverobject.environment.interaction.{EscapeFromEnvironment, InteractingWithEnvironment, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.serverobject.environment.interaction.{InteractingWithEnvironment, RespondsToZoneEnvironment}
|
||||
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.sourcing.VehicleSource
|
||||
import net.psforever.objects.vehicles.VehicleLockState
|
||||
import net.psforever.objects.vehicles.control.VehicleControl
|
||||
import net.psforever.objects.vehicles.interaction.WithWater
|
||||
import net.psforever.objects.vital.{ShieldCharge, SpawningActivity, Vitality}
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.ServiceManager
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.types._
|
||||
|
||||
|
|
@ -174,15 +174,17 @@ class VehicleControlPrepareForDeletionPassengerTest extends ActorTest {
|
|||
//}
|
||||
|
||||
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
val catchall = new TestProbe(system)
|
||||
val eventsProbe = new TestProbe(system)
|
||||
val cargoProbe = new TestProbe(system)
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(10))
|
||||
ServiceManager.boot
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
GUID(guid)
|
||||
|
||||
override def SetupNumberPools(): Unit = {}
|
||||
override def VehicleEvents = vehicleProbe.ref
|
||||
override def AvatarEvents = eventsProbe.ref
|
||||
override def LocalEvents = eventsProbe.ref
|
||||
override def VehicleEvents = eventsProbe.ref
|
||||
}
|
||||
zone.actor = system.spawn(ZoneActor(zone), "test-zone-actor")
|
||||
// crappy workaround but without it the zone doesn't get initialized in time
|
||||
|
|
@ -191,7 +193,6 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
|
|||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Zone = zone
|
||||
val cargoProbe = new TestProbe(system)
|
||||
vehicle.Actor = cargoProbe.ref
|
||||
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
||||
lodestar.Faction = PlanetSideEmpire.TR
|
||||
|
|
@ -220,39 +221,45 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
|
|||
"if with mounted cargo, eject it when marked for deconstruction" in {
|
||||
lodestar.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
val vehicle_msg = vehicleProbe.receiveN(6, 500 milliseconds)
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(4), 4, true, PlanetSideGUID(2))) => ;
|
||||
val vehicleMsgs = eventsProbe.receiveN(6, 10.seconds)
|
||||
val cargoMsgs = cargoProbe.receiveN(1, 1.seconds)
|
||||
vehicleMsgs.head match {
|
||||
case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(4), 4, true, PlanetSideGUID(2))) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-1: ${vehicle_msg(5)}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-1: ${vehicleMsgs.head}")
|
||||
}
|
||||
assert(player2.VehicleSeated.isEmpty)
|
||||
assert(lodestar.Seats(0).occupant.isEmpty)
|
||||
//cargo dismounting messages
|
||||
vehicle_msg(1) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => ;
|
||||
vehicleMsgs(1) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-2: ${vehicle_msg.head}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-2: ${vehicleMsgs(1)}")
|
||||
}
|
||||
vehicle_msg(2) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => ;
|
||||
vehicleMsgs(2) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-3: ${vehicle_msg(1)}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-3: ${vehicleMsgs(2)}")
|
||||
}
|
||||
vehicle_msg(3) match {
|
||||
vehicleMsgs(3) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0))) => ;
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-4: ${vehicle_msg(2)}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-4: ${vehicleMsgs(3)}")
|
||||
}
|
||||
vehicle_msg(4) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))) => ;
|
||||
vehicleMsgs(4) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-5: ${vehicle_msg(3)}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-5: ${vehicleMsgs(4)}")
|
||||
}
|
||||
vehicle_msg(5) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0))) => ;
|
||||
vehicleMsgs(5) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0))) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-6: ${vehicle_msg(4)}")
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-6: ${vehicleMsgs(5)}")
|
||||
}
|
||||
cargoMsgs.head match {
|
||||
case Vehicle.Deconstruct(_) => ()
|
||||
case _ =>
|
||||
assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-7: ${cargoMsgs.head}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -393,7 +400,7 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
|||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
val player2 = Player(VehicleTest.avatar1.copy(basic = VehicleTest.avatar1.basic.copy(faction = PlanetSideEmpire.NC)))
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
"Vehicle Control" should {
|
||||
|
|
@ -578,67 +585,13 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
|
|||
// }
|
||||
//}
|
||||
|
||||
class VehicleControlInteractWithWaterPartialTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 =
|
||||
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
val playerProbe = TestProbe()
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
|
||||
val zone = new Zone(
|
||||
id = "test-zone",
|
||||
new ZoneMap(name = "test-map") {
|
||||
environment = List(pool)
|
||||
},
|
||||
zoneNumber = 0
|
||||
) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def LivePlayers = List(player1)
|
||||
override def Vehicles = List(vehicle)
|
||||
}
|
||||
zone.blockMap.addTo(vehicle)
|
||||
zone.blockMap.addTo(pool)
|
||||
|
||||
guid.register(player1, 1)
|
||||
guid.register(vehicle, 2)
|
||||
player1.Zone = zone
|
||||
player1.Spawn()
|
||||
vehicle.Zone = zone
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Seats(0).mount(player1)
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
player1.Actor = playerProbe.ref
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-control")
|
||||
|
||||
"VehicleControl" should {
|
||||
"causes disability when the vehicle drives too deep in water (check driver messaging)" in {
|
||||
vehicle.Position = Vector3(5,5,-3) //right in the pool
|
||||
vehicle.zoneInteractions() //trigger
|
||||
|
||||
val msg_drown = playerProbe.receiveOne(250 milliseconds)
|
||||
assert(
|
||||
msg_drown match {
|
||||
case InteractingWithEnvironment(
|
||||
p2,
|
||||
Some(OxygenStateTarget(PlanetSideGUID(2), _, OxygenState.Suffocation, 100f))
|
||||
) => (p2 eq pool)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlInteractWithWaterTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 =
|
||||
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
val avatarProbe = TestProbe()
|
||||
class VehicleControlInteractWithWaterWadingTest extends ActorTest {
|
||||
val playerProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(10, 10, 10, 0, 0))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 2, 2, 0, 0))
|
||||
val zone = new Zone(
|
||||
id = "test-zone",
|
||||
new ZoneMap(name = "test-map") {
|
||||
|
|
@ -650,53 +603,48 @@ class VehicleControlInteractWithWaterTest extends ActorTest {
|
|||
GUID(guid)
|
||||
override def LivePlayers = List(player1)
|
||||
override def Vehicles = List(vehicle)
|
||||
override def AvatarEvents = avatarProbe.ref
|
||||
override def VehicleEvents = avatarProbe.ref
|
||||
|
||||
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
|
||||
}
|
||||
zone.blockMap.addTo(vehicle)
|
||||
zone.blockMap.addTo(pool)
|
||||
|
||||
guid.register(player1, 1)
|
||||
guid.register(vehicle, 2)
|
||||
guid.register(player1.avatar.locker, 5)
|
||||
player1.Zone = zone
|
||||
player1.Spawn()
|
||||
vehicle.Zone = zone
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Seats(0).mount(player1)
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
|
||||
player1.Actor = playerProbe.ref
|
||||
vehicle.Actor = vehicleProbe.ref
|
||||
|
||||
"VehicleControl" should {
|
||||
"causes disability when the vehicle drives too deep in water" in {
|
||||
vehicle.Position = Vector3(5,5,3) //right in the pool
|
||||
vehicle.zoneInteractions() //trigger
|
||||
"report when the vehicle starts treading water" in {
|
||||
vehicle.Position = Vector3(1, 1, 6)
|
||||
vehicle.zoneInteractions()
|
||||
vehicleProbe.expectNoMessage(2.seconds)
|
||||
playerProbe.expectNoMessage()
|
||||
|
||||
val msg_drown = playerProbe.receiveOne(250 milliseconds)
|
||||
assert(msg_drown match {
|
||||
case InteractingWithEnvironment(body, _) => body eq pool
|
||||
case _ => false
|
||||
})
|
||||
val msg_disable = vehicleProbe.receiveOne(10 seconds)
|
||||
assert(msg_disable match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, VehicleControl.Disable(true)) => true
|
||||
case _ => false
|
||||
})
|
||||
vehicle.Position = Vector3(1, 1, 4f)
|
||||
vehicle.zoneInteractions()
|
||||
val vehicleMsgs = vehicleProbe.receiveN(1, 5.seconds)
|
||||
vehicleMsgs.head match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
playerProbe.expectNoMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlStopInteractWithWaterTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 =
|
||||
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
class VehicleControlInteractWithWaterStartDrowningTest extends ActorTest {
|
||||
val playerProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 2, 2, 0, 0))
|
||||
val zone = new Zone(
|
||||
id = "test-zone",
|
||||
new ZoneMap(name = "test-map") {
|
||||
|
|
@ -721,39 +669,290 @@ class VehicleControlStopInteractWithWaterTest extends ActorTest {
|
|||
vehicle.Seats(0).mount(player1)
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
player1.Actor = playerProbe.ref
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-control")
|
||||
vehicle.Actor = vehicleProbe.ref
|
||||
|
||||
"VehicleControl" should {
|
||||
"stop becoming disabled if the vehicle drives out of the water" in {
|
||||
vehicle.Position = Vector3(5,5,-3) //right in the pool
|
||||
vehicle.zoneInteractions() //trigger
|
||||
val msg_drown = playerProbe.receiveOne(250 milliseconds)
|
||||
assert(
|
||||
msg_drown match {
|
||||
case InteractingWithEnvironment(
|
||||
p2,
|
||||
Some(OxygenStateTarget(PlanetSideGUID(2), _, OxygenState.Suffocation, 100f))
|
||||
) => (p2 eq pool)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
||||
vehicle.Position = Vector3.Zero //that's enough of that
|
||||
"report when the vehicle starts drowning" in {
|
||||
vehicle.Position = Vector3(1, 1, 6)
|
||||
vehicle.zoneInteractions()
|
||||
val msg_recover = playerProbe.receiveOne(250 milliseconds)
|
||||
assert(
|
||||
msg_recover match {
|
||||
case EscapeFromEnvironment(
|
||||
p2,
|
||||
Some(OxygenStateTarget(PlanetSideGUID(2), _, OxygenState.Recovery, _))
|
||||
) => (p2 eq pool)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
vehicleProbe.expectNoMessage(2.seconds)
|
||||
playerProbe.expectNoMessage()
|
||||
|
||||
vehicle.Position = Vector3(1, 1, 0f)
|
||||
vehicle.zoneInteractions()
|
||||
val vehicleMsgs = vehicleProbe.receiveN(3, 5.seconds)
|
||||
val playerMsgs = playerProbe.receiveN(1, 1.seconds)
|
||||
vehicleMsgs.head match {
|
||||
case RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
vehicleMsgs(1) match {
|
||||
case RespondsToZoneEnvironment.Timer(WithWater.WaterAction, _, _, _) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
vehicleMsgs(2) match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
playerMsgs.head match {
|
||||
case InteractingWithEnvironment(somePool, Some(OxygenStateTarget(ValidPlanetSideGUID(2), _, OxygenState.Suffocation, 100.0f)))
|
||||
if somePool eq pool => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//class VehicleControlInteractWithWaterStopDrowningTest extends ActorTest {
|
||||
// val playerProbe = TestProbe()
|
||||
// val vehicleProbe = TestProbe()
|
||||
// val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
// val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
// val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
// val pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 2, 2, 0, 0))
|
||||
// val zone = new Zone(
|
||||
// id = "test-zone",
|
||||
// new ZoneMap(name = "test-map") {
|
||||
// environment = List(pool)
|
||||
// },
|
||||
// zoneNumber = 0
|
||||
// ) {
|
||||
// override def SetupNumberPools() = {}
|
||||
// GUID(guid)
|
||||
// override def LivePlayers = List(player1)
|
||||
// override def Vehicles = List(vehicle)
|
||||
// }
|
||||
// zone.blockMap.addTo(vehicle)
|
||||
// zone.blockMap.addTo(pool)
|
||||
//
|
||||
// guid.register(player1, 1)
|
||||
// guid.register(vehicle, 2)
|
||||
// player1.Zone = zone
|
||||
// player1.Spawn()
|
||||
// vehicle.Zone = zone
|
||||
// vehicle.Faction = PlanetSideEmpire.TR
|
||||
// vehicle.Seats(0).mount(player1)
|
||||
// player1.VehicleSeated = vehicle.GUID
|
||||
// player1.Actor = playerProbe.ref
|
||||
// vehicle.Actor = vehicleProbe.ref
|
||||
//
|
||||
// "VehicleControl" should {
|
||||
// "report when the vehicle stops drowning" in {
|
||||
// vehicle.Position = Vector3(1, 1, 6)
|
||||
// vehicle.zoneInteractions()
|
||||
// vehicleProbe.expectNoMessage(2.seconds)
|
||||
// playerProbe.expectNoMessage()
|
||||
//
|
||||
// vehicle.Position = Vector3(1, 1, 0f)
|
||||
// vehicle.zoneInteractions()
|
||||
// val vehicleMsgs = vehicleProbe.receiveN(3, 5.seconds)
|
||||
// val playerMsgs = playerProbe.receiveN(1, 1.seconds)
|
||||
// vehicleMsgs.head match {
|
||||
// case RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs(1) match {
|
||||
// case RespondsToZoneEnvironment.Timer(WithWater.WaterAction, _, _, _) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs(2) match {
|
||||
// case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// playerMsgs.head match {
|
||||
// case InteractingWithEnvironment(somePool, Some(OxygenStateTarget(ValidPlanetSideGUID(2), _, OxygenState.Suffocation, 100.0f)))
|
||||
// if somePool eq pool => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
//
|
||||
// //escape drowning
|
||||
// vehicle.Position = Vector3(1, 1, 4.7f)
|
||||
// vehicle.zoneInteractions()
|
||||
// val vehicleMsgs2 = vehicleProbe.receiveN(2, 5.seconds)
|
||||
// val playerMsgs2 = playerProbe.receiveN(1, 1.seconds)
|
||||
// vehicleMsgs2.head match {
|
||||
// case RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs2(1) match {
|
||||
// case RespondsToZoneEnvironment.Timer(WithWater.WaterAction, _, _, _) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// playerMsgs2.head match {
|
||||
// case EscapeFromEnvironment(somePool, Some(OxygenStateTarget(ValidPlanetSideGUID(2), _, OxygenState.Recovery, _)))
|
||||
// if somePool eq pool => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
class VehicleControlInteractWithWaterStopWadingTest extends ActorTest {
|
||||
val playerProbe = TestProbe()
|
||||
val vehicleProbe = TestProbe()
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
val pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 2, 2, 0, 0))
|
||||
val zone = new Zone(
|
||||
id = "test-zone",
|
||||
new ZoneMap(name = "test-map") {
|
||||
environment = List(pool)
|
||||
},
|
||||
zoneNumber = 0
|
||||
) {
|
||||
override def SetupNumberPools() = {}
|
||||
GUID(guid)
|
||||
override def LivePlayers = List(player1)
|
||||
override def Vehicles = List(vehicle)
|
||||
}
|
||||
zone.blockMap.addTo(vehicle)
|
||||
zone.blockMap.addTo(pool)
|
||||
|
||||
guid.register(player1, 1)
|
||||
guid.register(vehicle, 2)
|
||||
player1.Zone = zone
|
||||
player1.Spawn()
|
||||
vehicle.Zone = zone
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Seats(0).mount(player1)
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
player1.Actor = playerProbe.ref
|
||||
vehicle.Actor = vehicleProbe.ref
|
||||
|
||||
"VehicleControl" should {
|
||||
"report when the vehicle stops wading" in {
|
||||
vehicle.Position = Vector3(1, 1, 6)
|
||||
vehicle.zoneInteractions()
|
||||
vehicleProbe.expectNoMessage(2.seconds)
|
||||
playerProbe.expectNoMessage()
|
||||
|
||||
vehicle.Position = Vector3(1, 1, 4f)
|
||||
vehicle.zoneInteractions()
|
||||
val vehicleMsgs = vehicleProbe.receiveN(1, 5.seconds)
|
||||
playerProbe.expectNoMessage()
|
||||
vehicleMsgs.head match {
|
||||
case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
|
||||
//stop wading
|
||||
vehicle.Position = Vector3(1, 1, 6f)
|
||||
vehicle.zoneInteractions()
|
||||
val vehicleMsgs2 = vehicleProbe.receiveN(1, 5.seconds)
|
||||
playerProbe.expectNoMessage()
|
||||
vehicleMsgs2.head match {
|
||||
case RespondsToZoneEnvironment.StopTimer(EnvironmentAttribute.Water) => ()
|
||||
case _ =>
|
||||
assert(false, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//class VehicleControlInteractWithWaterFullStopTest extends ActorTest {
|
||||
// val playerProbe = TestProbe()
|
||||
// val vehicleProbe = TestProbe()
|
||||
// val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
// val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
|
||||
// val guid = new NumberPoolHub(new MaxNumberSource(15))
|
||||
// val pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 2, 2, 0, 0))
|
||||
// val zone = new Zone(
|
||||
// id = "test-zone",
|
||||
// new ZoneMap(name = "test-map") {
|
||||
// environment = List(pool)
|
||||
// },
|
||||
// zoneNumber = 0
|
||||
// ) {
|
||||
// override def SetupNumberPools() = {}
|
||||
// GUID(guid)
|
||||
// override def LivePlayers = List(player1)
|
||||
// override def Vehicles = List(vehicle)
|
||||
// }
|
||||
// zone.blockMap.addTo(vehicle)
|
||||
// zone.blockMap.addTo(pool)
|
||||
//
|
||||
// guid.register(player1, 1)
|
||||
// guid.register(vehicle, 2)
|
||||
// player1.Zone = zone
|
||||
// player1.Spawn()
|
||||
// vehicle.Zone = zone
|
||||
// vehicle.Faction = PlanetSideEmpire.TR
|
||||
// vehicle.Seats(0).mount(player1)
|
||||
// player1.VehicleSeated = vehicle.GUID
|
||||
// player1.Actor = playerProbe.ref
|
||||
// vehicle.Actor = vehicleProbe.ref
|
||||
//
|
||||
// "VehicleControl" should {
|
||||
// "report when the vehicle stops interacting with water altogether" in {
|
||||
// vehicle.Position = Vector3(1, 1, 6)
|
||||
// vehicle.zoneInteractions()
|
||||
// vehicleProbe.expectNoMessage(2.seconds)
|
||||
// playerProbe.expectNoMessage()
|
||||
// //wading and drowning
|
||||
// vehicle.Position = Vector3(1, 1, 0f)
|
||||
// vehicle.zoneInteractions()
|
||||
// val vehicleMsgs = vehicleProbe.receiveN(3, 5.seconds)
|
||||
// val playerMsgs = playerProbe.receiveN(1, 1.seconds)
|
||||
// vehicleMsgs.head match {
|
||||
// case RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs(1) match {
|
||||
// case RespondsToZoneEnvironment.Timer(WithWater.WaterAction, _, _, _) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs(2) match {
|
||||
// case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// playerMsgs.head match {
|
||||
// case InteractingWithEnvironment(somePool, Some(OxygenStateTarget(ValidPlanetSideGUID(2), _, OxygenState.Suffocation, 100.0f)))
|
||||
// if somePool eq pool => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
//
|
||||
// //escape drowning and wading
|
||||
// vehicle.Position = Vector3(1, 1, 6f)
|
||||
// vehicle.zoneInteractions()
|
||||
// val vehicleMsgs2 = vehicleProbe.receiveN(2, 5.seconds)
|
||||
// val playerMsgs2 = playerProbe.receiveN(1, 1.seconds)
|
||||
// vehicleMsgs2.head match {
|
||||
// case RespondsToZoneEnvironment.StopTimer(WithWater.WaterAction) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// vehicleMsgs2(1) match {
|
||||
// case RespondsToZoneEnvironment.StopTimer(EnvironmentAttribute.Water) => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// playerMsgs2.head match {
|
||||
// case EscapeFromEnvironment(somePool, Some(OxygenStateTarget(ValidPlanetSideGUID(2), _, OxygenState.Recovery, _)))
|
||||
// if somePool eq pool => ()
|
||||
// case _ =>
|
||||
// assert(false, "")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
class VehicleControlInteractWithLavaTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury) //guid=2
|
||||
val player1 =
|
||||
|
|
|
|||
Loading…
Reference in a new issue