diff --git a/common/src/main/scala/net/psforever/objects/Deployables.scala b/common/src/main/scala/net/psforever/objects/Deployables.scala
index eea8be37..de1c9ce0 100644
--- a/common/src/main/scala/net/psforever/objects/Deployables.scala
+++ b/common/src/main/scala/net/psforever/objects/Deployables.scala
@@ -22,7 +22,8 @@ object Deployables {
DeployedItem.portable_manned_turret_nc -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_nc) },
DeployedItem.portable_manned_turret_tr -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) },
DeployedItem.portable_manned_turret_vs -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs) },
- DeployedItem.deployable_shield_generator -> { ()=> new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator) }
+ DeployedItem.deployable_shield_generator -> { ()=> new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator) },
+ DeployedItem.router_telepad_deployable -> { () => new TelepadDeployable(GlobalDefinitions.router_telepad_deployable) }
).withDefaultValue( { ()=> new ExplosiveDeployable(GlobalDefinitions.boomer) } )
}
}
diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 6515c43d..622a7482 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -642,6 +642,8 @@ object GlobalDefinitions {
val advanced_ace = ConstructionItemDefinition(CItem.advanced_ace)
+ val router_telepad = ConstructionItemDefinition(CItem.router_telepad)
+
val fury_weapon_systema = ToolDefinition(ObjectClass.fury_weapon_systema)
val quadassault_weapon_system = ToolDefinition(ObjectClass.quadassault_weapon_system)
@@ -859,6 +861,10 @@ object GlobalDefinitions {
val portable_manned_turret_vs = TurretDeployableDefinition(DeployedItem.portable_manned_turret_vs)
val deployable_shield_generator = new ShieldGeneratorDefinition
+
+ val router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
+
+ val internal_router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
init_deployables()
/*
@@ -894,6 +900,8 @@ object GlobalDefinitions {
val respawn_tube_tower = new SpawnTubeDefinition(733)
+ val teleportpad_terminal = new TeleportPadTerminalDefinition
+
val adv_med_terminal = new MedicalTerminalDefinition(38)
val crystals_health_a = new MedicalTerminalDefinition(225)
@@ -4059,6 +4067,13 @@ object GlobalDefinitions {
advanced_ace.Modes(2).Item(DeployedItem.deployable_shield_generator -> Set(CertificationType.AssaultEngineering))
advanced_ace.Tile = InventoryTile.Tile93
+ router_telepad.Name = "router_telepad"
+ router_telepad.Size = EquipmentSize.Pistol
+ router_telepad.Modes += new ConstructionFireMode
+ router_telepad.Modes.head.Item(DeployedItem.router_telepad_deployable -> Set(CertificationType.GroundSupport))
+ router_telepad.Tile = InventoryTile.Tile33
+ router_telepad.Packet = new TelepadConverter
+
fury_weapon_systema.Name = "fury_weapon_systema"
fury_weapon_systema.Size = EquipmentSize.VehicleWeapon
fury_weapon_systema.AmmoTypes += hellfire_ammo
@@ -5121,6 +5136,8 @@ object GlobalDefinitions {
router.MaxShields = 800 + 1
router.Seats += 0 -> new SeatDefinition()
router.MountPoints += 1 -> 0
+ router.Utilities += 1 -> UtilityType.teleportpad_terminal
+ router.Utilities += 2 -> UtilityType.internal_router_telepad_deployable
router.TrunkSize = InventoryTile.Tile1511
router.TrunkOffset = 30
router.Deployment = true
@@ -5515,5 +5532,16 @@ object GlobalDefinitions {
deployable_shield_generator.MaxHealth = 1700
deployable_shield_generator.DeployTime = Duration.create(6000, "ms")
deployable_shield_generator.Model = StandardResolutions.ComplexDeployables
+
+ router_telepad_deployable.Name = "router_telepad_deployable"
+ router_telepad_deployable.MaxHealth = 100
+ router_telepad_deployable.DeployTime = Duration.create(1, "ms")
+ router_telepad_deployable.Packet = new TelepadDeployableConverter
+ router_telepad_deployable.Model = StandardResolutions.SimpleDeployables
+
+ internal_router_telepad_deployable.Name = "router_telepad_deployable"
+ internal_router_telepad_deployable.MaxHealth = 1
+ internal_router_telepad_deployable.DeployTime = Duration.create(1, "ms")
+ internal_router_telepad_deployable.Packet = new InternalTelepadDeployableConverter
}
}
diff --git a/common/src/main/scala/net/psforever/objects/Telepad.scala b/common/src/main/scala/net/psforever/objects/Telepad.scala
new file mode 100644
index 00000000..d4bfb523
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/Telepad.scala
@@ -0,0 +1,14 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects
+
+import net.psforever.objects.ce.TelepadLike
+import net.psforever.objects.definition.ConstructionItemDefinition
+
+class Telepad(private val cdef : ConstructionItemDefinition) extends ConstructionItem(cdef)
+ with TelepadLike
+
+object Telepad {
+ def apply(cdef : ConstructionItemDefinition) : Telepad = {
+ new Telepad(cdef)
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/scala/net/psforever/objects/TelepadDeployable.scala b/common/src/main/scala/net/psforever/objects/TelepadDeployable.scala
new file mode 100644
index 00000000..3916384b
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/TelepadDeployable.scala
@@ -0,0 +1,8 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects
+
+import net.psforever.objects.ce.{SimpleDeployable, TelepadLike}
+import net.psforever.objects.definition.DeployableDefinition
+
+class TelepadDeployable(ddef : DeployableDefinition) extends SimpleDeployable(ddef)
+ with TelepadLike
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index 9306468d..249923ba 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -378,7 +378,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
def Utilities : Map[Int, Utility] = utilities
/**
- * Get a referenece ot a certain `Utility` attached to this `Vehicle`.
+ * Get a reference to a certain `Utility` attached to this `Vehicle`.
* @param utilNumber the attachment number of the `Utility`
* @return the `Utility` or `None` (if invalid)
*/
@@ -396,6 +396,15 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
}
+ def Utility(utilType : UtilityType.Value) : Option[PlanetSideServerObject] = {
+ utilities.values.find(_.UtilType == utilType) match {
+ case Some(util) =>
+ Some(util())
+ case None =>
+ None
+ }
+ }
+
override def DeployTime = Definition.DeployTime
override def UndeployTime = Definition.UndeployTime
diff --git a/common/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala b/common/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala
index 972fc9e0..c3e3358c 100644
--- a/common/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala
+++ b/common/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala
@@ -29,12 +29,13 @@ class DeployableToolbox {
* keys: categories, values: quantity storage object
*/
private val categoryCounts = DeployableCategory.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
- //)
+ categoryCounts(DeployableCategory.Telepads).Max = 1024
/**
* a map of bins for keeping track of the quantities of individual deployables
* keys: deployable types, values: quantity storage object
*/
private val deployableCounts = DeployedItem.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
+ deployableCounts(DeployedItem.router_telepad_deployable).Max = 1024
/**
* a map of tracked/owned individual deployables
* keys: categories, values: deployable objects
@@ -523,8 +524,8 @@ object DeployableToolbox {
}
}
if(certifications.contains(CertificationType.GroundSupport)) {
- counts(DeployedItem.router_telepad_deployable).Max = 1
- categories(DeployableCategory.Telepads).Max = 1
+ counts(DeployedItem.router_telepad_deployable).Max = 1024
+ categories(DeployableCategory.Telepads).Max = 1024
}
}
@@ -589,9 +590,9 @@ object DeployableToolbox {
AddToDeployableQuantities(counts, categories, FortificationEngineering, certificationSet ++ Set(FortificationEngineering))
}
- case GroundSupport =>
- counts(DeployedItem.router_telepad_deployable).Max = 1024
- categories(DeployableCategory.Telepads).Max = 1024
+// case GroundSupport =>
+// counts(DeployedItem.router_telepad_deployable).Max = 1024
+// categories(DeployableCategory.Telepads).Max = 1024
case _ => ;
}
@@ -657,9 +658,9 @@ object DeployableToolbox {
RemoveFromDeployablesQuantities(counts, categories, FortificationEngineering, certificationSet)
}
- case GroundSupport =>
- counts(DeployedItem.router_telepad_deployable).Max = 0
- categories(DeployableCategory.Telepads).Max = 0
+// case GroundSupport =>
+// counts(DeployedItem.router_telepad_deployable).Max = 0
+// categories(DeployableCategory.Telepads).Max = 0
case _ => ;
}
diff --git a/common/src/main/scala/net/psforever/objects/ce/Deployable.scala b/common/src/main/scala/net/psforever/objects/ce/Deployable.scala
index d04c6b01..e72c7544 100644
--- a/common/src/main/scala/net/psforever/objects/ce/Deployable.scala
+++ b/common/src/main/scala/net/psforever/objects/ce/Deployable.scala
@@ -118,7 +118,8 @@ object Deployable {
DeployedItem.portable_manned_turret_tr.id -> DeployableIcon.FieldTurret,
DeployedItem.portable_manned_turret_nc.id -> DeployableIcon.FieldTurret,
DeployedItem.portable_manned_turret_vs.id -> DeployableIcon.FieldTurret,
- DeployedItem.deployable_shield_generator.id -> DeployableIcon.AegisShieldGenerator
+ DeployedItem.deployable_shield_generator.id -> DeployableIcon.AegisShieldGenerator,
+ DeployedItem.router_telepad_deployable.id -> DeployableIcon.RouterTelepad
).withDefaultValue(DeployableIcon.Boomer)
}
diff --git a/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala b/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala
new file mode 100644
index 00000000..8c2bc3db
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala
@@ -0,0 +1,85 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.ce
+
+import akka.actor.ActorContext
+import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
+import net.psforever.objects.serverobject.structures.Amenity
+import net.psforever.objects.vehicles.Utility
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.PlanetSideGUID
+
+trait TelepadLike {
+ private var router : Option[PlanetSideGUID] = None
+ private var activated : Boolean = false
+
+ def Router : Option[PlanetSideGUID] = router
+
+ def Router_=(rguid : PlanetSideGUID) : Option[PlanetSideGUID] = Router_=(Some(rguid))
+
+ def Router_=(rguid : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
+ router match {
+ case None =>
+ router = rguid
+ case Some(_) =>
+ if(rguid.isEmpty || rguid.contains(PlanetSideGUID(0))) {
+ router = None
+ }
+ }
+ Router
+ }
+
+ def Active : Boolean = activated
+
+ def Active_=(state : Boolean) : Boolean = {
+ activated = state
+ Active
+ }
+}
+
+object TelepadLike {
+ final case class Activate(obj : PlanetSideGameObject with TelepadLike)
+
+ final case class Deactivate(obj : PlanetSideGameObject with TelepadLike)
+
+ /**
+ * Assemble some logic for a provided object.
+ * @param obj an `Amenity` object;
+ * anticipating a `Terminal` object using this same definition
+ * @param context hook to the local `Actor` system
+ */
+ def Setup(obj : Amenity, context : ActorContext) : Unit = {
+ obj.asInstanceOf[TelepadLike].Router = obj.Owner.GUID
+ }
+
+ /**
+ * An analysis of the active system of teleportation utilized by Router vehicles.
+ * Information about the two endpoints - an internal telepad and a remote telepad - are collected, if they are applicable.
+ * The vehicle "Router" itself must be in the drive state of `Deployed`.
+ * @param router the vehicle that serves as the container of an internal telepad unit
+ * @param zone where the router is located
+ * @return the pair of units that compose the teleportation system
+ */
+ def AppraiseTeleportationSystem(router : Vehicle, zone : Zone) : Option[(Utility.InternalTelepad, TelepadDeployable)] = {
+ import net.psforever.objects.vehicles.UtilityType
+ import net.psforever.types.DriveState
+ router.Utility(UtilityType.internal_router_telepad_deployable) match {
+ //if the vehicle has an internal telepad, it is allowed to be a Router (that's a weird way of saying it)
+ case Some(util : Utility.InternalTelepad) =>
+ //check for a readied remote telepad
+ zone.GUID(util.Telepad) match {
+ case Some(telepad : TelepadDeployable) =>
+ //determine whether to activate both the Router's internal telepad and the deployed remote telepad
+ if(router.DeploymentState == DriveState.Deployed && util.Active && telepad.Active) {
+ Some((util, telepad))
+ }
+ else {
+ None
+ }
+ case _ =>
+ None
+ }
+ case _ =>
+ None
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/InternalTelepadDeployableConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/InternalTelepadDeployableConverter.scala
new file mode 100644
index 00000000..a2b50cf9
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/InternalTelepadDeployableConverter.scala
@@ -0,0 +1,14 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.ce.TelepadLike
+import net.psforever.packet.game.objectcreate._
+
+import scala.util.{Success, Try}
+
+class InternalTelepadDeployableConverter extends ObjectCreateConverter[PlanetSideGameObject with TelepadLike]() {
+ override def ConstructorData(obj : PlanetSideGameObject with TelepadLike) : Try[ContainedTelepadDeployableData] = {
+ Success(ContainedTelepadDeployableData(101, obj.Router.get))
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/TelepadConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/TelepadConverter.scala
new file mode 100644
index 00000000..4a0b9094
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/TelepadConverter.scala
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.Telepad
+import net.psforever.packet.game.objectcreate._
+
+import scala.util.{Failure, Success, Try}
+
+class TelepadConverter extends ObjectCreateConverter[Telepad]() {
+ override def ConstructorData(obj : Telepad) : Try[TelepadData] = {
+ obj.Router match {
+ case Some(_) =>
+ Success(TelepadData (0, obj.Router))
+ case None =>
+ Failure(new IllegalStateException("TelepadConverter: telepad needs to know id of its router"))
+ }
+ }
+
+ override def DetailedConstructorData(obj : Telepad) : Try[DetailedTelepadData] = {
+ obj.Router match {
+ case Some(_) =>
+ Success(DetailedTelepadData (0, obj.Router))
+ case None =>
+ Failure(new IllegalStateException("TelepadConverter: telepad needs to know id of its router"))
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/TelepadDeployableConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/TelepadDeployableConverter.scala
new file mode 100644
index 00000000..b424b070
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/TelepadDeployableConverter.scala
@@ -0,0 +1,46 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.definition.converter
+
+import net.psforever.objects.TelepadDeployable
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.packet.game.objectcreate._
+
+import scala.util.{Failure, Success, Try}
+
+class TelepadDeployableConverter extends ObjectCreateConverter[TelepadDeployable]() {
+ override def ConstructorData(obj : TelepadDeployable) : Try[TelepadDeployableData] = {
+ if(obj.Router.isEmpty || obj.Router.contains(PlanetSideGUID(0))) {
+ Failure(new IllegalStateException("TelepadDeployableConverter: telepad deployable needs to know id of its router"))
+ }
+ else {
+ if(obj.Health > 0) {
+ Success(TelepadDeployableData(
+ PlacementData(obj.Position, obj.Orientation),
+ obj.Faction,
+ bops = false,
+ destroyed = false,
+ unk1 = 2,
+ unk2 = true,
+ obj.Router.get,
+ obj.Owner.getOrElse(PlanetSideGUID(0)),
+ unk3 = 87,
+ unk4 = 12
+ ))
+ }
+ else {
+ Success(TelepadDeployableData(
+ PlacementData(obj.Position, obj.Orientation),
+ obj.Faction,
+ bops = false,
+ destroyed = true,
+ unk1 = 2,
+ unk2 = true,
+ obj.Router.get,
+ owner_guid = PlanetSideGUID(0),
+ unk3 = 0,
+ unk4 = 6
+ ))
+ }
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
index ceaf90c1..1b0b3797 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
@@ -212,6 +212,10 @@ object EquipmentTerminalDefinition {
"command_detonater" -> MakeSimpleItem(command_detonater),
"flail_targeting_laser" -> MakeSimpleItem(flail_targeting_laser)
)
+ /**
+ * A single-element `Map` of the one piece of `Equipment` specific to the Router.
+ */
+ val routerTerminal : Map[String, () => Equipment] = Map("router_telepad" -> MakeTelepad(router_telepad))
/**
* Create a new `Tool` from provided `EquipmentDefinition` objects.
@@ -334,6 +338,13 @@ object EquipmentTerminalDefinition {
*/
private def MakeConstructionItem(cdef : ConstructionItemDefinition)() : ConstructionItem = ConstructionItem(cdef)
+ /**
+ * na
+ * @param cdef na
+ * @return na
+ */
+ private def MakeTelepad(cdef : ConstructionItemDefinition)() : Telepad = Telepad(cdef)
+
/**
* Accept a simplified blueprint for some piece of `Equipment` and create an actual piece of `Equipment` based on it.
* Used specifically for the reconstruction of `Equipment` via an `Loadout`.
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TeleportPadTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TeleportPadTerminalDefinition.scala
new file mode 100644
index 00000000..4dd9872e
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TeleportPadTerminalDefinition.scala
@@ -0,0 +1,30 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.terminals
+
+import akka.actor.ActorContext
+import net.psforever.objects.Player
+import net.psforever.objects.serverobject.structures.Amenity
+import net.psforever.packet.game.ItemTransactionMessage
+
+class TeleportPadTerminalDefinition extends EquipmentTerminalDefinition(853) {
+ Name = "teleport_pad_terminal"
+
+ def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ Terminal.BuyEquipment(EquipmentTerminalDefinition.routerTerminal("router_telepad")())
+ }
+}
+
+object TeleportPadTerminalDefinition {
+ /**
+ * Assemble some logic for a provided object.
+ * @param obj an `Amenity` object;
+ * anticipating a `Terminal` object using this same definition
+ * @param context hook to the local `Actor` system
+ */
+ def Setup(obj : Amenity, context : ActorContext) : Unit = {
+ import akka.actor.{ActorRef, Props}
+ if(obj.Actor == ActorRef.noSender) {
+ obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
index 6aab6d0d..387660a0 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
@@ -40,13 +40,13 @@ class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
if(Faction == player.Faction || HackedBy.isDefined) {
msg.transaction_type match {
case TransactionType.Buy | TransactionType.Learn =>
- tdef.Buy(player, msg)
+ Buy(player, msg)
case TransactionType.Sell =>
- tdef.Sell(player, msg)
+ Sell(player, msg)
case TransactionType.Loadout =>
- tdef.Loadout(player, msg)
+ Loadout(player, msg)
case _ =>
Terminal.NoDeal()
@@ -57,6 +57,18 @@ class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
}
}
+ def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ tdef.Buy(player, msg)
+ }
+
+ def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ tdef.Sell(player, msg)
+ }
+
+ def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ tdef.Loadout(player, msg)
+ }
+
def Definition : TerminalDefinition = tdef
}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala
index dc7dfe27..ee984b87 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala
@@ -2,10 +2,13 @@
package net.psforever.objects.vehicles
import akka.actor.ActorContext
-import net.psforever.objects.{GlobalDefinitions, Vehicle}
+import net.psforever.objects.definition.DeployableDefinition
+import net.psforever.objects._
+import net.psforever.objects.ce.TelepadLike
import net.psforever.objects.serverobject.structures.Amenity
-import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, OrderTerminalABDefinition, Terminal, TerminalDefinition}
+import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeDefinition}
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
/**
* An `Enumeration` of the available vehicular utilities.
@@ -21,7 +24,9 @@ object UtilityType extends Enumeration {
ams_respawn_tube,
matrix_terminalc,
order_terminala,
- order_terminalb
+ order_terminalb,
+ teleportpad_terminal,
+ internal_router_telepad_deployable
= Value
}
@@ -94,13 +99,17 @@ object Utility {
new TerminalUtility(GlobalDefinitions.order_terminala)
case UtilityType.order_terminalb =>
new TerminalUtility(GlobalDefinitions.order_terminalb)
+ case UtilityType.teleportpad_terminal =>
+ new TeleportPadTerminalUtility(GlobalDefinitions.teleportpad_terminal)
+ case UtilityType.internal_router_telepad_deployable =>
+ new InternalTelepad(GlobalDefinitions.internal_router_telepad_deployable)
}
/**
* Override for `SpawnTube` objects so that they inherit the spatial characteristics of their `Owner`.
* @param tubeDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
- private class SpawnTubeUtility(tubeDef : SpawnTubeDefinition) extends SpawnTube(tubeDef) {
+ class SpawnTubeUtility(tubeDef : SpawnTubeDefinition) extends SpawnTube(tubeDef) {
override def Position = Owner.Position
override def Orientation = Owner.Orientation
}
@@ -109,11 +118,60 @@ object Utility {
* Override for `Terminal` objects so that they inherit the spatial characteristics of their `Owner`.
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
- private class TerminalUtility(tdef : TerminalDefinition) extends Terminal(tdef) {
+ class TerminalUtility(tdef : TerminalDefinition) extends Terminal(tdef) {
override def Position = Owner.Position
override def Orientation = Owner.Orientation
}
+ /**
+ * na
+ * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
+ */
+ class TeleportPadTerminalUtility(tdef : TerminalDefinition) extends TerminalUtility(tdef) {
+ /**
+ * na
+ * @param player na
+ * @param msg na
+ * @return na
+ */
+ override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
+ val reply = super.Buy(player, msg)
+ reply match {
+ case Terminal.BuyEquipment(obj : Telepad) =>
+ obj.Router = Owner.GUID
+ case _ => ;
+ }
+ reply
+ }
+ }
+
+ /**
+ * The internal telepad is a component that is contained by the Router when it deploys
+ * and allows it to serve as one of the terminal points of a Router-telepad teleportation system.
+ * @param ddef na
+ */
+ class InternalTelepad(ddef : DeployableDefinition) extends Amenity
+ with TelepadLike {
+ /** a link to the telepad that serves as the other endpoint of this teleportation system */
+ private var activeTelepad : Option[PlanetSideGUID] = None
+
+ def Telepad : Option[PlanetSideGUID] = activeTelepad
+
+ def Telepad_=(rguid : PlanetSideGUID) : Option[PlanetSideGUID] = Telepad_=(Some(rguid))
+
+ def Telepad_=(rguid : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
+ activeTelepad = rguid
+ Telepad
+ }
+
+ override def Position = Owner.Position
+ override def Orientation = Owner.Orientation
+ /** the router is the owner */
+ override def Router : Option[PlanetSideGUID] = Some(Owner.GUID)
+
+ def Definition = ddef
+ }
+
/**
* Provide the called-out object's logic.
* @param util the type of the `Amenity` object
@@ -128,5 +186,11 @@ object Utility {
OrderTerminalABDefinition.Setup
case UtilityType.order_terminalb =>
OrderTerminalABDefinition.Setup
+ case UtilityType.teleportpad_terminal =>
+ TeleportPadTerminalDefinition.Setup
+ case UtilityType.internal_router_telepad_deployable =>
+ TelepadLike.Setup
}
+
+ //private def defaultSetup(o1 : Amenity, o2 : ActorContext) : Unit = { }
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
index 1690ff21..4cb2f77c 100644
--- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -232,6 +232,21 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}
}
+ /**
+ * Recover an object from the globally unique identifier system by the number that was assigned previously.
+ * @param object_guid the globally unique identifier requested
+ * @return the associated object, if it exists
+ * @see `GUID(Int)`
+ */
+ def GUID(object_guid : Option[PlanetSideGUID]) : Option[PlanetSideGameObject] = {
+ object_guid match {
+ case Some(oguid) =>
+ GUID(oguid.guid)
+ case None =>
+ None
+ }
+ }
+
/**
* Recover an object from the globally unique identifier system by the number that was assigned previously.
* @param object_guid the globally unique identifier requested
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ContainedTelepadDeployableData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ContainedTelepadDeployableData.scala
new file mode 100644
index 00000000..2d7ff06e
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ContainedTelepadDeployableData.scala
@@ -0,0 +1,37 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import net.psforever.packet.game.PlanetSideGUID
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * na
+ */
+final case class ContainedTelepadDeployableData(unk : Int,
+ router_guid : PlanetSideGUID) extends ConstructorData {
+ override def bitsize : Long = 59L
+}
+
+object ContainedTelepadDeployableData extends Marshallable[ContainedTelepadDeployableData] {
+ implicit val codec : Codec[ContainedTelepadDeployableData] = (
+ ("unk" | uint(7)) ::
+ ("router_guid" | PlanetSideGUID.codec) ::
+ uint16 ::
+ uint4 ::
+ uint16
+ ).exmap[ContainedTelepadDeployableData] (
+ {
+ case unk :: rguid :: 0 :: 8 :: 0 :: HNil =>
+ Attempt.successful(ContainedTelepadDeployableData(unk, rguid))
+ case _ :: _ :: _ :: _ :: _ :: HNil =>
+ Attempt.failure(Err("invalid rek data format"))
+ },
+ {
+ case ContainedTelepadDeployableData(unk, rguid) =>
+ Attempt.successful(unk :: rguid :: 0 :: 8 :: 0 :: HNil)
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedTelepadData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedTelepadData.scala
new file mode 100644
index 00000000..230fb666
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedTelepadData.scala
@@ -0,0 +1,48 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import net.psforever.packet.game.PlanetSideGUID
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * A representation of the telepad portion of `ObjectCreateDetailedMessage` packet data.
+ * This data will help construct the "cosntruction tool"
+ * that can be obtained from the Router vehicle - the Router telepad.
+ * It issued to construct a bidirectional teleportation point associated with a Router if that Router is deployed.
+ * @param unk na
+ * @param router_guid the Router
+ */
+final case class DetailedTelepadData(unk : Int, router_guid : Option[PlanetSideGUID]) extends ConstructorData {
+ override def bitsize : Long = {
+ val rguidSize = if(router_guid.nonEmpty) 16 else 0
+ 51L + rguidSize
+ }
+}
+
+object DetailedTelepadData extends Marshallable[DetailedTelepadData] {
+ def apply(unk : Int) : DetailedTelepadData = DetailedTelepadData(unk, None)
+
+ def apply(unk : Int, router_guid : PlanetSideGUID) : DetailedTelepadData = DetailedTelepadData(unk, Some(router_guid))
+
+ implicit val codec : Codec[DetailedTelepadData] = (
+ ("unk" | uint(6)) ::
+ optional(bool, "router_guid" | PlanetSideGUID.codec) ::
+ uint(24) ::
+ uint(18) ::
+ uint2
+ ).exmap[DetailedTelepadData] (
+ {
+ case unk :: rguid :: 1 :: 1 :: 0 :: HNil =>
+ Attempt.successful(DetailedTelepadData(unk, rguid))
+ case _ =>
+ Attempt.failure(Err("invalid detailed telepad format"))
+ },
+ {
+ case DetailedTelepadData(unk, rguid) =>
+ Attempt.successful(unk :: rguid :: 1 :: 1 :: 0 :: HNil)
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index 5f32d07f..c4766570 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -230,8 +230,6 @@ object ObjectClass {
final val repeater = 730
final val rocklet = 737
final val rotarychaingun_mosquito = 740
- final val router_telepad = 743
- final val router_telepad_deployable = 744
final val scythe = 747
final val six_shooter = 761
final val skyguard_weapon_system = 788
@@ -276,7 +274,7 @@ object ObjectClass {
final val nano_dispenser = 577
final val command_detonater = 213
final val flail_targeting_laser = 297
- //ace deployables
+ //deployables
final val ace = 32
final val advanced_ace = 39
final val boomer = 148
@@ -284,6 +282,8 @@ object ObjectClass {
final val he_mine = 388
final val jammer_mine = 420
final val motionalarmsensor = 575
+ final val router_telepad = 743
+ final val router_telepad_deployable = 744
final val sensor_shield = 752
final val spitfire_aa = 819
final val spitfire_cloaked = 825
@@ -602,7 +602,6 @@ object ObjectClass {
case ObjectClass.repeater => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.rocklet => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
// case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
- case ObjectClass.router_telepad => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon") //TODO belongs here?
case ObjectClass.scythe => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
// case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
case ObjectClass.spiker => ConstructorData.genericCodec(DetailedWeaponData.codec, "weapon")
@@ -660,11 +659,11 @@ object ObjectClass {
case ObjectClass.medicalapplicator => ConstructorData.genericCodec(DetailedWeaponData.codec, "tool")
case ObjectClass.nano_dispenser => ConstructorData.genericCodec(DetailedWeaponData.codec, "tool")
case ObjectClass.remote_electronics_kit => ConstructorData.genericCodec(DetailedREKData.codec, "tool")
- //case ObjectClass.router_telepad => ConstructorData.genericCodec(*.codec, "tool") //TODO
case ObjectClass.trek => ConstructorData.genericCodec(DetailedWeaponData.codec, "tool")
//ace deployable
case ObjectClass.ace => ConstructorData.genericCodec(DetailedACEData.codec, "ace")
case ObjectClass.advanced_ace => ConstructorData.genericCodec(DetailedACEData.codec, "advanced ace")
+ case ObjectClass.router_telepad => ConstructorData.genericCodec(DetailedTelepadData.codec, "router telepad")
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
//other
case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(false), "avatar")
@@ -951,11 +950,12 @@ object ObjectClass {
case ObjectClass.medicalapplicator => ConstructorData.genericCodec(WeaponData.codec, "tool")
case ObjectClass.nano_dispenser => ConstructorData.genericCodec(WeaponData.codec, "tool")
case ObjectClass.remote_electronics_kit => ConstructorData.genericCodec(REKData.codec, "tool")
- //case ObjectClass.router_telepad => ConstructorData.genericCodec(WeaponData.codec, "tool") //TODO
case ObjectClass.trek => ConstructorData.genericCodec(WeaponData.codec, "tool")
- //ace deployables
+ //deployables
case ObjectClass.ace => ConstructorData.genericCodec(ACEData.codec, "ace")
case ObjectClass.advanced_ace => ConstructorData.genericCodec(ACEData.codec, "advanced ace")
+ case ObjectClass.router_telepad => ConstructorData.genericCodec(TelepadData.codec, "router telepad")
+ case ObjectClass.router_telepad_deployable => ConstructorData.genericCodec(ContainedTelepadDeployableData.codec, "router telepad")
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
//vehicles?
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
@@ -1182,11 +1182,11 @@ object ObjectClass {
case ObjectClass.medicalapplicator => DroppedItemData.genericCodec(WeaponData.codec, "tool")
case ObjectClass.nano_dispenser => DroppedItemData.genericCodec(WeaponData.codec, "tool")
case ObjectClass.remote_electronics_kit => DroppedItemData.genericCodec(REKData.codec, " tool")
- //case ObjectClass.router_telepad => DroppedItemData.genericCodec(WeaponData.codec, "tool") //TODO
case ObjectClass.trek => DroppedItemData.genericCodec(WeaponData.codec, "tool")
- //ace deployables
+ //deployables
case ObjectClass.ace => DroppedItemData.genericCodec(ACEData.codec, "ace")
- case ObjectClass.advanced_ace => DroppedItemData.genericCodec(ACEData.codec, "advanced ace") //todo temporary?
+ case ObjectClass.advanced_ace => DroppedItemData.genericCodec(ACEData.codec, "advanced ace")
+ case ObjectClass.router_telepad => DroppedItemData.genericCodec(TelepadData.codec, "router telepad") //TODO not correct
case ObjectClass.boomer_trigger => DroppedItemData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
case ObjectClass.boomer => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
case ObjectClass.he_mine => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
@@ -1202,6 +1202,7 @@ object ObjectClass {
case ObjectClass.portable_manned_turret_nc => ConstructorData.genericCodec(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret_tr => ConstructorData.genericCodec(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret_vs => ConstructorData.genericCodec(OneMannedFieldTurretData.codec, "field turret")
+ case ObjectClass.router_telepad_deployable => ConstructorData.genericCodec(TelepadDeployableData.codec, "telepad deployable")
//projectiles
case ObjectClass.hunter_seeker_missile_projectile => ConstructorData.genericCodec(TrackedProjectileData.codec, "projectile")
case ObjectClass.oicw_projectile => ConstructorData.genericCodec(TrackedProjectileData.codec, "projectile")
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadData.scala
new file mode 100644
index 00000000..b4ed0f0b
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadData.scala
@@ -0,0 +1,46 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import net.psforever.packet.game.PlanetSideGUID
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * A representation of the telepad portion of `ObjectCreateMessage` packet data.
+ * This data will help construct the "cosntruction tool"
+ * that can be obtained from the Router vehicle - the Router telepad.
+ * It issued to construct a bidirectional teleportation point associated with a Router if that Router is deployed.
+ * @param unk na
+ * @param router_guid the Router
+ */
+final case class TelepadData(unk : Int, router_guid : Option[PlanetSideGUID]) extends ConstructorData {
+ override def bitsize : Long = {
+ val rguidSize = if(router_guid.nonEmpty) 16 else 0
+ 34L + rguidSize
+ }
+}
+
+object TelepadData extends Marshallable[TelepadData] {
+ def apply(unk : Int) : TelepadData = TelepadData(unk, None)
+
+ def apply(unk : Int, router_guid : PlanetSideGUID) : TelepadData = TelepadData(unk, Some(router_guid))
+
+ implicit val codec : Codec[TelepadData] = (
+ ("unk" | uint(6)) ::
+ optional(bool, "router_guid" | PlanetSideGUID.codec) ::
+ uint(27)
+ ).exmap[TelepadData] (
+ {
+ case unk :: rguid :: 0 :: HNil =>
+ Attempt.successful(TelepadData(unk, rguid))
+ case _ =>
+ Attempt.failure(Err("invalid telepad format"))
+ },
+ {
+ case TelepadData(unk, rguid) =>
+ Attempt.successful(unk :: rguid :: 0 :: HNil)
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadDeployableData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadDeployableData.scala
new file mode 100644
index 00000000..369f641a
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/TelepadDeployableData.scala
@@ -0,0 +1,55 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.PlanetSideEmpire
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * A representation of simple objects that are spawned by the adaptive construction engine.
+ * @param pos na
+ * @param faction na
+ * @param bops na
+ * @param destroyed na
+ * @param unk1 na
+ * @param unk2 na
+ * @param router_guid the associated Router vehicle;
+ * this is an essential non-blank (16u 0x0) field;
+ * a blanked field will cause the client to crash
+ * @param owner_guid the owner of this telepad
+ * @param unk3 na
+ * @param unk4 na
+ */
+//TODO might be CommonFieldData
+final case class TelepadDeployableData(pos : PlacementData,
+ faction : PlanetSideEmpire.Value,
+ bops : Boolean,
+ destroyed : Boolean,
+ unk1 : Int,
+ unk2 : Boolean,
+ router_guid : PlanetSideGUID,
+ owner_guid : PlanetSideGUID,
+ unk3 : Int,
+ unk4 : Int) extends ConstructorData {
+ override def bitsize : Long = {
+ val posSize = pos.bitsize
+ 59 + posSize
+ }
+}
+
+object TelepadDeployableData extends Marshallable[TelepadDeployableData] {
+ implicit val codec : Codec[TelepadDeployableData] = (
+ ("pos" | PlacementData.codec) ::
+ ("faction" | PlanetSideEmpire.codec) ::
+ ("bops" | bool) ::
+ ("destroyed" | bool) ::
+ ("unk1" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
+ ("unk2" | bool) ::
+ ("router_guid" | PlanetSideGUID.codec) ::
+ ("owner_guid" | PlanetSideGUID.codec) ::
+ ("unk3" | uint16L) ::
+ ("unk4" | uint4)
+ ).as[TelepadDeployableData]
+}
diff --git a/common/src/main/scala/services/RemoverActor.scala b/common/src/main/scala/services/RemoverActor.scala
index 703b53c2..1f9f7371 100644
--- a/common/src/main/scala/services/RemoverActor.scala
+++ b/common/src/main/scala/services/RemoverActor.scala
@@ -226,6 +226,7 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
* No entries in the first pool.
*/
def ClearAll() : Unit = {
+ trace("all tasks have been cleared")
firstTask.cancel
firstHeap = Nil
}
diff --git a/common/src/main/scala/services/local/LocalAction.scala b/common/src/main/scala/services/local/LocalAction.scala
index 4dc109d4..76964b84 100644
--- a/common/src/main/scala/services/local/LocalAction.scala
+++ b/common/src/main/scala/services/local/LocalAction.scala
@@ -1,12 +1,13 @@
// Copyright (c) 2017 PSForever
package services.local
-import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.terminals.CaptureTerminal
+import net.psforever.objects.vehicles.Utility
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.types.{PlanetSideEmpire, Vector3}
@@ -23,9 +24,11 @@ object LocalAction {
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action
final case class HackCaptureTerminal(player_guid : PlanetSideGUID, continent : Zone, target : CaptureTerminal, unk1 : Long, unk2 : Long = 8L, isResecured : Boolean) extends Action
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
+ final case class RouterTelepadTransport(player_guid : PlanetSideGUID, passenger_guid : PlanetSideGUID, src_guid : PlanetSideGUID, dest_guid : PlanetSideGUID) extends Action
+ final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
+ final case class ToggleTeleportSystem(player_guid : PlanetSideGUID, router : Vehicle, systemPlan : Option[(Utility.InternalTelepad, TelepadDeployable)]) extends Action
final case class TriggerEffect(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID) extends Action
final case class TriggerEffectInfo(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID, unk1 : Boolean, unk2 : Long) extends Action
final case class TriggerEffectLocation(player_guid : PlanetSideGUID, effect : String, pos : Vector3, orient : Vector3) extends Action
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
- final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
}
diff --git a/common/src/main/scala/services/local/LocalResponse.scala b/common/src/main/scala/services/local/LocalResponse.scala
index 7963a1af..f2cae70c 100644
--- a/common/src/main/scala/services/local/LocalResponse.scala
+++ b/common/src/main/scala/services/local/LocalResponse.scala
@@ -2,7 +2,8 @@
package services.local
import net.psforever.objects.ce.Deployable
-import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.vehicles.Utility
+import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.packet.game._
import net.psforever.types.{PlanetSideEmpire, Vector3}
@@ -19,7 +20,10 @@ object LocalResponse {
final case class HackCaptureTerminal(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long, isResecured: Boolean) extends Response
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
+ final case class RouterTelepadMessage(msg : String) extends Response
+ final case class RouterTelepadTransport(passenger_guid : PlanetSideGUID, src_guid : PlanetSideGUID, dest_guid : PlanetSideGUID) extends Response
+ final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
+ final case class ToggleTeleportSystem(router : Vehicle, systemPlan : Option[(Utility.InternalTelepad, TelepadDeployable)]) extends Response
final case class TriggerEffect(target: PlanetSideGUID, effect: String, effectInfo: Option[TriggeredEffect] = None, triggeredLocation: Option[TriggeredEffectLocation] = None) extends Response
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
- final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
}
diff --git a/common/src/main/scala/services/local/LocalService.scala b/common/src/main/scala/services/local/LocalService.scala
index 8f5c5e21..39ec51f2 100644
--- a/common/src/main/scala/services/local/LocalService.scala
+++ b/common/src/main/scala/services/local/LocalService.scala
@@ -7,18 +7,21 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.terminals.CaptureTerminal
import net.psforever.objects.zones.{InterstellarCluster, Zone}
-import net.psforever.objects.{BoomerDeployable, GlobalDefinitions, PlanetSideGameObject, TurretDeployable}
+import net.psforever.objects._
import net.psforever.packet.game.{PlanetSideGUID, TriggeredEffect, TriggeredEffectLocation}
import net.psforever.objects.vital.Vitality
import net.psforever.types.Vector3
-import services.local.support.{DeployableRemover, DoorCloseActor, HackClearActor, HackCaptureActor}
+import services.local.support._
import services.vehicle.{VehicleAction, VehicleServiceMessage}
-import services.{GenericEventBus, Service, ServiceManager}
+import services.{GenericEventBus, RemoverActor, Service, ServiceManager}
import scala.util.Success
import scala.concurrent.duration._
import akka.pattern.ask
+import net.psforever.objects.vehicles.{Utility, UtilityType}
import services.ServiceManager.Lookup
+import services.support.SupportActor
+
import scala.concurrent.duration.Duration
class LocalService extends Actor {
@@ -26,6 +29,7 @@ class LocalService extends Actor {
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
private val hackCapturer = context.actorOf(Props[HackCaptureActor], "local-hack-capturer")
private val engineer = context.actorOf(Props[DeployableRemover], "deployable-remover-agent")
+ private val teleportDeployment : ActorRef = context.actorOf(Props[RouterTelepadActivation], "telepad-activate-agent")
private [this] val log = org.log4s.getLogger
var cluster : ActorRef = Actor.noSender
@@ -111,10 +115,18 @@ class LocalService extends Actor {
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
)
+ case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) =>
+ LocalEvents.publish(
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid))
+ )
case LocalAction.SetEmpire(object_guid, empire) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.SetEmpire(object_guid, empire))
)
+ case LocalAction.ToggleTeleportSystem(player_guid, router, system_plan) =>
+ LocalEvents.publish(
+ LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ToggleTeleportSystem(router, system_plan))
+ )
case LocalAction.TriggerEffect(player_guid, effect, target) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerEffect(target, effect))
@@ -219,6 +231,12 @@ class LocalService extends Actor {
case _ => ;
}
+ case DeployableRemover.EliminateDeployable(obj : TelepadDeployable, guid, pos, zone) =>
+ obj.Active = false
+ //ClearSpecific will also remove objects that do not have GUID's; we may not have a GUID at this time
+ teleportDeployment ! SupportActor.ClearSpecific(List(obj), zone)
+ EliminateDeployable(obj, guid, pos, zone.Id)
+
case DeployableRemover.EliminateDeployable(obj, guid, pos, zone) =>
EliminateDeployable(obj, guid, pos, zone.Id)
@@ -227,6 +245,54 @@ class LocalService extends Actor {
LocalServiceResponse(s"/${zone.Id}/Local", Service.defaultPlayerGUID, LocalResponse.ObjectDelete(trigger_guid, 0))
)
+ //message to RouterTelepadActivation
+ case LocalServiceMessage.Telepads(msg) =>
+ teleportDeployment forward msg
+
+ //from RouterTelepadActivation
+ case RouterTelepadActivation.ActivateTeleportSystem(telepad, zone) =>
+ val remoteTelepad = telepad.asInstanceOf[TelepadDeployable]
+ remoteTelepad.Active = true
+ zone.GUID(remoteTelepad.Router) match {
+ case Some(router : Vehicle) =>
+ router.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(internalTelepad : Utility.InternalTelepad) =>
+ //get rid of previous linked remote telepad (if any)
+ zone.GUID(internalTelepad.Telepad) match {
+ case Some(old : TelepadDeployable) =>
+ log.info(s"ActivateTeleportSystem: old remote telepad@${old.GUID.guid} linked to internal@${internalTelepad.GUID.guid} will be deconstructed")
+ old.Active = false
+ engineer ! SupportActor.ClearSpecific(List(old), zone)
+ engineer ! RemoverActor.AddTask(old, zone, Some(0 seconds))
+ case _ => ;
+ }
+ internalTelepad.Telepad = remoteTelepad.GUID
+ if(internalTelepad.Active) {
+ log.info(s"ActivateTeleportSystem: fully deployed router@${router.GUID.guid} in ${zone.Id} will link internal@${internalTelepad.GUID.guid} and remote@${remoteTelepad.GUID.guid}")
+ LocalEvents.publish(
+ LocalServiceResponse(s"/${zone.Id}/Local", Service.defaultPlayerGUID, LocalResponse.ToggleTeleportSystem(router, Some((internalTelepad, remoteTelepad))))
+ )
+ }
+ else {
+ remoteTelepad.OwnerName match {
+ case Some(name) =>
+ LocalEvents.publish(
+ LocalServiceResponse(s"/$name/Local", Service.defaultPlayerGUID, LocalResponse.RouterTelepadMessage("@Teleport_NotDeployed"))
+ )
+ case None => ;
+ }
+ }
+ case _ =>
+ log.error(s"ActivateTeleportSystem: vehicle@${router.GUID.guid} in ${zone.Id} is not a router?")
+ RouterTelepadError(remoteTelepad, zone, "@Telepad_NoDeploy_RouterLost")
+ }
+ case Some(o) =>
+ log.error(s"ActivateTeleportSystem: ${o.Definition.Name}@${o.GUID.guid} in ${zone.Id} is not a router")
+ RouterTelepadError(remoteTelepad, zone, "@Telepad_NoDeploy_RouterLost")
+ case None =>
+ RouterTelepadError(remoteTelepad, zone, "@Telepad_NoDeploy_RouterLost")
+ }
+
//synchronized damage calculations
case Vitality.DamageOn(target : Deployable, func) =>
func(target)
@@ -236,6 +302,24 @@ class LocalService extends Actor {
log.warn(s"Unhandled message $msg from $sender")
}
+ /**
+ * na
+ * @param telepad na
+ * @param zone na
+ * @param msg na
+ */
+ def RouterTelepadError(telepad : TelepadDeployable, zone : Zone, msg : String) : Unit = {
+ telepad.OwnerName match {
+ case Some(name) =>
+ LocalEvents.publish(
+ LocalServiceResponse(s"/$name/Local", Service.defaultPlayerGUID, LocalResponse.RouterTelepadMessage(msg))
+ )
+ case None => ;
+ }
+ engineer ! SupportActor.ClearSpecific(List(telepad), zone)
+ engineer ! RemoverActor.AddTask(telepad, zone, Some(0 seconds))
+ }
+
/**
* Common behavior for distributing information about a deployable's destruction or deconstruction.
*
diff --git a/common/src/main/scala/services/local/LocalServiceMessage.scala b/common/src/main/scala/services/local/LocalServiceMessage.scala
index 4bbe7291..07164a26 100644
--- a/common/src/main/scala/services/local/LocalServiceMessage.scala
+++ b/common/src/main/scala/services/local/LocalServiceMessage.scala
@@ -5,4 +5,6 @@ final case class LocalServiceMessage(forChannel : String, actionMessage : LocalA
object LocalServiceMessage {
final case class Deployables(msg : Any)
+
+ final case class Telepads(msg : Any)
}
diff --git a/common/src/main/scala/services/local/support/RouterTelepadActivation.scala b/common/src/main/scala/services/local/support/RouterTelepadActivation.scala
new file mode 100644
index 00000000..25f54881
--- /dev/null
+++ b/common/src/main/scala/services/local/support/RouterTelepadActivation.scala
@@ -0,0 +1,142 @@
+// Copyright (c) 2017 PSForever
+package services.local.support
+
+import akka.actor.Cancellable
+import net.psforever.objects.zones.Zone
+import net.psforever.objects._
+import services.support.{SimilarityComparator, SupportActor}
+
+import scala.concurrent.duration._
+
+class RouterTelepadActivation extends SupportActor[RouterTelepadActivation.Entry] {
+ var activationTask : Cancellable = DefaultCancellable.obj
+ var telepadList : List[RouterTelepadActivation.Entry] = List()
+ val sameEntryComparator = new SimilarityComparator[RouterTelepadActivation.Entry]() {
+ def Test(entry1 : RouterTelepadActivation.Entry, entry2 : RouterTelepadActivation.Entry) : Boolean = {
+ (entry1.obj eq entry2.obj) && (entry1.zone eq entry2.zone) && entry1.obj.GUID == entry2.obj.GUID
+ }
+ }
+ val firstStandardTime : FiniteDuration = 60 seconds
+
+ def InclusionTest(entry : RouterTelepadActivation.Entry) : Boolean = {
+ val obj = entry.obj
+ obj.isInstanceOf[TelepadDeployable] && !obj.asInstanceOf[TelepadDeployable].Active
+ }
+
+ def receive : Receive = entryManagementBehaviors
+ .orElse {
+ case RouterTelepadActivation.AddTask(obj, zone, duration) =>
+ val entry = RouterTelepadActivation.Entry(obj, zone, duration.getOrElse(firstStandardTime).toNanos)
+ if(InclusionTest(entry) && !telepadList.exists(test => sameEntryComparator.Test(test, entry))) {
+ if(entry.duration == 0) {
+ //skip the queue altogether
+ ActivationTask(entry)
+ }
+ else if(telepadList.isEmpty) {
+ //we were the only entry so the event must be started from scratch
+ telepadList = List(entry)
+ trace(s"an activation task has been added: $entry")
+ RetimeFirstTask()
+ }
+ else {
+ //unknown number of entries; append, sort, then re-time tasking
+ val oldHead = telepadList.head
+ if(!telepadList.exists(test => sameEntryComparator.Test(test, entry))) {
+ telepadList = (telepadList :+ entry).sortBy(entry => entry.time + entry.duration)
+ trace(s"an activation task has been added: $entry")
+ if(oldHead != telepadList.head) {
+ RetimeFirstTask()
+ }
+ }
+ else {
+ trace(s"$obj is already queued")
+ }
+ }
+ }
+ else {
+ trace(s"$obj either does not qualify for this behavior or is already queued")
+ }
+
+ //private messages from self to self
+ case RouterTelepadActivation.TryActivate() =>
+ activationTask.cancel
+ val now : Long = System.nanoTime
+ val (in, out) = telepadList.partition(entry => { now - entry.time >= entry.duration })
+ telepadList = out
+ in.foreach { ActivationTask }
+ RetimeFirstTask()
+ trace(s"router activation task has found ${in.size} items to process")
+
+ case _ => ;
+ }
+
+ /**
+ * Common function to reset the first task's delayed execution.
+ * Cancels the scheduled timer and will only restart the timer if there is at least one entry in the first pool.
+ * @param now the time (in nanoseconds);
+ * defaults to the current time (in nanoseconds)
+ */
+ def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
+ activationTask.cancel
+ if(telepadList.nonEmpty) {
+ val short_timeout : FiniteDuration = math.max(1, telepadList.head.duration - (now - telepadList.head.time)) nanoseconds
+ import scala.concurrent.ExecutionContext.Implicits.global
+ activationTask = context.system.scheduler.scheduleOnce(short_timeout, self, RouterTelepadActivation.TryActivate())
+ }
+ }
+
+ def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
+ PartitionTargetsFromList(telepadList, targets.map { RouterTelepadActivation.Entry(_, zone, 0) }, zone) match {
+ case (Nil, _) =>
+ debug(s"no tasks matching the targets $targets have been hurried")
+ case (in, out) =>
+ debug(s"the following tasks have been hurried: $in")
+ telepadList = out
+ if(out.nonEmpty) {
+ RetimeFirstTask()
+ }
+ in.foreach { ActivationTask }
+ }
+ }
+
+ def HurryAll() : Unit = {
+ trace("all tasks have been hurried")
+ activationTask.cancel
+ telepadList.foreach { ActivationTask }
+ telepadList = Nil
+ }
+
+ def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
+ PartitionTargetsFromList(telepadList, targets.map { RouterTelepadActivation.Entry(_, zone, 0) }, zone) match {
+ case (Nil, _) =>
+ debug(s"no tasks matching the targets $targets have been cleared")
+ case (in, out) =>
+ debug(s"the following tasks have been cleared: $in")
+ telepadList = out //.sortBy(entry => entry.time + entry.duration)
+ if(out.nonEmpty) {
+ RetimeFirstTask()
+ }
+ }
+ }
+
+ def ClearAll() : Unit = {
+ trace("all tasks have been cleared")
+ activationTask.cancel
+ telepadList = Nil
+ }
+
+ def ActivationTask(entry : SupportActor.Entry) : Unit = {
+ entry.obj.asInstanceOf[TelepadDeployable].Active = true
+ context.parent ! RouterTelepadActivation.ActivateTeleportSystem(entry.obj, entry.zone)
+ }
+}
+
+object RouterTelepadActivation {
+ final case class Entry(_obj : PlanetSideGameObject, _zone : Zone, _duration : Long) extends SupportActor.Entry(_obj, _zone, _duration)
+
+ final case class AddTask(obj : PlanetSideGameObject, zone : Zone, duration : Option[FiniteDuration] = None)
+
+ final case class TryActivate()
+
+ final case class ActivateTeleportSystem(telepad : PlanetSideGameObject, zone : Zone)
+}
diff --git a/common/src/main/scala/services/support/SupportActor.scala b/common/src/main/scala/services/support/SupportActor.scala
index 820bcee8..0fb0d6a6 100644
--- a/common/src/main/scala/services/support/SupportActor.scala
+++ b/common/src/main/scala/services/support/SupportActor.scala
@@ -73,17 +73,12 @@ abstract class SupportActor[A <: SupportActor.Entry] extends Actor {
//a - find targets from entries
val locatedTargets = for {
a <- targets
- b <- list//.filter(entry => entry.zone == zone)
+ b <- list
if b.obj.HasGUID && a.obj.HasGUID && comparator.Test(b, a)
} yield b
if(locatedTargets.nonEmpty) {
//b - entries, after the found targets are removed (cull any non-GUID entries while at it)
- val retained = for {
- a <- locatedTargets
- b <- list
- if b.obj.HasGUID && a.obj.HasGUID && !comparator.Test(b, a)
- } yield b
- (locatedTargets, retained)
+ (locatedTargets, list filterNot locatedTargets.toSet)
}
else {
(Nil, list)
diff --git a/common/src/main/scala/services/vehicle/VehicleAction.scala b/common/src/main/scala/services/vehicle/VehicleAction.scala
index 1ae95ea8..c47b332f 100644
--- a/common/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/common/src/main/scala/services/vehicle/VehicleAction.scala
@@ -1,8 +1,9 @@
// Copyright (c) 2017 PSForever
package services.vehicle
-import net.psforever.objects.{PlanetSideGameObject, Vehicle}
+import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.equipment.Equipment
+import net.psforever.objects.vehicles.Utility
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.PlanetSideGUID
@@ -26,7 +27,7 @@ object VehicleAction {
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class SeatPermissions(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Action
final case class StowEquipment(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
- final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle) extends Action
+ final case class UnloadVehicle(player_guid : PlanetSideGUID, continent : Zone, vehicle : Vehicle, vehicle_guid : PlanetSideGUID) extends Action
final case class UnstowEquipment(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID) extends Action
final case class VehicleState(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Action
final case class SendResponse(player_guid: PlanetSideGUID, msg : PlanetSideGamePacket) extends Action
diff --git a/common/src/main/scala/services/vehicle/VehicleResponse.scala b/common/src/main/scala/services/vehicle/VehicleResponse.scala
index 5c972bf6..07ba5901 100644
--- a/common/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/common/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -2,7 +2,8 @@
package services.vehicle
import net.psforever.objects.serverobject.tube.SpawnTube
-import net.psforever.objects.{PlanetSideGameObject, Vehicle}
+import net.psforever.objects.vehicles.Utility
+import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
import net.psforever.packet.game.objectcreate.ConstructorData
@@ -30,7 +31,7 @@ object VehicleResponse {
final case class RevealPlayer(player_guid : PlanetSideGUID) extends Response
final case class SeatPermissions(vehicle_guid : PlanetSideGUID, seat_group : Int, permission : Long) extends Response
final case class StowEquipment(vehicle_guid : PlanetSideGUID, slot : Int, itype : Int, iguid : PlanetSideGUID, idata : ConstructorData) extends Response
- final case class UnloadVehicle(vehicle_guid : PlanetSideGUID) extends Response
+ final case class UnloadVehicle(vehicle : Vehicle, vehicle_guid : PlanetSideGUID) extends Response
final case class UnstowEquipment(item_guid : PlanetSideGUID) extends Response
final case class VehicleState(vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, unk4 : Int, wheel_direction : Int, unk5 : Boolean, unk6 : Boolean) extends Response
final case class SendResponse(msg: PlanetSideGamePacket) extends Response
diff --git a/common/src/main/scala/services/vehicle/VehicleService.scala b/common/src/main/scala/services/vehicle/VehicleService.scala
index cacfae0d..e727528f 100644
--- a/common/src/main/scala/services/vehicle/VehicleService.scala
+++ b/common/src/main/scala/services/vehicle/VehicleService.scala
@@ -105,10 +105,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.StowEquipment(vehicle_guid, slot, definition.ObjectId, item.GUID, definition.Packet.DetailedConstructorData(item).get))
)
- case VehicleAction.UnloadVehicle(player_guid, continent, vehicle) =>
+ case VehicleAction.UnloadVehicle(player_guid, continent, vehicle, vehicle_guid) =>
vehicleDecon ! RemoverActor.ClearSpecific(List(vehicle), continent) //precaution
VehicleEvents.publish(
- VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnloadVehicle(vehicle.GUID))
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnloadVehicle(vehicle, vehicle_guid))
)
case VehicleAction.UnstowEquipment(player_guid, item_guid) =>
VehicleEvents.publish(
diff --git a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
index b60c9356..8bcf4fae 100644
--- a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
+++ b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
@@ -4,6 +4,7 @@ package services.vehicle.support
import net.psforever.objects.Vehicle
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.Zone
+import net.psforever.types.DriveState
import services.{RemoverActor, Service}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
@@ -43,8 +44,9 @@ class VehicleRemover extends RemoverActor {
override def SecondJob(entry : RemoverActor.Entry) : Unit = {
val vehicle = entry.obj.asInstanceOf[Vehicle]
val zone = entry.zone
+ vehicle.DeploymentState = DriveState.Mobile
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
- context.parent ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle))
+ context.parent ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID))
super.SecondJob(entry)
}
diff --git a/common/src/test/scala/base/ActorTest.scala b/common/src/test/scala/base/ActorTest.scala
index a1d594fc..7b265209 100644
--- a/common/src/test/scala/base/ActorTest.scala
+++ b/common/src/test/scala/base/ActorTest.scala
@@ -1,7 +1,7 @@
+// Copyright (c) 2017 PSForever
package base
-// Copyright (c) 2017 PSForever
-import akka.actor.ActorSystem
+import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.testkit.{ImplicitSender, TestKit}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
@@ -48,4 +48,28 @@ object ActorTest {
}
out
}
+
+ /**
+ * A middleman Actor that accepts a `Props` object to instantiate and accepts messages back from it.
+ * The purpose is to bypass a message receive issue with the `ActorTest` / `TestKit` class
+ * that does not properly queue messages dispatched to it
+ * when messages may be sent to it via a `context.parent` call.
+ * Please do not wrap and parameterize Props objects like this during normal Ops.
+ * @param actorProps the uninitialized `Actor` that uses `context.parent` to direct communication
+ * @param sendTo where to send mesages that have originated from an `actorProps` object;
+ * typically should point back to the test environment constructed by `TestKit`
+ */
+ class SupportActorInterface(actorProps : Props, sendTo : ActorRef) extends Actor {
+ val test = context.actorOf(actorProps, "support-actor")
+
+ def receive : Receive = {
+ case msg =>
+ (if(sender == test) {
+ sendTo
+ }
+ else {
+ test
+ }) ! msg
+ }
+ }
}
diff --git a/common/src/test/scala/game/objectcreate/ContainedTelepadDeployableDataTest.scala b/common/src/test/scala/game/objectcreate/ContainedTelepadDeployableDataTest.scala
new file mode 100644
index 00000000..f32b7c26
--- /dev/null
+++ b/common/src/test/scala/game/objectcreate/ContainedTelepadDeployableDataTest.scala
@@ -0,0 +1,38 @@
+// Copyright (c) 2017 PSForever
+package game.objectcreate
+
+import net.psforever.packet.PacketCoding
+import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
+import net.psforever.packet.game.objectcreate._
+import org.specs2.mutable._
+import scodec.bits._
+
+class ContainedTelepadDeployableDataTest extends Specification {
+ val string = hex"178f0000004080f42b00182cb0202000100000"
+
+ "ContainedTelepadDeployableData" should {
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ObjectCreateMessage(len, cls, guid, parent, data) =>
+ len mustEqual 143
+ cls mustEqual 744
+ guid mustEqual PlanetSideGUID(432)
+ parent.isDefined mustEqual true
+ parent.get.guid mustEqual PlanetSideGUID(385)
+ parent.get.slot mustEqual 2
+ data.isDefined mustEqual true
+ data.get.isInstanceOf[ContainedTelepadDeployableData] mustEqual true
+ data.get.asInstanceOf[ContainedTelepadDeployableData].unk mustEqual 101
+ data.get.asInstanceOf[ContainedTelepadDeployableData].router_guid mustEqual PlanetSideGUID(385)
+ case _ =>
+ ko
+ }
+ }
+ "encode" in {
+ val obj = ContainedTelepadDeployableData(101, PlanetSideGUID(385))
+ val msg = ObjectCreateMessage(744, PlanetSideGUID(432), ObjectCreateMessageParent(PlanetSideGUID(385), 2), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ pkt mustEqual string
+ }
+ }
+}
diff --git a/common/src/test/scala/game/objectcreate/TelepadDataTest.scala b/common/src/test/scala/game/objectcreate/TelepadDataTest.scala
new file mode 100644
index 00000000..10507986
--- /dev/null
+++ b/common/src/test/scala/game/objectcreate/TelepadDataTest.scala
@@ -0,0 +1,40 @@
+// Copyright (c) 2017 PSForever
+package game.objectcreate
+
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.packet.game.objectcreate._
+import org.specs2.mutable._
+import scodec.bits._
+
+class TelepadDataTest extends Specification {
+ val string = hex"17 86000000 5700 f3a a201 80 0302020000000"
+ //TODO validate the unknown fields before router_guid for testing
+
+ "TelepadData" should {
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ObjectCreateMessage(len, cls, guid, parent, data) =>
+ len mustEqual 134
+ cls mustEqual ObjectClass.router_telepad
+ guid mustEqual PlanetSideGUID(418)
+ parent.isDefined mustEqual true
+ parent.get.guid mustEqual PlanetSideGUID(430)
+ parent.get.slot mustEqual 0
+ data.isDefined mustEqual true
+ data.get.isInstanceOf[TelepadData] mustEqual true
+ data.get.asInstanceOf[TelepadData].router_guid mustEqual Some(PlanetSideGUID(385))
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val obj = TelepadData(0, PlanetSideGUID(385))
+ val msg = ObjectCreateMessage(ObjectClass.router_telepad, PlanetSideGUID(418), ObjectCreateMessageParent(PlanetSideGUID(430), 0), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+ }
+}
diff --git a/common/src/test/scala/game/objectcreate/TelepadDeployableDataTest.scala b/common/src/test/scala/game/objectcreate/TelepadDeployableDataTest.scala
new file mode 100644
index 00000000..e0d74b44
--- /dev/null
+++ b/common/src/test/scala/game/objectcreate/TelepadDeployableDataTest.scala
@@ -0,0 +1,61 @@
+// Copyright (c) 2017 PSForever
+package game.objectcreate
+
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.packet.game.objectcreate._
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+import org.specs2.mutable._
+import scodec.bits._
+
+class TelepadDeployableDataTest extends Specification {
+ val string = hex"17 c8000000 f42 6101 fbcfc 0fd43 6903 00 00 79 05 8101 ae01 5700c"
+ //TODO validate the unknown fields before router_guid for testing
+
+ "TelepadData" should {
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ObjectCreateMessage(len, cls, guid, parent, data) =>
+ len mustEqual 200
+ cls mustEqual ObjectClass.router_telepad_deployable
+ guid mustEqual PlanetSideGUID(353)
+ parent.isDefined mustEqual false
+ data.isDefined mustEqual true
+ data.get.isInstanceOf[TelepadDeployableData] mustEqual true
+ val teledata = data.get.asInstanceOf[TelepadDeployableData]
+ teledata.pos.coord mustEqual Vector3(6559.961f, 1960.1172f, 13.640625f)
+ teledata.pos.orient mustEqual Vector3.z(109.6875f)
+ teledata.pos.vel.isDefined mustEqual false
+ teledata.faction mustEqual PlanetSideEmpire.TR
+ teledata.bops mustEqual false
+ teledata.destroyed mustEqual false
+ teledata.unk1 mustEqual 2
+ teledata.unk2 mustEqual true
+ teledata.router_guid mustEqual PlanetSideGUID(385)
+ teledata.owner_guid mustEqual PlanetSideGUID(430)
+ teledata.unk3 mustEqual 87
+ teledata.unk4 mustEqual 12
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val obj = TelepadDeployableData(
+ PlacementData(
+ Vector3(6559.961f, 1960.1172f, 13.640625f),
+ Vector3.z(109.6875f)
+ ),
+ PlanetSideEmpire.TR,
+ false, false, 2, true,
+ PlanetSideGUID(385),
+ PlanetSideGUID(430),
+ 87, 12
+ )
+ val msg = ObjectCreateMessage(ObjectClass.router_telepad_deployable, PlanetSideGUID(353), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+ }
+}
diff --git a/common/src/test/scala/game/objectcreatedetailed/DetailedTelepadDataTest.scala b/common/src/test/scala/game/objectcreatedetailed/DetailedTelepadDataTest.scala
new file mode 100644
index 00000000..5145b00e
--- /dev/null
+++ b/common/src/test/scala/game/objectcreatedetailed/DetailedTelepadDataTest.scala
@@ -0,0 +1,66 @@
+// Copyright (c) 2017 PSForever
+package game.objectcreatedetailed
+
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.packet.game.objectcreate._
+import org.specs2.mutable._
+import scodec.bits._
+
+class DetailedTelepadDataTest extends Specification {
+ val string = hex"18 97000000 4f00 f3a e301 80 4a680400000200008"
+ val string_short = hex"18 87000000 2a00 f3a 5d01 89 8000000200008"
+ //TODO validate the unknown fields before router_guid for testing
+
+ "DetailedTelepadData" should {
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
+ len mustEqual 151
+ cls mustEqual ObjectClass.router_telepad
+ guid mustEqual PlanetSideGUID(483)
+ parent.isDefined mustEqual true
+ parent.get.guid mustEqual PlanetSideGUID(414)
+ parent.get.slot mustEqual 0
+ data.isDefined mustEqual true
+ data.get.isInstanceOf[DetailedTelepadData] mustEqual true
+ data.get.asInstanceOf[DetailedTelepadData].router_guid mustEqual Some(PlanetSideGUID(564))
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (short)" in {
+ PacketCoding.DecodePacket(string_short).require match {
+ case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
+ len mustEqual 135
+ cls mustEqual ObjectClass.router_telepad
+ guid mustEqual PlanetSideGUID(349)
+ parent.isDefined mustEqual true
+ parent.get.guid mustEqual PlanetSideGUID(340)
+ parent.get.slot mustEqual 9
+ data.isDefined mustEqual true
+ data.get.isInstanceOf[DetailedTelepadData] mustEqual true
+ data.get.asInstanceOf[DetailedTelepadData].router_guid mustEqual None
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val obj = DetailedTelepadData(18, PlanetSideGUID(564))
+ val msg = ObjectCreateDetailedMessage(ObjectClass.router_telepad, PlanetSideGUID(483), ObjectCreateMessageParent(PlanetSideGUID(414), 0), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (short)" in {
+ val obj = DetailedTelepadData(32)
+ val msg = ObjectCreateDetailedMessage(ObjectClass.router_telepad, PlanetSideGUID(349), ObjectCreateMessageParent(PlanetSideGUID(340), 9), obj)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_short
+ }
+ }
+}
diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala
index 72e32608..4b4d8c1d 100644
--- a/common/src/test/scala/objects/ConverterTest.scala
+++ b/common/src/test/scala/objects/ConverterTest.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
+import net.psforever.objects.vehicles.UtilityType
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
@@ -166,6 +167,35 @@ class ConverterTest extends Specification {
}
}
+ "Telepad" should {
+ "convert (success)" in {
+ val obj = new Telepad(GlobalDefinitions.router_telepad)
+ obj.Router = PlanetSideGUID(1001)
+ obj.Definition.Packet.ConstructorData(obj) match {
+ case Success(pkt) =>
+ pkt mustEqual TelepadData(0, PlanetSideGUID(1001))
+ case _ =>
+ ko
+ }
+
+ obj.Definition.Packet.DetailedConstructorData(obj) match {
+ case Success(pkt) =>
+ pkt mustEqual DetailedTelepadData(0, PlanetSideGUID(1001))
+ case _ =>
+ ko
+ }
+ }
+
+ "convert (failure; no router)" in {
+ val obj = new Telepad(GlobalDefinitions.router_telepad)
+ //obj.Router = PlanetSideGUID(1001)
+ obj.Definition.Packet.ConstructorData(obj).isFailure mustEqual true
+
+
+ obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
+ }
+ }
+
"SmallDeployable" should {
"convert" in {
val obj = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
@@ -299,6 +329,79 @@ class ConverterTest extends Specification {
}
}
+ "TelepadDeployable" should {
+ "convert (success)" in {
+ val obj = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ obj.Faction = PlanetSideEmpire.TR
+ obj.GUID = PlanetSideGUID(90)
+ obj.Router = PlanetSideGUID(1001)
+ obj.Owner = PlanetSideGUID(5001)
+ obj.Health = 1
+ obj.Definition.Packet.ConstructorData(obj) match {
+ case Success(pkt) =>
+ pkt mustEqual TelepadDeployableData(
+ PlacementData(Vector3.Zero, Vector3.Zero),
+ PlanetSideEmpire.TR,
+ bops = false,
+ destroyed = false,
+ unk1 = 2, unk2 = true,
+ router_guid = PlanetSideGUID(1001),
+ owner_guid = PlanetSideGUID(5001),
+ unk3 = 87, unk4 = 12
+ )
+ case _ =>
+ ko
+ }
+ }
+
+ "convert (success; destroyed)" in {
+ val obj = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ obj.Faction = PlanetSideEmpire.TR
+ obj.GUID = PlanetSideGUID(90)
+ obj.Router = PlanetSideGUID(1001)
+ obj.Owner = PlanetSideGUID(5001)
+ obj.Health = 0
+ obj.Definition.Packet.ConstructorData(obj) match {
+ case Success(pkt) =>
+ pkt mustEqual TelepadDeployableData(
+ PlacementData(Vector3.Zero, Vector3.Zero),
+ PlanetSideEmpire.TR,
+ bops = false,
+ destroyed = true,
+ unk1 = 2, unk2 = true,
+ router_guid = PlanetSideGUID(1001),
+ owner_guid = PlanetSideGUID(0),
+ unk3 = 0, unk4 = 6
+ )
+ case _ =>
+ ko
+ }
+ }
+
+ "convert (failure; no router)" in {
+ val obj = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ obj.Faction = PlanetSideEmpire.TR
+ obj.GUID = PlanetSideGUID(90)
+ //obj.Router = PlanetSideGUID(1001)
+ obj.Owner = PlanetSideGUID(5001)
+ obj.Health = 1
+ obj.Definition.Packet.ConstructorData(obj).isFailure mustEqual true
+
+ obj.Router = PlanetSideGUID(0)
+ obj.Definition.Packet.ConstructorData(obj).isFailure mustEqual true
+ }
+
+ "convert (failure; detailed)" in {
+ val obj = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ obj.Faction = PlanetSideEmpire.TR
+ obj.GUID = PlanetSideGUID(90)
+ obj.Router = PlanetSideGUID(1001)
+ obj.Owner = PlanetSideGUID(5001)
+ obj.Health = 1
+ obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
+ }
+ }
+
"Player" should {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val obj : Player = {
@@ -525,6 +628,15 @@ class ConverterTest extends Specification {
ams.Definition.Packet.ConstructorData(ams).isSuccess mustEqual true
//did not initialize the utilities, but the converter did not fail
}
+
+ "convert to packet (4)" in {
+ val
+ router = Vehicle(GlobalDefinitions.router)
+ router.GUID = PlanetSideGUID(413)
+ router.Utility(UtilityType.teleportpad_terminal).get.GUID = PlanetSideGUID(1413)
+ router.Utility(UtilityType.internal_router_telepad_deployable).get.GUID = PlanetSideGUID(2413)
+ router.Definition.Packet.ConstructorData(router).isSuccess mustEqual true
+ }
}
"DestroyedVehicle" should {
diff --git a/common/src/test/scala/objects/DeployableTest.scala b/common/src/test/scala/objects/DeployableTest.scala
index e79ac0d9..1c299dbd 100644
--- a/common/src/test/scala/objects/DeployableTest.scala
+++ b/common/src/test/scala/objects/DeployableTest.scala
@@ -318,6 +318,38 @@ class TurretControlBetrayalMountTest extends ActorTest {
}
}
+class TelepadDeployableTest extends Specification {
+ "Telepad" should {
+ "construct" in {
+ val obj = new Telepad(GlobalDefinitions.router_telepad)
+ obj.Active mustEqual false
+ obj.Router mustEqual None
+ }
+
+ "activate and deactivate" in {
+ val obj = new Telepad(GlobalDefinitions.router_telepad)
+ obj.Active mustEqual false
+ obj.Active = true
+ obj.Active mustEqual true
+ obj.Active = false
+ obj.Active mustEqual false
+ }
+
+ "keep track of a Router" in {
+ val obj = new Telepad(GlobalDefinitions.router_telepad)
+ obj.Router mustEqual None
+ obj.Router = PlanetSideGUID(1)
+ obj.Router mustEqual Some(PlanetSideGUID(1))
+ obj.Router = None
+ obj.Router mustEqual None
+ obj.Router = PlanetSideGUID(1)
+ obj.Router mustEqual Some(PlanetSideGUID(1))
+ obj.Router = PlanetSideGUID(0)
+ obj.Router mustEqual None
+ }
+ }
+}
+
object DeployableTest {
class TurretInitializer(obj : TurretDeployable) extends Actor {
def receive : Receive = {
diff --git a/common/src/test/scala/objects/DeployableToolboxTest.scala b/common/src/test/scala/objects/DeployableToolboxTest.scala
index afc96888..6edd30f8 100644
--- a/common/src/test/scala/objects/DeployableToolboxTest.scala
+++ b/common/src/test/scala/objects/DeployableToolboxTest.scala
@@ -20,10 +20,12 @@ class DeployableToolboxTest extends Specification {
obj.Initialize(Set())
val list = obj.UpdateUI()
list.size mustEqual DeployedItem.values.size - 3 //extra field turrets
- list.foreach({case(_,curr,_,max) =>
+ val (routers, allOthers) = list.partition({ case((_,_,_,max)) => max == 1024 })
+ allOthers.foreach({case(_,curr,_,max) =>
curr mustEqual 0
max mustEqual 0
})
+ routers.length mustEqual 1
ok
}
@@ -44,7 +46,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (AssaultEngineering)" in {
@@ -64,7 +66,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (FortificationEngineering)" in {
@@ -84,7 +86,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (AdvancedEngineering)" in {
@@ -104,7 +106,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (AdvancedHacking)" in {
@@ -124,7 +126,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (without CombatEngineering)" in {
@@ -144,7 +146,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"initialization (GroundSupport)" in {
@@ -164,6 +166,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"can not initialize twice" in {
@@ -183,7 +186,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.Initialize(Set(AdvancedEngineering)) mustEqual false
obj.CountDeployable(DeployedItem.boomer)._2 mustEqual 0
@@ -200,7 +203,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"uninitialized fields can not accept deployables" in {
@@ -240,7 +243,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
CombatEngineering,
@@ -260,7 +263,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
FortificationEngineering,
@@ -280,7 +283,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
AssaultEngineering,
@@ -300,7 +303,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
AssaultEngineering,
@@ -320,7 +323,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
AdvancedHacking,
@@ -340,7 +343,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"change accessible fields by adding by certification type (GroundSupport)" in {
@@ -360,7 +363,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
GroundSupport,
@@ -400,7 +403,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.AddToDeployableQuantities(
AdvancedEngineering,
@@ -420,7 +423,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"change accessible fields by removing by certification types (all)" in {
@@ -440,7 +443,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
GroundSupport,
@@ -460,7 +463,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
AdvancedHacking,
@@ -480,7 +483,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
FortificationEngineering,
@@ -500,7 +503,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
AssaultEngineering,
@@ -520,7 +523,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
CombatEngineering,
@@ -540,7 +543,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"change accessible fields by removing by certification type (AdvancedEngineering)" in {
@@ -560,7 +563,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 1
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 1
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 1
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
obj.RemoveFromDeployableQuantities(
AdvancedEngineering,
@@ -580,7 +583,7 @@ class DeployableToolboxTest extends Specification {
obj.CountDeployable(DeployedItem.portable_manned_turret_tr)._2 mustEqual 0
obj.CountDeployable(DeployedItem.portable_manned_turret_vs)._2 mustEqual 0
obj.CountDeployable(DeployedItem.deployable_shield_generator)._2 mustEqual 0
- obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 0
+ obj.CountDeployable(DeployedItem.router_telepad_deployable)._2 mustEqual 1024
}
"can not remove deployables from an unpopulated field" in {
diff --git a/common/src/test/scala/objects/EquipmentTest.scala b/common/src/test/scala/objects/EquipmentTest.scala
index 6c638c94..1fc0aeea 100644
--- a/common/src/test/scala/objects/EquipmentTest.scala
+++ b/common/src/test/scala/objects/EquipmentTest.scala
@@ -5,7 +5,7 @@ import net.psforever.objects._
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.GlobalDefinitions._
-import net.psforever.objects.ce.DeployedItem
+import net.psforever.objects.ce.{DeployedItem, TelepadLike}
import net.psforever.objects.definition._
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.CertificationType
diff --git a/common/src/test/scala/objects/UtilityTest.scala b/common/src/test/scala/objects/UtilityTest.scala
index b061a0ff..13920cb5 100644
--- a/common/src/test/scala/objects/UtilityTest.scala
+++ b/common/src/test/scala/objects/UtilityTest.scala
@@ -3,7 +3,7 @@ package objects
import akka.actor.{Actor, ActorRef, Props}
import base.ActorTest
-import net.psforever.objects.{GlobalDefinitions, Vehicle}
+import net.psforever.objects._
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles._
import net.psforever.packet.game.PlanetSideGUID
@@ -18,7 +18,7 @@ class UtilityTest extends Specification {
obj.UtilType mustEqual UtilityType.order_terminala
obj().isInstanceOf[Terminal] mustEqual true
obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 613
- obj().asInstanceOf[Terminal].Actor == ActorRef.noSender
+ obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender
}
"create an order_terminalb object" in {
@@ -26,7 +26,7 @@ class UtilityTest extends Specification {
obj.UtilType mustEqual UtilityType.order_terminalb
obj().isInstanceOf[Terminal] mustEqual true
obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 614
- obj().asInstanceOf[Terminal].Actor == ActorRef.noSender
+ obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender
}
"create a matrix_terminalc object" in {
@@ -34,7 +34,7 @@ class UtilityTest extends Specification {
obj.UtilType mustEqual UtilityType.matrix_terminalc
obj().isInstanceOf[Terminal] mustEqual true
obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 519
- obj().asInstanceOf[Terminal].Actor == ActorRef.noSender
+ obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender
}
"create an ams_respawn_tube object" in {
@@ -43,7 +43,53 @@ class UtilityTest extends Specification {
obj.UtilType mustEqual UtilityType.ams_respawn_tube
obj().isInstanceOf[SpawnTube] mustEqual true
obj().asInstanceOf[SpawnTube].Definition.ObjectId mustEqual 49
- obj().asInstanceOf[SpawnTube].Actor == ActorRef.noSender
+ obj().asInstanceOf[SpawnTube].Actor mustEqual ActorRef.noSender
+ }
+
+ "create a teleportpad_terminal object" in {
+ val obj = Utility(UtilityType.teleportpad_terminal, UtilityTest.vehicle)
+ obj.UtilType mustEqual UtilityType.teleportpad_terminal
+ obj().isInstanceOf[Terminal] mustEqual true
+ obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 853
+ obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender
+ }
+
+ "teleportpad_terminal produces a telepad object (router_telepad)" in {
+ import net.psforever.packet.game.ItemTransactionMessage
+ import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, TransactionType}
+ val veh = Vehicle(GlobalDefinitions.quadstealth)
+ val obj = Utility(UtilityType.teleportpad_terminal, UtilityTest.vehicle)
+ veh.GUID = PlanetSideGUID(101)
+ obj().Owner = veh //hack
+ obj().GUID = PlanetSideGUID(1)
+
+ val msg = obj().asInstanceOf[Terminal].Buy(
+ Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)),
+ ItemTransactionMessage(PlanetSideGUID(853), TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
+ )
+ msg.isInstanceOf[Terminal.BuyEquipment] mustEqual true
+ msg.asInstanceOf[Terminal.BuyEquipment].item.isInstanceOf[Telepad] mustEqual true
+ }
+
+ "create an internal_router_telepad_deployable object" in {
+ val obj = Utility(UtilityType.internal_router_telepad_deployable, UtilityTest.vehicle)
+ obj.UtilType mustEqual UtilityType.internal_router_telepad_deployable
+ obj().isInstanceOf[Utility.InternalTelepad] mustEqual true
+ obj().asInstanceOf[Utility.InternalTelepad].Definition.ObjectId mustEqual 744
+ obj().asInstanceOf[Utility.InternalTelepad].Actor mustEqual ActorRef.noSender
+ }
+
+ "internal_router_telepad_deployable can keep track of an object's GUID (presumedly, it's a Telepad)" in {
+ val obj = Utility(UtilityType.internal_router_telepad_deployable, UtilityTest.vehicle)
+ val inpad = obj().asInstanceOf[Utility.InternalTelepad]
+
+ inpad.Telepad mustEqual None
+ inpad.Telepad = PlanetSideGUID(5)
+ inpad.Telepad mustEqual Some(PlanetSideGUID(5))
+ inpad.Telepad = PlanetSideGUID(6)
+ inpad.Telepad mustEqual Some(PlanetSideGUID(6))
+ inpad.Telepad = None
+ inpad.Telepad mustEqual None
}
"be located with their owner (terminal)" in {
@@ -71,10 +117,25 @@ class UtilityTest extends Specification {
obj().Position mustEqual veh.Position
obj().Orientation mustEqual veh.Orientation
}
+
+ "be located with their owner (internal telepad)" in {
+ val veh = Vehicle(GlobalDefinitions.quadstealth)
+ val obj = Utility(UtilityType.internal_router_telepad_deployable, veh)
+ obj().Position mustEqual veh.Position
+ obj().Orientation mustEqual veh.Orientation
+
+ import net.psforever.types.Vector3
+ veh.Position = Vector3(1, 2, 3)
+ veh.Orientation = Vector3(4, 5, 6)
+ veh.GUID = PlanetSideGUID(101)
+ obj().Position mustEqual veh.Position
+ obj().Orientation mustEqual veh.Orientation
+ obj().asInstanceOf[Utility.InternalTelepad].Router mustEqual Some(veh.GUID)
+ }
}
}
-class Utility1Test extends ActorTest {
+class UtilityTerminalATest extends ActorTest {
"Utility" should {
"wire an order_terminala Actor" in {
val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle)
@@ -88,7 +149,7 @@ class Utility1Test extends ActorTest {
}
}
-class Utility2Test extends ActorTest {
+class UtilityTerminalBTest extends ActorTest {
"Utility" should {
"wire an order_terminalb Actor" in {
val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle)
@@ -102,7 +163,7 @@ class Utility2Test extends ActorTest {
}
}
-class Utility3Test extends ActorTest {
+class UtilityTerminalCTest extends ActorTest {
"Utility" should {
"wire a matrix_terminalc Actor" in {
val obj = Utility(UtilityType.matrix_terminalc, UtilityTest.vehicle)
@@ -116,7 +177,7 @@ class Utility3Test extends ActorTest {
}
}
-class Utility4Test extends ActorTest {
+class UtilityRespawnTubeTest extends ActorTest {
"Utility" should {
"wire an ams_respawn_tube Actor" in {
val obj = Utility(UtilityType.ams_respawn_tube, UtilityTest.vehicle)
@@ -130,6 +191,38 @@ class Utility4Test extends ActorTest {
}
}
+class UtilityTelepadTerminalTest extends ActorTest {
+ "Utility" should {
+ "wire a teleportpad_terminal Actor" in {
+ val obj = Utility(UtilityType.teleportpad_terminal, UtilityTest.vehicle)
+ obj().GUID = PlanetSideGUID(1)
+ assert(obj().Actor == ActorRef.noSender)
+
+ system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! ""
+ receiveOne(Duration.create(100, "ms")) //consume and discard
+ assert(obj().Actor != ActorRef.noSender)
+ }
+ }
+}
+
+class UtilityInternalTelepadTest extends ActorTest {
+ "Utility" should {
+ "wire a teleportpad_terminal Actor" in {
+ val veh = Vehicle(GlobalDefinitions.quadstealth)
+ veh.GUID = PlanetSideGUID(101)
+ val obj = Utility(UtilityType.internal_router_telepad_deployable, veh)
+ obj().GUID = PlanetSideGUID(1)
+ assert(obj().Actor == ActorRef.noSender)
+ assert(obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID))
+
+ system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! ""
+ receiveOne(Duration.create(100, "ms")) //consume and discard
+ assert(obj().Actor == ActorRef.noSender)
+ assert(obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID))
+ }
+ }
+}
+
object UtilityTest {
val vehicle = Vehicle(GlobalDefinitions.quadstealth)
diff --git a/common/src/test/scala/service/LocalServiceTest.scala b/common/src/test/scala/service/LocalServiceTest.scala
index 55001453..f52f007f 100644
--- a/common/src/test/scala/service/LocalServiceTest.scala
+++ b/common/src/test/scala/service/LocalServiceTest.scala
@@ -3,7 +3,7 @@ package service
import akka.actor.Props
import base.ActorTest
-import net.psforever.objects.{GlobalDefinitions, SensorDeployable}
+import net.psforever.objects.{GlobalDefinitions, SensorDeployable, Vehicle}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.packet.game._
import net.psforever.types.{PlanetSideEmpire, Vector3}
@@ -154,6 +154,19 @@ class ProximityTerminalEffectTest extends ActorTest {
}
}
+class RouterTelepadTransportTest extends ActorTest {
+ ServiceManager.boot(system)
+
+ "LocalService" should {
+ "pass RouterTelepadTransport" in {
+ val service = system.actorOf(Props[LocalService], "l_service")
+ service ! Service.Join("test")
+ service ! LocalServiceMessage("test", LocalAction.RouterTelepadTransport(PlanetSideGUID(10), PlanetSideGUID(11), PlanetSideGUID(12), PlanetSideGUID(13)))
+ expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.RouterTelepadTransport(PlanetSideGUID(11), PlanetSideGUID(12), PlanetSideGUID(13))))
+ }
+ }
+}
+
class SetEmpireTest extends ActorTest {
ServiceManager.boot(system)
val obj = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
@@ -168,6 +181,20 @@ class SetEmpireTest extends ActorTest {
}
}
+class ToggleTeleportSystemTest extends ActorTest {
+ ServiceManager.boot(system)
+
+ "LocalService" should {
+ "pass ToggleTeleportSystem" in {
+ val router = Vehicle(GlobalDefinitions.router)
+ val service = system.actorOf(Props[LocalService], "l_service")
+ service ! Service.Join("test")
+ service ! LocalServiceMessage("test", LocalAction.ToggleTeleportSystem(PlanetSideGUID(10), router, None))
+ expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.ToggleTeleportSystem(router, None)))
+ }
+ }
+}
+
class TriggerEffectTest extends ActorTest {
ServiceManager.boot(system)
diff --git a/common/src/test/scala/service/RemoverActorTest.scala b/common/src/test/scala/service/RemoverActorTest.scala
index c0cc6234..1de2dc50 100644
--- a/common/src/test/scala/service/RemoverActorTest.scala
+++ b/common/src/test/scala/service/RemoverActorTest.scala
@@ -5,7 +5,7 @@ import akka.actor.{ActorRef, Props}
import akka.routing.RandomPool
import akka.testkit.TestProbe
import base.ActorTest
-import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.TaskResolver
@@ -21,24 +21,26 @@ import scala.concurrent.duration._
// "RemoverActor" should {
// "handle a simple task" in {
// expectNoMsg(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// val remover = system.actorOf(
+// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
+// "test-remover"
+// )
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
//
-// val reply1 = probe.receiveOne(200 milliseconds)
+// val reply1 = receiveOne(500 milliseconds)
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// val reply2 = probe.receiveOne(200 milliseconds)
+// val reply2 = receiveOne(500 milliseconds)
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
-// probe.expectNoMsg(1 seconds) //delay
-// val reply3 = probe.receiveOne(300 milliseconds)
+// expectNoMsg(1 seconds) //delay
+// val reply3 = receiveOne(500 milliseconds)
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = probe.receiveOne(300 milliseconds)
+// val reply4 = receiveOne(500 milliseconds)
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = probe.receiveOne(300 milliseconds)
+// val reply5 = receiveOne(500 milliseconds)
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = probe.receiveOne(500 milliseconds)
+// val reply6 = receiveOne(500 milliseconds)
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = probe.receiveOne(500 milliseconds)
+// val reply7 = receiveOne(500 milliseconds)
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
// }
// }
@@ -50,29 +52,31 @@ import scala.concurrent.duration._
// "RemoverActor" should {
// "handle a simple task (timed)" in {
// expectNoMsg(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// val remover = system.actorOf(
+// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
+// "test-remover"
+// )
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(100 milliseconds))
//
-// val reply1 = probe.receiveOne(200 milliseconds)
+// val reply1 = receiveOne(500 milliseconds)
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// val reply2 = probe.receiveOne(200 milliseconds)
+// val reply2 = receiveOne(500 milliseconds)
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
// //no delay
-// val reply3 = probe.receiveOne(300 milliseconds)
+// val reply3 = receiveOne(500 milliseconds)
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = probe.receiveOne(300 milliseconds)
+// val reply4 = receiveOne(500 milliseconds)
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = probe.receiveOne(300 milliseconds)
+// val reply5 = receiveOne(500 milliseconds)
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = probe.receiveOne(300 milliseconds)
+// val reply6 = receiveOne(500 milliseconds)
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = probe.receiveOne(300 milliseconds)
+// val reply7 = receiveOne(500 milliseconds)
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
// }
// }
//}
-//
+
//class ExcludedRemoverActorTest extends ActorTest {
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
// val AlternateTestObject = new PlanetSideGameObject() { def Definition = new ObjectDefinition(0) { } }
@@ -81,7 +85,10 @@ import scala.concurrent.duration._
// "allow only specific objects" in {
// expectNoMsg(500 milliseconds)
// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
+// val remover = system.actorOf(
+// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
+// "test-remover"
+// )
// remover ! RemoverActor.AddTask(AlternateTestObject, Zone.Nowhere)
//
// val reply1 = probe.receiveOne(200 milliseconds)
@@ -91,7 +98,7 @@ import scala.concurrent.duration._
// }
// }
//}
-//
+
//class MultipleRemoverActorTest extends ActorTest {
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
@@ -494,44 +501,44 @@ object RemoverActorTest {
final case class DeletionTaskRunAlert()
- class TestRemover(probe : TestProbe) extends RemoverActor {
+ class TestRemover extends RemoverActor {
import net.psforever.objects.guid.{Task, TaskResolver}
val FirstStandardDuration = 1 seconds
val SecondStandardDuration = 100 milliseconds
def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
- probe.ref ! InclusionTestAlert()
- entry.obj.isInstanceOf[Equipment]
+ context.parent ! InclusionTestAlert()
+ true
}
def InitialJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! InitialJobAlert()
+ context.parent ! InitialJobAlert()
}
def FirstJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! FirstJobAlert()
+ context.parent ! FirstJobAlert()
}
override def SecondJob(entry : RemoverActor.Entry) : Unit = {
- probe.ref ! SecondJobAlert()
+ context.parent ! SecondJobAlert()
super.SecondJob(entry)
}
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
- probe.ref ! ClearanceTestAlert()
+ context.parent ! ClearanceTestAlert()
true
}
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
- probe.ref ! DeletionTaskAlert()
+ context.parent ! DeletionTaskAlert()
TaskResolver.GiveTask(new Task() {
- private val localProbe = probe
+ private val localProbe = context.parent
override def isComplete = Task.Resolution.Success
def Execute(resolver : ActorRef) : Unit = {
- localProbe.ref ! DeletionTaskRunAlert()
+ context.parent ! DeletionTaskRunAlert()
resolver ! scala.util.Success(this)
}
})
diff --git a/common/src/test/scala/service/RouterTelepadActivationTest.scala b/common/src/test/scala/service/RouterTelepadActivationTest.scala
new file mode 100644
index 00000000..2ad331ec
--- /dev/null
+++ b/common/src/test/scala/service/RouterTelepadActivationTest.scala
@@ -0,0 +1,171 @@
+// Copyright (c) 2017 PSForever
+package service
+
+import akka.actor.Props
+import base.ActorTest
+import net.psforever.objects._
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.PlanetSideGUID
+import services.local.support.RouterTelepadActivation
+import services.support.SupportActor
+
+import scala.concurrent.duration._
+
+class RouterTelepadActivationTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "construct" in {
+ system.actorOf(Props[RouterTelepadActivation], "activation-test-actor")
+ }
+ }
+}
+
+class RouterTelepadActivationSimpleTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "handle a task" in {
+ val telepad = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad.GUID = PlanetSideGUID(1)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad, Zone.Nowhere, Some(2 seconds))
+ expectMsg(3 seconds, RouterTelepadActivation.ActivateTeleportSystem(telepad, Zone.Nowhere))
+ }
+ }
+}
+
+class RouterTelepadActivationComplexTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "handle multiple tasks" in {
+ val telepad1 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad1.GUID = PlanetSideGUID(1)
+ val telepad2 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad2.GUID = PlanetSideGUID(2)
+ val telepad3 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad3.GUID = PlanetSideGUID(3)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad1, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad2, Zone.Nowhere, Some(3 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad3, Zone.Nowhere, Some(1 seconds))
+ val msgs = receiveN(3, 5 seconds) //organized by duration
+ assert(msgs.head.isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs.head.asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad3)
+ assert(msgs(1).isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs(1).asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad1)
+ assert(msgs(2).isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs(2).asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad2)
+ }
+ }
+}
+
+class RouterTelepadActivationHurryTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "hurry specific tasks" in {
+ val telepad1 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad1.GUID = PlanetSideGUID(1)
+ val telepad2 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad2.GUID = PlanetSideGUID(2)
+ val telepad3 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad3.GUID = PlanetSideGUID(3)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad1, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad2, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad3, Zone.Nowhere, Some(2 seconds))
+ obj ! SupportActor.HurrySpecific(List(telepad1, telepad2), Zone.Nowhere)
+ val msgs = receiveN(2, 1 seconds)
+ assert(msgs.head.isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs.head.asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad1)
+ assert(msgs(1).isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs(1).asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad2)
+ val last = receiveOne(3 seconds)
+ assert(last.isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(last.asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad3)
+ }
+ }
+}
+
+class RouterTelepadActivationHurryAllTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "hurry all tasks" in {
+ val telepad1 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad1.GUID = PlanetSideGUID(1)
+ val telepad2 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad2.GUID = PlanetSideGUID(2)
+ val telepad3 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad3.GUID = PlanetSideGUID(3)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad1, Zone.Nowhere, Some(7 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad2, Zone.Nowhere, Some(5 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad3, Zone.Nowhere, Some(6 seconds))
+ obj ! SupportActor.HurryAll()
+ val msgs = receiveN(3, 4 seconds) //organized by duration; note: all messages received before the earliest task should be performed
+ assert(msgs.head.isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs.head.asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad2)
+ assert(msgs(1).isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs(1).asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad3)
+ assert(msgs(2).isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs(2).asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad1)
+ }
+ }
+}
+
+class RouterTelepadActivationClearTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "clear specific tasks" in {
+ val telepad1 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad1.GUID = PlanetSideGUID(1)
+ val telepad2 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad2.GUID = PlanetSideGUID(2)
+ val telepad3 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad3.GUID = PlanetSideGUID(3)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad1, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad2, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad3, Zone.Nowhere, Some(2 seconds))
+ obj ! SupportActor.ClearSpecific(List(telepad1, telepad2), Zone.Nowhere)
+ val msgs = receiveN(1, 3 seconds) //should only receive telepad3
+ assert(msgs.head.isInstanceOf[RouterTelepadActivation.ActivateTeleportSystem])
+ assert(msgs.head.asInstanceOf[RouterTelepadActivation.ActivateTeleportSystem].telepad == telepad3)
+ }
+ }
+}
+
+class RouterTelepadActivationClearAllTest extends ActorTest {
+ "RouterTelepadActivation" should {
+ "clear all tasks" in {
+ val telepad1 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad1.GUID = PlanetSideGUID(1)
+ val telepad2 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad2.GUID = PlanetSideGUID(2)
+ val telepad3 = new TelepadDeployable(GlobalDefinitions.router_telepad_deployable)
+ telepad3.GUID = PlanetSideGUID(3)
+ val obj = system.actorOf(
+ Props(classOf[ActorTest.SupportActorInterface], Props[RouterTelepadActivation], self),
+ "activation-test-actor"
+ )
+
+ obj ! RouterTelepadActivation.AddTask(telepad1, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad2, Zone.Nowhere, Some(2 seconds))
+ obj ! RouterTelepadActivation.AddTask(telepad3, Zone.Nowhere, Some(2 seconds))
+ obj ! SupportActor.ClearAll()
+ expectNoMsg(4 seconds)
+ }
+ }
+}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 38d9e644..81541023 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -16,7 +16,7 @@ import services.ServiceManager.Lookup
import net.psforever.objects._
import net.psforever.objects.avatar.{Certification, DeployableToolbox}
import net.psforever.objects.ballistics._
-import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployedItem, SimpleDeployable}
+import net.psforever.objects.ce._
import net.psforever.objects.definition.{ConstructionFireMode, DeployableDefinition, ObjectDefinition, ToolDefinition}
import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter}
import net.psforever.objects.equipment.{CItem, _}
@@ -35,7 +35,7 @@ import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
-import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
+import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.serverobject.tube.SpawnTube
@@ -45,7 +45,7 @@ import net.psforever.objects.vital._
import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
-import services.{RemoverActor, _}
+import services.{RemoverActor, vehicle, _}
import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import services.galaxy.{GalaxyResponse, GalaxyServiceResponse}
import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
@@ -59,7 +59,9 @@ import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.util.Success
import akka.pattern.ask
-import services.local.support.HackCaptureActor
+import net.psforever.objects.vehicles.Utility.InternalTelepad
+import services.local.support.{HackCaptureActor, RouterTelepadActivation}
+import services.support.SupportActor
class WorldSessionActor extends Actor with MDCContextAware {
@@ -95,6 +97,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
var whenUsedLastKit : Long = 0
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
var drawDeloyableIcon : PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
+ var recentTeleportAttempt : Long = 0
+
var amsSpawnPoint : Option[SpawnTube] = None
var clientKeepAlive : Cancellable = DefaultCancellable.obj
var progressBarUpdate : Cancellable = DefaultCancellable.obj
@@ -124,6 +128,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
vehicleService ! Service.Leave()
avatarService ! Service.Leave()
galaxyService ! Service.Leave()
+ cluster ! Service.Leave()
LivePlayerList.Remove(sessionId)
if(player != null && player.HasGUID) {
val player_guid = player.GUID
@@ -141,15 +146,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
//handle orphaned deployables
DisownDeployables()
- //clean up boomer triggers
+ //clean up boomer triggers and telepads
val equipment = (
(player.Holsters()
.zipWithIndex
.map({ case ((slot, index)) => (index, slot.Equipment) })
.collect { case ((index, Some(obj))) => InventoryItem(obj, index) }
) ++ player.Inventory.Items)
- .filterNot({ case InventoryItem(obj, _) => obj.isInstanceOf[BoomerTrigger] })
- //TODO final character save before doing any of this
+ .filterNot({ case InventoryItem(obj, _) => obj.isInstanceOf[BoomerTrigger] || obj.isInstanceOf[Telepad] })
+ //TODO final character save before doing any of this (use equipment)
continent.Population ! Zone.Population.Release(avatar)
if(player.isAlive) {
//actually being alive or manually deconstructing
@@ -206,13 +211,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None =>
None
}) match {
- case Some(vehicle : Vehicle) =>
- vehicle.Seat(vehicle.PassengerInSeat(player).get).get.Occupant = None
- if(vehicle.Seats.values.count(_.isOccupied) == 0) {
- vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, vehicle.Definition.DeconstructionTime)) //start vehicle decay
- }
- vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
-
case Some(mobj : Mountable) =>
mobj.Seat(mobj.PassengerInSeat(player).get).get.Occupant = None
@@ -496,10 +494,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
case building : Building =>
log.info(s"Zone.Lattice.SpawnPoint: spawn point on $zone_id in building ${building.Id} selected")
case vehicle : Vehicle =>
+// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), continent))
+// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, vehicle.Definition.DeconstructionTime))
//TODO replace this bad math with good math or no math
//position the player alongside either of the AMS's terminals, facing away from it
- val side = if(System.currentTimeMillis() % 2 == 0) 1
- else -1
+ val side = if(System.currentTimeMillis() % 2 == 0) 1 else -1
//right | left
val z = spawn_tube.Orientation.z
val zrot = (z + 90) % 360
@@ -547,7 +546,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Zone.Ground.ItemOnGround(item : BoomerTrigger, pos, orient) =>
//dropped the trigger, no longer own the boomer; make certain whole faction is aware of that
val playerGUID = player.GUID
- continent.GUID(item.Companion.getOrElse(PlanetSideGUID(0))) match {
+ continent.GUID(item.Companion) match {
case Some(obj : BoomerDeployable) =>
val guid = obj.GUID
val factionOnContinentChannel = s"${continent.Id}/${player.Faction}"
@@ -586,7 +585,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Zone.Ground.ItemInHand(item : BoomerTrigger) =>
if(PutItemInHand(item)) {
//pick up the trigger, own the boomer; make certain whole faction is aware of that
- continent.GUID(item.Companion.getOrElse(PlanetSideGUID(0))) match {
+ continent.GUID(item.Companion) match {
case Some(obj : BoomerDeployable) =>
val guid = obj.GUID
val playerGUID = player.GUID
@@ -632,6 +631,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case GlobalDefinitions.advanced_ace =>
sendResponse(GenericObjectActionMessage(player.GUID, 212)) //put fdu down; it will be removed from the client's holster
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PutDownFDU(player.GUID))
+ case GlobalDefinitions.router_telepad => ;
case _ =>
log.warn(s"Zone.Deployable.DeployableIsBuilt: not sure what kind of construction item to animate - ${tool.Definition}")
}
@@ -706,6 +706,39 @@ class WorldSessionActor extends Actor with MDCContextAware {
FindReplacementConstructionItem(tool, index)
StopBundlingPackets()
+ case WorldSessionActor.FinalizeDeployable(obj : TelepadDeployable, tool, index) =>
+ StartBundlingPackets()
+ if(obj.Health > 0) {
+ val guid = obj.GUID
+ //router telepad deployable
+ val router = tool.asInstanceOf[Telepad].Router
+ //router must exist and be deployed
+ continent.GUID(router) match {
+ case Some(vehicle : Vehicle) =>
+ val routerGUID = router.get
+ if(vehicle.Health == 0) {
+ //the Telepad was successfully deployed; but, before it could configure, its Router was destroyed
+ sendResponse(ChatMsg(ChatMessageType.UNK_229, false, "", "@Telepad_NoDeploy_RouterLost", None))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent, Some(0 seconds)))
+ }
+ else {
+ log.info(s"FinalizeDeployable: setup for telepad #${guid.guid} in zone ${continent.Id}")
+ obj.Router = routerGUID //necessary; forwards link to the router
+ DeployableBuildActivity(obj)
+ CommonDestroyConstructionItem(tool, index)
+ StopBundlingPackets()
+ //it takes 60s for the telepad to become properly active
+ localService ! LocalServiceMessage.Telepads(RouterTelepadActivation.AddTask(obj, continent))
+ }
+
+ case _ =>
+ //the Telepad was successfully deployed; but, before it could configure, its Router was deconstructed
+ sendResponse(ChatMsg(ChatMessageType.UNK_229, false, "", "@Telepad_NoDeploy_RouterLost", None))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent, Some(0 seconds)))
+ }
+ }
+ StopBundlingPackets()
+
case WorldSessionActor.FinalizeDeployable(obj : SimpleDeployable, tool, index) =>
//tank_trap
StartBundlingPackets()
@@ -1132,8 +1165,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
DeconstructDeployable(obj, guid, pos)
}
else {
- DeconstructDeployable(obj, guid, pos, obj.Orientation, if(obj.MountPoints.isEmpty) 2
- else 1)
+ DeconstructDeployable(obj, guid, pos, obj.Orientation, if(obj.MountPoints.isEmpty) 2 else 1)
}
case LocalResponse.EliminateDeployable(obj : ComplexDeployable, guid, pos) =>
@@ -1152,6 +1184,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
DeconstructDeployable(obj, guid, pos, obj.Orientation, 2)
}
+ case LocalResponse.EliminateDeployable(obj : TelepadDeployable, guid, pos) =>
+ //if active, deactivate
+ if(obj.Active) {
+ obj.Active = false
+ sendResponse(GenericObjectActionMessage(guid, 116))
+ sendResponse(GenericObjectActionMessage(guid, 120))
+ }
+ //determine if no replacement teleport system exists
+ continent.GUID(obj.Router) match {
+ case Some(router : Vehicle) =>
+ //if the telepad was replaced, the new system is physically in place but not yet functional
+ if(router.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(internalTelepad : Utility.InternalTelepad) => internalTelepad.Telepad.contains(guid) //same telepad
+ case _ => true
+ }) {
+ //there is no replacement telepad; shut down the system
+ ToggleTeleportSystem(router, None)
+ }
+ case _ => ;
+ }
+ //standard deployable elimination behavior
+ if(obj.Health == 0) {
+ DeconstructDeployable(obj, guid, pos)
+ }
+ else {
+ DeconstructDeployable(obj, guid, pos, obj.Orientation, 2)
+ }
+
case LocalResponse.EliminateDeployable(obj, guid, pos) =>
if(obj.Health == 0) {
DeconstructDeployable(obj, guid, pos)
@@ -1215,9 +1275,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
}
+ case LocalResponse.RouterTelepadMessage(msg) =>
+ sendResponse(ChatMsg(ChatMessageType.UNK_229, false, "", msg, None))
+
+ case LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid) =>
+ StartBundlingPackets()
+ UseRouterTelepadEffect(passenger_guid, src_guid, dest_guid)
+ StopBundlingPackets()
+
case LocalResponse.SetEmpire(object_guid, empire) =>
sendResponse(SetEmpireMessage(object_guid, empire))
+ case LocalResponse.ToggleTeleportSystem(router, system_plan) =>
+ ToggleTeleportSystem(router, system_plan)
+
case LocalResponse.TriggerEffect(target_guid, effect, effectInfo, triggerLocation) =>
sendResponse(TriggerEffectMessage(target_guid, effect, effectInfo, triggerLocation))
@@ -1257,12 +1328,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
val obj_guid : PlanetSideGUID = obj.GUID
val player_guid : PlanetSideGUID = tplayer.GUID
log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num")
- vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
PlayerActionsToCancel()
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
sendResponse(PlanetsideAttributeMessage(obj_guid, 68, 0)) //shield health
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, 0)) //capacitor
if(seat_num == 0) {
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
//simplistic vehicle ownership management
obj.Owner match {
case Some(owner_guid) =>
@@ -1288,8 +1359,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
case _ => ; //no weapons to update
}
- //sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) //TODO vehicle max health in definition
- vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
AccessContents(obj)
MountingAction(tplayer, obj, seat_num)
@@ -1313,9 +1382,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
else {
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, seat_num, true, obj.GUID))
}
- if(obj.Seats.values.count(_.isOccupied) == 0) {
- vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, obj.Definition.DeconstructionTime)) //start vehicle decay
- }
case Mountable.CanDismount(obj : Mountable, _) =>
log.warn(s"DismountVehicleMsg: $obj is some generic mountable object and nothing will happen")
@@ -1468,7 +1534,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
}
- case Terminal.BuyEquipment(item) => ;
+ case Terminal.BuyEquipment(item) =>
tplayer.Fit(item) match {
case Some(index) =>
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
@@ -1797,12 +1863,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param reply na
*/
def HandleVehicleServiceResponse(toChannel : String, guid : PlanetSideGUID, reply : VehicleResponse.Response) : Unit = {
- val tplayer_guid = if(player.HasGUID) player.GUID
- else PlanetSideGUID(0)
- reply match {
- case VehicleResponse.Ownership(vehicle_guid) =>
- sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid))
+ val tplayer_guid = if(player.HasGUID) player.GUID else PlanetSideGUID(0)
+ reply match {
case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) =>
sendResponse(ObjectAttachMessage(pad_guid, vehicle_guid, 3))
@@ -1881,6 +1944,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
}
+ case VehicleResponse.Ownership(vehicle_guid) =>
+ sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid))
+
case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) =>
if(tplayer_guid != guid) {
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type, attribute_value))
@@ -1906,7 +1972,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
- case VehicleResponse.UnloadVehicle(vehicle_guid) =>
+ case VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) =>
+ BeforeUnloadVehicle(vehicle)
sendResponse(ObjectDeleteMessage(vehicle_guid, 0))
case VehicleResponse.UnstowEquipment(item_guid) =>
@@ -2079,9 +2146,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
val wep = slot.Equipment.get
avatarService ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
})
- if(target.Definition == GlobalDefinitions.ams) {
- target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
- ClearCurrentAmsSpawnPoint()
+ target.Definition match {
+ case GlobalDefinitions.ams =>
+ target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
+ ClearCurrentAmsSpawnPoint()
+ case GlobalDefinitions.router =>
+ target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
+ BeforeUnloadVehicle(target)
+ localService ! LocalServiceMessage(continent.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None))
+ case _ => ;
}
avatarService ! AvatarServiceMessage(continentId, AvatarAction.Destroy(targetGUID, playerGUID, playerGUID, target.Position))
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), continent))
@@ -2574,7 +2647,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
//seated players
obj.asInstanceOf[Mountable].Seats.values
- .map(_.Occupant)
+ .map(_.Occupant)
.collect {
case Some(occupant) =>
if(occupant.isAlive) {
@@ -2590,6 +2663,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
})
+ normal
+ .filter(_.Definition.DeployCategory == DeployableCategory.Sensors)
+ .foreach(obj => { sendResponse(TriggerEffectMessage(obj.GUID, "on", true, 1000)) })
//draw our faction's deployables on the map
continent.DeployableList
.filter(obj => obj.Faction == faction && obj.Health > 0)
@@ -2624,7 +2700,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
//load vehicles in zone
val (wreckages, vehicles) = continent.Vehicles.partition(vehicle => { vehicle.Health == 0 && vehicle.Definition.DestroyedModel.nonEmpty })
- //active vehicles
+ //active vehicles (and some wreckage)
vehicles.foreach(vehicle => {
val vehicle_guid = vehicle.GUID
val vdefinition = vehicle.Definition
@@ -2646,22 +2722,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
ReloadVehicleAccessPermissions(vehicle)
})
- //Loop over vehicles again to add cargohold occupants after all vehicles have been created on the local client
- vehicles.foreach(vehicle => {
- vehicle.CargoHolds.foreach({ case (cargo_num, cargo) => {
- cargo.Occupant match {
- case Some(cargo_vehicle) =>
- if(cargo_vehicle.HasGUID) {
- StartBundlingPackets()
- sendResponse(ObjectAttachMessage(cargo_vehicle.GUID, vehicle.GUID, cargo_num))
- //todo: attaching the vehicle seems to work, but setting the mount point status doesn't?
- sendResponse(CargoMountPointStatusMessage(cargo_vehicle.GUID, vehicle.GUID, vehicle.GUID, PlanetSideGUID(0), cargo_num, CargoStatus.Occupied, 0))
- StopBundlingPackets()
- }
- case None => ; // No vehicle in cargo
- }
- }})
- })
//vehicle wreckages
wreckages.foreach(vehicle => {
sendResponse(
@@ -2672,6 +2732,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
)
})
+ //Loop over vehicles again to add cargohold occupants after all vehicles have been created on the local client
+ vehicles.filter(_.CargoHolds.nonEmpty).foreach(vehicle => {
+ vehicle.CargoHolds.foreach({ case (cargo_num, cargo) => {
+ cargo.Occupant match {
+ case Some(cargo_vehicle) =>
+ if(cargo_vehicle.HasGUID) {
+ sendResponse(ObjectAttachMessage(cargo_vehicle.GUID, vehicle.GUID, cargo_num))
+ //todo: attaching the vehicle seems to work, but setting the mount point status doesn't?
+ sendResponse(CargoMountPointStatusMessage(cargo_vehicle.GUID, vehicle.GUID, vehicle.GUID, PlanetSideGUID(0), cargo_num, CargoStatus.Occupied, 0))
+ }
+ case None => ; // No vehicle in cargo
+ }
+ }})
+ })
+ //special deploy states
+ val deployedVehicles = vehicles.filter(_.DeploymentState == DriveState.Deployed)
+ deployedVehicles.filter(_.Definition == GlobalDefinitions.ams).foreach(obj => {
+ sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
+ })
+ deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach(obj => {
+ sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, false, Vector3.Zero))
+ sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, false, Vector3.Zero))
+ ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
+ })
//implant terminals
continent.Map.TerminalToInterface.foreach({ case ((terminal_guid, interface_guid)) =>
@@ -3054,7 +3138,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(trigger : BoomerTrigger) =>
val playerGUID = player.GUID
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(playerGUID, item_guid))
- continent.GUID(trigger.Companion.getOrElse(PlanetSideGUID(0))) match {
+ continent.GUID(trigger.Companion) match {
case Some(boomer : BoomerDeployable) =>
val boomerGUID = boomer.GUID
boomer.Exploded = true
@@ -3231,7 +3315,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(obj : BoomerTrigger) =>
if(FindEquipmentToDelete(object_guid, obj)) {
- continent.GUID(obj.Companion.getOrElse(PlanetSideGUID(0))) match {
+ continent.GUID(obj.Companion) match {
case Some(boomer : BoomerDeployable) =>
boomer.Trigger = None
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(boomer, continent, Some(0 seconds)))
@@ -3279,7 +3363,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ;
}
+ case Some(obj : TelepadDeployable) =>
+ localService ! LocalServiceMessage.Telepads(SupportActor.ClearSpecific(List(obj), continent))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(obj), continent))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent, Some(0 seconds)))
+
case Some(obj : PlanetSideGameObject with Deployable) =>
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(obj), continent))
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent, Some(0 seconds)))
case Some(thing) =>
@@ -3618,38 +3708,50 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case Some(terminal : Terminal) =>
- if(terminal.Definition.isInstanceOf[MatrixTerminalDefinition]) {
- //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
- sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, terminal.Position))
- }
- else if(terminal.Definition.isInstanceOf[RepairRearmSiloDefinition]) {
- FindLocalVehicle match {
- case Some(vehicle) =>
- sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
- sendResponse(UseItemMessage(avatar_guid, item_used_guid, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId))
- case None =>
- log.error("UseItem: expected seated vehicle, but found none")
+ val tdef = terminal.Definition
+ val owned = terminal.Faction == player.Faction
+ val hacked = terminal.HackedBy.nonEmpty
+ if(owned) {
+ if(tdef.isInstanceOf[MatrixTerminalDefinition]) {
+ //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
+ sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, terminal.Position))
+ }
+ else if(tdef.isInstanceOf[RepairRearmSiloDefinition]) {
+ FindLocalVehicle match {
+ case Some(vehicle) =>
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId))
+ case None =>
+ log.error("UseItem: expected seated vehicle, but found none")
+ }
+ }
+ else if(tdef.isInstanceOf[TeleportPadTerminalDefinition]) {
+ //explicit request
+ terminal.Actor ! Terminal.Request(
+ player,
+ ItemTransactionMessage(object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0))
+ )
+ }
+ else {
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
}
}
+ else if(hacked) {
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ }
else {
- if(terminal.Faction != player.Faction && terminal.HackedBy.isEmpty) {
- player.Slot(player.DrawnSlot).Equipment match {
- case Some(tool: SimpleItem) =>
- if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
- val hackSpeed = GetPlayerHackSpeed(terminal)
+ player.Slot(player.DrawnSlot).Equipment match {
+ case Some(tool: SimpleItem) =>
+ if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
+ val hackSpeed = GetPlayerHackSpeed(terminal)
- if(hackSpeed > 0) {
- progressBarValue = Some(-hackSpeed)
- self ! WorldSessionActor.HackingProgress(progressType = 1, player, terminal, tool.GUID, hackSpeed, FinishHacking(terminal, 3212836864L))
- log.info("Hacking a terminal")
- }
+ if(hackSpeed > 0) {
+ progressBarValue = Some(-hackSpeed)
+ self ! WorldSessionActor.HackingProgress(progressType = 1, player, terminal, tool.GUID, hackSpeed, FinishHacking(terminal, 3212836864L))
+ log.info("Hacking a terminal")
}
- case _ => ;
- }
- } else if (terminal.Faction == player.Faction || !terminal.HackedBy.isEmpty) {
- // If hacked only allow access to the faction that hacked it
- // Otherwise allow the faction that owns the terminal to use it
- sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ }
+ case _ => ;
}
}
@@ -3662,6 +3764,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, true))
continent.Population ! Zone.Population.Release(avatar)
+ case Some(obj : TelepadDeployable) =>
+ continent.GUID(obj.Router) match {
+ case Some(vehicle : Vehicle) =>
+ vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(util : Utility.InternalTelepad) =>
+ UseRouterTelepadSystem(router = vehicle, internalTelepad = util, remoteTelepad = obj, src = obj, dest = util)
+ case _ =>
+ log.error(s"telepad@${object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}@${obj.Router.get.guid}")
+ }
+ case Some(o) =>
+ log.error(s"telepad@${object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}@${obj.Router.get.guid}")
+ case None => ;
+ }
+
+ case Some(obj : Utility.InternalTelepad) =>
+ continent.GUID(obj.Telepad) match {
+ case Some(pad : TelepadDeployable) =>
+ UseRouterTelepadSystem(router = obj.Owner.asInstanceOf[Vehicle], internalTelepad = obj, remoteTelepad = pad, src = obj, dest = pad)
+ case Some(o) =>
+ log.error(s"internal telepad@${object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}")
+ case None => ;
+ }
+
case Some(obj) =>
log.warn(s"UseItem: don't know how to handle $obj; taking a shot in the dark")
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
@@ -3719,7 +3844,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case turret =>
turret
}
- log.info(s"Constructing a ${ammoType}")
+ log.info(s"DeployObject: Constructing a ${ammoType}")
val dObj : PlanetSideGameObject with Deployable = Deployables.Make(ammoType)()
dObj.Position = pos
dObj.Orientation = orient
@@ -3735,9 +3860,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
taskResolver ! CallBackForTask(tasking, continent.Deployables, Zone.Deployable.Build(dObj, obj))
case Some(obj) =>
- log.warn(s"$obj is something?")
+ log.warn(s"DeployObject: $obj is something?")
case None =>
- log.warn("nothing?")
+ log.warn("DeployObject: nothing?")
}
@@ -3896,7 +4021,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"Hit: $msg")
(hit_info match {
case Some(hitInfo) =>
- continent.GUID(hitInfo.hitobject_guid.get) match {
+ continent.GUID(hitInfo.hitobject_guid) match {
case Some(obj : Player) =>
Some((obj, hitInfo.shot_origin, hitInfo.hit_pos))
case Some(obj : Vehicle) =>
@@ -3995,6 +4120,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//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
if(bailType == BailType.Bailed && seat_num == 0 && GlobalDefinitions.isFlightVehicle(obj.asInstanceOf[Vehicle].Definition)) {
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent))
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, Some(0 seconds))) // Immediately deconstruct vehicle
}
@@ -4153,7 +4279,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
-
case msg @ TargetingImplantRequest(list) =>
log.info("TargetingImplantRequest: "+msg)
@@ -4730,7 +4855,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* Disassociate this client's player (oneself) from a vehicle that he owns.
*/
- def DisownVehicle() : Unit = DisownVehicle(player)
+ def DisownVehicle() : Option[Vehicle] = DisownVehicle(player)
/**
* Disassociate a player from a vehicle that he owns.
@@ -4740,30 +4865,38 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @see `DisownVehicle(Player, Vehicle)`
* @param tplayer the player
*/
- def DisownVehicle(tplayer : Player) : Unit = {
+ def DisownVehicle(tplayer : Player) : Option[Vehicle] = {
tplayer.VehicleOwned match {
case Some(vehicle_guid) =>
+ tplayer.VehicleOwned = None
continent.GUID(vehicle_guid) match {
case Some(vehicle : Vehicle) =>
DisownVehicle(tplayer, vehicle)
- case _ => ;
+ case _ =>
+ None
}
- tplayer.VehicleOwned = None
- case None => ;
+ case None =>
+ None
}
}
/**
- * Disassociate a vehicle from the player that owns it.
- * When a vehicle is disowned
+ * Disassociate a vehicle from the player that owns it, if that player really was the previous owner.
* This is the vehicle side of vehicle ownership removal.
+ * Additionally, start the vehicle deconstruction timer.
* @see `DisownVehicle(Player)`
* @param tplayer the player
* @param vehicle the discovered vehicle
*/
- private def DisownVehicle(tplayer : Player, vehicle : Vehicle) : Unit = {
+ private def DisownVehicle(tplayer : Player, vehicle : Vehicle) : Option[Vehicle] = {
if(vehicle.Owner.contains(tplayer.GUID)) {
vehicle.Owner = None
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), continent))
+ vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, vehicle.Definition.DeconstructionTime)) //start vehicle decay
+ Some(vehicle)
+ }
+ else {
+ None
}
}
@@ -5299,13 +5432,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
def PermitEquipmentStow(equipment : Equipment, obj : PlanetSideGameObject with Container) : Boolean = {
equipment match {
- case item : BoomerTrigger =>
+ case _ : BoomerTrigger =>
obj.isInstanceOf[Player] //a BoomerTrigger can only be stowed in a player's holsters or inventory
case _ =>
true
}
}
+ /**
+ * na
+ * @param tool na
+ * @param obj na
+ */
def PerformToolAmmoChange(tool : Tool, obj : PlanetSideGameObject with Container) : Unit = {
val originalAmmoType = tool.AmmoType
do {
@@ -5514,6 +5652,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Drop the item if:
* - the item is cavern equipment
* - the item is a `BoomerTrigger` type object
+ * - the item is a `router_telepad` type object
* - the item is another faction's exclusive equipment
* @param tplayer the player
* @return true if the item is to be dropped; false, otherwise
@@ -5522,6 +5661,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
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)
}
@@ -5546,46 +5686,88 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* Perform specific operations depending on the target of deployment.
- * @param obj the object that has deployed
+ * @param obj the object that has had its deployment state changed
*/
def DeploymentActivities(obj : Deployment.DeploymentObject) : Unit = {
+ DeploymentActivities(obj, obj.DeploymentState)
+ }
+
+ /**
+ * Perform specific operations depending on the target of deployment.
+ * @param obj the object that has had its deployment state changed
+ * @param state the new deployment state
+ */
+ def DeploymentActivities(obj : Deployment.DeploymentObject, state : DriveState.Value) : Unit = {
obj match {
case vehicle : Vehicle =>
ReloadVehicleAccessPermissions(vehicle) //TODO we should not have to do this imho
-
- if(obj.Definition == GlobalDefinitions.ams) {
- obj.DeploymentState match {
+ //ams
+ if(vehicle.Definition == GlobalDefinitions.ams) {
+ state match {
case DriveState.Deployed =>
vehicleService ! VehicleServiceMessage.AMSDeploymentChange(continent)
- sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 1))
+ sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 1))
case DriveState.Undeploying =>
vehicleService ! VehicleServiceMessage.AMSDeploymentChange(continent)
- sendResponse(PlanetsideAttributeMessage(obj.GUID, 81, 0))
+ sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 81, 0))
case DriveState.Mobile | DriveState.State7 =>
case _ => ;
}
}
- if(obj.Definition == GlobalDefinitions.ant) {
- obj.DeploymentState match {
- case DriveState.Deployed =>
- // We only want this WSA (not other player's WSA) to manage timers
- if(vehicle.Seat(0).get.Occupant.contains(player)){
- // Start ntu regeneration
- // If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
- antChargingTick = context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuCharging(player, vehicle))
- }
- case DriveState.Undeploying =>
- // We only want this WSA (not other player's WSA) to manage timers
- if(vehicle.Seat(0).get.Occupant.contains(player)){
- antChargingTick.cancel() // Stop charging NTU if charging
- }
+ //ant
+ else if(vehicle.Definition == GlobalDefinitions.ant) {
+ state match {
+ case DriveState.Deployed =>
+ // We only want this WSA (not other player's WSA) to manage timers
+ if(vehicle.Seat(0).get.Occupant.contains(player)){
+ // Start ntu regeneration
+ // If vehicle sends UseItemMessage with silo as target NTU regeneration will be disabled and orb particles will be disabled
+ antChargingTick = context.system.scheduler.scheduleOnce(1000 milliseconds, self, NtuCharging(player, vehicle))
+ }
+ case DriveState.Undeploying =>
+ // We only want this WSA (not other player's WSA) to manage timers
+ if(vehicle.Seat(0).get.Occupant.contains(player)){
+ antChargingTick.cancel() // Stop charging NTU if charging
+ }
- avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 52, 0L)) // panel glow off
- avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 49, 0L)) // orb particles off
- case DriveState.Mobile | DriveState.State7 | DriveState.Deploying =>
- case _ => ;
- }
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 52, 0L)) // panel glow off
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(vehicle.GUID, 49, 0L)) // orb particles off
+ case DriveState.Mobile | DriveState.State7 | DriveState.Deploying =>
+ case _ => ;
}
+ }
+ //router
+ else if(vehicle.Definition == GlobalDefinitions.router) {
+ state match {
+ case DriveState.Deploying =>
+ vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(util : Utility.InternalTelepad) =>
+ util.Active = true
+ case _ =>
+ log.warn(s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state")
+ }
+ case DriveState.Deployed =>
+ //let the timer do all the work
+ localService ! LocalServiceMessage(continent.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), vehicle, TelepadLike.AppraiseTeleportationSystem(vehicle, continent)))
+ case DriveState.Undeploying =>
+ //deactivate internal router before trying to reset the system
+ vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(util : Utility.InternalTelepad) =>
+ //any telepads linked with internal mechanism must be deconstructed
+ continent.GUID(util.Telepad) match {
+ case Some(telepad : TelepadDeployable) =>
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), continent))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, continent, Some(0 milliseconds)))
+ case Some(_) | None => ;
+ }
+ util.Active = false
+ localService ! LocalServiceMessage(continent.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), vehicle, None))
+ case _ =>
+ log.warn(s"DeploymentActivities: could not find internal telepad in router@${vehicle.GUID.guid} while $state")
+ }
+ case _ => ;
+ }
+ }
case _ => ;
}
}
@@ -5934,7 +6116,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
})
val triggers = RemoveBoomerTriggersFromInventory()
- triggers.foreach(trigger =>{ NormalItemDrop(obj, continent, avatarService)(trigger) })
+ triggers.foreach(trigger => { NormalItemDrop(obj, continent, avatarService)(trigger) })
}
}
@@ -6628,7 +6810,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val item = definition.Item
val deployables = avatar.Deployables
val (curr, max) = deployables.CountDeployable(item)
- log.info(s"FinalizeDeployable: ${definition.Name}")
+ log.info(s"DeployableBuildActivity: ${definition.Name}")
//two potential messages related to numerical limitations of deployables
if(!avatar.Deployables.Available(obj)) {
val (removed, msg) = {
@@ -6640,9 +6822,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
removed match {
+ case Some(telepad : TelepadDeployable) =>
+ telepad.Owner = None
+ telepad.OwnerName = None
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), continent))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, continent, Some(0 seconds))) //normal decay
case Some(old) =>
- old.Position = Vector3.Zero
old.Owner = None
+ old.OwnerName = None
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(old), continent))
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(old, continent, Some(0 seconds)))
if(msg) { //max test
sendResponse(ChatMsg(ChatMessageType.UNK_229, false, "", s"@${definition.Descriptor}OldestDestroyed", None))
@@ -6651,6 +6839,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn(s"DeployableBuildActivity: how awkward: we probably shouldn't be allowed to build this deployable right now")
}
}
+ else if(obj.isInstanceOf[TelepadDeployable]) {
+ //always treat the telepad we are putting down as the first and only one
+ sendResponse(ObjectDeployedMessage.Success(definition.Name, 1, 1))
+ }
else {
sendResponse(ObjectDeployedMessage.Success(definition.Name, curr + 1, max))
val (catCurr, catMax) = deployables.CountCategory(item)
@@ -6940,9 +7132,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
def RemoveBoomerTriggersFromInventory() : List[BoomerTrigger] = {
val player_guid = player.GUID
- ((player.Inventory.Items.collect({ case entry @ InventoryItem(obj : BoomerTrigger, index) => (obj, index) })) ++
- (player.Holsters()
- .zipWithIndex
+ val holstersWithIndex = player.Holsters().zipWithIndex
+ ((player.Inventory.Items.collect({ case InventoryItem(obj : BoomerTrigger, index) => (obj, index) })) ++
+ (holstersWithIndex
.map({ case ((slot, index)) => (slot.Equipment, index) })
.collect { case ((Some(obj : BoomerTrigger), index)) => (obj, index) }
)
@@ -7128,6 +7320,158 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ /**
+ * Attempt to link the router teleport system using the provided terminal information.
+ * Although additional states are necessary to properly use the teleportation system,
+ * e.g., deployment state, active state of the endpoints, etc.,
+ * this decision is not made factoring those other conditions.
+ * @param router the vehicle that houses one end of the teleportation system (the `InternalTelepad` object)
+ * @param systemPlan specific object identification of the two endpoints of the teleportation system;
+ * if absent, the knowable endpoint is deleted from the client reflexively
+ */
+ def ToggleTeleportSystem(router : Vehicle, systemPlan : Option[(Utility.InternalTelepad, TelepadDeployable)]) : Unit = {
+ StartBundlingPackets()
+ systemPlan match {
+ case Some((internalTelepad, remoteTelepad)) =>
+ LinkRouterToRemoteTelepad(router, internalTelepad, remoteTelepad)
+ case _ =>
+ router.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(util : Utility.InternalTelepad) =>
+ sendResponse(ObjectDeleteMessage(util.GUID, 0))
+ case _ => ;
+ }
+ }
+ StopBundlingPackets()
+ }
+
+ /**
+ * Link the router teleport system using the provided terminal information.
+ * The internal telepad is made known of the remote telepad, creating the link.
+ * @param router the vehicle that houses one end of the teleportation system (the `internalTelepad`)
+ * @param internalTelepad the endpoint of the teleportation system housed by the router
+ * @param remoteTelepad the endpoint of the teleportation system that exists in the environment
+ */
+ def LinkRouterToRemoteTelepad(router : Vehicle, internalTelepad : Utility.InternalTelepad, remoteTelepad : TelepadDeployable) : Unit = {
+ internalTelepad.Telepad = remoteTelepad.GUID //necessary; backwards link to the (new) telepad
+ CreateRouterInternalTelepad(router, internalTelepad)
+ LinkRemoteTelepad(remoteTelepad.GUID)
+ }
+
+ /**
+ * Create the mechanism that serves as one endpoint of the linked router teleportation system.
+ *
+ * Technically, the mechanism - an `InternalTelepad` object - is always made to exist
+ * due to how the Router vehicle object is encoded into an `ObjectCreateMessage` packet.
+ * Regardless, that internal mechanism is created anew each time the system links a new remote telepad.
+ * @param router the vehicle that houses one end of the teleportation system (the `internalTelepad`)
+ * @param internalTelepad the endpoint of the teleportation system housed by the router
+ */
+ def CreateRouterInternalTelepad(router : Vehicle, internalTelepad : PlanetSideGameObject with TelepadLike) : Unit = {
+ //create the interal telepad each time the link is made
+ val rguid = router.GUID
+ val uguid = internalTelepad.GUID
+ val udef = internalTelepad.Definition
+ /*
+ the following instantiation and configuration creates the internal Router component
+ normally dispatched while the Router is transitioned into its Deploying state
+ it is safe, however, to perform these actions at any time during and after the Deploying state
+ */
+ sendResponse(
+ ObjectCreateMessage(
+ udef.ObjectId,
+ uguid,
+ ObjectCreateMessageParent(rguid, 2), //TODO stop assuming slot number
+ udef.Packet.ConstructorData(internalTelepad).get
+ )
+ )
+ sendResponse(GenericObjectActionMessage(uguid, 108))
+ sendResponse(GenericObjectActionMessage(uguid, 120))
+ /*
+ the following configurations create the interactive beam underneath the Deployed Router
+ normally dispatched after the warm-up timer has completed
+ */
+ sendResponse(GenericObjectActionMessage(uguid, 108))
+ sendResponse(GenericObjectActionMessage(uguid, 112))
+ }
+
+ /**
+ * na
+ * @param telepadGUID na
+ */
+ def LinkRemoteTelepad(telepadGUID: PlanetSideGUID) : Unit = {
+ sendResponse(GenericObjectActionMessage(telepadGUID, 108))
+ sendResponse(GenericObjectActionMessage(telepadGUID, 112))
+ }
+
+ /**
+ * A player uses a fully-linked Router teleportation system.
+ * @param router the Router vehicle
+ * @param internalTelepad the internal telepad within the Router vehicle
+ * @param remoteTelepad the remote telepad that is currently associated with this Router
+ * @param src the origin of the teleportation (where the player starts)
+ * @param dest the destination of the teleportation (where the player is going)
+ */
+ def UseRouterTelepadSystem(router: Vehicle, internalTelepad: InternalTelepad, remoteTelepad: TelepadDeployable, src: PlanetSideGameObject with TelepadLike, dest: PlanetSideGameObject with TelepadLike) = {
+ val time = System.nanoTime
+ if(time - recentTeleportAttempt > (2 seconds).toNanos && router.DeploymentState == DriveState.Deployed && internalTelepad.Active && remoteTelepad.Active) {
+ val pguid = player.GUID
+ val sguid = src.GUID
+ val dguid = dest.GUID
+ StartBundlingPackets()
+ sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z, player.Velocity)))
+ UseRouterTelepadEffect(pguid, sguid, dguid)
+ StopBundlingPackets()
+// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(router), continent))
+// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(router, continent, router.Definition.DeconstructionTime))
+ localService ! LocalServiceMessage(continent.Id, LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid))
+ }
+ else {
+ log.warn(s"UseRouterTelepadSystem: can not teleport")
+ }
+ recentTeleportAttempt = time
+ }
+
+ /**
+ * Animate(?) a player using a fully-linked Router teleportation system.
+ * In reality, this seems to do nothing visually?
+ * @param playerGUID the player being teleported
+ * @param srcGUID the origin of the teleportation
+ * @param destGUID the destination of the teleportation
+ */
+ def UseRouterTelepadEffect(playerGUID : PlanetSideGUID, srcGUID : PlanetSideGUID, destGUID : PlanetSideGUID) : Unit = {
+ sendResponse(PlanetsideAttributeMessage(playerGUID, 64, 1)) //what does this do?
+ sendResponse(GenericObjectActionMessage(srcGUID, 124))
+ sendResponse(GenericObjectActionMessage(destGUID, 128))
+ }
+
+ /**
+ * Before a vehicle is removed from the game world, the following actions must be performed.
+ * @param vehicle the vehicle
+ */
+ def BeforeUnloadVehicle(vehicle : Vehicle) : Unit = {
+ vehicle.Definition match {
+ case GlobalDefinitions.router =>
+ log.info("BeforeUnload: cleaning up after a router ...")
+ (vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
+ case Some(util : Utility.InternalTelepad) =>
+ val telepad = util.Telepad
+ util.Active = false
+ util.Telepad = None
+ continent.GUID(telepad)
+ case _ =>
+ None
+ }) match {
+ case Some(telepad : TelepadDeployable) =>
+ log.info(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
+ telepad.Active = false
+ localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), continent))
+ localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, continent, Some(0 seconds)))
+ case _ => ;
+ }
+ case _ => ;
+ }
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())