mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +00:00
Merge pull request #197 from Fate-JH/med-term
Proximity Terminals/Medical
This commit is contained in:
commit
dbc2ea8084
15 changed files with 770 additions and 4 deletions
|
|
@ -515,6 +515,14 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val respawn_tube_tower = new SpawnTubeDefinition(733)
|
val respawn_tube_tower = new SpawnTubeDefinition(733)
|
||||||
|
|
||||||
|
val adv_med_terminal = new MedicalTerminalDefinition(38)
|
||||||
|
|
||||||
|
val crystals_health_a = new MedicalTerminalDefinition(225)
|
||||||
|
|
||||||
|
val crystals_health_b = new MedicalTerminalDefinition(226)
|
||||||
|
|
||||||
|
val medical_terminal = new MedicalTerminalDefinition(529)
|
||||||
|
|
||||||
val spawn_pad = new VehicleSpawnPadDefinition
|
val spawn_pad = new VehicleSpawnPadDefinition
|
||||||
|
|
||||||
val mb_locker = new LockerDefinition
|
val mb_locker = new LockerDefinition
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import net.psforever.objects.Player
|
||||||
|
|
||||||
//temporary location for these messages
|
//temporary location for these messages
|
||||||
object CommonMessages {
|
object CommonMessages {
|
||||||
|
final case class Use(player : Player)
|
||||||
|
final case class Unuse(player : Player)
|
||||||
final case class Hack(player : Player)
|
final case class Hack(player : Player)
|
||||||
final case class ClearHack()
|
final case class ClearHack()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.packet.game.ItemTransactionMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The definition for any `Terminal` that is of a type "medical_terminal".
|
||||||
|
* This includes the limited proximity-based functionality of the formal medical terminals
|
||||||
|
* and the actual proximity-based functionality of the cavern crystals.<br>
|
||||||
|
* <br>
|
||||||
|
* Do not confuse the "medical_terminal" category and the actual `medical_terminal` object (529).
|
||||||
|
* Objects created by this definition being linked by their use of `ProximityTerminalUseMessage` is more accurate.
|
||||||
|
*/
|
||||||
|
class MedicalTerminalDefinition(objectId : Int) extends TerminalDefinition(objectId) {
|
||||||
|
Name = if(objectId == 38) {
|
||||||
|
"adv_med_terminal"
|
||||||
|
}
|
||||||
|
else if(objectId == 225) {
|
||||||
|
"crystals_health_a"
|
||||||
|
}
|
||||||
|
else if(objectId == 226) {
|
||||||
|
"crystals_health_b"
|
||||||
|
}
|
||||||
|
else if(objectId == 529) {
|
||||||
|
"medical_terminal"
|
||||||
|
}
|
||||||
|
else if(objectId == 689) {
|
||||||
|
"portable_med_terminal"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("medical terminal must be either object id 38, 225, 226, 529, or 689")
|
||||||
|
}
|
||||||
|
|
||||||
|
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A server object that is a "terminal" that can be accessed for amenities and services,
|
||||||
|
* triggered when a certain distance from the unit itself (proximity-based).<br>
|
||||||
|
* <br>
|
||||||
|
* Unlike conventional terminals, this structure is not necessarily structure-owned.
|
||||||
|
* For example, the cavern crystals are considered owner-neutral elements that are not attached to a `Building` object.
|
||||||
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
|
*/
|
||||||
|
class ProximityTerminal(tdef : MedicalTerminalDefinition) extends Terminal(tdef) {
|
||||||
|
private var users : Set[PlanetSideGUID] = Set.empty
|
||||||
|
|
||||||
|
def NumberUsers : Int = users.size
|
||||||
|
|
||||||
|
def AddUser(player_guid : PlanetSideGUID) : Int = {
|
||||||
|
users += player_guid
|
||||||
|
NumberUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
def RemoveUser(player_guid : PlanetSideGUID) : Int = {
|
||||||
|
users -= player_guid
|
||||||
|
NumberUsers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProximityTerminal {
|
||||||
|
/**
|
||||||
|
* Overloaded constructor.
|
||||||
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
|
*/
|
||||||
|
def apply(tdef : MedicalTerminalDefinition) : ProximityTerminal = {
|
||||||
|
new ProximityTerminal(tdef)
|
||||||
|
}
|
||||||
|
|
||||||
|
import akka.actor.ActorContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate an configure a `Terminal` object
|
||||||
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
|
* @param id the unique id that will be assigned to this entity
|
||||||
|
* @param context a context to allow the object to properly set up `ActorSystem` functionality
|
||||||
|
* @return the `Terminal` object
|
||||||
|
*/
|
||||||
|
def Constructor(tdef : MedicalTerminalDefinition)(id : Int, context : ActorContext) : Terminal = {
|
||||||
|
import akka.actor.Props
|
||||||
|
val obj = ProximityTerminal(tdef)
|
||||||
|
obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), s"${tdef.Name}_$id")
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* An `Actor` that handles messages being dispatched to a specific `ProximityTerminal`.
|
||||||
|
* Although this "terminal" itself does not accept the same messages as a normal `Terminal` object,
|
||||||
|
* it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`.
|
||||||
|
* @param term the proximity unit (terminal)
|
||||||
|
*/
|
||||||
|
class ProximityTerminalControl(term : ProximityTerminal) extends Actor with FactionAffinityBehavior.Check {
|
||||||
|
def FactionObject : FactionAffinity = term
|
||||||
|
|
||||||
|
def receive : Receive = checkBehavior.orElse {
|
||||||
|
case CommonMessages.Use(player) =>
|
||||||
|
val hadNoUsers = term.NumberUsers == 0
|
||||||
|
if(term.AddUser(player.GUID) == 1 && hadNoUsers) {
|
||||||
|
sender ! TerminalMessage(player, null, Terminal.StartProximityEffect(term))
|
||||||
|
}
|
||||||
|
|
||||||
|
case CommonMessages.Unuse(player) =>
|
||||||
|
val hadUsers = term.NumberUsers > 0
|
||||||
|
if(term.RemoveUser(player.GUID) == 0 && hadUsers) {
|
||||||
|
sender ! TerminalMessage(player, null, Terminal.StopProximityEffect(term))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
sender ! Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString : String = term.Definition.Name
|
||||||
|
}
|
||||||
|
|
@ -190,6 +190,18 @@ object Terminal {
|
||||||
*/
|
*/
|
||||||
final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange
|
final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the special effects caused by a proximity-base service.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
final case class StartProximityEffect(terminal : ProximityTerminal) extends Exchange
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the special effects caused by a proximity-base service.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
final case class StopProximityEffect(terminal : ProximityTerminal) extends Exchange
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor.
|
* Overloaded constructor.
|
||||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.objects.serverobject.ServerObjectBuilder
|
import net.psforever.objects.serverobject.ServerObjectBuilder
|
||||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
||||||
|
import net.psforever.objects.serverobject.terminals.ProximityTerminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
|
@ -130,6 +131,24 @@ class TerminalObjectBuilderTest extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProximityTerminalObjectBuilderTest extends ActorTest {
|
||||||
|
import net.psforever.objects.GlobalDefinitions.medical_terminal
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
|
"Terminal object" should {
|
||||||
|
"build" in {
|
||||||
|
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||||
|
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, ProximityTerminal.Constructor(medical_terminal)), hub), "term")
|
||||||
|
actor ! "!"
|
||||||
|
|
||||||
|
val reply = receiveOne(Duration.create(1000, "ms"))
|
||||||
|
assert(reply.isInstanceOf[Terminal])
|
||||||
|
assert(reply.asInstanceOf[Terminal].HasGUID)
|
||||||
|
assert(reply.asInstanceOf[Terminal].GUID == PlanetSideGUID(1))
|
||||||
|
assert(reply == hub(1).get)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class VehicleSpawnPadObjectBuilderTest extends ActorTest {
|
class VehicleSpawnPadObjectBuilderTest extends ActorTest {
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
"Vehicle spawn pad object" should {
|
"Vehicle spawn pad object" should {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects.terminal
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityTerminal, Terminal}
|
||||||
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
|
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||||
|
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
class MedicalTerminalTest extends Specification {
|
||||||
|
"MedicalTerminal" should {
|
||||||
|
"define (a)" in {
|
||||||
|
val a = new MedicalTerminalDefinition(38)
|
||||||
|
a.ObjectId mustEqual 38
|
||||||
|
a.Name mustEqual "adv_med_terminal"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (b)" in {
|
||||||
|
val b = new MedicalTerminalDefinition(225)
|
||||||
|
b.ObjectId mustEqual 225
|
||||||
|
b.Name mustEqual "crystals_health_a"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (c)" in {
|
||||||
|
val c = new MedicalTerminalDefinition(226)
|
||||||
|
c.ObjectId mustEqual 226
|
||||||
|
c.Name mustEqual "crystals_health_b"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (d)" in {
|
||||||
|
val d = new MedicalTerminalDefinition(529)
|
||||||
|
d.ObjectId mustEqual 529
|
||||||
|
d.Name mustEqual "medical_terminal"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (e)" in {
|
||||||
|
val e = new MedicalTerminalDefinition(689)
|
||||||
|
e.ObjectId mustEqual 689
|
||||||
|
e.Name mustEqual "portable_med_terminal"
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (invalid)" in {
|
||||||
|
var id : Int = (math.random * Int.MaxValue).toInt
|
||||||
|
if(id == 224) {
|
||||||
|
id += 2
|
||||||
|
}
|
||||||
|
else if(id == 37) {
|
||||||
|
id += 1
|
||||||
|
}
|
||||||
|
else if(id == 528) {
|
||||||
|
id += 1
|
||||||
|
}
|
||||||
|
else if(id == 688) {
|
||||||
|
id += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
new MedicalTerminalDefinition(id) must throwA[IllegalArgumentException]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Medical_Terminal" should {
|
||||||
|
"construct" in {
|
||||||
|
ProximityTerminal(GlobalDefinitions.medical_terminal).Actor mustEqual ActorRef.noSender
|
||||||
|
}
|
||||||
|
|
||||||
|
"can add a player to a list of users" in {
|
||||||
|
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
|
terminal.NumberUsers mustEqual 0
|
||||||
|
terminal.AddUser(PlanetSideGUID(10))
|
||||||
|
terminal.NumberUsers mustEqual 1
|
||||||
|
}
|
||||||
|
|
||||||
|
"can remove a player from a list of users" in {
|
||||||
|
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
|
terminal.AddUser(PlanetSideGUID(10))
|
||||||
|
terminal.NumberUsers mustEqual 1
|
||||||
|
terminal.RemoveUser(PlanetSideGUID(10))
|
||||||
|
terminal.NumberUsers mustEqual 0
|
||||||
|
}
|
||||||
|
|
||||||
|
"player can not interact with the proximity terminal normally (buy)" in {
|
||||||
|
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
||||||
|
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
|
||||||
|
|
||||||
|
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects.terminal
|
||||||
|
|
||||||
|
import akka.actor.{ActorSystem, Props}
|
||||||
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
|
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
|
||||||
|
import net.psforever.objects.serverobject.terminals._
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||||
|
import objects.ActorTest
|
||||||
|
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
|
class ProximityTerminalControl1Test extends ActorTest() {
|
||||||
|
"ProximityTerminalControl" should {
|
||||||
|
"construct (medical terminal)" in {
|
||||||
|
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
|
||||||
|
terminal.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], terminal), "test-term")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProximityTerminalControl2Test extends ActorTest() {
|
||||||
|
"ProximityTerminalControl can not process wrong messages" in {
|
||||||
|
val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
|
|
||||||
|
terminal.Actor !"hello"
|
||||||
|
val reply = receiveOne(Duration.create(500, "ms"))
|
||||||
|
assert(reply.isInstanceOf[Terminal.NoDeal])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol
|
||||||
|
class MedicalTerminalControl1Test extends ActorTest() {
|
||||||
|
"ProximityTerminalControl sends a message to the first new user only" in {
|
||||||
|
val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
|
player.GUID = PlanetSideGUID(10)
|
||||||
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
||||||
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
val reply = receiveOne(Duration.create(500, "ms"))
|
||||||
|
assert(reply.isInstanceOf[Terminal.TerminalMessage])
|
||||||
|
val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
|
||||||
|
assert(reply2.player == player)
|
||||||
|
assert(reply2.msg == null)
|
||||||
|
assert(reply2.response.isInstanceOf[Terminal.StartProximityEffect])
|
||||||
|
assert(reply2.response.asInstanceOf[Terminal.StartProximityEffect].terminal == terminal)
|
||||||
|
assert(terminal.NumberUsers == 1)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Use(player2)
|
||||||
|
expectNoMsg(Duration.create(500, "ms"))
|
||||||
|
assert(terminal.NumberUsers == 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MedicalTerminalControl2Test extends ActorTest() {
|
||||||
|
"ProximityTerminalControl sends a message to the last user only" in {
|
||||||
|
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
|
player.GUID = PlanetSideGUID(10)
|
||||||
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
||||||
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
receiveOne(Duration.create(500, "ms"))
|
||||||
|
terminal.Actor ! CommonMessages.Use(player2)
|
||||||
|
expectNoMsg(Duration.create(500, "ms"))
|
||||||
|
assert(terminal.NumberUsers == 2)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Unuse(player)
|
||||||
|
expectNoMsg(Duration.create(500, "ms"))
|
||||||
|
assert(terminal.NumberUsers == 1)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Unuse(player2)
|
||||||
|
val reply = receiveOne(Duration.create(500, "ms"))
|
||||||
|
assert(reply.isInstanceOf[Terminal.TerminalMessage])
|
||||||
|
val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
|
||||||
|
assert(reply2.player == player2)
|
||||||
|
assert(reply2.msg == null)
|
||||||
|
assert(reply2.response.isInstanceOf[Terminal.StopProximityEffect])
|
||||||
|
assert(reply2.response.asInstanceOf[Terminal.StopProximityEffect].terminal == terminal)
|
||||||
|
assert(terminal.NumberUsers == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MedicalTerminalControl3Test extends ActorTest() {
|
||||||
|
"ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in {
|
||||||
|
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
|
||||||
|
player.GUID = PlanetSideGUID(10)
|
||||||
|
val player2 = Player(Avatar("someothertest", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
|
||||||
|
player2.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
receiveOne(Duration.create(500, "ms"))
|
||||||
|
terminal.Actor ! CommonMessages.Use(player2)
|
||||||
|
expectNoMsg(Duration.create(500, "ms"))
|
||||||
|
assert(terminal.NumberUsers == 2)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Unuse(player2)
|
||||||
|
expectNoMsg(Duration.create(500, "ms"))
|
||||||
|
assert(terminal.NumberUsers == 1)
|
||||||
|
|
||||||
|
terminal.Actor ! CommonMessages.Unuse(player)
|
||||||
|
val reply = receiveOne(Duration.create(500, "ms"))
|
||||||
|
assert(reply.isInstanceOf[Terminal.TerminalMessage])
|
||||||
|
val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
|
||||||
|
assert(reply2.player == player) //important!
|
||||||
|
assert(reply2.msg == null)
|
||||||
|
assert(reply2.response.isInstanceOf[Terminal.StopProximityEffect])
|
||||||
|
assert(reply2.response.asInstanceOf[Terminal.StopProximityEffect].terminal == terminal)
|
||||||
|
assert(terminal.NumberUsers == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProximityTerminalControlTest {
|
||||||
|
def SetUpAgents(tdef : MedicalTerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, ProximityTerminal) = {
|
||||||
|
val terminal = ProximityTerminal(tdef)
|
||||||
|
terminal.Actor = system.actorOf(Props(classOf[ProximityTerminalControl], terminal), "test-term")
|
||||||
|
(Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.locks.IFFLock
|
||||||
import net.psforever.objects.serverobject.mblocker.Locker
|
import net.psforever.objects.serverobject.mblocker.Locker
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
|
@ -100,6 +100,8 @@ object Maps {
|
||||||
LocalObject(1186, Locker.Constructor)
|
LocalObject(1186, Locker.Constructor)
|
||||||
LocalObject(1187, Locker.Constructor)
|
LocalObject(1187, Locker.Constructor)
|
||||||
LocalObject(1188, Locker.Constructor)
|
LocalObject(1188, Locker.Constructor)
|
||||||
|
LocalObject(1492, ProximityTerminal.Constructor(medical_terminal)) //lobby
|
||||||
|
LocalObject(1494, ProximityTerminal.Constructor(medical_terminal)) //kitchen
|
||||||
LocalObject(1564, Terminal.Constructor(order_terminal))
|
LocalObject(1564, Terminal.Constructor(order_terminal))
|
||||||
LocalObject(1568, Terminal.Constructor(order_terminal))
|
LocalObject(1568, Terminal.Constructor(order_terminal))
|
||||||
LocalObject(1569, Terminal.Constructor(order_terminal))
|
LocalObject(1569, Terminal.Constructor(order_terminal))
|
||||||
|
|
@ -200,6 +202,8 @@ object Maps {
|
||||||
ObjectToBuilding(1186, 2)
|
ObjectToBuilding(1186, 2)
|
||||||
ObjectToBuilding(1187, 2)
|
ObjectToBuilding(1187, 2)
|
||||||
ObjectToBuilding(1188, 2)
|
ObjectToBuilding(1188, 2)
|
||||||
|
ObjectToBuilding(1492, 2)
|
||||||
|
ObjectToBuilding(1494, 2)
|
||||||
ObjectToBuilding(1564, 2)
|
ObjectToBuilding(1564, 2)
|
||||||
ObjectToBuilding(1568, 2)
|
ObjectToBuilding(1568, 2)
|
||||||
ObjectToBuilding(1569, 2)
|
ObjectToBuilding(1569, 2)
|
||||||
|
|
@ -452,6 +456,10 @@ object Maps {
|
||||||
LocalObject(691, Locker.Constructor)
|
LocalObject(691, Locker.Constructor)
|
||||||
LocalObject(692, Locker.Constructor)
|
LocalObject(692, Locker.Constructor)
|
||||||
LocalObject(693, Locker.Constructor)
|
LocalObject(693, Locker.Constructor)
|
||||||
|
LocalObject(778, ProximityTerminal.Constructor(medical_terminal))
|
||||||
|
LocalObject(779, ProximityTerminal.Constructor(medical_terminal))
|
||||||
|
LocalObject(780, ProximityTerminal.Constructor(medical_terminal))
|
||||||
|
LocalObject(781, ProximityTerminal.Constructor(medical_terminal))
|
||||||
LocalObject(842, Terminal.Constructor(order_terminal))
|
LocalObject(842, Terminal.Constructor(order_terminal))
|
||||||
LocalObject(843, Terminal.Constructor(order_terminal))
|
LocalObject(843, Terminal.Constructor(order_terminal))
|
||||||
LocalObject(844, Terminal.Constructor(order_terminal))
|
LocalObject(844, Terminal.Constructor(order_terminal))
|
||||||
|
|
@ -495,6 +503,10 @@ object Maps {
|
||||||
ObjectToBuilding(691, 2)
|
ObjectToBuilding(691, 2)
|
||||||
ObjectToBuilding(692, 2)
|
ObjectToBuilding(692, 2)
|
||||||
ObjectToBuilding(693, 2)
|
ObjectToBuilding(693, 2)
|
||||||
|
ObjectToBuilding(778, 2)
|
||||||
|
ObjectToBuilding(779, 2)
|
||||||
|
ObjectToBuilding(780, 2)
|
||||||
|
ObjectToBuilding(781, 2)
|
||||||
ObjectToBuilding(842, 2)
|
ObjectToBuilding(842, 2)
|
||||||
ObjectToBuilding(843, 2)
|
ObjectToBuilding(843, 2)
|
||||||
ObjectToBuilding(844, 2)
|
ObjectToBuilding(844, 2)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||||
import net.psforever.objects.serverobject.locks.IFFLock
|
import net.psforever.objects.serverobject.locks.IFFLock
|
||||||
import net.psforever.objects.serverobject.mblocker.Locker
|
import net.psforever.objects.serverobject.mblocker.Locker
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
|
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, ProximityTerminal, Terminal}
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
|
||||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
|
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
|
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
|
||||||
|
|
@ -66,6 +66,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
var speed : Float = 1.0f
|
var speed : Float = 1.0f
|
||||||
var spectator : Boolean = false
|
var spectator : Boolean = false
|
||||||
var admin : Boolean = false
|
var admin : Boolean = false
|
||||||
|
var usingMedicalTerminal : Option[PlanetSideGUID] = None
|
||||||
|
var usingProximityTerminal : Set[PlanetSideGUID] = Set.empty
|
||||||
|
var delayedProximityTerminalResets : Map[PlanetSideGUID, Cancellable] = Map.empty
|
||||||
|
|
||||||
var clientKeepAlive : Cancellable = DefaultCancellable.obj
|
var clientKeepAlive : Cancellable = DefaultCancellable.obj
|
||||||
var progressBarUpdate : Cancellable = DefaultCancellable.obj
|
var progressBarUpdate : Cancellable = DefaultCancellable.obj
|
||||||
|
|
@ -82,6 +85,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
LivePlayerList.Remove(sessionId)
|
LivePlayerList.Remove(sessionId)
|
||||||
if(player != null && player.HasGUID) {
|
if(player != null && player.HasGUID) {
|
||||||
val player_guid = player.GUID
|
val player_guid = player.GUID
|
||||||
|
//proximity vehicle terminals must be considered too
|
||||||
|
delayedProximityTerminalResets.foreach({case(_, task) => task.cancel})
|
||||||
|
usingProximityTerminal.foreach(term_guid => {
|
||||||
|
continent.GUID(term_guid) match {
|
||||||
|
case Some(obj : ProximityTerminal) =>
|
||||||
|
if(obj.NumberUsers > 0 && obj.RemoveUser(player_guid) == 0) { //refer to ProximityTerminalControl when modernizng
|
||||||
|
localService ! LocalServiceMessage(continent.Id, LocalAction.ProximityTerminalEffect(player_guid, term_guid, false))
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if(player.isAlive) {
|
if(player.isAlive) {
|
||||||
//actually being alive or manually deconstructing
|
//actually being alive or manually deconstructing
|
||||||
player.VehicleSeated match {
|
player.VehicleSeated match {
|
||||||
|
|
@ -371,6 +386,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
|
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
|
||||||
|
if(player.GUID != guid) {
|
||||||
|
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
|
||||||
|
}
|
||||||
|
|
||||||
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
|
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
|
||||||
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
|
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
|
||||||
|
|
||||||
|
|
@ -955,8 +975,28 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
|
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Terminal.StartProximityEffect(term) =>
|
||||||
|
val player_guid = player.GUID
|
||||||
|
val term_guid = term.GUID
|
||||||
|
StartUsingProximityUnit(term) //redundant but cautious
|
||||||
|
sendResponse(ProximityTerminalUseMessage(player_guid, term_guid, true))
|
||||||
|
localService ! LocalServiceMessage(continent.Id, LocalAction.ProximityTerminalEffect(player_guid, term_guid, true))
|
||||||
|
|
||||||
|
case Terminal.StopProximityEffect(term) =>
|
||||||
|
val player_guid = player.GUID
|
||||||
|
val term_guid = term.GUID
|
||||||
|
StopUsingProximityUnit(term) //redundant but cautious
|
||||||
|
sendResponse(ProximityTerminalUseMessage(player_guid, term_guid, false))
|
||||||
|
localService ! LocalServiceMessage(continent.Id, LocalAction.ProximityTerminalEffect(player_guid, term_guid, false))
|
||||||
|
|
||||||
case Terminal.NoDeal() =>
|
case Terminal.NoDeal() =>
|
||||||
log.warn(s"$tplayer made a request but the terminal rejected the order $msg")
|
val order : String = if(msg == null) {
|
||||||
|
s"order $msg"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"missing order"
|
||||||
|
}
|
||||||
|
log.warn(s"${tplayer.Name} made a request but the terminal rejected the $order")
|
||||||
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))
|
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1244,6 +1284,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DelayedProximityUnitStop(terminal) =>
|
||||||
|
StopUsingProximityUnit(terminal)
|
||||||
|
|
||||||
case ResponseToSelf(pkt) =>
|
case ResponseToSelf(pkt) =>
|
||||||
log.info(s"Received a direct message: $pkt")
|
log.info(s"Received a direct message: $pkt")
|
||||||
sendResponse(pkt)
|
sendResponse(pkt)
|
||||||
|
|
@ -1437,6 +1480,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
player.FacingYawUpper = yaw_upper
|
player.FacingYawUpper = yaw_upper
|
||||||
player.Crouching = is_crouching
|
player.Crouching = is_crouching
|
||||||
player.Jumping = is_jumping
|
player.Jumping = is_jumping
|
||||||
|
|
||||||
|
if(vel.isDefined && usingMedicalTerminal.isDefined) {
|
||||||
|
StopUsingProximityUnit(continent.GUID(usingMedicalTerminal.get).get.asInstanceOf[ProximityTerminal])
|
||||||
|
}
|
||||||
val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
|
val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
|
||||||
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
|
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
|
||||||
case None => false
|
case None => false
|
||||||
|
|
@ -1885,6 +1932,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
//TODO remove this kludge; explore how to stop BuyExoSuit(Max) sending a tardy ObjectHeldMessage(me, 255)
|
//TODO remove this kludge; explore how to stop BuyExoSuit(Max) sending a tardy ObjectHeldMessage(me, 255)
|
||||||
if(player.ExoSuit != ExoSuitType.MAX && (player.DrawnSlot = held_holsters) != before) {
|
if(player.ExoSuit != ExoSuitType.MAX && (player.DrawnSlot = held_holsters) != before) {
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
|
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
|
||||||
|
if(player.VisibleSlots.contains(held_holsters)) {
|
||||||
|
usingMedicalTerminal match {
|
||||||
|
case Some(term_guid) =>
|
||||||
|
StopUsingProximityUnit(continent.GUID(term_guid).get.asInstanceOf[ProximityTerminal])
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ AvatarJumpMessage(state) =>
|
case msg @ AvatarJumpMessage(state) =>
|
||||||
|
|
@ -2156,6 +2210,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case Some(obj : SpawnTube) =>
|
case Some(obj : SpawnTube) =>
|
||||||
//deconstruction
|
//deconstruction
|
||||||
PlayerActionsToCancel()
|
PlayerActionsToCancel()
|
||||||
|
CancelAllProximityUnits()
|
||||||
player.Release
|
player.Release
|
||||||
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, true))
|
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, true))
|
||||||
continent.Population ! Zone.Population.Release(avatar)
|
continent.Population ! Zone.Population.Release(avatar)
|
||||||
|
|
@ -2173,6 +2228,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case msg @ ProximityTerminalUseMessage(player_guid, object_guid, _) =>
|
||||||
|
log.info(s"ProximityTerminal: $msg")
|
||||||
|
continent.GUID(object_guid) match {
|
||||||
|
case Some(obj : ProximityTerminal) =>
|
||||||
|
if(usingProximityTerminal.contains(object_guid)) {
|
||||||
|
SelectProximityUnit(obj)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StartUsingProximityUnit(obj)
|
||||||
|
}
|
||||||
|
case Some(obj) => ;
|
||||||
|
log.warn(s"ProximityTerminal: object is not a terminal - $obj")
|
||||||
|
case None =>
|
||||||
|
log.warn(s"ProximityTerminal: no object with guid $object_guid found")
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ UnuseItemMessage(player_guid, object_guid) =>
|
case msg @ UnuseItemMessage(player_guid, object_guid) =>
|
||||||
log.info("UnuseItem: " + msg)
|
log.info("UnuseItem: " + msg)
|
||||||
continent.GUID(object_guid) match {
|
continent.GUID(object_guid) match {
|
||||||
|
|
@ -3429,6 +3500,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
|
||||||
}
|
}
|
||||||
PlayerActionsToCancel()
|
PlayerActionsToCancel()
|
||||||
|
CancelAllProximityUnits()
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -3472,7 +3544,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
if(speed > 1) {
|
if(speed > 1) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_SPEED, false, "", "1.000", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_SPEED, false, "", "1.000", None))
|
||||||
speed = 1f
|
speed = 1f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3484,6 +3556,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
def AvatarCreate() : Unit = {
|
def AvatarCreate() : Unit = {
|
||||||
player.Spawn
|
player.Spawn
|
||||||
player.Health = 50 //TODO temp
|
player.Health = 50 //TODO temp
|
||||||
|
player.Armor = 25
|
||||||
val packet = player.Definition.Packet
|
val packet = player.Definition.Packet
|
||||||
val dcdata = packet.DetailedConstructorData(player).get
|
val dcdata = packet.DetailedConstructorData(player).get
|
||||||
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
|
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
|
||||||
|
|
@ -3599,6 +3672,186 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start using a proximity-base service.
|
||||||
|
* Special note is warranted in the case of a medical terminal or an advanced medical terminal.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
def StartUsingProximityUnit(terminal : ProximityTerminal) : Unit = {
|
||||||
|
val term_guid = terminal.GUID
|
||||||
|
if(!usingProximityTerminal.contains(term_guid)) {
|
||||||
|
usingProximityTerminal += term_guid
|
||||||
|
terminal.Definition match {
|
||||||
|
case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal =>
|
||||||
|
usingMedicalTerminal = Some(term_guid)
|
||||||
|
case _ =>
|
||||||
|
SetDelayedProximityUnitReset(terminal)
|
||||||
|
}
|
||||||
|
terminal.Actor ! CommonMessages.Use(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop using a proximity-base service.
|
||||||
|
* Special note is warranted when determining the identity of the proximity terminal.
|
||||||
|
* Medical terminals of both varieties can be cancelled by movement.
|
||||||
|
* Other sorts of proximity-based units are put on a timer.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
def StopUsingProximityUnit(terminal : ProximityTerminal) : Unit = {
|
||||||
|
val term_guid = terminal.GUID
|
||||||
|
if(usingProximityTerminal.contains(term_guid)) {
|
||||||
|
usingProximityTerminal -= term_guid
|
||||||
|
ClearDelayedProximityUnitReset(term_guid)
|
||||||
|
if(usingMedicalTerminal.contains(term_guid)) {
|
||||||
|
usingMedicalTerminal = None
|
||||||
|
}
|
||||||
|
terminal.Actor ! CommonMessages.Unuse(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For pure proximity-based units and services, a manual attempt at cutting off the functionality.
|
||||||
|
* First, if an existing timer can be found, cancel it.
|
||||||
|
* Then, create a new timer.
|
||||||
|
* If this timer completes, a message will be sent that will attempt to disassociate from the target proximity unit.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
def SetDelayedProximityUnitReset(terminal : ProximityTerminal) : Unit = {
|
||||||
|
val terminal_guid = terminal.GUID
|
||||||
|
ClearDelayedProximityUnitReset(terminal_guid)
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
delayedProximityTerminalResets += terminal_guid ->
|
||||||
|
context.system.scheduler.scheduleOnce(3000 milliseconds, self, DelayedProximityUnitStop(terminal))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For pure proximity-based units and services, disable any manual attempt at cutting off the functionality.
|
||||||
|
* If an existing timer can be found, cancel it.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
def ClearDelayedProximityUnitReset(terminal_guid : PlanetSideGUID) : Unit = {
|
||||||
|
delayedProximityTerminalResets.get(terminal_guid) match {
|
||||||
|
case Some(task) =>
|
||||||
|
task.cancel
|
||||||
|
delayedProximityTerminalResets -= terminal_guid
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cease all current interactions with proximity-based units.
|
||||||
|
* Pair with `PlayerActionsToCancel`, except when logging out (stopping).
|
||||||
|
* This operations may invoke callback messages.
|
||||||
|
* @see `postStop`<br>
|
||||||
|
* `Terminal.StopProximityEffects`
|
||||||
|
*/
|
||||||
|
def CancelAllProximityUnits() : Unit = {
|
||||||
|
delayedProximityTerminalResets.foreach({case(term_guid, task) =>
|
||||||
|
task.cancel
|
||||||
|
delayedProximityTerminalResets -= term_guid
|
||||||
|
})
|
||||||
|
usingProximityTerminal.foreach(term_guid => {
|
||||||
|
StopUsingProximityUnit(continent.GUID(term_guid).get.asInstanceOf[ProximityTerminal])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine which functionality to pursue, by being given a generic proximity-functional unit
|
||||||
|
* and determinig which kind of unit is being utilized.
|
||||||
|
* @param terminal the proximity-based unit
|
||||||
|
*/
|
||||||
|
def SelectProximityUnit(terminal : ProximityTerminal) : Unit = {
|
||||||
|
terminal.Definition match {
|
||||||
|
case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal =>
|
||||||
|
ProximityMedicalTerminal(terminal)
|
||||||
|
|
||||||
|
case GlobalDefinitions.crystals_health_a | GlobalDefinitions.crystals_health_b =>
|
||||||
|
SetDelayedProximityUnitReset(terminal)
|
||||||
|
ProximityHealCrystal(terminal)
|
||||||
|
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When standing on the platform of a(n advanced) medical terminal,
|
||||||
|
* resotre the player's health and armor points (when they need their health and armor points restored).
|
||||||
|
* If the player is both fully healed and fully repaired, stop using the terminal.
|
||||||
|
* @param unit the medical terminal
|
||||||
|
*/
|
||||||
|
def ProximityMedicalTerminal(unit : ProximityTerminal) : Unit = {
|
||||||
|
val healthFull : Boolean = if(player.Health < player.MaxHealth) {
|
||||||
|
HealAction(player)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
val armorFull : Boolean = if(player.Armor < player.MaxArmor) {
|
||||||
|
ArmorRepairAction(player)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
if(healthFull && armorFull) {
|
||||||
|
log.info(s"${player.Name} is all fixed up")
|
||||||
|
StopUsingProximityUnit(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When near a red cavern crystal, resotre the player's health (when they need their health restored).
|
||||||
|
* If the player is fully healed, stop using the crystal.
|
||||||
|
* @param unit the healing crystal
|
||||||
|
*/
|
||||||
|
def ProximityHealCrystal(unit : ProximityTerminal) : Unit = {
|
||||||
|
val healthFull : Boolean = if(player.Health < player.MaxHealth) {
|
||||||
|
HealAction(player)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
if(healthFull) {
|
||||||
|
log.info(s"${player.Name} is all healed up")
|
||||||
|
StopUsingProximityUnit(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore, at most, a specific amount of health points on a player.
|
||||||
|
* Send messages to connected client and to events system.
|
||||||
|
* @param tplayer the player
|
||||||
|
* @param repairValue the amount to heal;
|
||||||
|
* 10 by default
|
||||||
|
* @return whether the player can be repaired for any more health points
|
||||||
|
*/
|
||||||
|
def HealAction(tplayer : Player, healValue : Int = 10) : Boolean = {
|
||||||
|
log.info(s"Dispensing health to ${tplayer.Name} - <3")
|
||||||
|
val player_guid = tplayer.GUID
|
||||||
|
tplayer.Health = tplayer.Health + healValue
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player_guid, 0, tplayer.Health))
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, tplayer.Health))
|
||||||
|
tplayer.Health == tplayer.MaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore, at most, a specific amount of personal armor points on a player.
|
||||||
|
* Send messages to connected client and to events system.
|
||||||
|
* @param tplayer the player
|
||||||
|
* @param repairValue the amount to repair;
|
||||||
|
* 10 by default
|
||||||
|
* @return whether the player can be repaired for any more armor points
|
||||||
|
*/
|
||||||
|
def ArmorRepairAction(tplayer : Player, repairValue : Int = 10) : Boolean = {
|
||||||
|
log.info(s"Dispensing armor to ${tplayer.Name} - c[=")
|
||||||
|
val player_guid = tplayer.GUID
|
||||||
|
tplayer.Armor = tplayer.Armor + repairValue
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player_guid, 4, tplayer.Armor))
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 4, tplayer.Armor))
|
||||||
|
tplayer.Armor == tplayer.MaxArmor
|
||||||
|
}
|
||||||
|
|
||||||
def failWithError(error : String) = {
|
def failWithError(error : String) = {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
sendResponse(ConnectionClose())
|
sendResponse(ConnectionClose())
|
||||||
|
|
@ -3644,6 +3897,7 @@ object WorldSessionActor {
|
||||||
private final case class ListAccountCharacters()
|
private final case class ListAccountCharacters()
|
||||||
private final case class SetCurrentAvatar(tplayer : Player)
|
private final case class SetCurrentAvatar(tplayer : Player)
|
||||||
private final case class VehicleLoaded(vehicle : Vehicle)
|
private final case class VehicleLoaded(vehicle : Vehicle)
|
||||||
|
private final case class DelayedProximityUnitStop(unit : ProximityTerminal)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A message that indicates the user is using a remote electronics kit to hack some server object.
|
* A message that indicates the user is using a remote electronics kit to hack some server object.
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,6 @@ object LocalAction {
|
||||||
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
||||||
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||||
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||||
|
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@ object LocalResponse {
|
||||||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||||
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||||
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||||
|
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
||||||
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ class LocalService extends Actor {
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
||||||
)
|
)
|
||||||
|
case LocalAction.ProximityTerminalEffect(player_guid, object_guid, effectState) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
|
||||||
|
)
|
||||||
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
|
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
||||||
|
|
|
||||||
115
pslogin/src/test/scala/LocalServiceTest.scala
Normal file
115
pslogin/src/test/scala/LocalServiceTest.scala
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
import akka.actor.Props
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
import services.Service
|
||||||
|
import services.local._
|
||||||
|
|
||||||
|
class LocalService1Test extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"construct" in {
|
||||||
|
system.actorOf(Props[LocalService], "service")
|
||||||
|
assert(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalService2Test extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"subscribe" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
assert(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalService3Test extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"subscribe to a specific channel" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! Service.Leave()
|
||||||
|
assert(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalService4Test extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"subscribe" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! Service.LeaveAll()
|
||||||
|
assert(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalService5Test extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"pass an unhandled message" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! "hello"
|
||||||
|
expectNoMsg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoorClosesTest extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"pass DoorCloses" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! LocalServiceMessage("test", LocalAction.DoorCloses(PlanetSideGUID(10), PlanetSideGUID(40)))
|
||||||
|
expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.DoorCloses(PlanetSideGUID(40))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HackClearTest extends ActorTest {
|
||||||
|
val obj = new PlanetSideServerObject() {
|
||||||
|
def Faction = PlanetSideEmpire.NEUTRAL
|
||||||
|
def Definition = null
|
||||||
|
GUID = PlanetSideGUID(40)
|
||||||
|
}
|
||||||
|
|
||||||
|
"LocalService" should {
|
||||||
|
"pass HackClear" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! LocalServiceMessage("test", LocalAction.HackClear(PlanetSideGUID(10), obj, 0L, 1000L))
|
||||||
|
expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.HackClear(PlanetSideGUID(40), 0L, 1000L)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProximityTerminalEffectTest extends ActorTest {
|
||||||
|
"LocalService" should {
|
||||||
|
"pass ProximityTerminalEffect" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! LocalServiceMessage("test", LocalAction.ProximityTerminalEffect(PlanetSideGUID(10), PlanetSideGUID(40), true))
|
||||||
|
expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.ProximityTerminalEffect(PlanetSideGUID(40), true)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TriggerSoundTest extends ActorTest {
|
||||||
|
import net.psforever.packet.game.TriggeredSound
|
||||||
|
|
||||||
|
"LocalService" should {
|
||||||
|
"pass TriggerSound" in {
|
||||||
|
val service = system.actorOf(Props[LocalService], "service")
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! LocalServiceMessage("test", LocalAction.TriggerSound(PlanetSideGUID(10), TriggeredSound.LockedOut, Vector3(1.1f, 2.2f, 3.3f), 0, 0.75f))
|
||||||
|
expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.TriggerSound(TriggeredSound.LockedOut, Vector3(1.1f, 2.2f, 3.3f), 0, 0.75f)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LocalServiceTest {
|
||||||
|
//decoy
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue