mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-04-24 21:35:21 +00:00
Revert "Let's Move Item, Together (#429)"
This reverts commit 3f2240947b.
This commit is contained in:
parent
4fa2be151b
commit
896fa7bf07
41 changed files with 1730 additions and 3096 deletions
|
|
@ -48,8 +48,8 @@ lazy val commonSettings = Seq(
|
||||||
"org.scala-graph" %% "graph-core" % "1.12.5",
|
"org.scala-graph" %% "graph-core" % "1.12.5",
|
||||||
"io.kamon" %% "kamon-bundle" % "2.1.0",
|
"io.kamon" %% "kamon-bundle" % "2.1.0",
|
||||||
"io.kamon" %% "kamon-apm-reporter" % "2.1.0",
|
"io.kamon" %% "kamon-apm-reporter" % "2.1.0",
|
||||||
"org.json4s" %% "json4s-native" % "3.6.8"
|
"org.json4s" %% "json4s-native" % "3.6.8",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val pscryptoSettings = Seq(
|
lazy val pscryptoSettings = Seq(
|
||||||
|
|
|
||||||
|
|
@ -57,33 +57,6 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
|
||||||
private var lfs : Boolean = false
|
private var lfs : Boolean = false
|
||||||
|
|
||||||
private var vehicleOwned : Option[PlanetSideGUID] = None
|
private var vehicleOwned : Option[PlanetSideGUID] = None
|
||||||
/** key - object id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
* */
|
|
||||||
private var lastUsedEquipmentTimes : mutable.LongMap[Long] = mutable.LongMap[Long]()
|
|
||||||
/** exo-suit times are sorted by `Enumeration` order, which was determined by packet process<br>
|
|
||||||
* key - exo-suit id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
* */
|
|
||||||
private val lastUsedExoSuitTimes : Array[Long] = Array.fill[Long](ExoSuitType.values.size)(0L)
|
|
||||||
/** mechanized exo-suit times are sorted by subtype distinction, which was determined by packet process<br>
|
|
||||||
* key - subtype id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
* */
|
|
||||||
private val lastUsedMaxExoSuitTimes : Array[Long] = Array.fill[Long](4)(0L) //invalid, ai, av, aa
|
|
||||||
/** key - object id<br>
|
|
||||||
* value - time last acquired (from a terminal) (ms)
|
|
||||||
* */
|
|
||||||
private var lastPurchaseTimes : mutable.LongMap[Long] = mutable.LongMap[Long]()
|
|
||||||
/**
|
|
||||||
* To reload purchase and use timers, a string representing the item must be produced.
|
|
||||||
* Point directly from the object id to the object definition and get the `Name` from that definition.
|
|
||||||
* Allocate only when an item is purchased or used.
|
|
||||||
* The keys match the keys for both `lastUsedEquipmentTimes` and `lastPurchaseTimes`.<br>
|
|
||||||
* key - object id<br>
|
|
||||||
* value - most basic object definition information
|
|
||||||
*/
|
|
||||||
private val objectTypeNameReference : mutable.LongMap[String] = new mutable.LongMap[String]()
|
|
||||||
|
|
||||||
def CharId : Long = char_id
|
def CharId : Long = char_id
|
||||||
|
|
||||||
|
|
@ -216,8 +189,7 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
|
||||||
|
|
||||||
def FifthSlot : EquipmentSlot = {
|
def FifthSlot : EquipmentSlot = {
|
||||||
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
|
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
|
||||||
val obj = new LockerEquipment(locker)
|
Equipment = locker
|
||||||
Equipment = obj
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,70 +220,6 @@ class Avatar(private val char_id : Long, val name : String, val faction : Planet
|
||||||
VehicleOwned
|
VehicleOwned
|
||||||
}
|
}
|
||||||
|
|
||||||
def GetLastUsedTime(code : Int) : Long = {
|
|
||||||
lastUsedEquipmentTimes.get(code) match {
|
|
||||||
case Some(time) => time
|
|
||||||
case None => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastUsedTime(code : ExoSuitType.Value) : Long = {
|
|
||||||
lastUsedExoSuitTimes(code.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastUsedTime(code : ExoSuitType.Value, subtype : Int) : Long = {
|
|
||||||
if(code == ExoSuitType.MAX) {
|
|
||||||
lastUsedMaxExoSuitTimes(subtype)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GetLastUsedTime(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetAllLastUsedTimes : Map[Long, Long] = lastUsedEquipmentTimes.toMap
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : Int, time : Long) : Unit = {
|
|
||||||
lastUsedEquipmentTimes += code.toLong -> time
|
|
||||||
}
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value) : Unit = SetLastUsedTime(code, System.currentTimeMillis())
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value, time : Long) : Unit = {
|
|
||||||
lastUsedExoSuitTimes(code.id) = time
|
|
||||||
}
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value, subtype : Int, time : Long) : Unit = {
|
|
||||||
if(code == ExoSuitType.MAX) {
|
|
||||||
lastUsedMaxExoSuitTimes(subtype) = time
|
|
||||||
}
|
|
||||||
SetLastUsedTime(code, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastPurchaseTime(code : Int) : Long = {
|
|
||||||
lastPurchaseTimes.get(code) match {
|
|
||||||
case Some(time) => time
|
|
||||||
case None => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetAllLastPurchaseTimes : Map[Long, Long] = lastPurchaseTimes.toMap
|
|
||||||
|
|
||||||
def SetLastPurchaseTime(code : Int, time : Long) : Unit = {
|
|
||||||
lastPurchaseTimes += code.toLong -> time
|
|
||||||
}
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id : Long) : String = {
|
|
||||||
objectTypeNameReference.get(id) match {
|
|
||||||
case Some(name) => name
|
|
||||||
case None => ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id : Long, name : String) : String = {
|
|
||||||
objectTypeNameReference(id) = name
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
def Definition : AvatarDefinition = GlobalDefinitions.avatar
|
def Definition : AvatarDefinition = GlobalDefinitions.avatar
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
|
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
|
||||||
import net.psforever.types.{CertificationType, PlanetSideGUID}
|
import net.psforever.types.PlanetSideGUID
|
||||||
import services.RemoverActor
|
import services.RemoverActor
|
||||||
import services.local.{LocalAction, LocalServiceMessage}
|
import services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
|
||||||
|
|
@ -123,48 +123,4 @@ object Deployables {
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the deployables backend information.
|
|
||||||
* @param avatar the player's core
|
|
||||||
*/
|
|
||||||
def InitializeDeployableQuantities(avatar : Avatar) : Boolean = {
|
|
||||||
log.info("Setting up combat engineering ...")
|
|
||||||
avatar.Deployables.Initialize(avatar.Certifications.toSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the UI elements for deployables.
|
|
||||||
* @param avatar the player's core
|
|
||||||
*/
|
|
||||||
def InitializeDeployableUIElements(avatar : Avatar) : List[(Int,Int,Int,Int)] = {
|
|
||||||
log.info("Setting up combat engineering UI ...")
|
|
||||||
avatar.Deployables.UpdateUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The player learned a new certification.
|
|
||||||
* Update the deployables user interface elements if it was an "Engineering" certification.
|
|
||||||
* The certification "Advanced Hacking" also relates to an element.
|
|
||||||
* @param certification the certification that was added
|
|
||||||
* @param certificationSet all applicable certifications
|
|
||||||
*/
|
|
||||||
def AddToDeployableQuantities(avatar : Avatar, certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : List[(Int,Int,Int,Int)] = {
|
|
||||||
avatar.Deployables.AddToDeployableQuantities(certification, certificationSet)
|
|
||||||
avatar.Deployables.UpdateUI(certification)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The player forgot a certification he previously knew.
|
|
||||||
* Update the deployables user interface elements if it was an "Engineering" certification.
|
|
||||||
* The certification "Advanced Hacking" also relates to an element.
|
|
||||||
* @param certification the certification that was added
|
|
||||||
* @param certificationSet all applicable certifications
|
|
||||||
*/
|
|
||||||
def RemoveFromDeployableQuantities(avatar : Avatar, certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : List[(Int,Int,Int,Int)] = {
|
|
||||||
avatar.Deployables.RemoveFromDeployableQuantities(certification, certificationSet)
|
|
||||||
avatar.Deployables.UpdateUI(certification)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1703,11 +1703,11 @@ object GlobalDefinitions {
|
||||||
plasma_grenade_ammo.Name = "plasma_grenade_ammo"
|
plasma_grenade_ammo.Name = "plasma_grenade_ammo"
|
||||||
plasma_grenade_ammo.Size = EquipmentSize.Blocked
|
plasma_grenade_ammo.Size = EquipmentSize.Blocked
|
||||||
|
|
||||||
bullet_9mm.Name = "9mmbullet"
|
bullet_9mm.Name = "bullet_9mm"
|
||||||
bullet_9mm.Capacity = 50
|
bullet_9mm.Capacity = 50
|
||||||
bullet_9mm.Tile = InventoryTile.Tile33
|
bullet_9mm.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
bullet_9mm_AP.Name="9mmbullet_AP"
|
bullet_9mm_AP.Name="bullet_9mm_AP"
|
||||||
bullet_9mm_AP.Capacity = 50
|
bullet_9mm_AP.Capacity = 50
|
||||||
bullet_9mm_AP.Tile = InventoryTile.Tile33
|
bullet_9mm_AP.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
|
|
@ -1841,7 +1841,7 @@ object GlobalDefinitions {
|
||||||
trek_ammo.Name = "trek_ammo"
|
trek_ammo.Name = "trek_ammo"
|
||||||
trek_ammo.Size = EquipmentSize.Blocked
|
trek_ammo.Size = EquipmentSize.Blocked
|
||||||
|
|
||||||
bullet_35mm.Name = "35mmbullet"
|
bullet_35mm.Name = "bullet_35mm"
|
||||||
bullet_35mm.Capacity = 100
|
bullet_35mm.Capacity = 100
|
||||||
bullet_35mm.Tile = InventoryTile.Tile44
|
bullet_35mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
@ -1889,11 +1889,11 @@ object GlobalDefinitions {
|
||||||
liberator_bomb.Capacity = 20
|
liberator_bomb.Capacity = 20
|
||||||
liberator_bomb.Tile = InventoryTile.Tile44
|
liberator_bomb.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_25mm.Name = "25mmbullet"
|
bullet_25mm.Name = "bullet_25mm"
|
||||||
bullet_25mm.Capacity = 150
|
bullet_25mm.Capacity = 150
|
||||||
bullet_25mm.Tile = InventoryTile.Tile44
|
bullet_25mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_75mm.Name = "75mmbullet"
|
bullet_75mm.Name = "bullet_75mm"
|
||||||
bullet_75mm.Capacity = 100
|
bullet_75mm.Capacity = 100
|
||||||
bullet_75mm.Tile = InventoryTile.Tile44
|
bullet_75mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
@ -1913,11 +1913,11 @@ object GlobalDefinitions {
|
||||||
reaver_rocket.Capacity = 12
|
reaver_rocket.Capacity = 12
|
||||||
reaver_rocket.Tile = InventoryTile.Tile44
|
reaver_rocket.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_20mm.Name = "20mmbullet"
|
bullet_20mm.Name = "bullet_20mm"
|
||||||
bullet_20mm.Capacity = 200
|
bullet_20mm.Capacity = 200
|
||||||
bullet_20mm.Tile = InventoryTile.Tile44
|
bullet_20mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_12mm.Name = "12mmbullet"
|
bullet_12mm.Name = "bullet_12mm"
|
||||||
bullet_12mm.Capacity = 300
|
bullet_12mm.Capacity = 300
|
||||||
bullet_12mm.Tile = InventoryTile.Tile44
|
bullet_12mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
@ -1929,7 +1929,7 @@ object GlobalDefinitions {
|
||||||
wasp_gun_ammo.Capacity = 150
|
wasp_gun_ammo.Capacity = 150
|
||||||
wasp_gun_ammo.Tile = InventoryTile.Tile44
|
wasp_gun_ammo.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_15mm.Name = "15mmbullet"
|
bullet_15mm.Name = "bullet_15mm"
|
||||||
bullet_15mm.Capacity = 360
|
bullet_15mm.Capacity = 360
|
||||||
bullet_15mm.Tile = InventoryTile.Tile44
|
bullet_15mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
@ -1953,7 +1953,7 @@ object GlobalDefinitions {
|
||||||
colossus_tank_cannon_ammo.Capacity = 110
|
colossus_tank_cannon_ammo.Capacity = 110
|
||||||
colossus_tank_cannon_ammo.Tile = InventoryTile.Tile44
|
colossus_tank_cannon_ammo.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_105mm.Name = "105mmbullet"
|
bullet_105mm.Name = "bullet_105mm"
|
||||||
bullet_105mm.Capacity = 100
|
bullet_105mm.Capacity = 100
|
||||||
bullet_105mm.Tile = InventoryTile.Tile44
|
bullet_105mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
@ -1981,7 +1981,7 @@ object GlobalDefinitions {
|
||||||
peregrine_sparrow_ammo.Capacity = 150
|
peregrine_sparrow_ammo.Capacity = 150
|
||||||
peregrine_sparrow_ammo.Tile = InventoryTile.Tile44
|
peregrine_sparrow_ammo.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
bullet_150mm.Name = "150mmbullet"
|
bullet_150mm.Name = "bullet_150mm"
|
||||||
bullet_150mm.Capacity = 50
|
bullet_150mm.Capacity = 50
|
||||||
bullet_150mm.Tile = InventoryTile.Tile44
|
bullet_150mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import akka.actor.Actor
|
|
||||||
import net.psforever.objects.definition.EquipmentDefinition
|
import net.psforever.objects.definition.EquipmentDefinition
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.{Container, GridInventory}
|
import net.psforever.objects.inventory.{Container, GridInventory}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
|
||||||
import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage}
|
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
|
||||||
import services.Service
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The companion of a `Locker` that is carried with a player
|
* The companion of a `Locker` that is carried with a player
|
||||||
|
|
@ -19,18 +11,9 @@ import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
* The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`.
|
* The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`.
|
||||||
* The inventory of this object is accessed using a game world `Locker` object (`mb_locker`).
|
* The inventory of this object is accessed using a game world `Locker` object (`mb_locker`).
|
||||||
*/
|
*/
|
||||||
class LockerContainer extends PlanetSideServerObject
|
class LockerContainer extends Equipment with Container {
|
||||||
with Container {
|
|
||||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
|
||||||
private val inventory = GridInventory(30, 20)
|
private val inventory = GridInventory(30, 20)
|
||||||
|
|
||||||
def Faction : PlanetSideEmpire.Value = faction
|
|
||||||
|
|
||||||
override def Faction_=(fact : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = {
|
|
||||||
faction = fact
|
|
||||||
Faction
|
|
||||||
}
|
|
||||||
|
|
||||||
def Inventory : GridInventory = inventory
|
def Inventory : GridInventory = inventory
|
||||||
|
|
||||||
def VisibleSlots : Set[Int] = Set.empty[Int]
|
def VisibleSlots : Set[Int] = Set.empty[Int]
|
||||||
|
|
@ -43,79 +26,3 @@ object LockerContainer {
|
||||||
new LockerContainer()
|
new LockerContainer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LockerEquipment(locker : LockerContainer) extends Equipment
|
|
||||||
with Container {
|
|
||||||
private val obj = locker
|
|
||||||
|
|
||||||
override def GUID : PlanetSideGUID = obj.GUID
|
|
||||||
|
|
||||||
override def GUID_=(guid : PlanetSideGUID) : PlanetSideGUID = obj.GUID_=(guid)
|
|
||||||
|
|
||||||
override def HasGUID : Boolean = obj.HasGUID
|
|
||||||
|
|
||||||
override def Invalidate() : Unit = obj.Invalidate()
|
|
||||||
|
|
||||||
override def Faction : PlanetSideEmpire.Value = obj.Faction
|
|
||||||
|
|
||||||
def Inventory : GridInventory = obj.Inventory
|
|
||||||
|
|
||||||
def VisibleSlots : Set[Int] = Set.empty[Int]
|
|
||||||
|
|
||||||
def Definition : EquipmentDefinition = obj.Definition
|
|
||||||
}
|
|
||||||
|
|
||||||
class LockerContainerControl(locker : LockerContainer, toChannel : String) extends Actor
|
|
||||||
with ContainableBehavior {
|
|
||||||
def ContainerObject = locker
|
|
||||||
|
|
||||||
def receive : Receive = containerBehavior
|
|
||||||
.orElse {
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def MessageDeferredCallback(msg : Any) : Unit = {
|
|
||||||
msg match {
|
|
||||||
case Containable.MoveItem(_, item, _) =>
|
|
||||||
//momentarily put item back where it was originally
|
|
||||||
val obj = ContainerObject
|
|
||||||
obj.Find(item) match {
|
|
||||||
case Some(slot) =>
|
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
toChannel,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
|
||||||
)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val zone = locker.Zone
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(toChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID))
|
|
||||||
}
|
|
||||||
|
|
||||||
def PutItemInSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val zone = locker.Zone
|
|
||||||
val definition = item.Definition
|
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
toChannel,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
ObjectCreateDetailedMessage(
|
|
||||||
definition.ObjectId,
|
|
||||||
item.GUID,
|
|
||||||
ObjectCreateMessageParent(locker.GUID, slot),
|
|
||||||
definition.Packet.DetailedConstructorData(item).get
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def SwapItemCallback(item : Equipment) : Unit = {
|
|
||||||
val zone = locker.Zone
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(toChannel, AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(locker.GUID, item.GUID, Vector3.Zero, 0f)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -216,8 +216,6 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
|
||||||
|
|
||||||
def Locker : LockerContainer = core.Locker
|
def Locker : LockerContainer = core.Locker
|
||||||
|
|
||||||
def FifthSlot : EquipmentSlot = core.FifthSlot
|
|
||||||
|
|
||||||
override def Fit(obj : Equipment) : Option[Int] = {
|
override def Fit(obj : Equipment) : Option[Int] = {
|
||||||
recursiveHolsterFit(holsters.iterator, obj.Size) match {
|
recursiveHolsterFit(holsters.iterator, obj.Size) match {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
|
|
@ -610,30 +608,6 @@ class Player(private val core : Avatar) extends PlanetSideServerObject
|
||||||
|
|
||||||
def VehicleOwned_=(guid : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = core.VehicleOwned_=(guid)
|
def VehicleOwned_=(guid : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = core.VehicleOwned_=(guid)
|
||||||
|
|
||||||
def GetLastUsedTime(code : Int) : Long = core.GetLastUsedTime(code)
|
|
||||||
|
|
||||||
def GetLastUsedTime(code : ExoSuitType.Value) : Long = core.GetLastUsedTime(code)
|
|
||||||
|
|
||||||
def GetLastUsedTime(code : ExoSuitType.Value, subtype : Int) : Long = core.GetLastUsedTime(code, subtype)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : Int, time : Long) : Unit = core.SetLastUsedTime(code, time)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value): Unit = core.SetLastUsedTime(code)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value, time : Long) : Unit = core.SetLastUsedTime(code, time)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value, subtype : Int): Unit = core.SetLastUsedTime(code, subtype)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code : ExoSuitType.Value, subtype : Int, time : Long) : Unit = core.SetLastUsedTime(code, subtype, time)
|
|
||||||
|
|
||||||
def GetLastPurchaseTime(code : Int) : Long = core.GetLastPurchaseTime(code)
|
|
||||||
|
|
||||||
def SetLastPurchaseTime(code : Int, time : Long) : Unit = core.SetLastPurchaseTime(code, time)
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id : Long) : String = core.ObjectTypeNameReference(id)
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id : Long, name : String) : String = core.ObjectTypeNameReference(id, name)
|
|
||||||
|
|
||||||
def DamageModel = exosuit.asInstanceOf[DamageResistanceModel]
|
def DamageModel = exosuit.asInstanceOf[DamageResistanceModel]
|
||||||
|
|
||||||
def Definition : AvatarDefinition = core.Definition
|
def Definition : AvatarDefinition = core.Definition
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,10 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.definition.ExoSuitDefinition
|
|
||||||
import net.psforever.objects.equipment.EquipmentSlot
|
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
|
||||||
import net.psforever.objects.loadouts.InfantryLoadout
|
|
||||||
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
||||||
import net.psforever.types.ExoSuitType
|
|
||||||
import services.Service
|
import services.Service
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
|
|
||||||
object Players {
|
object Players {
|
||||||
private val log = org.log4s.getLogger("Players")
|
private val log = org.log4s.getLogger("Players")
|
||||||
|
|
||||||
|
|
@ -53,71 +46,4 @@ object Players {
|
||||||
log.info(s"$medic had revived $name")
|
log.info(s"$medic had revived $name")
|
||||||
target.Zone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.Revive(target.GUID))
|
target.Zone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.Revive(target.GUID))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
|
|
||||||
* Remove any encountered items and add them to an output `List`.
|
|
||||||
* @param iter the `Iterator` of `EquipmentSlot`s
|
|
||||||
* @param index a number that equals the "current" holster slot (`EquipmentSlot`)
|
|
||||||
* @param list a persistent `List` of `Equipment` in the holster slots
|
|
||||||
* @return a `List` of `Equipment` in the holster slots
|
|
||||||
*/
|
|
||||||
@tailrec def clearHolsters(iter : Iterator[EquipmentSlot], index : Int = 0, list : List[InventoryItem] = Nil) : List[InventoryItem] = {
|
|
||||||
if(!iter.hasNext) {
|
|
||||||
list
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val slot = iter.next
|
|
||||||
slot.Equipment match {
|
|
||||||
case Some(equipment) =>
|
|
||||||
slot.Equipment = None
|
|
||||||
clearHolsters(iter, index + 1, InventoryItem(equipment, index) +: list)
|
|
||||||
case None =>
|
|
||||||
clearHolsters(iter, index + 1, list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
|
|
||||||
* For any slots that are not yet occupied by an item, search through the `List` and find an item that fits in that slot.
|
|
||||||
* Add that item to the slot and remove it from the list.
|
|
||||||
* @param iter the `Iterator` of `EquipmentSlot`s
|
|
||||||
* @param list a `List` of all `Equipment` that is not yet assigned to a holster slot or an inventory slot
|
|
||||||
* @return the `List` of all `Equipment` not yet assigned to a holster slot or an inventory slot
|
|
||||||
*/
|
|
||||||
@tailrec def fillEmptyHolsters(iter : Iterator[EquipmentSlot], list : List[InventoryItem]) : List[InventoryItem] = {
|
|
||||||
if(!iter.hasNext) {
|
|
||||||
list
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val slot = iter.next
|
|
||||||
if(slot.Equipment.isEmpty) {
|
|
||||||
list.find(item => item.obj.Size == slot.Size) match {
|
|
||||||
case Some(obj) =>
|
|
||||||
val index = list.indexOf(obj)
|
|
||||||
slot.Equipment = obj.obj
|
|
||||||
fillEmptyHolsters(iter, list.take(index) ++ list.drop(index + 1))
|
|
||||||
case None =>
|
|
||||||
fillEmptyHolsters(iter, list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fillEmptyHolsters(iter, list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def CertificationToUseExoSuit(player : Player, exosuit : ExoSuitType.Value, subtype : Int) : Boolean = {
|
|
||||||
ExoSuitDefinition.Select(exosuit, player.Faction).Permissions match {
|
|
||||||
case Nil =>
|
|
||||||
true
|
|
||||||
case permissions if subtype != 0 =>
|
|
||||||
val certs = player.Certifications
|
|
||||||
certs.intersect(permissions.toSet).nonEmpty &&
|
|
||||||
certs.intersect(InfantryLoadout.DetermineSubtypeC(subtype)).nonEmpty
|
|
||||||
case permissions =>
|
|
||||||
player.Certifications.intersect(permissions.toSet).nonEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import net.psforever.objects.definition.VehicleDefinition
|
import net.psforever.objects.definition.VehicleDefinition
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem, InventoryTile}
|
import net.psforever.objects.inventory.{Container, GridInventory, InventoryTile}
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
|
@ -17,7 +17,6 @@ import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.util.{Success, Try}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server-side support object that represents a vehicle.<br>
|
* The server-side support object that represents a vehicle.<br>
|
||||||
|
|
@ -453,20 +452,6 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def Collisions(dest : Int, width : Int, height : Int) : Try[List[InventoryItem]] = {
|
|
||||||
weapons.get(dest) match {
|
|
||||||
case Some(slot) =>
|
|
||||||
slot.Equipment match {
|
|
||||||
case Some(item) =>
|
|
||||||
Success(List(InventoryItem(item, dest)))
|
|
||||||
case None =>
|
|
||||||
Success(List())
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
super.Collisions(dest, width, height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the `Vehicle` `Trunk` space.
|
* A reference to the `Vehicle` `Trunk` space.
|
||||||
* @return this `Vehicle` `Trunk`
|
* @return this `Vehicle` `Trunk`
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,22 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.{Players, _}
|
import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, ImplantSlot, Player, Players, Tool}
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry}
|
||||||
import net.psforever.objects.definition.ImplantDefinition
|
import net.psforever.objects.definition.ImplantDefinition
|
||||||
import net.psforever.objects.equipment.{Ammo, Equipment, EquipmentSize, JammableBehavior, JammableUnit}
|
import net.psforever.objects.equipment.{Ammo, JammableBehavior, JammableUnit}
|
||||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
|
||||||
import net.psforever.objects.loadouts.Loadout
|
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
|
||||||
import net.psforever.objects.vital.{PlayerSuicide, Vitality}
|
import net.psforever.objects.vital.{PlayerSuicide, Vitality}
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.damage.Damageable
|
import net.psforever.objects.serverobject.damage.Damageable
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.repair.Repairable
|
import net.psforever.objects.serverobject.repair.Repairable
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
|
||||||
import net.psforever.objects.vital._
|
import net.psforever.objects.vital._
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideGUID, Vector3}
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import services.Service
|
||||||
import services.{RemoverActor, Service}
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import services.local.{LocalAction, LocalServiceMessage}
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
@ -31,119 +25,112 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
||||||
class PlayerControl(player : Player) extends Actor
|
class PlayerControl(player : Player) extends Actor
|
||||||
with JammableBehavior
|
with JammableBehavior
|
||||||
with Damageable
|
with Damageable {
|
||||||
with ContainableBehavior {
|
|
||||||
def JammableObject = player
|
def JammableObject = player
|
||||||
def DamageableObject = player
|
def DamageableObject = player
|
||||||
def ContainerObject = player
|
|
||||||
|
|
||||||
private[this] val log = org.log4s.getLogger(player.Name)
|
private [this] val log = org.log4s.getLogger(player.Name)
|
||||||
private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
|
private [this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
|
||||||
|
|
||||||
// A collection of timers for each slot to trigger stamina drain on an interval
|
// A collection of timers for each slot to trigger stamina drain on an interval
|
||||||
val implantSlotStaminaDrainTimers = mutable.HashMap(0 -> DefaultCancellable.obj, 1 -> DefaultCancellable.obj, 2 -> DefaultCancellable.obj)
|
val implantSlotStaminaDrainTimers = mutable.HashMap(0 -> DefaultCancellable.obj, 1 -> DefaultCancellable.obj, 2 -> DefaultCancellable.obj)
|
||||||
// control agency for the player's locker container (dedicated inventory slot #5)
|
|
||||||
val lockerControlAgent : ActorRef = {
|
|
||||||
val locker = player.Locker
|
|
||||||
locker.Zone = player.Zone
|
|
||||||
locker.Actor = context.actorOf(Props(classOf[LockerContainerControl], locker, player.Name), PlanetSideServerObject.UniqueActorName(locker))
|
|
||||||
}
|
|
||||||
|
|
||||||
override def postStop() : Unit = {
|
|
||||||
context.stop(lockerControlAgent)
|
|
||||||
player.Locker.Actor = ActorRef.noSender
|
|
||||||
implantSlotStaminaDrainTimers.values.foreach { _.cancel }
|
|
||||||
}
|
|
||||||
|
|
||||||
def receive : Receive = jammableBehavior
|
def receive : Receive = jammableBehavior
|
||||||
.orElse(takesDamage)
|
.orElse(takesDamage)
|
||||||
.orElse(containerBehavior)
|
|
||||||
.orElse {
|
.orElse {
|
||||||
case Player.ImplantActivation(slot : Int, status : Int) =>
|
case Player.ImplantActivation(slot: Int, status : Int) =>
|
||||||
// todo: disable implants with stamina cost when changing armour type
|
// todo: disable implants with stamina cost when changing armour type
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
val implantSlot = player.ImplantSlot(slot)
|
||||||
|
|
||||||
// Allow uninitialized implants to be deactivated in case they're stuck in a state where they are no longer active or initialized but still draining stamina (e.g. by EMP)
|
// Allow uninitialized implants to be deactivated in case they're stuck in a state where they are no longer active or initialized but still draining stamina (e.g. by EMP)
|
||||||
if (status == 0 && (implantSlot.Active || !implantSlot.Initialized)) {
|
if(status == 0 && (implantSlot.Active || !implantSlot.Initialized)) {
|
||||||
// Cancel stamina drain timer
|
// Cancel stamina drain timer
|
||||||
implantSlotStaminaDrainTimers(slot).cancel()
|
implantSlotStaminaDrainTimers(slot).cancel()
|
||||||
implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj
|
implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj
|
||||||
implantSlot.Active = false
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect
|
implantSlot.Active = false
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DeactivateImplantSlot(player.GUID, slot))
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DeactivateImplantSlot(player.GUID, slot))
|
||||||
|
} else if (status == 1 && implantSlot.Initialized && !player.Fatigued) {
|
||||||
|
implantSlot.Installed match {
|
||||||
|
case Some(implant: ImplantDefinition) =>
|
||||||
|
if(implantSlot.Active) {
|
||||||
|
// Some events such as zoning will reset the implant on the client side without sending a deactivation packet
|
||||||
|
// But the implant will remain in an active state server side. For now, allow reactivation of the implant.
|
||||||
|
// todo: Deactivate implants server side when actions like zoning happen. (Other actions?)
|
||||||
|
log.warn(s"Implant ${slot} is already active, but activating again")
|
||||||
|
implantSlotStaminaDrainTimers(slot).cancel()
|
||||||
|
implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj
|
||||||
|
}
|
||||||
|
implantSlot.Active = true
|
||||||
|
|
||||||
|
if (implant.ActivationStaminaCost >= 0) {
|
||||||
|
player.Stamina -= implant.ActivationStaminaCost // Activation stamina drain
|
||||||
|
}
|
||||||
|
|
||||||
|
if(implant.StaminaCost > 0 && implant.GetCostIntervalByExoSuit(player.ExoSuit) > 0) { // Ongoing stamina drain, if applicable
|
||||||
|
implantSlotStaminaDrainTimers(slot) = context.system.scheduler.schedule(0 seconds, implant.GetCostIntervalByExoSuit(player.ExoSuit) milliseconds, self, Player.DrainStamina(implant.StaminaCost))
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2 + 1)) // Activation sound / effect
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.ActivateImplantSlot(player.GUID, slot))
|
||||||
|
case _ => ;
|
||||||
}
|
}
|
||||||
else if (status == 1 && implantSlot.Initialized && !player.Fatigued) {
|
}
|
||||||
implantSlot.Installed match {
|
else {
|
||||||
case Some(implant : ImplantDefinition) =>
|
log.warn(s"Can't handle ImplantActivation: Player GUID: ${player.GUID} Slot ${slot} Status: ${status} Initialized: ${implantSlot.Initialized} Active: ${implantSlot.Active} Fatigued: ${player.Fatigued}")
|
||||||
if (implantSlot.Active) {
|
}
|
||||||
// Some events such as zoning will reset the implant on the client side without sending a deactivation packet
|
|
||||||
// But the implant will remain in an active state server side. For now, allow reactivation of the implant.
|
case Player.UninitializeImplant(slot: Int) => {
|
||||||
// todo: Deactivate implants server side when actions like zoning happen. (Other actions?)
|
PlayerControl.UninitializeImplant(player, slot)
|
||||||
log.warn(s"Implant $slot is already active, but activating again")
|
}
|
||||||
implantSlotStaminaDrainTimers(slot).cancel()
|
|
||||||
implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj
|
case Player.ImplantInitializationStart(slot: Int) =>
|
||||||
}
|
val implantSlot = player.ImplantSlot(slot)
|
||||||
implantSlot.Active = true
|
if(implantSlot.Installed.isDefined) {
|
||||||
if (implant.ActivationStaminaCost >= 0) {
|
if(implantSlot.Initialized) {
|
||||||
player.Stamina -= implant.ActivationStaminaCost // Activation stamina drain
|
PlayerControl.UninitializeImplant(player, slot)
|
||||||
}
|
|
||||||
if (implant.StaminaCost > 0 && implant.GetCostIntervalByExoSuit(player.ExoSuit) > 0) { // Ongoing stamina drain, if applicable
|
|
||||||
implantSlotStaminaDrainTimers(slot) = context.system.scheduler.schedule(0 seconds, implant.GetCostIntervalByExoSuit(player.ExoSuit) milliseconds, self, Player.DrainStamina(implant.StaminaCost))
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2 + 1)) // Activation sound / effect
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.ActivateImplantSlot(player.GUID, slot))
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Player.UninitializeImplant(slot : Int) =>
|
// Start client side initialization timer
|
||||||
PlayerControl.UninitializeImplant(player, slot)
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(player.GUID, ActionProgressMessage(slot + 6, 0)))
|
||||||
|
|
||||||
case Player.ImplantInitializationStart(slot : Int) =>
|
// Callback after initialization timer to complete initialization
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
implantSlot.InitializeTimer = context.system.scheduler.scheduleOnce(implantSlot.MaxTimer seconds, self, Player.ImplantInitializationComplete(slot))
|
||||||
if (implantSlot.Installed.isDefined) {
|
}
|
||||||
if (implantSlot.Initialized) {
|
|
||||||
PlayerControl.UninitializeImplant(player, slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start client side initialization timer
|
case Player.ImplantInitializationComplete(slot: Int) =>
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(player.GUID, ActionProgressMessage(slot + 6, 0)))
|
val implantSlot = player.ImplantSlot(slot)
|
||||||
|
if(implantSlot.Installed.isDefined) {
|
||||||
// Callback after initialization timer to complete initialization
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1)))
|
||||||
implantSlot.InitializeTimer = context.system.scheduler.scheduleOnce(implantSlot.MaxTimer seconds, self, Player.ImplantInitializationComplete(slot))
|
implantSlot.Initialized = true
|
||||||
|
if(implantSlot.InitializeTimer != DefaultCancellable.obj) {
|
||||||
|
implantSlot.InitializeTimer.cancel()
|
||||||
|
implantSlot.InitializeTimer = DefaultCancellable.obj
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case Player.ImplantInitializationComplete(slot : Int) =>
|
case Player.DrainStamina(amount : Int) =>
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
player.Stamina -= amount
|
||||||
if (implantSlot.Installed.isDefined) {
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1)))
|
|
||||||
implantSlot.Initialized = true
|
|
||||||
if (implantSlot.InitializeTimer != DefaultCancellable.obj) {
|
|
||||||
implantSlot.InitializeTimer.cancel()
|
|
||||||
implantSlot.InitializeTimer = DefaultCancellable.obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Player.DrainStamina(amount : Int) =>
|
case Player.StaminaChanged(currentStamina : Int) =>
|
||||||
player.Stamina -= amount
|
if(currentStamina == 0) {
|
||||||
|
player.Fatigued = true
|
||||||
|
player.skipStaminaRegenForTurns += 4
|
||||||
|
for(slot <- 0 to player.Implants.length - 1) { // Disable all implants
|
||||||
|
self ! Player.ImplantActivation(slot, 0)
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.SendResponseTargeted(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)))
|
||||||
|
}
|
||||||
|
} else if (player.Fatigued && currentStamina >= 20) {
|
||||||
|
player.Fatigued = false
|
||||||
|
for(slot <- 0 to player.Implants.length - 1) { // Re-enable all implants
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.SendResponseTargeted(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case Player.StaminaChanged(currentStamina : Int) =>
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
||||||
if (currentStamina == 0) {
|
|
||||||
player.Fatigued = true
|
|
||||||
player.skipStaminaRegenForTurns += 4
|
|
||||||
player.Implants.indices.foreach { slot => // Disable all implants
|
|
||||||
self ! Player.ImplantActivation(slot, 0)
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.SendResponseTargeted(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (player.Fatigued && currentStamina >= 20) {
|
|
||||||
player.Fatigued = false
|
|
||||||
player.Implants.indices.foreach { slot => // Re-enable all implants
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.SendResponseTargeted(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
|
||||||
case Player.Die() =>
|
case Player.Die() =>
|
||||||
if (player.isAlive) {
|
if(player.isAlive) {
|
||||||
PlayerControl.DestructionAwareness(player, None)
|
PlayerControl.DestructionAwareness(player, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +138,7 @@ class PlayerControl(player : Player) extends Actor
|
||||||
//heal
|
//heal
|
||||||
val originalHealth = player.Health
|
val originalHealth = player.Health
|
||||||
val definition = player.Definition
|
val definition = player.Definition
|
||||||
if (player.MaxHealth > 0 && originalHealth < player.MaxHealth &&
|
if(player.MaxHealth > 0 && originalHealth < player.MaxHealth &&
|
||||||
user.Faction == player.Faction &&
|
user.Faction == player.Faction &&
|
||||||
item.Magazine > 0 &&
|
item.Magazine > 0 &&
|
||||||
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) {
|
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) {
|
||||||
|
|
@ -159,14 +146,14 @@ class PlayerControl(player : Player) extends Actor
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val uname = user.Name
|
val uname = user.Name
|
||||||
val guid = player.GUID
|
val guid = player.GUID
|
||||||
if (!(player.isMoving || user.isMoving)) { //only allow stationary heals
|
if(!(player.isMoving || user.isMoving)) { //only allow stationary heals
|
||||||
val newHealth = player.Health = originalHealth + 10
|
val newHealth = player.Health = originalHealth + 10
|
||||||
val magazine = item.Discharge
|
val magazine = item.Discharge
|
||||||
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)))
|
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)))
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
||||||
player.History(HealFromEquipment(PlayerSource(player), PlayerSource(user), newHealth - originalHealth, GlobalDefinitions.medicalapplicator))
|
player.History(HealFromEquipment(PlayerSource(player), PlayerSource(user), newHealth - originalHealth, GlobalDefinitions.medicalapplicator))
|
||||||
}
|
}
|
||||||
if (player != user) {
|
if(player != user) {
|
||||||
//"Someone is trying to heal you"
|
//"Someone is trying to heal you"
|
||||||
events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 55, 1))
|
events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 55, 1))
|
||||||
//progress bar remains visible for all heal attempts
|
//progress bar remains visible for all heal attempts
|
||||||
|
|
@ -176,7 +163,7 @@ class PlayerControl(player : Player) extends Actor
|
||||||
|
|
||||||
case CommonMessages.Use(user, Some(item : Tool)) if item.Definition == GlobalDefinitions.medicalapplicator =>
|
case CommonMessages.Use(user, Some(item : Tool)) if item.Definition == GlobalDefinitions.medicalapplicator =>
|
||||||
//revive
|
//revive
|
||||||
if (user != player &&
|
if(user != player &&
|
||||||
user.Faction == player.Faction &&
|
user.Faction == player.Faction &&
|
||||||
user.isAlive && !user.isMoving &&
|
user.isAlive && !user.isMoving &&
|
||||||
!player.isAlive && !player.isBackpack &&
|
!player.isAlive && !player.isBackpack &&
|
||||||
|
|
@ -191,7 +178,7 @@ class PlayerControl(player : Player) extends Actor
|
||||||
case CommonMessages.Use(user, Some(item : Tool)) if item.Definition == GlobalDefinitions.bank =>
|
case CommonMessages.Use(user, Some(item : Tool)) if item.Definition == GlobalDefinitions.bank =>
|
||||||
val originalArmor = player.Armor
|
val originalArmor = player.Armor
|
||||||
val definition = player.Definition
|
val definition = player.Definition
|
||||||
if (player.MaxArmor > 0 && originalArmor < player.MaxArmor &&
|
if(player.MaxArmor > 0 && originalArmor < player.MaxArmor &&
|
||||||
user.Faction == player.Faction &&
|
user.Faction == player.Faction &&
|
||||||
item.AmmoType == Ammo.armor_canister && item.Magazine > 0 &&
|
item.AmmoType == Ammo.armor_canister && item.Magazine > 0 &&
|
||||||
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) {
|
Vector3.Distance(user.Position, player.Position) < definition.RepairDistance) {
|
||||||
|
|
@ -199,15 +186,15 @@ class PlayerControl(player : Player) extends Actor
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val uname = user.Name
|
val uname = user.Name
|
||||||
val guid = player.GUID
|
val guid = player.GUID
|
||||||
if (!(player.isMoving || user.isMoving)) { //only allow stationary repairs
|
if(!(player.isMoving || user.isMoving)) { //only allow stationary repairs
|
||||||
val newArmor = player.Armor = originalArmor + Repairable.Quality + RepairValue(item) + definition.RepairMod
|
val newArmor = player.Armor = originalArmor + Repairable.Quality + RepairValue(item) + definition.RepairMod
|
||||||
val magazine = item.Discharge
|
val magazine = item.Discharge
|
||||||
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)))
|
events ! AvatarServiceMessage(uname, AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)))
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
||||||
player.History(RepairFromEquipment(PlayerSource(player), PlayerSource(user), newArmor - originalArmor, GlobalDefinitions.bank))
|
player.History(RepairFromEquipment(PlayerSource(player), PlayerSource(user), newArmor - originalArmor, GlobalDefinitions.bank))
|
||||||
}
|
}
|
||||||
if (player != user) {
|
if(player != user) {
|
||||||
if (player.isAlive) {
|
if(player.isAlive) {
|
||||||
//"Someone is trying to repair you" gets strobed twice for visibility
|
//"Someone is trying to repair you" gets strobed twice for visibility
|
||||||
val msg = AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 56, 1))
|
val msg = AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 56, 1))
|
||||||
events ! msg
|
events ! msg
|
||||||
|
|
@ -219,263 +206,6 @@ class PlayerControl(player : Player) extends Actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case Terminal.TerminalMessage(_, msg, order) =>
|
|
||||||
order match {
|
|
||||||
case Terminal.BuyExosuit(exosuit, subtype) =>
|
|
||||||
val time = System.currentTimeMillis
|
|
||||||
var toDelete : List[InventoryItem] = Nil
|
|
||||||
val originalSuit = player.ExoSuit
|
|
||||||
val originalSubtype = Loadout.DetermineSubtype(player)
|
|
||||||
val requestToChangeArmor = originalSuit != exosuit || originalSubtype != subtype
|
|
||||||
val allowedToChangeArmor = Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
|
||||||
if (time - player.GetLastUsedTime(exosuit, subtype) < 30000L) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
|
||||||
})
|
|
||||||
val result = if (requestToChangeArmor && allowedToChangeArmor) {
|
|
||||||
log.info(s"${player.Name} wants to change to a different exo-suit - $exosuit")
|
|
||||||
player.SetLastUsedTime(exosuit, subtype, System.currentTimeMillis())
|
|
||||||
val beforeHolsters = Players.clearHolsters(player.Holsters().iterator)
|
|
||||||
val beforeInventory = player.Inventory.Clear()
|
|
||||||
//change suit
|
|
||||||
val originalArmor = player.Armor
|
|
||||||
player.ExoSuit = exosuit //changes the value of MaxArmor to reflect the new exo-suit
|
|
||||||
val toMaxArmor = player.MaxArmor
|
|
||||||
if (originalSuit != exosuit || originalSubtype != subtype || originalArmor > toMaxArmor) {
|
|
||||||
player.History(HealFromExoSuitChange(PlayerSource(player), exosuit))
|
|
||||||
player.Armor = toMaxArmor
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.Armor = originalArmor
|
|
||||||
}
|
|
||||||
//ensure arm is down, even if it needs to go back up
|
|
||||||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
|
||||||
player.DrawnSlot = Player.HandsDownSlot
|
|
||||||
}
|
|
||||||
val normalHolsters = if (originalSuit == ExoSuitType.MAX) {
|
|
||||||
val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max)
|
|
||||||
toDelete ++= maxWeapons
|
|
||||||
normalWeapons
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
beforeHolsters
|
|
||||||
}
|
|
||||||
//populate holsters
|
|
||||||
val (afterHolsters, finalInventory) = if (exosuit == ExoSuitType.MAX) {
|
|
||||||
(normalHolsters, Players.fillEmptyHolsters(List(player.Slot(4)).iterator, normalHolsters) ++ beforeInventory)
|
|
||||||
}
|
|
||||||
else if (originalSuit == exosuit) { //note - this will rarely be the situation
|
|
||||||
(normalHolsters, Players.fillEmptyHolsters(player.Holsters().iterator, normalHolsters))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val (afterHolsters, toInventory) = normalHolsters.partition(elem => elem.obj.Size == player.Slot(elem.start).Size)
|
|
||||||
afterHolsters.foreach({ elem => player.Slot(elem.start).Equipment = elem.obj })
|
|
||||||
val remainder = Players.fillEmptyHolsters(player.Holsters().iterator, toInventory ++ beforeInventory)
|
|
||||||
(
|
|
||||||
player.Holsters()
|
|
||||||
.zipWithIndex
|
|
||||||
.map { case (slot, i) => (slot.Equipment, i) }
|
|
||||||
.collect { case (Some(obj), index) => InventoryItem(obj, index) }
|
|
||||||
.toList,
|
|
||||||
remainder
|
|
||||||
)
|
|
||||||
}
|
|
||||||
//put items back into inventory
|
|
||||||
val (stow, drop) = if (originalSuit == exosuit) {
|
|
||||||
(finalInventory, Nil)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val (a, b) = GridInventory.recoverInventory(finalInventory, player.Inventory)
|
|
||||||
(a, b.map {
|
|
||||||
InventoryItem(_, -1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
stow.foreach { elem =>
|
|
||||||
player.Inventory.InsertQuickly(elem.start, elem.obj)
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id,
|
|
||||||
AvatarAction.ChangeExosuit(player.GUID, exosuit, subtype, player.LastDrawnSlot, exosuit == ExoSuitType.MAX && requestToChangeArmor,
|
|
||||||
beforeHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterHolsters,
|
|
||||||
beforeInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, stow, drop,
|
|
||||||
toDelete.map { case InventoryItem(obj, _) => (obj, obj.GUID) }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result))
|
|
||||||
|
|
||||||
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
|
|
||||||
log.info(s"wants to change equipment loadout to their option #${msg.unk1 + 1}")
|
|
||||||
val fallbackSubtype = 0
|
|
||||||
val fallbackSuit = ExoSuitType.Standard
|
|
||||||
val originalSuit = player.ExoSuit
|
|
||||||
val originalSubtype = Loadout.DetermineSubtype(player)
|
|
||||||
//sanitize exo-suit for change
|
|
||||||
val dropPred = ContainableBehavior.DropPredicate(player)
|
|
||||||
val oldHolsters = Players.clearHolsters(player.Holsters().iterator)
|
|
||||||
val dropHolsters = oldHolsters.filter(dropPred)
|
|
||||||
val oldInventory = player.Inventory.Clear()
|
|
||||||
val dropInventory = oldInventory.filter(dropPred)
|
|
||||||
val toDeleteOrDrop : List[InventoryItem] = (player.FreeHand.Equipment match {
|
|
||||||
case Some(obj) =>
|
|
||||||
val out = InventoryItem(obj, -1)
|
|
||||||
player.FreeHand.Equipment = None
|
|
||||||
if (dropPred(out)) {
|
|
||||||
List(out)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Nil
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
Nil
|
|
||||||
}) ++ dropHolsters ++ dropInventory
|
|
||||||
//a loadout with a prohibited exo-suit type will result in the fallback exo-suit type
|
|
||||||
//imposed 5min delay on mechanized exo-suit switches
|
|
||||||
val time = System.currentTimeMillis()
|
|
||||||
val (nextSuit, nextSubtype) = if (Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
|
||||||
if (time - player.GetLastUsedTime(exosuit, subtype) < 30000L) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
|
||||||
})) {
|
|
||||||
(exosuit, subtype)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn(s"no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead")
|
|
||||||
player.SetLastUsedTime(fallbackSuit, fallbackSubtype, time)
|
|
||||||
(fallbackSuit, fallbackSubtype)
|
|
||||||
}
|
|
||||||
//sanitize (incoming) inventory
|
|
||||||
//TODO equipment permissions; these loops may be expanded upon in future
|
|
||||||
val curatedHolsters = for {
|
|
||||||
item <- holsters
|
|
||||||
//id = item.obj.Definition.ObjectId
|
|
||||||
//lastTime = player.GetLastUsedTime(id)
|
|
||||||
if true
|
|
||||||
} yield item
|
|
||||||
val curatedInventory = for {
|
|
||||||
item <- inventory
|
|
||||||
//id = item.obj.Definition.ObjectId
|
|
||||||
//lastTime = player.GetLastUsedTime(id)
|
|
||||||
if true
|
|
||||||
} yield item
|
|
||||||
//update suit internally
|
|
||||||
val originalArmor = player.Armor
|
|
||||||
player.ExoSuit = nextSuit
|
|
||||||
val toMaxArmor = player.MaxArmor
|
|
||||||
if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
|
|
||||||
player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit))
|
|
||||||
player.Armor = toMaxArmor
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.Armor = originalArmor
|
|
||||||
}
|
|
||||||
//ensure arm is down, even if it needs to go back up
|
|
||||||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
|
||||||
player.DrawnSlot = Player.HandsDownSlot
|
|
||||||
}
|
|
||||||
//a change due to exo-suit permissions mismatch will result in (more) items being re-arranged and/or dropped
|
|
||||||
//dropped items are not registered and can just be forgotten
|
|
||||||
val (afterHolsters, afterInventory) = if (nextSuit == exosuit) {
|
|
||||||
(
|
|
||||||
//melee slot preservation for MAX
|
|
||||||
if (nextSuit == ExoSuitType.MAX) {
|
|
||||||
holsters.filter(_.start == 4)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
curatedHolsters.filterNot(dropPred)
|
|
||||||
},
|
|
||||||
curatedInventory.filterNot(dropPred)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//our exo-suit type was hijacked by changing permissions; we shouldn't even be able to use that loadout(!)
|
|
||||||
//holsters
|
|
||||||
val leftoversForInventory = Players.fillEmptyHolsters(
|
|
||||||
player.Holsters().iterator,
|
|
||||||
(curatedHolsters ++ curatedInventory).filterNot(dropPred)
|
|
||||||
)
|
|
||||||
val finalHolsters = player.Holsters()
|
|
||||||
.zipWithIndex
|
|
||||||
.collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) }
|
|
||||||
.toList
|
|
||||||
//inventory
|
|
||||||
val (finalInventory, _) = GridInventory.recoverInventory(leftoversForInventory, player.Inventory)
|
|
||||||
(finalHolsters, finalInventory)
|
|
||||||
}
|
|
||||||
(afterHolsters ++ afterInventory).foreach { entry => entry.obj.Faction = player.Faction }
|
|
||||||
toDeleteOrDrop.foreach { entry => entry.obj.Faction = PlanetSideEmpire.NEUTRAL }
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id,
|
|
||||||
AvatarAction.ChangeLoadout(player.GUID, nextSuit, nextSubtype, player.LastDrawnSlot, exosuit == ExoSuitType.MAX,
|
|
||||||
oldHolsters.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterHolsters,
|
|
||||||
oldInventory.map { case InventoryItem(obj, _) => (obj, obj.GUID) }, afterInventory, toDeleteOrDrop)
|
|
||||||
)
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true))
|
|
||||||
|
|
||||||
case _ => ; //terminal messages not handled here
|
|
||||||
}
|
|
||||||
|
|
||||||
case Zone.Ground.ItemOnGround(item, _, _) => ;
|
|
||||||
val name = player.Name
|
|
||||||
val zone = player.Zone
|
|
||||||
val avatarEvents = zone.AvatarEvents
|
|
||||||
val localEvents = zone.LocalEvents
|
|
||||||
item match {
|
|
||||||
case trigger : BoomerTrigger =>
|
|
||||||
//dropped the trigger, no longer own the boomer; make certain whole faction is aware of that
|
|
||||||
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name}) match {
|
|
||||||
case (Some(boomer : BoomerDeployable), Some(avatar)) =>
|
|
||||||
val guid = boomer.GUID
|
|
||||||
val factionChannel = boomer.Faction.toString
|
|
||||||
if(avatar.Deployables.Remove(boomer)) {
|
|
||||||
boomer.Faction = PlanetSideEmpire.NEUTRAL
|
|
||||||
boomer.AssignOwnership(None)
|
|
||||||
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach { case (currElem, curr, maxElem, max) =>
|
|
||||||
avatarEvents ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max))
|
|
||||||
avatarEvents ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr))
|
|
||||||
}
|
|
||||||
localEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(boomer, zone))
|
|
||||||
localEvents ! LocalServiceMessage(factionChannel,
|
|
||||||
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss,
|
|
||||||
DeployableInfo(guid, DeployableIcon.Boomer, boomer.Position, PlanetSideGUID(0))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
avatarEvents ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, guid, PlanetSideEmpire.NEUTRAL))
|
|
||||||
}
|
|
||||||
case _ => ; //pointless trigger? or a trigger being deleted?
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
case Zone.Ground.CanNotDropItem(_, item, reason) =>
|
|
||||||
log.warn(s"${player.Name} tried to drop a ${item.Definition.Name} on the ground, but it $reason")
|
|
||||||
|
|
||||||
case Zone.Ground.ItemInHand(_) => ;
|
|
||||||
|
|
||||||
case Zone.Ground.CanNotPickupItem(_, item_guid, reason) =>
|
|
||||||
log.warn(s"${player.Name} failed to pick up an item ($item_guid) from the ground because $reason")
|
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -524,22 +254,27 @@ class PlayerControl(player : Player) extends Actor
|
||||||
* @param target an object that can be affected by the jammered status
|
* @param target an object that can be affected by the jammered status
|
||||||
* @param dur the duration of the timer, in milliseconds
|
* @param dur the duration of the timer, in milliseconds
|
||||||
*/
|
*/
|
||||||
override def StartJammeredStatus(target : Any, dur : Int) : Unit = {
|
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
|
||||||
//TODO these features
|
case obj : Player =>
|
||||||
val zone = player.Zone
|
//TODO these features
|
||||||
|
val guid = obj.GUID
|
||||||
|
val zone = obj.Zone
|
||||||
|
val zoneId = zone.Id
|
||||||
|
val events = zone.AvatarEvents
|
||||||
|
|
||||||
player.Implants.indices.foreach { slot => // Deactivate & uninitialize all implants
|
for(slot <- 0 to player.Implants.length - 1) { // Deactivate & uninitialize all implants
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect
|
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect
|
||||||
self ! Player.ImplantActivation(slot, 0)
|
self ! Player.ImplantActivation(slot, 0)
|
||||||
PlayerControl.UninitializeImplant(player, slot)
|
PlayerControl.UninitializeImplant(player, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 10)
|
obj.skipStaminaRegenForTurns = math.max(obj.skipStaminaRegenForTurns, 10)
|
||||||
super.StartJammeredStatus(target, dur)
|
super.StartJammeredStatus(target, dur)
|
||||||
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
override def CancelJammeredStatus(target: Any): Unit = {
|
override def CancelJammeredStatus(target: Any): Unit = {
|
||||||
player.Implants.indices.foreach { slot => // Start reinitializing all implants
|
for(slot <- 0 to player.Implants.length - 1) { // Start reinitializing all implants
|
||||||
self ! Player.ImplantInitializationStart(slot)
|
self ! Player.ImplantInitializationStart(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,101 +299,6 @@ class PlayerControl(player : Player) extends Actor
|
||||||
else {
|
else {
|
||||||
item.FireMode.Modifiers.Damage3
|
item.FireMode.Modifiers.Damage3
|
||||||
}
|
}
|
||||||
|
|
||||||
def MessageDeferredCallback(msg : Any) : Unit = {
|
|
||||||
msg match {
|
|
||||||
case Containable.MoveItem(_, item, _) =>
|
|
||||||
//momentarily put item back where it was originally
|
|
||||||
val obj = ContainerObject
|
|
||||||
obj.Find(item) match {
|
|
||||||
case Some(slot) =>
|
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
|
||||||
)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val obj = ContainerObject
|
|
||||||
val zone = obj.Zone
|
|
||||||
val name = player.Name
|
|
||||||
val toChannel = if(obj.VisibleSlots.contains(slot) || obj.isBackpack) zone.Id else name
|
|
||||||
val events = zone.AvatarEvents
|
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
|
||||||
if(slot == obj.DrawnSlot) {
|
|
||||||
obj.DrawnSlot = Player.HandsDownSlot
|
|
||||||
}
|
|
||||||
events ! AvatarServiceMessage(toChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID))
|
|
||||||
}
|
|
||||||
|
|
||||||
def PutItemInSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val obj = ContainerObject
|
|
||||||
val guid = obj.GUID
|
|
||||||
val zone = obj.Zone
|
|
||||||
val events = zone.AvatarEvents
|
|
||||||
val name = player.Name
|
|
||||||
val definition = item.Definition
|
|
||||||
val msg = AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
ObjectCreateDetailedMessage(
|
|
||||||
definition.ObjectId,
|
|
||||||
item.GUID,
|
|
||||||
ObjectCreateMessageParent(guid, slot),
|
|
||||||
definition.Packet.DetailedConstructorData(item).get
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if(obj.isBackpack) {
|
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
|
||||||
events ! AvatarServiceMessage(zone.Id, msg)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val faction = obj.Faction
|
|
||||||
item.Faction = faction
|
|
||||||
events ! AvatarServiceMessage(name, msg)
|
|
||||||
if(obj.VisibleSlots.contains(slot)) {
|
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
|
||||||
}
|
|
||||||
//handle specific types of items
|
|
||||||
item match {
|
|
||||||
case trigger : BoomerTrigger =>
|
|
||||||
//pick up the trigger, own the boomer; make certain whole faction is aware of that
|
|
||||||
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name }) match {
|
|
||||||
case (Some(boomer : BoomerDeployable), Some(avatar))
|
|
||||||
if !boomer.OwnerName.contains(name) || boomer.Faction != faction =>
|
|
||||||
val bguid = boomer.GUID
|
|
||||||
val faction = player.Faction
|
|
||||||
val factionChannel = faction.toString
|
|
||||||
if(avatar.Deployables.Add(boomer)) {
|
|
||||||
boomer.Faction = faction
|
|
||||||
boomer.AssignOwnership(player)
|
|
||||||
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach { case (currElem, curr, maxElem, max) =>
|
|
||||||
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max))
|
|
||||||
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr))
|
|
||||||
}
|
|
||||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone))
|
|
||||||
events ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction))
|
|
||||||
zone.LocalEvents ! LocalServiceMessage(factionChannel,
|
|
||||||
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build,
|
|
||||||
DeployableInfo(bguid, DeployableIcon.Boomer, boomer.Position, boomer.Owner.getOrElse(PlanetSideGUID(0)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case _ => ; //pointless trigger?
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def SwapItemCallback(item : Equipment) : Unit = {
|
|
||||||
val obj = ContainerObject
|
|
||||||
val zone = obj.Zone
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object PlayerControl {
|
object PlayerControl {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.definition.converter
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.LockerEquipment
|
import net.psforever.objects.LockerContainer
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.GridInventory
|
import net.psforever.objects.inventory.GridInventory
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
@ -9,8 +9,8 @@ import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||||
|
|
||||||
import scala.util.{Success, Try}
|
import scala.util.{Success, Try}
|
||||||
|
|
||||||
class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]() {
|
class LockerContainerConverter extends ObjectCreateConverter[LockerContainer]() {
|
||||||
override def ConstructorData(obj : LockerEquipment) : Try[LockerContainerData] = {
|
override def ConstructorData(obj : LockerContainer) : Try[LockerContainerData] = {
|
||||||
MakeInventory(obj.Inventory) match {
|
MakeInventory(obj.Inventory) match {
|
||||||
case Nil =>
|
case Nil =>
|
||||||
Success(LockerContainerData(None))
|
Success(LockerContainerData(None))
|
||||||
|
|
@ -19,7 +19,7 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def DetailedConstructorData(obj : LockerEquipment) : Try[DetailedLockerContainerData] = {
|
override def DetailedConstructorData(obj : LockerContainer) : Try[DetailedLockerContainerData] = {
|
||||||
if(obj.Inventory.Size > 0) {
|
if(obj.Inventory.Size > 0) {
|
||||||
Success(DetailedLockerContainerData(
|
Success(DetailedLockerContainerData(
|
||||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||||
|
|
|
||||||
|
|
@ -86,11 +86,12 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
obj.Weapons.collect { case (index, slot) if slot.Equipment.nonEmpty =>
|
obj.Weapons.map({
|
||||||
val equip : Equipment = slot.Equipment.get
|
case(index, slot) =>
|
||||||
val equipDef = equip.Definition
|
val equip : Equipment = slot.Equipment.get
|
||||||
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
val equipDef = equip.Definition
|
||||||
}.toList
|
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||||
|
}).toList
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def MakeUtilities(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
protected def MakeUtilities(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,6 @@ object GUIDTask {
|
||||||
private val localObject = obj
|
private val localObject = obj
|
||||||
private val localAccessor = guid
|
private val localAccessor = guid
|
||||||
|
|
||||||
override def Description : String = s"register $localObject"
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = if(localObject.HasGUID) {
|
override def isComplete : Task.Resolution.Value = if(localObject.HasGUID) {
|
||||||
Task.Resolution.Success
|
Task.Resolution.Success
|
||||||
}
|
}
|
||||||
|
|
@ -92,9 +90,6 @@ object GUIDTask {
|
||||||
def RegisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
def RegisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
|
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
|
||||||
}
|
}
|
||||||
def RegisterLocker(obj : LockerEquipment)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct tasking that registers the objects that are within the given container's inventory
|
* Construct tasking that registers the objects that are within the given container's inventory
|
||||||
|
|
@ -129,6 +124,8 @@ object GUIDTask {
|
||||||
obj match {
|
obj match {
|
||||||
case tool : Tool =>
|
case tool : Tool =>
|
||||||
RegisterTool(tool)
|
RegisterTool(tool)
|
||||||
|
case locker : LockerContainer =>
|
||||||
|
RegisterLocker(locker)
|
||||||
case _ =>
|
case _ =>
|
||||||
RegisterObjectTask(obj)
|
RegisterObjectTask(obj)
|
||||||
}
|
}
|
||||||
|
|
@ -218,8 +215,6 @@ object GUIDTask {
|
||||||
private val localObject = obj
|
private val localObject = obj
|
||||||
private val localAccessor = guid
|
private val localAccessor = guid
|
||||||
|
|
||||||
override def Description : String = s"unregister $localObject"
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = if(!localObject.HasGUID) {
|
override def isComplete : Task.Resolution.Value = if(!localObject.HasGUID) {
|
||||||
Task.Resolution.Success
|
Task.Resolution.Success
|
||||||
}
|
}
|
||||||
|
|
@ -259,9 +254,6 @@ object GUIDTask {
|
||||||
def UnregisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
def UnregisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, UnregisterInventory(obj))
|
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, UnregisterInventory(obj))
|
||||||
}
|
}
|
||||||
def UnregisterLocker(obj : LockerEquipment)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct tasking that unregisters the objects that are within the given container's inventory
|
* Construct tasking that unregisters the objects that are within the given container's inventory
|
||||||
|
|
@ -290,6 +282,8 @@ object GUIDTask {
|
||||||
obj match {
|
obj match {
|
||||||
case tool : Tool =>
|
case tool : Tool =>
|
||||||
UnregisterTool(tool)
|
UnregisterTool(tool)
|
||||||
|
case locker : LockerContainer =>
|
||||||
|
UnregisterLocker(locker)
|
||||||
case _ =>
|
case _ =>
|
||||||
UnregisterObjectTask(obj)
|
UnregisterObjectTask(obj)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package net.psforever.objects.guid
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
|
|
||||||
trait Task {
|
trait Task {
|
||||||
def Description : String = "write_descriptive_task_message"
|
|
||||||
def Execute(resolver : ActorRef) : Unit
|
def Execute(resolver : ActorRef) : Unit
|
||||||
def isComplete : Task.Resolution.Value = Task.Resolution.Incomplete
|
def isComplete : Task.Resolution.Value = Task.Resolution.Incomplete
|
||||||
def Timeout : Long = 200L //milliseconds
|
def Timeout : Long = 200L //milliseconds
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class TaskResolver() extends Actor {
|
||||||
private def GiveTask(aTask : Task) : Unit = {
|
private def GiveTask(aTask : Task) : Unit = {
|
||||||
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(aTask)
|
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(aTask)
|
||||||
tasks += entry
|
tasks += entry
|
||||||
trace(s"enqueue and start task ${aTask.Description}")
|
trace(s"enqueue and start task $aTask")
|
||||||
entry.Execute(self)
|
entry.Execute(self)
|
||||||
StartTimeoutCheck()
|
StartTimeoutCheck()
|
||||||
}
|
}
|
||||||
|
|
@ -112,13 +112,12 @@ class TaskResolver() extends Actor {
|
||||||
private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], resolver : ActorRef = Actor.noSender) : Unit = {
|
private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], resolver : ActorRef = Actor.noSender) : Unit = {
|
||||||
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, subtasks.map(task => task.task), resolver)
|
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, subtasks.map(task => task.task), resolver)
|
||||||
tasks += entry
|
tasks += entry
|
||||||
trace(s"enqueue task ${task.Description}")
|
trace(s"enqueue task $task")
|
||||||
if(subtasks.isEmpty) { //a leaf in terms of task dependency; so, not dependent on any other work
|
if(subtasks.isEmpty) { //a leaf in terms of task dependency; so, not dependent on any other work
|
||||||
trace(s"start task ${task.Description}")
|
trace(s"start task $task")
|
||||||
entry.Execute(self)
|
entry.Execute(self)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
trace(s"enqueuing ${subtasks.length} substask(s) belonging to ${task.Description}")
|
|
||||||
subtasks.foreach({subtask =>
|
subtasks.foreach({subtask =>
|
||||||
context.parent ! TaskResolver.GiveSubtask(subtask.task, subtask.subs, self) //route back to submit subtask to pool
|
context.parent ! TaskResolver.GiveSubtask(subtask.task, subtask.subs, self) //route back to submit subtask to pool
|
||||||
})
|
})
|
||||||
|
|
@ -161,7 +160,7 @@ class TaskResolver() extends Actor {
|
||||||
private def GeneralOnSuccess(index : Int) : Unit = {
|
private def GeneralOnSuccess(index : Int) : Unit = {
|
||||||
val entry = tasks(index)
|
val entry = tasks(index)
|
||||||
entry.task.onSuccess()
|
entry.task.onSuccess()
|
||||||
trace(s"success with task ${entry.task.Description}")
|
trace(s"success with this task ${entry.task}")
|
||||||
if(entry.supertaskRef != ActorRef.noSender) {
|
if(entry.supertaskRef != ActorRef.noSender) {
|
||||||
entry.supertaskRef ! TaskResolver.CompletedSubtask(entry.task) //alert our dependent task's resolver that we have completed
|
entry.supertaskRef ! TaskResolver.CompletedSubtask(entry.task) //alert our dependent task's resolver that we have completed
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +177,7 @@ class TaskResolver() extends Actor {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
val entry = tasks(index)
|
val entry = tasks(index)
|
||||||
if(TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Success)) {
|
if(TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Success)) {
|
||||||
trace(s"start new task ${entry.task.Description}")
|
trace(s"start new task ${entry.task}")
|
||||||
entry.Execute(self)
|
entry.Execute(self)
|
||||||
StartTimeoutCheck()
|
StartTimeoutCheck()
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +224,7 @@ class TaskResolver() extends Actor {
|
||||||
private def GeneralOnFailure(index : Int, ex : Throwable) : Unit = {
|
private def GeneralOnFailure(index : Int, ex : Throwable) : Unit = {
|
||||||
val entry = tasks(index)
|
val entry = tasks(index)
|
||||||
val task = entry.task
|
val task = entry.task
|
||||||
trace(s"failure with task ${task.Description}")
|
trace(s"failure with this task $task")
|
||||||
task.onAbort(ex)
|
task.onAbort(ex)
|
||||||
task.onFailure(ex)
|
task.onFailure(ex)
|
||||||
if(entry.supertaskRef != ActorRef.noSender) {
|
if(entry.supertaskRef != ActorRef.noSender) {
|
||||||
|
|
@ -268,7 +267,7 @@ class TaskResolver() extends Actor {
|
||||||
private def PropagateAbort(index : Int, ex : Throwable) : Unit = {
|
private def PropagateAbort(index : Int, ex : Throwable) : Unit = {
|
||||||
tasks(index).subtasks.foreach({subtask =>
|
tasks(index).subtasks.foreach({subtask =>
|
||||||
if(subtask.isComplete == Task.Resolution.Success) {
|
if(subtask.isComplete == Task.Resolution.Success) {
|
||||||
trace(s"aborting task ${subtask.Description}")
|
trace(s"aborting task $subtask")
|
||||||
subtask.onAbort(ex)
|
subtask.onAbort(ex)
|
||||||
}
|
}
|
||||||
context.parent ! Broadcast(TaskResolver.AbortTask(subtask, ex))
|
context.parent ! Broadcast(TaskResolver.AbortTask(subtask, ex))
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ class GridInventory extends Container {
|
||||||
* Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.<br>
|
* Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* A "collision" is considered a situation where the stowed placards of two items would overlap in some way.
|
* A "collision" is considered a situation where the stowed placards of two items would overlap in some way.
|
||||||
* The grid keeps track of the location of items by storing the primitive of their GUID in one or more cells.
|
* The gridkeeps track of the location of items by storing the primitive of their GUID in one or more cells.
|
||||||
* Two primitives can not be stored in the same cell.
|
* Two primitives can not be stored in the same cell.
|
||||||
* If placing two items into the same inventory leads to a situation where two primitive values might be in the same cell,
|
* If placing two items into the same inventory leads to a situation where two primitive values might be in the same cell,
|
||||||
* that is a collision.
|
* that is a collision.
|
||||||
|
|
@ -188,19 +188,17 @@ class GridInventory extends Container {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
|
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
|
||||||
items
|
items.values.foreach({ item : InventoryItem =>
|
||||||
.map { case (_, item : InventoryItem) => item }
|
val actualItemStart : Int = item.start - offset
|
||||||
.foreach { item : InventoryItem =>
|
val itemx : Int = actualItemStart % width
|
||||||
val actualItemStart : Int = item.start - offset
|
val itemy : Int = actualItemStart / width
|
||||||
val itemx : Int = actualItemStart % width
|
val tile = item.obj.Tile
|
||||||
val itemy : Int = actualItemStart / width
|
val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.Width > startx } else { itemx <= startw }
|
||||||
val tile = item.obj.Tile
|
val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.Height > starty } else { itemy <= starth }
|
||||||
val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.Width > startx } else { itemx <= startw }
|
if(clipsOnX && clipsOnY) {
|
||||||
val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.Height > starty } else { itemy <= starth }
|
collisions += item
|
||||||
if(clipsOnX && clipsOnY) {
|
|
||||||
collisions += item
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
Success(collisions.toList)
|
Success(collisions.toList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +578,7 @@ class GridInventory extends Container {
|
||||||
def Clear() : List[InventoryItem] = {
|
def Clear() : List[InventoryItem] = {
|
||||||
val list = items.values.toList
|
val list = items.values.toList
|
||||||
items.clear
|
items.clear
|
||||||
entryIndex.set(0)
|
//entryIndex.set(0)
|
||||||
grid = SetCellsOnlyNoOffset(0, width, height)
|
grid = SetCellsOnlyNoOffset(0, width, height)
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
@ -781,17 +779,4 @@ object GridInventory {
|
||||||
node.down.get(node.x, node.y + height, node.width, node.height - height)
|
node.down.get(node.x, node.y + height, node.width, node.height - height)
|
||||||
node.right.get(node.x + width, node.y, node.width - width, height)
|
node.right.get(node.x + width, node.y, node.width - width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
def toPrintedList(inv : GridInventory) : String = {
|
|
||||||
val list = new StringBuilder
|
|
||||||
list.append("\n")
|
|
||||||
inv.Items.zipWithIndex.foreach { case (InventoryItem(obj, start), index) =>
|
|
||||||
list.append(s"${index+1}: ${obj.Definition.Name}@${obj.GUID} -> $start\n")
|
|
||||||
}
|
|
||||||
list.toString
|
|
||||||
}
|
|
||||||
|
|
||||||
def toPrintedGrid(inv : GridInventory) : String = {
|
|
||||||
new StringBuilder().append("\n").append(inv.grid.toSeq.grouped(inv.width).mkString("\n")).toString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,7 @@ object Loadout {
|
||||||
def Create(vehicle : Vehicle, label : String) : Loadout = {
|
def Create(vehicle : Vehicle, label : String) : Loadout = {
|
||||||
VehicleLoadout(
|
VehicleLoadout(
|
||||||
label,
|
label,
|
||||||
packageSimplifications(vehicle.Weapons.collect { case (index, slot) if slot.Equipment.nonEmpty =>
|
packageSimplifications(vehicle.Weapons.map({ case (index, weapon) => InventoryItem(weapon.Equipment.get, index) }).toList),
|
||||||
InventoryItem(slot.Equipment.get, index) }.toList
|
|
||||||
),
|
|
||||||
packageSimplifications(vehicle.Trunk.Items),
|
packageSimplifications(vehicle.Trunk.Items),
|
||||||
vehicle.Definition
|
vehicle.Definition
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,631 +0,0 @@
|
||||||
// Copyright (c) 2020 PSForever
|
|
||||||
package net.psforever.objects.serverobject.containable
|
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef}
|
|
||||||
import akka.pattern.{AskTimeoutException, ask}
|
|
||||||
import akka.util.Timeout
|
|
||||||
import net.psforever.objects.equipment.Equipment
|
|
||||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
|
||||||
import net.psforever.objects.zones.Zone
|
|
||||||
import net.psforever.objects.{BoomerTrigger, GlobalDefinitions, Player}
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import scala.util.Success
|
|
||||||
|
|
||||||
/** Parent of all standard (input) messages handled by a `ContainableBehavior` object for the purposes of item transfer */
|
|
||||||
sealed trait ContainableMsg
|
|
||||||
/** `ContainableBehavior` messages that are allowed to be temporarily blocked in event of a complicated item transfer */
|
|
||||||
sealed trait DeferrableMsg extends ContainableMsg
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mixin for handling synchronized movement of `Equipment` items into or out from `Container` entities.
|
|
||||||
* The most important feature of this synchronization is the movmement of equipment
|
|
||||||
* out from one container into another container
|
|
||||||
* without causing representation overlap, overwriting, or unintended stacking of other equipment
|
|
||||||
* including equipment that has nort yet been inserted.
|
|
||||||
*/
|
|
||||||
trait ContainableBehavior {
|
|
||||||
_ : Actor =>
|
|
||||||
def ContainerObject : PlanetSideServerObject with Container
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A flag for handling deferred messages during an attempt at complicated item movement (`MoveItem`) procedures.
|
|
||||||
* Complicated item movement procedures generally occur when the source and the destination are not the same container.
|
|
||||||
* The flag is set to `1` on the destination and is designed to block interference other normal insertion messages
|
|
||||||
* by taking those messages and pushing them back into the mailbox after a short delay.
|
|
||||||
* If two attempts on the same destination occur due to extremely coincidental item movement messages,
|
|
||||||
* the flag is set to `2` and most all messages involving item movement and item insertion are deferred.
|
|
||||||
* The destination is set back to normal - flag to `0` - when both of the attempts short-circuit due to timeout.
|
|
||||||
*/
|
|
||||||
private var waitOnMoveItemOps : Int = 0
|
|
||||||
|
|
||||||
final val containerBehavior : Receive = {
|
|
||||||
/* messages that modify delivery order */
|
|
||||||
case ContainableBehavior.Wait() => Wait()
|
|
||||||
|
|
||||||
case ContainableBehavior.Resume() => Resume()
|
|
||||||
|
|
||||||
case repeatMsg @ ContainableBehavior.Defer(msg, sentBy) =>
|
|
||||||
//received a previously blocked message; is it still blocked?
|
|
||||||
msg match {
|
|
||||||
case _ : ContainableMsg if waitOnMoveItemOps == 2 => RepeatMessageLater(repeatMsg)
|
|
||||||
case _ : DeferrableMsg if waitOnMoveItemOps == 1 => RepeatMessageLater(repeatMsg)
|
|
||||||
case _ => self.tell(msg, sentBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
case msg : ContainableMsg if waitOnMoveItemOps == 2 =>
|
|
||||||
//all standard messages are blocked
|
|
||||||
RepeatMessageLater(ContainableBehavior.Defer(msg, sender))
|
|
||||||
MessageDeferredCallback(msg)
|
|
||||||
|
|
||||||
case msg : DeferrableMsg if waitOnMoveItemOps == 1 =>
|
|
||||||
//insertion messages not related to an item move attempt are blocked
|
|
||||||
RepeatMessageLater(ContainableBehavior.Defer(msg, sender))
|
|
||||||
MessageDeferredCallback(msg)
|
|
||||||
|
|
||||||
/* normal messages */
|
|
||||||
case Containable.RemoveItemFromSlot(None, Some(slot)) =>
|
|
||||||
sender ! LocalRemoveItemFromSlot(slot)
|
|
||||||
|
|
||||||
case Containable.RemoveItemFromSlot(Some(item), _) =>
|
|
||||||
sender ! LocalRemoveItemFromSlot(item)
|
|
||||||
|
|
||||||
case Containable.PutItemInSlot(item, dest) => /* can be deferred */
|
|
||||||
sender ! LocalPutItemInSlot(item, dest)
|
|
||||||
|
|
||||||
case Containable.PutItemInSlotOnly(item, dest) => /* can be deferred */
|
|
||||||
sender ! LocalPutItemInSlotOnly(item, dest)
|
|
||||||
|
|
||||||
case Containable.PutItemAway(item) => /* can be deferred */
|
|
||||||
sender ! LocalPutItemAway(item)
|
|
||||||
|
|
||||||
case Containable.PutItemInSlotOrAway(item, dest) => /* can be deferred */
|
|
||||||
sender ! LocalPutItemInSlotOrAway(item, dest)
|
|
||||||
|
|
||||||
case msg @ Containable.MoveItem(destination, equipment, destSlot) => /* can be deferred */
|
|
||||||
if(ContainableBehavior.TestPutItemInSlot(destination, equipment, destSlot).nonEmpty) { //test early, before we try to move the item
|
|
||||||
val source = ContainerObject
|
|
||||||
val item = equipment
|
|
||||||
val dest = destSlot
|
|
||||||
LocalRemoveItemFromSlot(item) match {
|
|
||||||
case Containable.ItemFromSlot(_, Some(_), slot @ Some(originalSlot)) =>
|
|
||||||
if(source eq destination) {
|
|
||||||
//when source and destination are the same, moving the item can be performed in one pass
|
|
||||||
LocalPutItemInSlot(item, dest) match {
|
|
||||||
case Containable.ItemPutInSlot(_, _, _, None) => ; //success
|
|
||||||
case Containable.ItemPutInSlot(_, _, _, Some(swapItem)) => //success, but with swap item
|
|
||||||
LocalPutItemInSlotOnlyOrAway(swapItem, slot) match {
|
|
||||||
case Containable.ItemPutInSlot(_, _, _, None) => ;
|
|
||||||
case _ =>
|
|
||||||
source.Zone.Ground.tell(Zone.Ground.DropItem(swapItem, source.Position, Vector3.z(source.Orientation.z)), source.Actor) //drop it
|
|
||||||
}
|
|
||||||
case _ : Containable.CanNotPutItemInSlot => //failure case ; try restore original item placement
|
|
||||||
LocalPutItemInSlot(item, originalSlot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//destination sync
|
|
||||||
destination.Actor ! ContainableBehavior.Wait()
|
|
||||||
implicit val timeout = new Timeout(1000 milliseconds)
|
|
||||||
val moveItemOver = ask(destination.Actor, ContainableBehavior.MoveItemPutItemInSlot(item, dest))
|
|
||||||
moveItemOver.onSuccess {
|
|
||||||
case Containable.ItemPutInSlot(_, _, _, None) => ; //successful
|
|
||||||
|
|
||||||
case Containable.ItemPutInSlot(_, _, _, Some(swapItem)) => //successful, but with swap item
|
|
||||||
PutItBackOrDropIt(source, swapItem, slot, destination.Actor)
|
|
||||||
|
|
||||||
case _ : Containable.CanNotPutItemInSlot => //failure case ; try restore original item placement
|
|
||||||
PutItBackOrDropIt(source, item, slot, source.Actor)
|
|
||||||
}
|
|
||||||
moveItemOver.onFailure {
|
|
||||||
case _ => //failure case ; try restore original item placement
|
|
||||||
PutItBackOrDropIt(source, item, slot, source.Actor)
|
|
||||||
}
|
|
||||||
//always do this
|
|
||||||
moveItemOver
|
|
||||||
.recover { case _ : AskTimeoutException => destination.Actor ! ContainableBehavior.Resume() }
|
|
||||||
.onComplete { _ => destination.Actor ! ContainableBehavior.Resume() }
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
//we could not find the item to be moved in the source location; trying to act on old data?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
MessageDeferredCallback(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContainableBehavior.MoveItemPutItemInSlot(item, dest) =>
|
|
||||||
sender ! LocalPutItemInSlot(item, dest)
|
|
||||||
|
|
||||||
case ContainableBehavior.MoveItemPutItemInSlotOrAway(item, dest) =>
|
|
||||||
sender ! LocalPutItemInSlotOrAway(item, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions (message control) */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defer a message until later.
|
|
||||||
* @see `ContainableBehavior.Defer`
|
|
||||||
* @see `DeferrableMsg`
|
|
||||||
* @param msg the message to defer
|
|
||||||
*/
|
|
||||||
def RepeatMessageLater(msg : Any) : Unit = {
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
context.system.scheduler.scheduleOnce(100 milliseconds, self, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment the flag for blocking messages.
|
|
||||||
*/
|
|
||||||
def Wait() : Unit = {
|
|
||||||
waitOnMoveItemOps = math.min(waitOnMoveItemOps + 1, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrement the flag for blocking messages.
|
|
||||||
*/
|
|
||||||
def Resume() : Unit = {
|
|
||||||
waitOnMoveItemOps = math.max(0, waitOnMoveItemOps - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop blocking messages.
|
|
||||||
*/
|
|
||||||
def Reset() : Unit = {
|
|
||||||
waitOnMoveItemOps = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions (item transfer) */
|
|
||||||
|
|
||||||
private def LocalRemoveItemFromSlot(slot : Int) : Any = {
|
|
||||||
val source = ContainerObject
|
|
||||||
val (outSlot, item) = ContainableBehavior.TryRemoveItemFromSlot(source, slot)
|
|
||||||
item match {
|
|
||||||
case Some(thing) => RemoveItemFromSlotCallback(thing, outSlot.get)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
Containable.ItemFromSlot(source, item, outSlot)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalRemoveItemFromSlot(item : Equipment) : Any = {
|
|
||||||
val source = ContainerObject
|
|
||||||
val(slot, retItem) = ContainableBehavior.TryRemoveItemFromSlot(source, item)
|
|
||||||
retItem match {
|
|
||||||
case Some(thing) => RemoveItemFromSlotCallback(thing, slot.get)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
Containable.ItemFromSlot(source, Some(item), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalPutItemInSlot(item : Equipment, dest : Int) : Any = {
|
|
||||||
val destination = ContainerObject
|
|
||||||
ContainableBehavior.TryPutItemInSlot(destination, item, dest) match {
|
|
||||||
case (true, swapItem) =>
|
|
||||||
swapItem match {
|
|
||||||
case Some(thing) => SwapItemCallback(thing)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
PutItemInSlotCallback(item, dest)
|
|
||||||
Containable.ItemPutInSlot(destination, item, dest, swapItem)
|
|
||||||
case (false, _) =>
|
|
||||||
Containable.CanNotPutItemInSlot(destination, item, dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalPutItemInSlotOnly(item : Equipment, dest : Int) : Any = {
|
|
||||||
val destination = ContainerObject
|
|
||||||
if(ContainableBehavior.TryPutItemInSlotOnly(destination, item, dest)) {
|
|
||||||
PutItemInSlotCallback(item, dest)
|
|
||||||
Containable.ItemPutInSlot(destination, item, dest, None)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Containable.CanNotPutItemInSlot(destination, item, dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalPutItemAway(item : Equipment) : Any = {
|
|
||||||
val destination = ContainerObject
|
|
||||||
ContainableBehavior.TryPutItemAway(destination, item) match {
|
|
||||||
case Some(dest) =>
|
|
||||||
PutItemInSlotCallback(item, dest)
|
|
||||||
Containable.ItemPutInSlot(destination, item, dest, None)
|
|
||||||
case _ =>
|
|
||||||
Containable.CanNotPutItemInSlot(destination, item, -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalPutItemInSlotOrAway(item : Equipment, dest : Option[Int]) : Any = {
|
|
||||||
val destination = ContainerObject
|
|
||||||
ContainableBehavior.TryPutItemInSlotOrAway(destination, item, dest) match {
|
|
||||||
case (Some(slot), swapItem) =>
|
|
||||||
swapItem match {
|
|
||||||
case Some(thing) => SwapItemCallback(thing)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
PutItemInSlotCallback(item, slot)
|
|
||||||
Containable.ItemPutInSlot(destination, item, slot, swapItem)
|
|
||||||
case (None, _) =>
|
|
||||||
Containable.CanNotPutItemInSlot(destination, item, dest.getOrElse(-1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def LocalPutItemInSlotOnlyOrAway(item : Equipment, dest : Option[Int]) : Any = {
|
|
||||||
val destination = ContainerObject
|
|
||||||
ContainableBehavior.TryPutItemInSlotOnlyOrAway(destination, item, dest) match {
|
|
||||||
case (Some(slot), None) =>
|
|
||||||
PutItemInSlotCallback(item, slot)
|
|
||||||
Containable.ItemPutInSlot(destination, item, slot, None)
|
|
||||||
case _ =>
|
|
||||||
Containable.CanNotPutItemInSlot(destination, item, dest.getOrElse(-1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A controlled response where, in certain situations,
|
|
||||||
* it is appropriate to attempt to place an item into a specific container,
|
|
||||||
* first testing a specific slot,
|
|
||||||
* and attempting anywhere available in the container if not that slot,
|
|
||||||
* and, if nowhere is available, then it gets dropped on the ground.
|
|
||||||
* The inserted item is not permitted to swap places with another item in this case.
|
|
||||||
* @param container the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @param slot in which slot the insertion is prioritized (upper left corner of item)
|
|
||||||
* @param to a recipient to redirect the response message
|
|
||||||
* @param timeout how long the request has to complete before expiring
|
|
||||||
*/
|
|
||||||
private def PutItBackOrDropIt(container : PlanetSideServerObject with Container, item : Equipment, slot : Option[Int], to : ActorRef)(implicit timeout : Timeout) : Unit = {
|
|
||||||
val restore = ask(container.Actor, ContainableBehavior.MoveItemPutItemInSlotOrAway(item, slot))
|
|
||||||
restore.onSuccess {
|
|
||||||
case _ : Containable.CanNotPutItemInSlot =>
|
|
||||||
container.Zone.Ground.tell(Zone.Ground.DropItem(item, container.Position, Vector3.z(container.Orientation.z)), to)
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
restore.onFailure {
|
|
||||||
case _ =>
|
|
||||||
container.Zone.Ground.tell(Zone.Ground.DropItem(item, container.Position, Vector3.z(container.Orientation.z)), to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reaction to the initial deferrence of a message that should handle the visual aspects of not immediately addressing the message.
|
|
||||||
* To be implemented.
|
|
||||||
* @param msg the deferred message
|
|
||||||
*/
|
|
||||||
def MessageDeferredCallback(msg : Any) : Unit
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reaction to an item being removed a container.
|
|
||||||
* To be implemented.
|
|
||||||
* @param item the item that was removed
|
|
||||||
* @param slot the slot from which is was removed
|
|
||||||
*/
|
|
||||||
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reaction to an item being placed into a container.
|
|
||||||
* To be implemented.
|
|
||||||
* @param item the item that was removed
|
|
||||||
* @param slot the slot from which is was removed
|
|
||||||
*/
|
|
||||||
def PutItemInSlotCallback(item : Equipment, slot : Int) : Unit
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reaction to the existence of a swap item being produced from a container into the environment.
|
|
||||||
* To be implemented.
|
|
||||||
* @param item the item that was removed
|
|
||||||
*/
|
|
||||||
def SwapItemCallback(item : Equipment) : Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
object ContainableBehavior {
|
|
||||||
/** Control message for temporarily blocking some messages to maintain integrity of underlying `Container` object */
|
|
||||||
private case class Wait()
|
|
||||||
/** Control message for unblocking all messages */
|
|
||||||
private case class Resume()
|
|
||||||
/** Internal message for the purpose of refreshing a blocked message in the mailbox */
|
|
||||||
private case class Defer(msg : Any, from : ActorRef)
|
|
||||||
|
|
||||||
/* The same as `PutItemInSlot`, but is not a `DeferrableMsg` for the purposes of completing a `MoveItem` */
|
|
||||||
private case class MoveItemPutItemInSlot(item : Equipment, slot : Int) extends ContainableMsg
|
|
||||||
/* The same as `PutItemInSlotOrAway`, but is not a `DeferrableMsg` for the purposes of completing a `MoveItem` */
|
|
||||||
private case class MoveItemPutItemInSlotOrAway(item : Equipment, slot : Option[Int]) extends ContainableMsg
|
|
||||||
|
|
||||||
/* Functions */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the target item can be found in a container, remove the item from the container.
|
|
||||||
* This process can fail if the item can not be found or if it can not be removed for some reason.
|
|
||||||
* @see `Container.Find`
|
|
||||||
* @see `EquipmentSlot.Equipment`
|
|
||||||
* @param source the container in which the `item` is currently located
|
|
||||||
* @param item the item to be removed
|
|
||||||
* @return a `Tuple` of two optional values;
|
|
||||||
* the first is from what index in the container the `item` was removed, if it was removed;
|
|
||||||
* the second is the item again, if it has been removed;
|
|
||||||
* will use `(None, None)` to report failure
|
|
||||||
*/
|
|
||||||
def TryRemoveItemFromSlot(source : PlanetSideServerObject with Container, item : Equipment) : (Option[Int], Option[Equipment]) = {
|
|
||||||
source.Find(item) match {
|
|
||||||
case slot @ Some(index) =>
|
|
||||||
source.Slot(index).Equipment = None
|
|
||||||
if(source.Slot(index).Equipment.isEmpty) {
|
|
||||||
(slot, Some(item))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the target slot of a container contains an item, remove that item from the container
|
|
||||||
* fromthe upper left corner position of the item as found in the container.
|
|
||||||
* This process can fail if no item can be found or if it can not be removed for some reason.
|
|
||||||
* @see `Container.Find`
|
|
||||||
* @see `EquipmentSlot.Equipment`
|
|
||||||
* @param source the container in which the `slot` is to be searched
|
|
||||||
* @param slot where the container will be searched
|
|
||||||
* @return a `Tuple` of two values;
|
|
||||||
* the first is from what `slot` in the container an `item` was removed, if any item removed;
|
|
||||||
* the second is the item, if it has been removed;
|
|
||||||
* will use `(None, None)` to report failure
|
|
||||||
*/
|
|
||||||
def TryRemoveItemFromSlot(source : PlanetSideServerObject with Container, slot : Int) : (Option[Int], Option[Equipment]) = {
|
|
||||||
val (item, outSlot) = source.Slot(slot).Equipment match {
|
|
||||||
case Some(thing) => (Some(thing), source.Find(thing))
|
|
||||||
case None => (None, None)
|
|
||||||
}
|
|
||||||
source.Slot(slot).Equipment = None
|
|
||||||
item match {
|
|
||||||
case Some(_) if item.nonEmpty && source.Slot(slot).Equipment.isEmpty =>
|
|
||||||
(outSlot, item)
|
|
||||||
case None =>
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Are the conditions for an item insertion acceptable?
|
|
||||||
* If another item occupies the expected region of insertion (collision of bounding regions),
|
|
||||||
* the insertion can still be permitted with the assumption that
|
|
||||||
* the displaced item ("swap item") will have to be put somewhere else.
|
|
||||||
* @see `ContainableBehavior.PermitEquipmentStow`
|
|
||||||
* @see `Container.Collisions`
|
|
||||||
* @see `InventoryTile`
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be tested for insertion
|
|
||||||
* @param dest the upper left corner of the insertion position
|
|
||||||
* @return the results of the insertion test, if an insertion can be permitted;
|
|
||||||
* `None`, otherwise, and the insertion is not permitted
|
|
||||||
*/
|
|
||||||
def TestPutItemInSlot(destination : PlanetSideServerObject with Container, item : Equipment, dest : Int) : Option[List[InventoryItem]] = {
|
|
||||||
if(ContainableBehavior.PermitEquipmentStow(destination, item)) {
|
|
||||||
val tile = item.Definition.Tile
|
|
||||||
val destinationCollisionTest = destination.Collisions(dest, tile.Width, tile.Height)
|
|
||||||
destinationCollisionTest match {
|
|
||||||
case Success(Nil) => Some(Nil) //no item to swap
|
|
||||||
case Success(out @ List(_)) => Some(out) //one item to swap
|
|
||||||
case _ => None //abort when too many items at destination or other failure case
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None //blocked insertion (object type not permitted in container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put an item in a container at the given position.
|
|
||||||
* The inserted item may swap places with another item.
|
|
||||||
* If the new item can not be inserted, the swap item is kept in its original position.
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @param dest in which slot the insertion is expected to occur (upper left corner of item)
|
|
||||||
* @return a `Tuple` of two values;
|
|
||||||
* the first is `true` if the insertion occurred; and, `false`, otherwise
|
|
||||||
* the second is an optional item that was removed from a coincidental position in the container ("swap item")
|
|
||||||
*/
|
|
||||||
def TryPutItemInSlot(destination : PlanetSideServerObject with Container, item : Equipment, dest : Int) : (Boolean, Option[Equipment]) = {
|
|
||||||
ContainableBehavior.TestPutItemInSlot(destination, item, dest) match {
|
|
||||||
case Some(results) =>
|
|
||||||
//insert and swap, if applicable
|
|
||||||
val (swapItem, swapSlot) = results match {
|
|
||||||
case List(InventoryItem(obj, start)) => (Some(obj), start)
|
|
||||||
case _ => (None, dest)
|
|
||||||
}
|
|
||||||
destination.Slot(swapSlot).Equipment = None
|
|
||||||
if((destination.Slot(dest).Equipment = item).contains(item)) {
|
|
||||||
(true, swapItem)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//put the swapItem back
|
|
||||||
destination.Slot(swapSlot).Equipment = swapItem
|
|
||||||
(false, None)
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
(false, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put an item in a container at the given position.
|
|
||||||
* The inserted item is not permitted to swap places with another item in this case.
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @param dest in which slot the insertion is expected to occur (upper left corner of item)
|
|
||||||
* @return `true` if the insertion occurred;
|
|
||||||
* `false`, otherwise
|
|
||||||
*/
|
|
||||||
def TryPutItemInSlotOnly(destination : PlanetSideServerObject with Container, item : Equipment, dest : Int) : Boolean = {
|
|
||||||
ContainableBehavior.TestPutItemInSlot(destination, item, dest).contains(Nil) && (destination.Slot(dest).Equipment = item).contains(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put an item in a container in the whatever position it cleanly fits.
|
|
||||||
* The inserted item will not swap places with another item in this case.
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @return the slot index of the insertion point;
|
|
||||||
* `None`, if a clean insertion is not possible
|
|
||||||
*/
|
|
||||||
def TryPutItemAway(destination : PlanetSideServerObject with Container, item : Equipment) : Option[Int] = {
|
|
||||||
destination.Fit(item) match {
|
|
||||||
case out @ Some(dest)
|
|
||||||
if ContainableBehavior.PermitEquipmentStow(destination, item) && (destination.Slot(dest).Equipment = item).contains(item) =>
|
|
||||||
out
|
|
||||||
case _ =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to put an item in a container at the given position.
|
|
||||||
* The inserted item may swap places with another item at this time.
|
|
||||||
* If the targeted insertion at this position fails,
|
|
||||||
* attempt to put the item in the container in the whatever position it cleanly fits.
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @param dest in which specific slot the insertion is first tested (upper left corner of item)
|
|
||||||
* @return na
|
|
||||||
*/
|
|
||||||
def TryPutItemInSlotOrAway(destination : PlanetSideServerObject with Container, item : Equipment, dest : Option[Int]) : (Option[Int], Option[Equipment]) = {
|
|
||||||
(dest match {
|
|
||||||
case Some(slot) => ContainableBehavior.TryPutItemInSlot(destination, item, slot)
|
|
||||||
case None => (false, None)
|
|
||||||
}) match {
|
|
||||||
case (true, swapItem) =>
|
|
||||||
(dest, swapItem)
|
|
||||||
case _ =>
|
|
||||||
ContainableBehavior.TryPutItemAway(destination, item) match {
|
|
||||||
case out @ Some(_) => (out, None)
|
|
||||||
case None => (None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to put an item in a container at the given position.
|
|
||||||
* The inserted item may not swap places with another item at this time.
|
|
||||||
* If the targeted insertion at this position fails,
|
|
||||||
* attempt to put the item in the container in the whatever position it cleanly fits.
|
|
||||||
* @param destination the container
|
|
||||||
* @param item the item to be inserted
|
|
||||||
* @param dest in which specific slot the insertion is first tested (upper left corner of item)
|
|
||||||
* @return na
|
|
||||||
*/
|
|
||||||
def TryPutItemInSlotOnlyOrAway(destination : PlanetSideServerObject with Container, item : Equipment, dest : Option[Int]) : (Option[Int], Option[Equipment]) = {
|
|
||||||
(dest match {
|
|
||||||
case Some(slot) if ContainableBehavior.TestPutItemInSlot(destination, item, slot).contains(Nil) => ContainableBehavior.TryPutItemInSlot(destination, item, slot)
|
|
||||||
case None => (false, None)
|
|
||||||
}) match {
|
|
||||||
case (true, swapItem) =>
|
|
||||||
(dest, swapItem)
|
|
||||||
case _ =>
|
|
||||||
ContainableBehavior.TryPutItemAway(destination, item) match {
|
|
||||||
case out @ Some(_) => (out, None)
|
|
||||||
case None => (None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply incontestable, arbitrary limitations
|
|
||||||
* whereby certain items are denied insertion into certain containers
|
|
||||||
* for vaguely documented but assuredly fantastic excuses on the part of the developer.
|
|
||||||
* @param destination the container
|
|
||||||
* @param equipment the item to be inserted
|
|
||||||
* @return `true`, if the object is allowed to contain the type of equipment object;
|
|
||||||
* `false`, otherwise
|
|
||||||
*/
|
|
||||||
def PermitEquipmentStow(destination : PlanetSideServerObject with Container, equipment : Equipment) : Boolean = {
|
|
||||||
import net.psforever.objects.{BoomerTrigger, Player}
|
|
||||||
equipment match {
|
|
||||||
case _ : BoomerTrigger =>
|
|
||||||
//a BoomerTrigger can only be stowed in a player's holsters or inventory
|
|
||||||
//this is only a requirement until they, and their Boomer explosive complement, are cleaned-up properly
|
|
||||||
destination.isInstanceOf[Player]
|
|
||||||
case _ =>
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A predicate used to determine if an `InventoryItem` object contains `Equipment` that should be dropped.
|
|
||||||
* Used to filter through lists of object data before it is placed into a player's inventory.
|
|
||||||
* Drop the item if:<br>
|
|
||||||
* - the item is cavern equipment<br>
|
|
||||||
* - the item is a `BoomerTrigger` type object<br>
|
|
||||||
* - the item is a `router_telepad` type object<br>
|
|
||||||
* - the item is another faction's exclusive equipment
|
|
||||||
* @param tplayer the player
|
|
||||||
* @return true if the item is to be dropped; false, otherwise
|
|
||||||
*/
|
|
||||||
def DropPredicate(tplayer : Player) : InventoryItem => Boolean = entry => {
|
|
||||||
val objDef = entry.obj.Definition
|
|
||||||
val faction = GlobalDefinitions.isFactionEquipment(objDef)
|
|
||||||
GlobalDefinitions.isCavernEquipment(objDef) ||
|
|
||||||
objDef == GlobalDefinitions.router_telepad ||
|
|
||||||
entry.obj.isInstanceOf[BoomerTrigger] ||
|
|
||||||
(faction != tplayer.Faction && faction != PlanetSideEmpire.NEUTRAL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Containable {
|
|
||||||
final case class RemoveItemFromSlot(item : Option[Equipment], slot : Option[Int]) extends ContainableMsg
|
|
||||||
|
|
||||||
object RemoveItemFromSlot {
|
|
||||||
def apply(slot : Int) : RemoveItemFromSlot = RemoveItemFromSlot(None, Some(slot))
|
|
||||||
|
|
||||||
def apply(item : Equipment) : RemoveItemFromSlot = RemoveItemFromSlot(Some(item), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A response for the `RemoveItemFromSlot` message.
|
|
||||||
* It serves the dual purpose of reporting a missing item (by not reporting any slot information)
|
|
||||||
* and reporting no item ata given position (by not reporting any item information).
|
|
||||||
* @param obj the container
|
|
||||||
* @param item the equipment that was removed
|
|
||||||
* @param slot the index position from which any item was removed
|
|
||||||
*/
|
|
||||||
final case class ItemFromSlot(obj : PlanetSideServerObject with Container, item : Option[Equipment], slot : Option[Int])
|
|
||||||
|
|
||||||
final case class PutItemInSlot(item : Equipment, slot : Int) extends DeferrableMsg
|
|
||||||
|
|
||||||
final case class PutItemInSlotOnly(item : Equipment, slot : Int) extends DeferrableMsg
|
|
||||||
|
|
||||||
final case class PutItemAway(item : Equipment) extends DeferrableMsg
|
|
||||||
|
|
||||||
final case class PutItemInSlotOrAway(item : Equipment, slot : Option[Int]) extends DeferrableMsg
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "successful insertion" response for the variety message of messages that attempt to insert an item into a container.
|
|
||||||
* @param obj the container
|
|
||||||
* @param item the equipment that was inserted
|
|
||||||
* @param slot the slot position into which the item was inserted
|
|
||||||
* @param swapped_item any other item, previously in the container, that was displaced to make room for this insertion
|
|
||||||
*/
|
|
||||||
final case class ItemPutInSlot(obj : PlanetSideServerObject with Container, item : Equipment, slot : Int, swapped_item : Option[Equipment])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "failed insertion" response for the variety message of messages that attempt to insert an item into a container.
|
|
||||||
* @param obj the container
|
|
||||||
* @param item the equipment that was not inserted
|
|
||||||
* @param slot the slot position into which the item should have been inserted;
|
|
||||||
* `-1` if no insertion slot was reported in the original message or discovered in the process of inserting
|
|
||||||
*/
|
|
||||||
final case class CanNotPutItemInSlot(obj : PlanetSideServerObject with Container, item : Equipment, slot : Int)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The item should already be contained by us.
|
|
||||||
* The item is being removed from our containment and placed into a fixed slot position in another container.
|
|
||||||
* `MoveItem` is a process that may be complicated and is one reason why `DeferrableMsg`s are employed.
|
|
||||||
* @param destination the container into which the item is being placed
|
|
||||||
* @param item the item
|
|
||||||
* @param destination_slot where in the destination container the item is being placed
|
|
||||||
*/
|
|
||||||
final case class MoveItem(destination : PlanetSideServerObject with Container, item : Equipment, destination_slot : Int) extends DeferrableMsg
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.{ActorContext, ActorRef}
|
import akka.actor.ActorContext
|
||||||
import net.psforever.objects.definition.ImplantDefinition
|
import net.psforever.objects.definition.ImplantDefinition
|
||||||
import net.psforever.objects.{Player, Vehicle}
|
import net.psforever.objects.{Player, Vehicle}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
|
@ -92,14 +92,6 @@ class OrderTerminalDefinition(objId : Int) extends TerminalDefinition(objId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
tabs.get(msg.msg.item_page) match {
|
|
||||||
case Some(page) =>
|
|
||||||
page.Dispatch(sender, terminal, msg)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object OrderTerminalDefinition {
|
object OrderTerminalDefinition {
|
||||||
|
|
@ -108,9 +100,8 @@ object OrderTerminalDefinition {
|
||||||
* @see `ItemTransactionMessage`
|
* @see `ItemTransactionMessage`
|
||||||
*/
|
*/
|
||||||
sealed trait Tab {
|
sealed trait Tab {
|
||||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -128,10 +119,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
msg.player.Actor ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,13 +144,6 @@ object OrderTerminalDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
msg.response match {
|
|
||||||
case _ : Terminal.BuyExosuit => msg.player.Actor ! msg
|
|
||||||
case _ => sender ! msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -191,10 +171,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
sender ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -211,10 +187,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
sender ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -243,10 +215,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
sender ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -304,10 +272,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
msg.player.Actor ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -336,14 +300,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
val player = msg.player
|
|
||||||
player.Zone.GUID(player.VehicleOwned) match {
|
|
||||||
case Some(vehicle : Vehicle) => vehicle.Actor ! msg
|
|
||||||
case _ => sender ! Terminal.TerminalMessage(player, msg.msg, Terminal.NoDeal())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -375,10 +331,6 @@ object OrderTerminalDefinition {
|
||||||
Terminal.NoDeal()
|
Terminal.NoDeal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
sender ! msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef}
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
|
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||||
|
|
@ -30,10 +30,7 @@ class TerminalControl(term : Terminal) extends Actor
|
||||||
.orElse(canBeRepairedByNanoDispenser)
|
.orElse(canBeRepairedByNanoDispenser)
|
||||||
.orElse {
|
.orElse {
|
||||||
case Terminal.Request(player, msg) =>
|
case Terminal.Request(player, msg) =>
|
||||||
TerminalControl.Dispatch(
|
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
||||||
sender,
|
|
||||||
term,
|
|
||||||
Terminal.TerminalMessage(player, msg, term.Request(player, msg)))
|
|
||||||
|
|
||||||
case CommonMessages.Use(player, Some(item : SimpleItem)) if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
case CommonMessages.Use(player, Some(item : SimpleItem)) if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||||
//TODO setup certifications check
|
//TODO setup certifications check
|
||||||
|
|
@ -49,16 +46,5 @@ class TerminalControl(term : Terminal) extends Actor
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override def toString : String = term.Definition.Name
|
override def toString : String = term.Definition.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
object TerminalControl {
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = {
|
|
||||||
msg.response match {
|
|
||||||
case Terminal.NoDeal() => sender ! msg
|
|
||||||
case _ =>
|
|
||||||
terminal.Definition.Dispatch(sender, terminal, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.definition.converter.TerminalConverter
|
import net.psforever.objects.definition.converter.TerminalConverter
|
||||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||||
|
|
@ -23,6 +22,4 @@ abstract class TerminalDefinition(objectId : Int) extends AmenityDefinition(obje
|
||||||
* @return a message that resolves the transaction
|
* @return a message that resolves the transaction
|
||||||
*/
|
*/
|
||||||
def Request(player : Player, msg : Any) : Terminal.Exchange
|
def Request(player : Player, msg : Any) : Terminal.Exchange
|
||||||
|
|
||||||
def Dispatch(sender : ActorRef, terminal : Terminal, msg : Terminal.TerminalMessage) : Unit = { }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,21 @@
|
||||||
// Copyright (c) 2017-2020 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
|
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
|
||||||
import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons}
|
import net.psforever.objects.equipment.JammableMountedWeapons
|
||||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
|
||||||
import net.psforever.objects.serverobject.damage.DamageableVehicle
|
import net.psforever.objects.serverobject.damage.DamageableVehicle
|
||||||
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
||||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
|
||||||
import net.psforever.objects.vital.VehicleShieldCharge
|
import net.psforever.objects.vital.VehicleShieldCharge
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types._
|
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
|
||||||
import services.RemoverActor
|
import services.{RemoverActor, Service}
|
||||||
import net.psforever.packet.game._
|
|
||||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
|
||||||
import services.Service
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
|
||||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -43,8 +36,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
with CargoBehavior
|
with CargoBehavior
|
||||||
with DamageableVehicle
|
with DamageableVehicle
|
||||||
with RepairableVehicle
|
with RepairableVehicle
|
||||||
with JammableMountedWeapons
|
with JammableMountedWeapons {
|
||||||
with ContainableBehavior {
|
|
||||||
|
|
||||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||||
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
||||||
|
|
@ -56,7 +48,6 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
def DeploymentObject = vehicle
|
def DeploymentObject = vehicle
|
||||||
def DamageableObject = vehicle
|
def DamageableObject = vehicle
|
||||||
def RepairableObject = vehicle
|
def RepairableObject = vehicle
|
||||||
def ContainerObject = vehicle
|
|
||||||
|
|
||||||
/** cheap flag for whether the vehicle is decaying */
|
/** cheap flag for whether the vehicle is decaying */
|
||||||
var decaying : Boolean = false
|
var decaying : Boolean = false
|
||||||
|
|
@ -81,7 +72,6 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
.orElse(jammableBehavior)
|
.orElse(jammableBehavior)
|
||||||
.orElse(takesDamage)
|
.orElse(takesDamage)
|
||||||
.orElse(canBeRepairedByNanoDispenser)
|
.orElse(canBeRepairedByNanoDispenser)
|
||||||
.orElse(containerBehavior)
|
|
||||||
.orElse {
|
.orElse {
|
||||||
case Vehicle.Ownership(None) =>
|
case Vehicle.Ownership(None) =>
|
||||||
LoseOwnership()
|
LoseOwnership()
|
||||||
|
|
@ -153,52 +143,6 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Terminal.TerminalMessage(player, msg, reply) =>
|
|
||||||
reply match {
|
|
||||||
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
|
|
||||||
org.log4s.getLogger(vehicle.Definition.Name).info(s"changing vehicle equipment loadout to ${player.Name}'s option #${msg.unk1 + 1}")
|
|
||||||
//remove old inventory
|
|
||||||
val oldInventory = vehicle.Inventory.Clear().map { case InventoryItem(obj, _) => (obj, obj.GUID) }
|
|
||||||
//"dropped" items are lost; if it doesn't go in the trunk, it vanishes into the nanite cloud
|
|
||||||
val (_, afterInventory) = inventory.partition(ContainableBehavior.DropPredicate(player))
|
|
||||||
val (oldWeapons, newWeapons, finalInventory) = if(vehicle.Definition == definition) {
|
|
||||||
//vehicles are the same type
|
|
||||||
//TODO want to completely swap weapons, but holster icon vanishes temporarily after swap
|
|
||||||
//TODO BFR arms must be swapped properly
|
|
||||||
// //remove old weapons
|
|
||||||
// val oldWeapons = vehicle.Weapons.values.collect { case slot if slot.Equipment.nonEmpty =>
|
|
||||||
// val obj = slot.Equipment.get
|
|
||||||
// slot.Equipment = None
|
|
||||||
// (obj, obj.GUID)
|
|
||||||
// }.toList
|
|
||||||
// (oldWeapons, weapons, afterInventory)
|
|
||||||
//TODO for now, just refill ammo; assume weapons stay the same
|
|
||||||
vehicle.Weapons
|
|
||||||
.collect { case (_, slot) if slot.Equipment.nonEmpty => slot.Equipment.get }
|
|
||||||
.collect { case weapon : Tool =>
|
|
||||||
weapon.AmmoSlots.foreach { ammo => ammo.Box.Capacity = ammo.Box.Definition.Capacity }
|
|
||||||
}
|
|
||||||
(Nil, Nil, afterInventory)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//vehicle loadout is not for this vehicle
|
|
||||||
//do not transfer over weapon ammo
|
|
||||||
if(vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset) {
|
|
||||||
(Nil, Nil, afterInventory) //trunk is the same dimensions, however
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//accommodate as much of inventory as possible
|
|
||||||
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory)
|
|
||||||
(Nil, Nil, stow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalInventory.foreach { _.obj.Faction = vehicle.Faction }
|
|
||||||
player.Zone.VehicleEvents ! VehicleServiceMessage(player.Zone.Id, VehicleAction.ChangeLoadout(vehicle.GUID, oldWeapons, newWeapons, oldInventory, finalInventory))
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true))
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Vehicle.Deconstruct(time) =>
|
case Vehicle.Deconstruct(time) =>
|
||||||
time match {
|
time match {
|
||||||
case Some(delay) =>
|
case Some(delay) =>
|
||||||
|
|
@ -300,73 +244,6 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def MessageDeferredCallback(msg : Any) : Unit = {
|
|
||||||
msg match {
|
|
||||||
case Containable.MoveItem(_, item, _) =>
|
|
||||||
//momentarily put item back where it was originally
|
|
||||||
val obj = ContainerObject
|
|
||||||
obj.Find(item) match {
|
|
||||||
case Some(slot) =>
|
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
self.toString,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
|
||||||
)
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val zone = ContainerObject.Zone
|
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(self.toString, VehicleAction.UnstowEquipment(Service.defaultPlayerGUID, item.GUID))
|
|
||||||
}
|
|
||||||
|
|
||||||
def PutItemInSlotCallback(item : Equipment, slot : Int) : Unit = {
|
|
||||||
val obj = ContainerObject
|
|
||||||
val oguid = obj.GUID
|
|
||||||
val zone = obj.Zone
|
|
||||||
val channel = self.toString
|
|
||||||
val events = zone.VehicleEvents
|
|
||||||
val iguid = item.GUID
|
|
||||||
val definition = item.Definition
|
|
||||||
item.Faction = obj.Faction
|
|
||||||
events ! VehicleServiceMessage(
|
|
||||||
//TODO when a new weapon, the equipment slot ui goes blank, but the weapon functions; remount vehicle to correct it
|
|
||||||
if(obj.VisibleSlots.contains(slot)) zone.Id else channel,
|
|
||||||
VehicleAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
ObjectCreateMessage(
|
|
||||||
definition.ObjectId,
|
|
||||||
iguid,
|
|
||||||
ObjectCreateMessageParent(oguid, slot),
|
|
||||||
definition.Packet.ConstructorData(item).get
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
item match {
|
|
||||||
case box : AmmoBox =>
|
|
||||||
events ! VehicleServiceMessage(
|
|
||||||
channel,
|
|
||||||
VehicleAction.InventoryState2(Service.defaultPlayerGUID, iguid, oguid, box.Capacity)
|
|
||||||
)
|
|
||||||
case weapon : Tool =>
|
|
||||||
weapon.AmmoSlots.map { slot => slot.Box }.foreach { box =>
|
|
||||||
events ! VehicleServiceMessage(
|
|
||||||
channel,
|
|
||||||
VehicleAction.InventoryState2(Service.defaultPlayerGUID, iguid, weapon.GUID, box.Capacity)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def SwapItemCallback(item : Equipment) : Unit = {
|
|
||||||
val obj = ContainerObject
|
|
||||||
val zone = obj.Zone
|
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(self.toString, VehicleAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
def LoseOwnership() : Unit = {
|
def LoseOwnership() : Unit = {
|
||||||
val obj = MountableObject
|
val obj = MountableObject
|
||||||
Vehicles.Disown(obj.GUID, obj)
|
Vehicles.Disown(obj.GUID, obj)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ package net.psforever.objects.zones
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
import services.Service
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
@ -30,28 +28,19 @@ class ZoneGroundActor(zone : Zone, equipmentOnGround : ListBuffer[Equipment]) ex
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
equipmentOnGround += item
|
equipmentOnGround += item
|
||||||
item.Position = pos
|
|
||||||
item.Orientation = orient
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.DropItem(Service.defaultPlayerGUID, item))
|
|
||||||
Zone.Ground.ItemOnGround(item, pos, orient)
|
Zone.Ground.ItemOnGround(item, pos, orient)
|
||||||
})
|
})
|
||||||
|
|
||||||
case Zone.Ground.PickupItem(item_guid) =>
|
case Zone.Ground.PickupItem(item_guid) =>
|
||||||
sender ! (FindItemOnGround(item_guid) match {
|
sender ! (FindItemOnGround(item_guid) match {
|
||||||
case Some(item) =>
|
case Some(item) =>
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PickupItem(Service.defaultPlayerGUID, item, 0))
|
|
||||||
Zone.Ground.ItemInHand(item)
|
Zone.Ground.ItemInHand(item)
|
||||||
case None =>
|
case None =>
|
||||||
Zone.Ground.CanNotPickupItem(zone, item_guid, "can not find")
|
Zone.Ground.CanNotPickupItem(zone, item_guid, "can not find")
|
||||||
})
|
})
|
||||||
|
|
||||||
case Zone.Ground.RemoveItem(item_guid) =>
|
case Zone.Ground.RemoveItem(item_guid) =>
|
||||||
//intentionally no callback
|
FindItemOnGround(item_guid) //intentionally no callback
|
||||||
FindItemOnGround(item_guid) match {
|
|
||||||
case Some(item) =>
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PickupItem(Service.defaultPlayerGUID, item, 0))
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,26 +63,13 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
|
||||||
}
|
}
|
||||||
|
|
||||||
case Zone.Corpse.Add(player) =>
|
case Zone.Corpse.Add(player) =>
|
||||||
val (playerIsCorpse, playerInZone) = CorpseAdd(player, playerMap, corpseList)
|
CorpseAdd(player, corpseList)
|
||||||
if(playerIsCorpse) {
|
|
||||||
if(!playerInZone && player.Actor == ActorRef.noSender) {
|
|
||||||
player.Zone = zone
|
|
||||||
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"corpse_of_${player.CharId}_${player.GUID.guid}_${System.currentTimeMillis}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Zone.Corpse.Remove(player) =>
|
case Zone.Corpse.Remove(player) =>
|
||||||
if(CorpseRemove(player, corpseList)) {
|
CorpseRemove(player, corpseList)
|
||||||
PlayerLeave(player)
|
|
||||||
}
|
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
def PlayerLeave(player : Player) : Unit = {
|
|
||||||
context.stop(player.Actor)
|
|
||||||
player.Actor = ActorRef.noSender
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ZonePopulationActor {
|
object ZonePopulationActor {
|
||||||
|
|
@ -161,33 +148,18 @@ object ZonePopulationActor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the given `player` passes a condition check, add it to the list.
|
* If the given `player` passes a condition check, add it to the list.
|
||||||
* Also, ensure that "this player" is not currently counted among the living.
|
|
||||||
* @param player a `Player` object
|
* @param player a `Player` object
|
||||||
* @param playerMap the mapping of `Avatar` objects to `Player` objects
|
|
||||||
* @param corpseList a list of `Player` objects
|
* @param corpseList a list of `Player` objects
|
||||||
* @return a `Tuple` of two flags;
|
* @return true, if the `player` was added to the list;
|
||||||
* the first is whether the player was turned into a corpse or not;
|
* false, otherwise
|
||||||
* the second is whether the player was found in the zone before being turned into a corpse
|
|
||||||
*/
|
*/
|
||||||
def CorpseAdd(player : Player, playerMap : TrieMap[Avatar, Option[Player]], corpseList : ListBuffer[Player]) : (Boolean, Boolean) = {
|
def CorpseAdd(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
|
||||||
if(player.isBackpack) {
|
if(player.isBackpack) {
|
||||||
val playerFoundInZone = playerMap.find {
|
corpseList += player
|
||||||
case (_, Some(p)) => p.CharId == player.CharId
|
true
|
||||||
case (_, None) => false
|
|
||||||
} match {
|
|
||||||
case Some((a, _)) => PopulationRelease(a, playerMap).nonEmpty
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
corpseList.find { _ eq player } match {
|
|
||||||
case None => ;
|
|
||||||
corpseList += player
|
|
||||||
(true, playerFoundInZone)
|
|
||||||
case _ =>
|
|
||||||
(false, false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(false, false)
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,19 +167,20 @@ object ZonePopulationActor {
|
||||||
* Remove the given `player` from the list.
|
* Remove the given `player` from the list.
|
||||||
* @param player a `Player` object
|
* @param player a `Player` object
|
||||||
* @param corpseList a list of `Player` objects
|
* @param corpseList a list of `Player` objects
|
||||||
* @return `true`, if the corpse was found and removed;
|
|
||||||
* `false`, otherwise
|
|
||||||
*/
|
*/
|
||||||
def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
|
def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = {
|
||||||
recursiveFindCorpse(corpseList.iterator, player) match {
|
recursiveFindCorpse(corpseList.iterator, player) match {
|
||||||
case None =>
|
case None => ;
|
||||||
false
|
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
corpseList.remove(index)
|
corpseList.remove(index)
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def PlayerLeave(player : Player) : Unit = {
|
||||||
|
player.Actor ! akka.actor.PoisonPill
|
||||||
|
player.Actor = ActorRef.noSender
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recursive function that finds and removes a specific player from a list of players.
|
* A recursive function that finds and removes a specific player from a list of players.
|
||||||
* @param iter an `Iterator` of `Player` objects
|
* @param iter an `Iterator` of `Player` objects
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class AvatarService(zone : Zone) extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DestroyDisplay(killer, victim, method, unk))
|
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DestroyDisplay(killer, victim, method, unk))
|
||||||
)
|
)
|
||||||
case AvatarAction.DropItem(player_guid, item) =>
|
case AvatarAction.DropItem(player_guid, item, _) =>
|
||||||
val definition = item.Definition
|
val definition = item.Definition
|
||||||
val objectData = DroppedItemData(
|
val objectData = DroppedItemData(
|
||||||
PlacementData(item.Position, item.Orientation),
|
PlacementData(item.Position, item.Orientation),
|
||||||
|
|
@ -179,10 +179,21 @@ class AvatarService(zone : Zone) extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, sequence, end, target))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, sequence, end, target))
|
||||||
)
|
)
|
||||||
case AvatarAction.PickupItem(player_guid, item, unk) =>
|
case AvatarAction.PickupItem(player_guid, _, target, slot, item, unk) =>
|
||||||
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item.GUID, unk))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, {
|
||||||
|
val itemGUID = item.GUID
|
||||||
|
if(target.VisibleSlots.contains(slot)) {
|
||||||
|
val definition = item.Definition
|
||||||
|
val containerData = ObjectCreateMessageParent(target.GUID, slot)
|
||||||
|
val objectData = definition.Packet.ConstructorData(item).get
|
||||||
|
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, itemGUID, containerData, objectData))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AvatarResponse.ObjectDelete(itemGUID, unk)
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
case AvatarAction.PutDownFDU(player_guid) =>
|
case AvatarAction.PutDownFDU(player_guid) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
|
|
@ -227,19 +238,6 @@ class AvatarService(zone : Zone) extends Actor {
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.TeardownConnection())
|
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.TeardownConnection())
|
||||||
)
|
)
|
||||||
|
|
||||||
case AvatarAction.TerminalOrderResult(terminal, term_action, result) =>
|
|
||||||
AvatarEvents.publish(
|
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.TerminalOrderResult(terminal, term_action, result))
|
|
||||||
)
|
|
||||||
case AvatarAction.ChangeExosuit(target, exosuit, subtype, slot, maxhand, old_holsters, holsters, old_inventory, inventory, drop, delete) =>
|
|
||||||
AvatarEvents.publish(
|
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.ChangeExosuit(target, exosuit, subtype, slot, maxhand, old_holsters, holsters, old_inventory, inventory, drop, delete))
|
|
||||||
)
|
|
||||||
case AvatarAction.ChangeLoadout(target, exosuit, subtype, slot, maxhand, old_holsters, holsters, old_inventory, inventory, drop) =>
|
|
||||||
AvatarEvents.publish(
|
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.ChangeLoadout(target, exosuit, subtype, slot, maxhand, old_holsters, holsters, old_inventory, inventory, drop))
|
|
||||||
)
|
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,7 +259,6 @@ class AvatarService(zone : Zone) extends Actor {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.warn(s"Unhandled message $msg from $sender")
|
log.warn(s"Unhandled message $msg from $sender")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
import net.psforever.objects.inventory.Container
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
|
import net.psforever.packet.game.ImplantAction
|
||||||
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
|
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ object AvatarAction {
|
||||||
final case class ActivateImplantSlot(player_guid : PlanetSideGUID, slot : Int) extends Action
|
final case class ActivateImplantSlot(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
||||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
||||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment) extends Action
|
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class GenericObjectAction(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, action_code : Int) extends Action
|
final case class GenericObjectAction(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, action_code : Int) extends Action
|
||||||
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
||||||
|
|
@ -48,7 +49,7 @@ object AvatarAction {
|
||||||
final case class PlanetsideAttributeToAll(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttributeToAll(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action
|
final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||||
final case class PickupItem(player_guid : PlanetSideGUID, item : Equipment, unk : Int = 0) extends Action
|
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
||||||
final case class ProjectileAutoLockAwareness(mode : Int) extends Action
|
final case class ProjectileAutoLockAwareness(mode : Int) extends Action
|
||||||
final case class ProjectileExplodes(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, projectile : Projectile) extends Action
|
final case class ProjectileExplodes(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, projectile : Projectile) extends Action
|
||||||
final case class ProjectileState(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, sequence : Int, end : Boolean, hit_target : PlanetSideGUID) extends Action
|
final case class ProjectileState(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, sequence : Int, end : Boolean, hit_target : PlanetSideGUID) extends Action
|
||||||
|
|
@ -63,10 +64,6 @@ object AvatarAction {
|
||||||
final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action
|
final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action
|
||||||
final case class SendResponseTargeted(target_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action
|
final case class SendResponseTargeted(target_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action
|
||||||
|
|
||||||
final case class TerminalOrderResult(terminal_guid : PlanetSideGUID, action : TransactionType.Value, result : Boolean) extends Action
|
|
||||||
final case class ChangeExosuit(target_guid : PlanetSideGUID, exosuit : ExoSuitType.Value, subtype : Int, last_drawn_slot : Int, new_max_hand : Boolean, old_holsters : List[(Equipment, PlanetSideGUID)], holsters : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], inventory : List[InventoryItem], drop : List[InventoryItem], delete : List[(Equipment, PlanetSideGUID)]) extends Action
|
|
||||||
final case class ChangeLoadout(target_guid : PlanetSideGUID, exosuit : ExoSuitType.Value, subtype : Int, last_drawn_slot : Int, new_max_hand : Boolean, old_holsters : List[(Equipment, PlanetSideGUID)], holsters : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], inventory : List[InventoryItem], drop : List[InventoryItem]) extends Action
|
|
||||||
|
|
||||||
final case class TeardownConnection() extends Action
|
final case class TeardownConnection() extends Action
|
||||||
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||||
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ package services.avatar
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
import net.psforever.packet.game.ObjectCreateMessage
|
import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage}
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
import services.GenericEventBusMsg
|
import services.GenericEventBusMsg
|
||||||
|
|
||||||
final case class AvatarServiceResponse(toChannel : String,
|
final case class AvatarServiceResponse(toChannel : String,
|
||||||
|
|
@ -57,10 +56,6 @@ object AvatarResponse {
|
||||||
final case class SendResponse(msg: PlanetSideGamePacket) extends Response
|
final case class SendResponse(msg: PlanetSideGamePacket) extends Response
|
||||||
final case class SendResponseTargeted(target_guid : PlanetSideGUID, msg: PlanetSideGamePacket) extends Response
|
final case class SendResponseTargeted(target_guid : PlanetSideGUID, msg: PlanetSideGamePacket) extends Response
|
||||||
|
|
||||||
final case class TerminalOrderResult(terminal_guid : PlanetSideGUID, action : TransactionType.Value, result : Boolean) extends Response
|
|
||||||
final case class ChangeExosuit(target_guid : PlanetSideGUID, exosuit : ExoSuitType.Value, subtype : Int, last_drawn_slot : Int, new_max_hand : Boolean, old_holsters : List[(Equipment, PlanetSideGUID)], holsters : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], inventory : List[InventoryItem], drop : List[InventoryItem], delete : List[(Equipment, PlanetSideGUID)]) extends Response
|
|
||||||
final case class ChangeLoadout(target_guid : PlanetSideGUID, exosuit : ExoSuitType.Value, subtype : Int, last_drawn_slot : Int, new_max_hand : Boolean, old_holsters : List[(Equipment, PlanetSideGUID)], holsters : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], inventory : List[InventoryItem], drop : List[InventoryItem]) extends Response
|
|
||||||
|
|
||||||
final case class TeardownConnection() extends Response
|
final case class TeardownConnection() extends Response
|
||||||
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,6 @@ class VehicleService(zone : Zone) extends Actor {
|
||||||
VehicleEvents.publish(
|
VehicleEvents.publish(
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed, delay))
|
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed, delay))
|
||||||
)
|
)
|
||||||
|
|
||||||
case VehicleAction.ChangeLoadout(target_guid, removed_weapons, new_weapons, old_inventory, new_inventory) =>
|
|
||||||
VehicleEvents.publish(
|
|
||||||
VehicleServiceResponse(s"/$forChannel/Vehicle", Service.defaultPlayerGUID, VehicleResponse.ChangeLoadout(target_guid, removed_weapons, new_weapons, old_inventory, new_inventory))
|
|
||||||
)
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package services.vehicle
|
||||||
|
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
|
|
@ -48,6 +47,4 @@ object VehicleAction {
|
||||||
final case class TransferPassengerChannel(player_guid : PlanetSideGUID, temp_channel : String, new_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Action
|
final case class TransferPassengerChannel(player_guid : PlanetSideGUID, temp_channel : String, new_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Action
|
||||||
|
|
||||||
final case class KickCargo(player_guid : PlanetSideGUID, cargo : Vehicle, speed : Int, delay : Long) extends Action
|
final case class KickCargo(player_guid : PlanetSideGUID, cargo : Vehicle, speed : Int, delay : Long) extends Action
|
||||||
|
|
||||||
final case class ChangeLoadout(target_guid : PlanetSideGUID, removed_weapons : List[(Equipment, PlanetSideGUID)], new_weapons : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], new_inventory : List[InventoryItem]) extends Action
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.vehicle
|
package services.vehicle
|
||||||
|
|
||||||
import net.psforever.objects.equipment.Equipment
|
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad.Reminders
|
import net.psforever.objects.serverobject.pad.VehicleSpawnPad.Reminders
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||||
|
|
@ -55,6 +53,4 @@ object VehicleResponse {
|
||||||
final case class TransferPassengerChannel(old_channel : String, temp_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Response
|
final case class TransferPassengerChannel(old_channel : String, temp_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Response
|
||||||
|
|
||||||
final case class KickCargo(cargo : Vehicle, speed : Int, delay : Long) extends Response
|
final case class KickCargo(cargo : Vehicle, speed : Int, delay : Long) extends Response
|
||||||
|
|
||||||
final case class ChangeLoadout(target_guid : PlanetSideGUID, removed_weapons : List[(Equipment, PlanetSideGUID)], new_weapons : List[InventoryItem], old_inventory : List[(Equipment, PlanetSideGUID)], new_inventory : List[InventoryItem]) extends Response
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -389,10 +389,7 @@ class AvatarTest extends Specification {
|
||||||
|
|
||||||
"the fifth slot is the locker wrapped in an EquipmentSlot" in {
|
"the fifth slot is the locker wrapped in an EquipmentSlot" in {
|
||||||
val (_, avatar) = CreatePlayer()
|
val (_, avatar) = CreatePlayer()
|
||||||
avatar.FifthSlot.Equipment match {
|
avatar.FifthSlot.Equipment.contains(avatar.Locker)
|
||||||
case Some(slot : LockerEquipment) => slot.Inventory mustEqual avatar.Locker.Inventory
|
|
||||||
case _ => ko
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"toString" in {
|
"toString" in {
|
||||||
|
|
|
||||||
|
|
@ -631,7 +631,7 @@ class ConverterTest extends Specification {
|
||||||
|
|
||||||
"LockerContainer" should {
|
"LockerContainer" should {
|
||||||
"convert to packet (empty)" in {
|
"convert to packet (empty)" in {
|
||||||
val obj = new LockerEquipment(LockerContainer())
|
val obj = LockerContainer()
|
||||||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||||
case Success(pkt) =>
|
case Success(pkt) =>
|
||||||
pkt mustEqual DetailedLockerContainerData(CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)), None)
|
pkt mustEqual DetailedLockerContainerData(CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)), None)
|
||||||
|
|
@ -648,7 +648,7 @@ class ConverterTest extends Specification {
|
||||||
|
|
||||||
"convert to packet (occupied)" in {
|
"convert to packet (occupied)" in {
|
||||||
import GlobalDefinitions._
|
import GlobalDefinitions._
|
||||||
val obj = new LockerEquipment(LockerContainer())
|
val obj = LockerContainer()
|
||||||
val rek = SimpleItem(remote_electronics_kit)
|
val rek = SimpleItem(remote_electronics_kit)
|
||||||
rek.GUID = PlanetSideGUID(1)
|
rek.GUID = PlanetSideGUID(1)
|
||||||
obj.Inventory += 0 -> rek
|
obj.Inventory += 0 -> rek
|
||||||
|
|
|
||||||
|
|
@ -525,136 +525,4 @@ class InventoryTest extends Specification {
|
||||||
ok
|
ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"InventoryEquiupmentSlot" should {
|
|
||||||
"insert, collide, insert" in {
|
|
||||||
val obj : GridInventory = GridInventory(7, 7)
|
|
||||||
obj.Slot(16).Equipment = bullet9mmBox1
|
|
||||||
//confirm all squares
|
|
||||||
obj.Slot( 8).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 9).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 10).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 11).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 12).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(15).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(16).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(17).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(18).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(19).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(22).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(23).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(24).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(25).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(26).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(29).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(30).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(31).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(32).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(33).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(36).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(37).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(38).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(39).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(40).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
//remove
|
|
||||||
obj.Slot(16).Equipment = None
|
|
||||||
obj.Slot( 8).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 9).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 10).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 11).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 12).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(15).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(16).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(17).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(18).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(19).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(22).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(23).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(24).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(25).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(26).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(29).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(30).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(31).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(32).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(33).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(36).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(37).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(38).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(39).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(40).Equipment.nonEmpty mustEqual false
|
|
||||||
//insert again
|
|
||||||
obj.Slot(16).Equipment = bullet9mmBox2
|
|
||||||
obj.Slot( 8).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 9).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 10).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 11).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 12).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(15).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(16).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(17).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(18).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(19).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(22).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(23).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(24).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(25).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(26).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(29).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(30).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(31).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(32).Equipment.nonEmpty mustEqual true
|
|
||||||
obj.Slot(33).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(36).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(37).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(38).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(39).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(40).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
//remove
|
|
||||||
obj.Slot(16).Equipment = None
|
|
||||||
obj.Slot( 8).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 9).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 10).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 11).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot( 12).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(15).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(16).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(17).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(18).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(19).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(22).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(23).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(24).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(25).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(26).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(29).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(30).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(31).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(32).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(33).Equipment.nonEmpty mustEqual false
|
|
||||||
//
|
|
||||||
obj.Slot(36).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(37).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(38).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(39).Equipment.nonEmpty mustEqual false
|
|
||||||
obj.Slot(40).Equipment.nonEmpty mustEqual false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,10 @@ class PlayerControlHealTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2, 0, 0)
|
player1.Position = Vector3(2, 0, 0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
||||||
player2.Zone = zone
|
player2.Zone = zone
|
||||||
player2.Spawn
|
player2.Spawn
|
||||||
guid.register(player2.Locker, 6)
|
|
||||||
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
||||||
|
|
||||||
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
|
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
|
||||||
|
|
@ -104,7 +102,6 @@ class PlayerControlHealSelfTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2, 0, 0)
|
player1.Position = Vector3(2, 0, 0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
|
|
||||||
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
|
val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
|
||||||
|
|
@ -170,12 +167,10 @@ class PlayerControlRepairTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2, 0, 0)
|
player1.Position = Vector3(2, 0, 0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
||||||
player2.Zone = zone
|
player2.Zone = zone
|
||||||
player2.Spawn
|
player2.Spawn
|
||||||
guid.register(player2.Locker, 6)
|
|
||||||
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
||||||
|
|
||||||
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
|
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
|
||||||
|
|
@ -248,7 +243,6 @@ class PlayerControlRepairSelfTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2, 0, 0)
|
player1.Position = Vector3(2, 0, 0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
|
|
||||||
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
|
val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
|
||||||
|
|
@ -315,12 +309,10 @@ class PlayerControlDamageTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2, 0, 0)
|
player1.Position = Vector3(2, 0, 0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
||||||
player2.Zone = zone
|
player2.Zone = zone
|
||||||
player2.Spawn
|
player2.Spawn
|
||||||
guid.register(player2.Locker, 6)
|
|
||||||
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
||||||
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
|
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
|
||||||
val projectile = tool.Projectile
|
val projectile = tool.Projectile
|
||||||
|
|
@ -393,12 +385,10 @@ class PlayerControlDeathStandingTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2,0,0)
|
player1.Position = Vector3(2,0,0)
|
||||||
guid.register(player1.Locker, 5)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
||||||
player2.Zone = zone
|
player2.Zone = zone
|
||||||
player2.Spawn
|
player2.Spawn
|
||||||
guid.register(player2.Locker, 6)
|
|
||||||
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
||||||
|
|
||||||
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
|
val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
|
||||||
|
|
@ -503,12 +493,10 @@ class PlayerControlDeathSeatedTest extends ActorTest {
|
||||||
player1.Zone = zone
|
player1.Zone = zone
|
||||||
player1.Spawn
|
player1.Spawn
|
||||||
player1.Position = Vector3(2,0,0)
|
player1.Position = Vector3(2,0,0)
|
||||||
guid.register(player1.Locker, 6)
|
|
||||||
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1), "player1-control")
|
||||||
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
val player2 = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=2
|
||||||
player2.Zone = zone
|
player2.Zone = zone
|
||||||
player2.Spawn
|
player2.Spawn
|
||||||
guid.register(player2.Locker, 7)
|
|
||||||
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2), "player2-control")
|
||||||
|
|
||||||
val vehicle = Vehicle(GlobalDefinitions.quadstealth) //guid=5
|
val vehicle = Vehicle(GlobalDefinitions.quadstealth) //guid=5
|
||||||
|
|
@ -607,4 +595,5 @@ class PlayerControlDeathSeatedTest extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
object PlayerControlTest { }
|
object PlayerControlTest { }
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ class PlayerTest extends Specification {
|
||||||
|
|
||||||
"can access the player's locker-space" in {
|
"can access the player's locker-space" in {
|
||||||
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
obj.Slot(5).Equipment.get.isInstanceOf[LockerEquipment] mustEqual true
|
obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
|
||||||
}
|
}
|
||||||
|
|
||||||
"can find equipment" in {
|
"can find equipment" in {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class GUIDTaskRegisterAvatarTest extends ActorTest {
|
||||||
obj.Slot(6).Equipment = obj_inv_ammo
|
obj.Slot(6).Equipment = obj_inv_ammo
|
||||||
val obj_locker = obj.Slot(5).Equipment.get
|
val obj_locker = obj.Slot(5).Equipment.get
|
||||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
|
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||||
|
|
||||||
assert(!obj.HasGUID)
|
assert(!obj.HasGUID)
|
||||||
assert(!obj_wep.HasGUID)
|
assert(!obj_wep.HasGUID)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class GUIDTaskRegisterPlayerTest extends ActorTest {
|
||||||
obj.Slot(6).Equipment = obj_inv_ammo
|
obj.Slot(6).Equipment = obj_inv_ammo
|
||||||
val obj_locker = obj.Slot(5).Equipment.get
|
val obj_locker = obj.Slot(5).Equipment.get
|
||||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
|
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||||
|
|
||||||
assert(!obj.HasGUID)
|
assert(!obj.HasGUID)
|
||||||
assert(!obj_wep.HasGUID)
|
assert(!obj_wep.HasGUID)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class GUIDTaskUnregisterAvatarTest extends ActorTest {
|
||||||
obj.Slot(6).Equipment = obj_inv_ammo
|
obj.Slot(6).Equipment = obj_inv_ammo
|
||||||
val obj_locker = obj.Slot(5).Equipment.get
|
val obj_locker = obj.Slot(5).Equipment.get
|
||||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
|
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||||
guid.register(obj, "dynamic")
|
guid.register(obj, "dynamic")
|
||||||
guid.register(obj_wep, "dynamic")
|
guid.register(obj_wep, "dynamic")
|
||||||
guid.register(obj_wep_ammo, "dynamic")
|
guid.register(obj_wep_ammo, "dynamic")
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class GUIDTaskUnregisterPlayerTest extends ActorTest {
|
||||||
obj.Slot(6).Equipment = obj_inv_ammo
|
obj.Slot(6).Equipment = obj_inv_ammo
|
||||||
val obj_locker = obj.Slot(5).Equipment.get
|
val obj_locker = obj.Slot(5).Equipment.get
|
||||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
|
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||||
guid.register(obj, "dynamic")
|
guid.register(obj, "dynamic")
|
||||||
guid.register(obj_wep, "dynamic")
|
guid.register(obj_wep, "dynamic")
|
||||||
guid.register(obj_wep_ammo, "dynamic")
|
guid.register(obj_wep_ammo, "dynamic")
|
||||||
|
|
|
||||||
|
|
@ -1,603 +0,0 @@
|
||||||
// Copyright (c) 2020 PSForever
|
|
||||||
import akka.actor.ActorRef
|
|
||||||
import akka.pattern.{AskTimeoutException, ask}
|
|
||||||
import akka.util.Timeout
|
|
||||||
import net.psforever.objects.{AmmoBox, GlobalDefinitions, Player, Tool}
|
|
||||||
import net.psforever.objects.equipment.{Ammo, Equipment}
|
|
||||||
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
|
||||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
|
||||||
import net.psforever.objects.serverobject.containable.Containable
|
|
||||||
import net.psforever.objects.zones.Zone
|
|
||||||
import net.psforever.packet.game.ObjectHeldMessage
|
|
||||||
import net.psforever.types.{PlanetSideGUID, TransactionType, Vector3}
|
|
||||||
import services.Service
|
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
import scala.concurrent.Future
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import scala.language.implicitConversions
|
|
||||||
|
|
||||||
object WorldSession {
|
|
||||||
/**
|
|
||||||
* Convert a boolean value into an integer value.
|
|
||||||
* Use: `true:Int` or `false:Int`
|
|
||||||
* @param b `true` or `false` (or `null`)
|
|
||||||
* @return 1 for `true`; 0 for `false`
|
|
||||||
*/
|
|
||||||
implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0
|
|
||||||
private implicit val timeout = new Timeout(5000 milliseconds)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this for placing equipment that has yet to be registered into a container,
|
|
||||||
* such as in support of changing ammunition types in `Tool` objects (weapons).
|
|
||||||
* If the object can not be placed into the container, it will be dropped onto the ground.
|
|
||||||
* It will also be dropped if it takes too long to be placed.
|
|
||||||
* Item swapping during the placement is not allowed.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `ChangeAmmoMessage`
|
|
||||||
* @see `Containable.CanNotPutItemInSlot`
|
|
||||||
* @see `Containable.PutItemAway`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Future.recover`
|
|
||||||
* @see `tell`
|
|
||||||
* @see `Zone.Ground.DropItem`
|
|
||||||
* @param obj the container
|
|
||||||
* @param item the item being manipulated
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def PutEquipmentInInventoryOrDrop(obj : PlanetSideServerObject with Container)(item : Equipment) : Future[Any] = {
|
|
||||||
val localContainer = obj
|
|
||||||
val localItem = item
|
|
||||||
val result = ask(localContainer.Actor, Containable.PutItemAway(localItem))
|
|
||||||
result.onComplete {
|
|
||||||
case scala.util.Failure(_) | scala.util.Success(_ : Containable.CanNotPutItemInSlot) =>
|
|
||||||
localContainer.Zone.Ground.tell(Zone.Ground.DropItem(localItem, localContainer.Position, Vector3.z(localContainer.Orientation.z)), localContainer.Actor)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this for placing equipment that has yet to be registered into a container,
|
|
||||||
* such as in support of changing ammunition types in `Tool` objects (weapons).
|
|
||||||
* Equipment will go wherever it fits in containing object, or be dropped if it fits nowhere.
|
|
||||||
* Item swapping during the placement is not allowed.
|
|
||||||
* @see `ChangeAmmoMessage`
|
|
||||||
* @see `GUIDTask.RegisterEquipment`
|
|
||||||
* @see `PutEquipmentInInventoryOrDrop`
|
|
||||||
* @see `Task`
|
|
||||||
* @see `TaskResolver.GiveTask`
|
|
||||||
* @param obj the container
|
|
||||||
* @param item the item being manipulated
|
|
||||||
* @return a `TaskResolver` object
|
|
||||||
*/
|
|
||||||
def PutNewEquipmentInInventoryOrDrop(obj : PlanetSideServerObject with Container)(item : Equipment) : TaskResolver.GiveTask = {
|
|
||||||
val localZone = obj.Zone
|
|
||||||
TaskResolver.GiveTask(
|
|
||||||
new Task() {
|
|
||||||
private val localContainer = obj
|
|
||||||
private val localItem = item
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
PutEquipmentInInventoryOrDrop(localContainer)(localItem)
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this for obtaining new equipment from a loadout specification.
|
|
||||||
* The loadout specification contains a specific slot position for placing the item.
|
|
||||||
* This request will (probably) be coincidental with a number of other such requests based on that loadout
|
|
||||||
* so items must be rigidly placed else cascade into a chaostic order.
|
|
||||||
* Item swapping during the placement is not allowed.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `AvatarAction.ObjectDelete`
|
|
||||||
* @see `ChangeAmmoMessage`
|
|
||||||
* @see `Containable.CanNotPutItemInSlot`
|
|
||||||
* @see `Containable.PutItemAway`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Future.recover`
|
|
||||||
* @see `GUIDTask.UnregisterEquipment`
|
|
||||||
* @see `tell`
|
|
||||||
* @see `Zone.AvatarEvents`
|
|
||||||
* @param obj the container
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param item the item being manipulated
|
|
||||||
* @param slot na
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def PutEquipmentInInventorySlot(obj : PlanetSideServerObject with Container, taskResolver : ActorRef)(item : Equipment, slot : Int) : Future[Any] = {
|
|
||||||
val localContainer = obj
|
|
||||||
val localItem = item
|
|
||||||
val localResolver = taskResolver
|
|
||||||
val result = ask(localContainer.Actor, Containable.PutItemInSlotOnly(localItem, slot))
|
|
||||||
result.onComplete {
|
|
||||||
case scala.util.Failure(_) | scala.util.Success(_ : Containable.CanNotPutItemInSlot) =>
|
|
||||||
localResolver ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this for obtaining new equipment from a loadout specification.
|
|
||||||
* The loadout specification contains a specific slot position for placing the item.
|
|
||||||
* This request will (probably) be coincidental with a number of other such requests based on that loadout
|
|
||||||
* so items must be rigidly placed else cascade into a chaostic order.
|
|
||||||
* Item swapping during the placement is not allowed.
|
|
||||||
* @see `GUIDTask.RegisterEquipment`
|
|
||||||
* @see `PutEquipmentInInventorySlot`
|
|
||||||
* @see `Task`
|
|
||||||
* @see `TaskResolver.GiveTask`
|
|
||||||
* @param obj the container
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param item the item being manipulated
|
|
||||||
* @param slot where the item will be placed in the container
|
|
||||||
* @return a `TaskResolver` object
|
|
||||||
*/
|
|
||||||
def PutLoadoutEquipmentInInventory(obj : PlanetSideServerObject with Container, taskResolver : ActorRef)(item : Equipment, slot : Int) : TaskResolver.GiveTask = {
|
|
||||||
val localZone = obj.Zone
|
|
||||||
TaskResolver.GiveTask(
|
|
||||||
new Task() {
|
|
||||||
private val localContainer = obj
|
|
||||||
private val localItem = item
|
|
||||||
private val localSlot = slot
|
|
||||||
private val localFunc : (Equipment,Int)=>Future[Any] = PutEquipmentInInventorySlot(obj, taskResolver)
|
|
||||||
|
|
||||||
override def Timeout : Long = 1000
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = {
|
|
||||||
if(localItem.HasGUID && localContainer.Find(localItem).nonEmpty)
|
|
||||||
Task.Resolution.Success
|
|
||||||
else
|
|
||||||
Task.Resolution.Incomplete
|
|
||||||
}
|
|
||||||
|
|
||||||
override def Description : String = s"PutEquipmentInInventorySlot - ${localItem.Definition.Name}"
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
localFunc(localItem, localSlot)
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for purchasing new equipment from a terminal and placing it somewhere in a player's loadout.
|
|
||||||
* Two levels of query are performed here based on the behavior expected of the item.
|
|
||||||
* First, an attempt is made to place the item anywhere in the target container as long as it does not cause swap items to be generated.
|
|
||||||
* Second, if it fails admission to the target container, an attempt is made to place it into the target player's free hand.
|
|
||||||
* If the container and the suggested player are the same, it will skip the second attempt.
|
|
||||||
* As a terminal operation, the player must receive a report regarding whether the transaction was successful.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `Containable.CanNotPutItemInSlot`
|
|
||||||
* @see `Containable.PutItemInSlotOnly`
|
|
||||||
* @see `GUIDTask.RegisterEquipment`
|
|
||||||
* @see `GUIDTask.UnregisterEquipment`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `PutEquipmentInInventorySlot`
|
|
||||||
* @see `TerminalMessageOnTimeout`
|
|
||||||
* @param obj the container
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param player na
|
|
||||||
* @param term na
|
|
||||||
* @param item the item being manipulated
|
|
||||||
* @return a `TaskResolver` object
|
|
||||||
*/
|
|
||||||
def BuyNewEquipmentPutInInventory(obj : PlanetSideServerObject with Container, taskResolver : ActorRef, player : Player, term : PlanetSideGUID)(item : Equipment) : TaskResolver.GiveTask = {
|
|
||||||
val localZone = obj.Zone
|
|
||||||
TaskResolver.GiveTask(
|
|
||||||
new Task() {
|
|
||||||
private val localContainer = obj
|
|
||||||
private val localItem = item
|
|
||||||
private val localPlayer = player
|
|
||||||
private val localResolver = taskResolver
|
|
||||||
private val localTermMsg : Boolean=>Unit = TerminalResult(term, localPlayer, TransactionType.Buy)
|
|
||||||
|
|
||||||
override def Timeout : Long = 1000
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = {
|
|
||||||
if(localItem.HasGUID && localContainer.Find(localItem).nonEmpty)
|
|
||||||
Task.Resolution.Success
|
|
||||||
else
|
|
||||||
Task.Resolution.Incomplete
|
|
||||||
}
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
TerminalMessageOnTimeout(
|
|
||||||
ask(localContainer.Actor, Containable.PutItemAway(localItem)),
|
|
||||||
localTermMsg
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case scala.util.Failure(_) | scala.util.Success(_ : Containable.CanNotPutItemInSlot) =>
|
|
||||||
if(localContainer != localPlayer) {
|
|
||||||
TerminalMessageOnTimeout(
|
|
||||||
PutEquipmentInInventorySlot(localPlayer, localResolver)(localItem, Player.FreeHandSlot),
|
|
||||||
localTermMsg
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case scala.util.Failure(_) | scala.util.Success(_ : Containable.CanNotPutItemInSlot) =>
|
|
||||||
localTermMsg(false)
|
|
||||||
case _ =>
|
|
||||||
localTermMsg(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
localResolver ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
|
|
||||||
localTermMsg(false)
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
localTermMsg(true)
|
|
||||||
}
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The primary use is to register new mechanized assault exo-suit armaments,
|
|
||||||
* place the newly registered weapon in hand,
|
|
||||||
* and then raise that hand (draw that slot) so that the weapon is active.
|
|
||||||
* (Players in MAX suits can not manipulate their drawn slot manually.)
|
|
||||||
* In general, this can be used for any equipment that is to be equipped to a player's hand then immediately drawn.
|
|
||||||
* Do not allow the item to be (mis)placed in any available slot.
|
|
||||||
* Item swapping during the placement is not allowed and the possibility should be proactively avoided.
|
|
||||||
* @throws `RuntimeException` if slot is not a player visible slot (holsters)
|
|
||||||
* @see `ask`
|
|
||||||
* @see `AvatarAction.ObjectDelete`
|
|
||||||
* @see `AvatarAction.SendResponse`
|
|
||||||
* @see `Containable.CanNotPutItemInSlot`
|
|
||||||
* @see `Containable.PutItemInSlotOnly`
|
|
||||||
* @see `GUIDTask.RegisterEquipment`
|
|
||||||
* @see `GUIDTask.UnregisterEquipment`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `ObjectHeldMessage`
|
|
||||||
* @see `Player.DrawnSlot`
|
|
||||||
* @see `Player.LastDrawnSlot`
|
|
||||||
* @see `Service.defaultPlayerGUID`
|
|
||||||
* @see `TaskResolver.GiveTask`
|
|
||||||
* @see `Zone.AvatarEvents`
|
|
||||||
* @param player the player whose visible slot will be equipped and drawn
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param item the item to equip
|
|
||||||
* @param slot the slot in which the item will be equipped
|
|
||||||
* @return a `TaskResolver` object
|
|
||||||
*/
|
|
||||||
def HoldNewEquipmentUp(player : Player, taskResolver : ActorRef)(item : Equipment, slot : Int) : TaskResolver.GiveTask = {
|
|
||||||
if(player.VisibleSlots.contains(slot)) {
|
|
||||||
val localZone = player.Zone
|
|
||||||
TaskResolver.GiveTask(
|
|
||||||
new Task() {
|
|
||||||
private val localPlayer = player
|
|
||||||
private val localGUID = player.GUID
|
|
||||||
private val localItem = item
|
|
||||||
private val localSlot = slot
|
|
||||||
private val localResolver = taskResolver
|
|
||||||
|
|
||||||
override def Timeout : Long = 1000
|
|
||||||
|
|
||||||
override def isComplete : Task.Resolution.Value = {
|
|
||||||
if(localPlayer.DrawnSlot == localSlot)
|
|
||||||
Task.Resolution.Success
|
|
||||||
else
|
|
||||||
Task.Resolution.Incomplete
|
|
||||||
}
|
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
|
||||||
ask(localPlayer.Actor, Containable.PutItemInSlotOnly(localItem, localSlot))
|
|
||||||
.onComplete {
|
|
||||||
case scala.util.Failure(_) | scala.util.Success(_ : Containable.CanNotPutItemInSlot) =>
|
|
||||||
localResolver ! GUIDTask.UnregisterEquipment(localItem)(localZone.GUID)
|
|
||||||
case _ =>
|
|
||||||
if(localPlayer.DrawnSlot != Player.HandsDownSlot) {
|
|
||||||
localPlayer.DrawnSlot = Player.HandsDownSlot
|
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(localPlayer.Name,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectHeldMessage(localGUID, Player.HandsDownSlot, false))
|
|
||||||
)
|
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(localZone.Id, AvatarAction.ObjectHeld(localGUID, localPlayer.LastDrawnSlot))
|
|
||||||
}
|
|
||||||
localPlayer.DrawnSlot = localSlot
|
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(localZone.Id,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectHeldMessage(localGUID, localSlot, false))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
resolver ! scala.util.Success(this)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//TODO log.error
|
|
||||||
throw new RuntimeException(s"provided slot $slot is not a player visible slot (holsters)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an item from the ground and put it into the given container.
|
|
||||||
* The zone in which the item is found is expected to be the same in which the container object is located.
|
|
||||||
* If the object can not be placed into the container, it is put back on the ground.
|
|
||||||
* The item that was collected off the ground, if it is placed back on the ground,
|
|
||||||
* will be positioned with respect to the container object rather than its original location.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `AvatarAction.ObjectDelete`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Zone.AvatarEvents`
|
|
||||||
* @see `Zone.Ground.CanNotPickUpItem`
|
|
||||||
* @see `Zone.Ground.ItemInHand`
|
|
||||||
* @see `Zone.Ground.PickUpItem`
|
|
||||||
* @see `PutEquipmentInInventoryOrDrop`
|
|
||||||
* @param obj the container into which the item will be placed
|
|
||||||
* @param item the item being collected from off the ground of the container's zone
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def PickUpEquipmentFromGround(obj : PlanetSideServerObject with Container)(item : Equipment) : Future[Any] = {
|
|
||||||
val localZone = obj.Zone
|
|
||||||
val localContainer = obj
|
|
||||||
val localItem = item
|
|
||||||
val future = ask(localZone.Ground, Zone.Ground.PickupItem(item.GUID))
|
|
||||||
future.onComplete {
|
|
||||||
case scala.util.Success(Zone.Ground.ItemInHand(_)) =>
|
|
||||||
PutEquipmentInInventoryOrDrop(localContainer)(localItem)
|
|
||||||
case scala.util.Success(Zone.Ground.CanNotPickupItem(_, item_guid, _)) =>
|
|
||||||
localZone.GUID(item_guid) match {
|
|
||||||
case Some(_) => ;
|
|
||||||
case None => //acting on old data?
|
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(localZone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item_guid))
|
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
future
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an item from a container and drop it on the ground.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `AvatarAction.ObjectDelete`
|
|
||||||
* @see `Containable.ItemFromSlot`
|
|
||||||
* @see `Containable.RemoveItemFromSlot`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Future.recover`
|
|
||||||
* @see `tell`
|
|
||||||
* @see `Zone.AvatarEvents`
|
|
||||||
* @see `Zone.Ground.DropItem`
|
|
||||||
* @param obj the container to search
|
|
||||||
* @param item the item to find and remove from the container
|
|
||||||
* @param pos an optional position where to drop the item on the ground;
|
|
||||||
* expected override from original container's position
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def DropEquipmentFromInventory(obj : PlanetSideServerObject with Container)(item : Equipment, pos : Option[Vector3] = None) : Future[Any] = {
|
|
||||||
val localContainer = obj
|
|
||||||
val localItem = item
|
|
||||||
val localPos = pos
|
|
||||||
val result = ask(localContainer.Actor, Containable.RemoveItemFromSlot(localItem))
|
|
||||||
result.onComplete {
|
|
||||||
case scala.util.Success(Containable.ItemFromSlot(_, Some(_), Some(_))) =>
|
|
||||||
localContainer.Zone.Ground.tell(Zone.Ground.DropItem(localItem, localPos.getOrElse(localContainer.Position), Vector3.z(localContainer.Orientation.z)), localContainer.Actor)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an item from a container and delete it.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `AvatarAction.ObjectDelete`
|
|
||||||
* @see `Containable.ItemFromSlot`
|
|
||||||
* @see `Containable.RemoveItemFromSlot`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Future.recover`
|
|
||||||
* @see `GUIDTask.UnregisterEquipment`
|
|
||||||
* @see `Zone.AvatarEvents`
|
|
||||||
* @param obj the container to search
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param item the item to find and remove from the container
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def RemoveOldEquipmentFromInventory(obj : PlanetSideServerObject with Container, taskResolver : ActorRef)(item : Equipment) : Future[Any] = {
|
|
||||||
val localContainer = obj
|
|
||||||
val localItem = item
|
|
||||||
val localResolver = taskResolver
|
|
||||||
val result = ask(localContainer.Actor, Containable.RemoveItemFromSlot(localItem))
|
|
||||||
result.onComplete {
|
|
||||||
case scala.util.Success(Containable.ItemFromSlot(_, Some(_), Some(_))) =>
|
|
||||||
localResolver ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primarily, remove an item from a container and delete it.
|
|
||||||
* As a terminal operation, the player must receive a report regarding whether the transaction was successful.
|
|
||||||
* At the end of a successful transaction, and only a successful transaction,
|
|
||||||
* the item that was removed is no longer considered a valid game object.
|
|
||||||
* Contrasting `RemoveOldEquipmentFromInventory` which identifies the actual item to be eliminated,
|
|
||||||
* this function uses the slot where the item is (should be) located.
|
|
||||||
* @see `ask`
|
|
||||||
* @see `Containable.ItemFromSlot`
|
|
||||||
* @see `Containable.RemoveItemFromSlot`
|
|
||||||
* @see `Future.onComplete`
|
|
||||||
* @see `Future.recover`
|
|
||||||
* @see `GUIDTask.UnregisterEquipment`
|
|
||||||
* @see `RemoveOldEquipmentFromInventory`
|
|
||||||
* @see `TerminalMessageOnTimeout`
|
|
||||||
* @see `TerminalResult`
|
|
||||||
* @param obj the container to search
|
|
||||||
* @param taskResolver na
|
|
||||||
* @param player the player who used the terminal
|
|
||||||
* @param term the unique identifier number of the terminal
|
|
||||||
* @param slot from which slot the equipment is to be removed
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def SellEquipmentFromInventory(obj : PlanetSideServerObject with Container, taskResolver : ActorRef, player : Player, term : PlanetSideGUID)(slot : Int) : Future[Any] = {
|
|
||||||
val localContainer = obj
|
|
||||||
val localPlayer = player
|
|
||||||
val localSlot = slot
|
|
||||||
val localResolver = taskResolver
|
|
||||||
val localTermMsg : Boolean=>Unit = TerminalResult(term, localPlayer, TransactionType.Sell)
|
|
||||||
val result = TerminalMessageOnTimeout(
|
|
||||||
ask(localContainer.Actor, Containable.RemoveItemFromSlot(localSlot)),
|
|
||||||
localTermMsg
|
|
||||||
)
|
|
||||||
result.onComplete {
|
|
||||||
case scala.util.Success(Containable.ItemFromSlot(_, Some(item), Some(_))) =>
|
|
||||||
localResolver ! GUIDTask.UnregisterEquipment(item)(localContainer.Zone.GUID)
|
|
||||||
localTermMsg(true)
|
|
||||||
case _ =>
|
|
||||||
localTermMsg(false)
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If a timeout occurs on the manipulation, declare a terminal transaction failure.
|
|
||||||
* @see `AskTimeoutException`
|
|
||||||
* @see `recover`
|
|
||||||
* @param future the item manipulation's `Future` object
|
|
||||||
* @param terminalMessage how to call the terminal message
|
|
||||||
* @return a `Future` that anticipates the resolution to this manipulation
|
|
||||||
*/
|
|
||||||
def TerminalMessageOnTimeout(future : Future[Any], terminalMessage : Boolean=>Unit) : Future[Any] = {
|
|
||||||
future.recover {
|
|
||||||
case _ : AskTimeoutException =>
|
|
||||||
terminalMessage(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Announced the result of this player's terminal use, to the player that used the terminal.
|
|
||||||
* This is a necessary step for regaining terminal use which is naturally blocked by the client after a transaction request.
|
|
||||||
* @see `AvatarAction.TerminalOrderResult`
|
|
||||||
* @see `ItemTransactionResultMessage`
|
|
||||||
* @see `TransactionType`
|
|
||||||
* @param guid the terminal's unique identifier
|
|
||||||
* @param player the player who used the terminal
|
|
||||||
* @param transaction what kind of transaction was involved in terminal use
|
|
||||||
* @param result the result of that transaction
|
|
||||||
*/
|
|
||||||
def TerminalResult(guid : PlanetSideGUID, player : Player, transaction : TransactionType.Value)(result : Boolean) : Unit = {
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.TerminalOrderResult(guid, transaction, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drop some items on the ground is a given location.
|
|
||||||
* The location corresponds to the previous container for those items.
|
|
||||||
* @see `Zone.Ground.DropItem`
|
|
||||||
* @param container the original object that contained the items
|
|
||||||
* @param drops the items to be dropped on the ground
|
|
||||||
*/
|
|
||||||
def DropLeftovers(container : PlanetSideServerObject with Container)(drops : List[InventoryItem]) : Unit = {
|
|
||||||
//drop or retire
|
|
||||||
val zone = container.Zone
|
|
||||||
val pos = container.Position
|
|
||||||
val orient = Vector3.z(container.Orientation.z)
|
|
||||||
//TODO make a sound when dropping stuff?
|
|
||||||
drops.foreach { entry => zone.Ground.tell(Zone.Ground.DropItem(entry.obj, pos, orient), container.Actor) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Within a specified `Container`, find the smallest number of `Equipment` objects of a certain qualifying type
|
|
||||||
* whose sum count is greater than, or equal to, a `desiredAmount` based on an accumulator method.<br>
|
|
||||||
* <br>
|
|
||||||
* In an occupied `List` of returned `Inventory` entries, all but the last entry is typically considered "emptied."
|
|
||||||
* For objects with contained quantities, the last entry may require having that quantity be set to a non-zero number.
|
|
||||||
* @param obj the `Container` to search
|
|
||||||
* @param filterTest test used to determine inclusivity of `Equipment` collection
|
|
||||||
* @param desiredAmount how much is requested
|
|
||||||
* @param counting test used to determine value of found `Equipment`;
|
|
||||||
* defaults to one per entry
|
|
||||||
* @return a `List` of all discovered entries totaling approximately the amount requested
|
|
||||||
*/
|
|
||||||
def FindEquipmentStock(obj : Container,
|
|
||||||
filterTest : Equipment=>Boolean,
|
|
||||||
desiredAmount : Int,
|
|
||||||
counting : Equipment=>Int = DefaultCount) : List[InventoryItem] = {
|
|
||||||
var currentAmount : Int = 0
|
|
||||||
obj.Inventory.Items
|
|
||||||
.filter(item => filterTest(item.obj))
|
|
||||||
.sortBy(_.start)
|
|
||||||
.takeWhile(entry => {
|
|
||||||
val previousAmount = currentAmount
|
|
||||||
currentAmount += counting(entry.obj)
|
|
||||||
previousAmount < desiredAmount
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default counting function for an item.
|
|
||||||
* Counts the number of item(s).
|
|
||||||
* @param e the `Equipment` object
|
|
||||||
* @return the quantity;
|
|
||||||
* always one
|
|
||||||
*/
|
|
||||||
def DefaultCount(e : Equipment) : Int = 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The counting function for an item of `AmmoBox`.
|
|
||||||
* Counts the `Capacity` of the ammunition.
|
|
||||||
* @param e the `Equipment` object
|
|
||||||
* @return the quantity
|
|
||||||
*/
|
|
||||||
def CountAmmunition(e : Equipment) : Int = {
|
|
||||||
e match {
|
|
||||||
case a : AmmoBox => a.Capacity
|
|
||||||
case _ => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The counting function for an item of `Tool` where the item is also a grenade.
|
|
||||||
* Counts the number of grenades.
|
|
||||||
* @see `GlobalDefinitions.isGrenade`
|
|
||||||
* @param e the `Equipment` object
|
|
||||||
* @return the quantity
|
|
||||||
*/
|
|
||||||
def CountGrenades(e : Equipment) : Int = {
|
|
||||||
e match {
|
|
||||||
case t : Tool => (GlobalDefinitions.isGrenade(t.Definition):Int) * t.Magazine
|
|
||||||
case _ => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag an `AmmoBox` object that matches for the given ammunition type.
|
|
||||||
* @param ammo the type of `Ammo` to check
|
|
||||||
* @param e the `Equipment` object
|
|
||||||
* @return `true`, if the object is an `AmmoBox` of the correct ammunition type; `false`, otherwise
|
|
||||||
*/
|
|
||||||
def FindAmmoBoxThatUses(ammo : Ammo.Value)(e : Equipment) : Boolean = {
|
|
||||||
e match {
|
|
||||||
case t : AmmoBox => t.AmmoType == ammo
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag a `Tool` object that matches for loading the given ammunition type.
|
|
||||||
* @param ammo the type of `Ammo` to check
|
|
||||||
* @param e the `Equipment` object
|
|
||||||
* @return `true`, if the object is a `Tool` that loads the correct ammunition type; `false`, otherwise
|
|
||||||
*/
|
|
||||||
def FindToolThatUses(ammo : Ammo.Value)(e : Equipment) : Boolean = {
|
|
||||||
e match {
|
|
||||||
case t : Tool =>
|
|
||||||
t.Definition.AmmoTypes.map { _.AmmoType }.contains(ammo)
|
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -163,7 +163,7 @@ class DroptItemTest extends ActorTest {
|
||||||
"AvatarService" should {
|
"AvatarService" should {
|
||||||
"pass DropItem" in {
|
"pass DropItem" in {
|
||||||
service ! Service.Join("test")
|
service ! Service.Join("test")
|
||||||
service ! AvatarServiceMessage("test", AvatarAction.DropItem(PlanetSideGUID(10), tool))
|
service ! AvatarServiceMessage("test", AvatarAction.DropItem(PlanetSideGUID(10), tool, Zone.Nowhere))
|
||||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt)))
|
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,16 +264,41 @@ class PlayerStateTest extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PickupItemTest extends ActorTest {
|
class PickupItemATest extends ActorTest {
|
||||||
|
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||||
|
obj.GUID = PlanetSideGUID(10)
|
||||||
|
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
|
||||||
|
|
||||||
|
val toolDef = GlobalDefinitions.beamer
|
||||||
|
val tool = Tool(toolDef)
|
||||||
|
tool.GUID = PlanetSideGUID(40)
|
||||||
|
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41)
|
||||||
|
val pkt = ObjectCreateMessage(
|
||||||
|
toolDef.ObjectId,
|
||||||
|
tool.GUID,
|
||||||
|
ObjectCreateMessageParent(PlanetSideGUID(10), 0),
|
||||||
|
toolDef.Packet.ConstructorData(tool).get
|
||||||
|
)
|
||||||
|
|
||||||
|
"pass PickUpItem as EquipmentInHand (visible pistol slot)" in {
|
||||||
|
ServiceManager.boot(system)
|
||||||
|
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName)
|
||||||
|
service ! Service.Join("test")
|
||||||
|
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 0, tool))
|
||||||
|
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PickupItemBTest extends ActorTest {
|
||||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||||
val tool = Tool(GlobalDefinitions.beamer)
|
val tool = Tool(GlobalDefinitions.beamer)
|
||||||
tool.GUID = PlanetSideGUID(40)
|
tool.GUID = PlanetSideGUID(40)
|
||||||
|
|
||||||
"pass PickUpItem" in {
|
"pass PickUpItem as ObjectDelete (not visible inventory space)" in {
|
||||||
ServiceManager.boot(system)
|
ServiceManager.boot(system)
|
||||||
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName)
|
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName)
|
||||||
service ! Service.Join("test")
|
service ! Service.Join("test")
|
||||||
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), tool))
|
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 6, tool))
|
||||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(tool.GUID, 0)))
|
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(tool.GUID, 0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue