mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Merge pull request #176 from Fate-JH/vehicle-terminal
Vehicle Terminals
This commit is contained in:
commit
47a0aa3e0c
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
/**
|
||||
* Used to initialize the value of a re-usable `Cancellable` object.
|
||||
* By convention, it always acts like it has been cancelled before and can be cancelled.
|
||||
* Should be replaced with pertinent `Cancellable` logic through the initialization of an executor.
|
||||
*/
|
||||
object DefaultCancellable {
|
||||
import akka.actor.Cancellable
|
||||
|
||||
protected class InternalCancellable extends Cancellable {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
final val obj : Cancellable = new InternalCancellable
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) {
|
|||
object ExoSuitDefinition {
|
||||
final val Standard = ExoSuitDefinition(ExoSuitType.Standard)
|
||||
Standard.MaxArmor = 50
|
||||
Standard.InventoryScale = new InventoryTile(9,6)
|
||||
Standard.InventoryScale = InventoryTile.Tile96
|
||||
Standard.InventoryOffset = 6
|
||||
Standard.Holster(0, EquipmentSize.Pistol)
|
||||
Standard.Holster(2, EquipmentSize.Rifle)
|
||||
|
|
@ -73,7 +73,7 @@ object ExoSuitDefinition {
|
|||
|
||||
final val Agile = ExoSuitDefinition(ExoSuitType.Agile)
|
||||
Agile.MaxArmor = 100
|
||||
Agile.InventoryScale = new InventoryTile(9,9)
|
||||
Agile.InventoryScale = InventoryTile.Tile99
|
||||
Agile.InventoryOffset = 6
|
||||
Agile.Holster(0, EquipmentSize.Pistol)
|
||||
Agile.Holster(1, EquipmentSize.Pistol)
|
||||
|
|
@ -83,7 +83,7 @@ object ExoSuitDefinition {
|
|||
final val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
|
||||
Reinforced.permission = 1
|
||||
Reinforced.MaxArmor = 200
|
||||
Reinforced.InventoryScale = new InventoryTile(12,9)
|
||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
||||
Reinforced.InventoryOffset = 6
|
||||
Reinforced.Holster(0, EquipmentSize.Pistol)
|
||||
Reinforced.Holster(1, EquipmentSize.Pistol)
|
||||
|
|
@ -94,7 +94,7 @@ object ExoSuitDefinition {
|
|||
final val Infiltration = ExoSuitDefinition(ExoSuitType.Standard)
|
||||
Infiltration.permission = 1
|
||||
Infiltration.MaxArmor = 0
|
||||
Infiltration.InventoryScale = new InventoryTile(6,6)
|
||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
||||
Infiltration.InventoryOffset = 6
|
||||
Infiltration.Holster(0, EquipmentSize.Pistol)
|
||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
||||
|
|
@ -102,7 +102,7 @@ object ExoSuitDefinition {
|
|||
final val MAX = ExoSuitDefinition(ExoSuitType.MAX)
|
||||
MAX.permission = 1
|
||||
MAX.MaxArmor = 650
|
||||
MAX.InventoryScale = new InventoryTile(16,12)
|
||||
MAX.InventoryScale = InventoryTile.Tile1612
|
||||
MAX.InventoryOffset = 6
|
||||
MAX.Holster(0, EquipmentSize.Max)
|
||||
MAX.Holster(4, EquipmentSize.Melee)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,7 @@ import scala.annotation.tailrec
|
|||
/**
|
||||
* From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.<br>
|
||||
* <br>
|
||||
* `InfantryLoadout` objects are composed of the following information, as if a blueprint:<br>
|
||||
* `Loadout` objects are composed of the following information, as if a blueprint:<br>
|
||||
* - the avatar's current exo-suit<br>
|
||||
* - the type of specialization, called a "subtype" (mechanized assault exo-suits only)<br>
|
||||
* - the contents of the avatar's occupied holster slots<br>
|
||||
|
|
@ -28,25 +28,24 @@ import scala.annotation.tailrec
|
|||
* Even a whole blueprint can be denied if the user lacks the necessary exo-suit certification.
|
||||
* A completely new piece of `Equipment` is constructed when the `Loadout` is regurgitated.<br>
|
||||
* <br>
|
||||
* The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `InfantryLoadout` entries.
|
||||
* The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `Loadout` entries.
|
||||
* The ten-long list is initialized with `FavoritesMessage` packets.
|
||||
* Specific entries are loaded or removed using `FavoritesRequest` packets.
|
||||
* @param player the player
|
||||
* @param label the name by which this inventory will be known when displayed in a Favorites list
|
||||
*/
|
||||
class InfantryLoadout(player : Player, private val label : String) {
|
||||
class Loadout(player : Player, private val label : String) {
|
||||
/** the exo-suit */
|
||||
private val exosuit : ExoSuitType.Value = player.ExoSuit
|
||||
/** the MAX specialization, to differentiate the three types of MAXes who all use the same exo-suit name */
|
||||
private val subtype =
|
||||
if(exosuit == ExoSuitType.MAX) {
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
player.Holsters().head.Equipment.get.Definition.ObjectId match {
|
||||
case ObjectClass.trhev_dualcycler | ObjectClass.nchev_scattercannon | ObjectClass.vshev_quasar =>
|
||||
player.Holsters().head.Equipment.get.Definition match {
|
||||
case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar =>
|
||||
1
|
||||
case ObjectClass.trhev_pounder | ObjectClass.nchev_falcon | ObjectClass.vshev_comet =>
|
||||
case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet =>
|
||||
2
|
||||
case ObjectClass.trhev_burster | ObjectClass.nchev_sparrow | ObjectClass.vshev_starfire =>
|
||||
case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire =>
|
||||
3
|
||||
case _ =>
|
||||
0
|
||||
|
|
@ -56,14 +55,14 @@ class InfantryLoadout(player : Player, private val label : String) {
|
|||
0
|
||||
}
|
||||
/** simplified representation of the holster `Equipment` */
|
||||
private val holsters : List[InfantryLoadout.SimplifiedEntry] =
|
||||
InfantryLoadout.packageSimplifications(player.Holsters())
|
||||
private val holsters : List[Loadout.SimplifiedEntry] =
|
||||
Loadout.packageSimplifications(player.Holsters())
|
||||
/** simplified representation of the inventory `Equipment` */
|
||||
private val inventory : List[InfantryLoadout.SimplifiedEntry] =
|
||||
InfantryLoadout.packageSimplifications(player.Inventory.Items.values.toList)
|
||||
private val inventory : List[Loadout.SimplifiedEntry] =
|
||||
Loadout.packageSimplifications(player.Inventory.Items.values.toList)
|
||||
|
||||
/**
|
||||
* The label by which this `InfantryLoadout` is called.
|
||||
* The label by which this `Loadout` is called.
|
||||
* @return the label
|
||||
*/
|
||||
def Label : String = label
|
||||
|
|
@ -87,26 +86,26 @@ class InfantryLoadout(player : Player, private val label : String) {
|
|||
def Subtype : Int = subtype
|
||||
|
||||
/**
|
||||
* The `Equipment` in the `Player`'s holster slots when this `InfantryLoadout` is created.
|
||||
* The `Equipment` in the `Player`'s holster slots when this `Loadout` is created.
|
||||
* @return a `List` of the holster item blueprints
|
||||
*/
|
||||
def Holsters : List[InfantryLoadout.SimplifiedEntry] = holsters
|
||||
def Holsters : List[Loadout.SimplifiedEntry] = holsters
|
||||
|
||||
/**
|
||||
* The `Equipment` in the `Player`'s inventory region when this `InfantryLoadout` is created.
|
||||
* The `Equipment` in the `Player`'s inventory region when this `Loadout` is created.
|
||||
* @return a `List` of the inventory item blueprints
|
||||
*/
|
||||
def Inventory : List[InfantryLoadout.SimplifiedEntry] = inventory
|
||||
def Inventory : List[Loadout.SimplifiedEntry] = inventory
|
||||
}
|
||||
|
||||
object InfantryLoadout {
|
||||
object Loadout {
|
||||
/**
|
||||
* A basic `Trait` connecting all of the `Equipment` blueprints.
|
||||
*/
|
||||
sealed trait Simplification
|
||||
|
||||
/**
|
||||
* An entry in the `InfantryLoadout`, wrapping around a slot index and what is in the slot index.
|
||||
* An entry in the `Loadout`, wrapping around a slot index and what is in the slot index.
|
||||
* @param item the `Equipment`
|
||||
* @param index the slot number where the `Equipment` is to be stowed
|
||||
* @see `InventoryItem`
|
||||
|
|
@ -163,7 +162,7 @@ object InfantryLoadout {
|
|||
* @return a `List` of simplified `Equipment`
|
||||
*/
|
||||
private def packageSimplifications(equipment : List[InventoryItem]) : List[SimplifiedEntry] = {
|
||||
recursiveInventorySimplifications(equipment.iterator)
|
||||
equipment.map(entry => { SimplifiedEntry(buildSimplification(entry.obj), entry.start) })
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -193,23 +192,6 @@ object InfantryLoadout {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse a `Player`'s inventory and transform `Equipment` into simplified blueprints.
|
||||
* @param iter an `Iterator`
|
||||
* @param list an updating `List` of simplified `Equipment` blueprints;
|
||||
* empty, by default
|
||||
* @return a `List` of simplified `Equipment` blueprints
|
||||
*/
|
||||
@tailrec private def recursiveInventorySimplifications(iter : Iterator[InventoryItem], list : List[SimplifiedEntry] = Nil) : List[SimplifiedEntry] = {
|
||||
if(!iter.hasNext) {
|
||||
list
|
||||
}
|
||||
else {
|
||||
val entry = iter.next
|
||||
recursiveInventorySimplifications(iter, list :+ SimplifiedEntry(buildSimplification(entry.obj), entry.start))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ammunition slots are internal connection points where `AmmoBox` units and their characteristics represent a `Tool`'s magazine.
|
||||
* Their simplification process has a layer of complexity that ensures that the content of the slot matches the type of content that should be in the slot.
|
||||
|
|
@ -235,7 +217,7 @@ object InfantryLoadout {
|
|||
else {
|
||||
ShorthandAmmotSlot(
|
||||
entry.AmmoTypeIndex,
|
||||
ShorthandAmmoBox(AmmoBoxDefinition(entry.Tool.Definition.AmmoTypes(entry.Definition.AmmoTypeIndices.head).id), 1)
|
||||
ShorthandAmmoBox(entry.Tool.AmmoTypes(entry.Definition.AmmoTypeIndices.head), 1)
|
||||
)
|
||||
}
|
||||
recursiveFireModeSimplications(iter, list :+ fmodeSimp)
|
||||
|
|
@ -32,7 +32,7 @@ class Player(private val name : String,
|
|||
private var drawnSlot : Int = Player.HandsDownSlot
|
||||
private var lastDrawnSlot : Int = 0
|
||||
|
||||
private val loadouts : Array[Option[InfantryLoadout]] = Array.fill[Option[InfantryLoadout]](10)(None)
|
||||
private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None)
|
||||
|
||||
private var bep : Long = 0
|
||||
private var cep : Long = 0
|
||||
|
|
@ -230,10 +230,10 @@ class Player(private val name : String,
|
|||
}
|
||||
|
||||
def SaveLoadout(label : String, line : Int) : Unit = {
|
||||
loadouts(line) = Some(new InfantryLoadout(this, label))
|
||||
loadouts(line) = Some(new Loadout(this, label))
|
||||
}
|
||||
|
||||
def LoadLoadout(line : Int) : Option[InfantryLoadout] = loadouts(line)
|
||||
def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line)
|
||||
|
||||
def DeleteLoadout(line : Int) : Unit = {
|
||||
loadouts(line) = None
|
||||
|
|
@ -551,7 +551,7 @@ object Player {
|
|||
player.ExoSuit = eSuit
|
||||
//inventory
|
||||
player.Inventory.Clear()
|
||||
player.Inventory.Resize(esuitDef.InventoryScale.width, esuitDef.InventoryScale.height)
|
||||
player.Inventory.Resize(esuitDef.InventoryScale.Width, esuitDef.InventoryScale.Height)
|
||||
player.Inventory.Offset = esuitDef.InventoryOffset
|
||||
//holsters
|
||||
(0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) })
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.definition.{AmmoBoxDefinition, ToolDefinition}
|
||||
import net.psforever.objects.definition.ToolDefinition
|
||||
import net.psforever.objects.equipment.{Ammo, Equipment, FireModeDefinition, FireModeSwitch}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
@ -13,14 +12,15 @@ import scala.annotation.tailrec
|
|||
* "Tool" is a very mechanical name while this class is intended for various weapons and support items.
|
||||
* The primary trait of a `Tool` is that it has something that counts as an "ammunition,"
|
||||
* depleted as the `Tool` is used, replaceable as long as one has an appropriate type of `AmmoBox` object.
|
||||
* (The former is always called "consuming;" the latter, "reloading.")<br>
|
||||
* <br>
|
||||
* (The former is always called "consuming;" the latter, "reloading.")
|
||||
* Some weapons Chainblade have ammunition but do not consume it.
|
||||
* @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
|
||||
*/
|
||||
class Tool(private val toolDef : ToolDefinition) extends Equipment with FireModeSwitch[FireModeDefinition] {
|
||||
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
|
||||
private var fireModeIndex : Int = 0
|
||||
private val ammoSlot : List[Tool.FireModeSlot] = Tool.LoadDefinition(this)
|
||||
/** current ammunition slot being used by this fire mode */
|
||||
private val ammoSlots : List[Tool.FireModeSlot] = Tool.LoadDefinition(this)
|
||||
|
||||
def FireModeIndex : Int = fireModeIndex
|
||||
|
||||
|
|
@ -36,24 +36,24 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
|
|||
FireMode
|
||||
}
|
||||
|
||||
def AmmoTypeIndex : Int = ammoSlot(fireModeIndex).AmmoTypeIndex
|
||||
def AmmoTypeIndex : Int = FireMode.AmmoTypeIndices(AmmoSlot.AmmoTypeIndex)
|
||||
|
||||
def AmmoTypeIndex_=(index : Int) : Int = {
|
||||
ammoSlot(fireModeIndex).AmmoTypeIndex = index % FireMode.AmmoTypeIndices.length
|
||||
AmmoSlot.AmmoTypeIndex = index % FireMode.AmmoTypeIndices.length
|
||||
AmmoTypeIndex
|
||||
}
|
||||
|
||||
def AmmoType : Ammo.Value = toolDef.AmmoTypes(AmmoTypeIndex)
|
||||
def AmmoType : Ammo.Value = toolDef.AmmoTypes(AmmoTypeIndex).AmmoType
|
||||
|
||||
def NextAmmoType : Ammo.Value = {
|
||||
AmmoTypeIndex = AmmoTypeIndex + 1
|
||||
AmmoSlot.AmmoTypeIndex = AmmoSlot.AmmoTypeIndex + 1
|
||||
AmmoType
|
||||
}
|
||||
|
||||
def Magazine : Int = ammoSlot(fireModeIndex).Magazine
|
||||
def Magazine : Int = AmmoSlot.Magazine
|
||||
|
||||
def Magazine_=(mag : Int) : Int = {
|
||||
ammoSlot(fireModeIndex).Magazine = Math.min(Math.max(0, mag), MaxMagazine)
|
||||
AmmoSlot.Magazine = Math.min(Math.max(0, mag), MaxMagazine)
|
||||
Magazine
|
||||
}
|
||||
|
||||
|
|
@ -61,15 +61,15 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
|
|||
|
||||
def NextDischarge : Int = math.min(Magazine, FireMode.Chamber)
|
||||
|
||||
def AmmoSlots : List[Tool.FireModeSlot] = ammoSlot
|
||||
def AmmoSlot : Tool.FireModeSlot = ammoSlots(FireMode.AmmoSlotIndex)
|
||||
|
||||
def MaxAmmoSlot : Int = ammoSlot.length
|
||||
def AmmoSlots : List[Tool.FireModeSlot] = ammoSlots
|
||||
|
||||
def MaxAmmoSlot : Int = ammoSlots.length
|
||||
|
||||
def Definition : ToolDefinition = toolDef
|
||||
|
||||
override def toString : String = {
|
||||
Tool.toString(this)
|
||||
}
|
||||
override def toString : String = Tool.toString(this)
|
||||
}
|
||||
|
||||
object Tool {
|
||||
|
|
@ -77,12 +77,6 @@ object Tool {
|
|||
new Tool(toolDef)
|
||||
}
|
||||
|
||||
def apply(guid : PlanetSideGUID, toolDef : ToolDefinition) : Tool = {
|
||||
val obj = new Tool(toolDef)
|
||||
obj.GUID = guid
|
||||
obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
||||
* @param tool the `Tool` being initialized
|
||||
|
|
@ -90,10 +84,10 @@ object Tool {
|
|||
def LoadDefinition(tool : Tool) : List[FireModeSlot] = {
|
||||
val tdef : ToolDefinition = tool.Definition
|
||||
val maxSlot = tdef.FireModes.maxBy(fmode => fmode.AmmoSlotIndex).AmmoSlotIndex
|
||||
buildFireModes(tool, (0 to maxSlot).iterator, tdef.FireModes.toList)
|
||||
buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList)
|
||||
}
|
||||
|
||||
@tailrec private def buildFireModes(tool : Tool, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = {
|
||||
@tailrec private def buildFireModes(tdef : ToolDefinition, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = {
|
||||
if(!iter.hasNext) {
|
||||
list
|
||||
}
|
||||
|
|
@ -101,9 +95,9 @@ object Tool {
|
|||
val index = iter.next
|
||||
fmodes.filter(fmode => fmode.AmmoSlotIndex == index) match {
|
||||
case fmode :: _ =>
|
||||
buildFireModes(tool, iter, fmodes, list :+ new FireModeSlot(tool, fmode))
|
||||
buildFireModes(tdef, iter, fmodes, list :+ new FireModeSlot(tdef, fmode))
|
||||
case Nil =>
|
||||
throw new IllegalArgumentException(s"tool ${tool.Definition.Name} ammo slot #$index is missing a fire mode specification; do not skip")
|
||||
throw new IllegalArgumentException(s"tool ${tdef.Name} ammo slot #$index is missing a fire mode specification; do not skip")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,32 +107,45 @@ object Tool {
|
|||
}
|
||||
|
||||
/**
|
||||
* A hidden class that manages the specifics of the given ammunition for the current fire mode of this tool.
|
||||
* It operates much closer to an "ammunition feed" rather than a fire mode.
|
||||
* The relationship to fire modes is at least one-to-one and at most one-to-many.
|
||||
* The `FireModeSlot` can be called the "magazine feed," an abstracted "ammunition slot."
|
||||
* Most weapons will have only one ammunition slot and swap different ammunition into it as needed.
|
||||
* In general to swap ammunition means to unload the onld ammunition and load the new ammunition.
|
||||
* Many weapons also have one ammunition slot and multiple fire modes using the same list of ammunition
|
||||
* This slot manages either of two ammunitions where one does not need to unload to be swapped to the other;
|
||||
* however, the fire mod has most likely been changed.
|
||||
* The Punisher -
|
||||
* six ammunition types in total,
|
||||
* two uniquely different types without unloading,
|
||||
* two exclusive groups of ammunition divided into 2 cycled types and 4 cycled types -
|
||||
* is an example of a weapon that benefits from this implementation.
|
||||
*/
|
||||
class FireModeSlot(private val tool : Tool, private val fdef : FireModeDefinition) {
|
||||
/*
|
||||
By way of demonstration:
|
||||
Suppressors have one fire mode, two types of ammunition, one slot (2)
|
||||
MA Pistols have two fire modes, one type of ammunition, one slot (1)
|
||||
Jackhammers have two fire modes, two types of ammunition, one slot (2)
|
||||
Punishers have two fire modes, five types of ammunition, two slots (2, 3)
|
||||
*/
|
||||
|
||||
/** if this fire mode has multiple types of ammunition */
|
||||
private var ammoTypeIndex : Int = fdef.AmmoTypeIndices.head
|
||||
/** a reference to the actual `AmmoBox` of this slot; will not synch up with `AmmoType` immediately */
|
||||
private var box : AmmoBox = AmmoBox(AmmoBoxDefinition(AmmoType)) //defaults to box of one round of the default type for this slot
|
||||
class FireModeSlot(private val tdef : ToolDefinition, private val fdef : FireModeDefinition) {
|
||||
/**
|
||||
* if this fire mode has multiple types of ammunition
|
||||
* this is the index of the fire mode's ammo List, not a reference to the tool's ammo List
|
||||
*/
|
||||
private var ammoTypeIndex : Int = 0
|
||||
/** a reference to the actual `AmmoBox` of this slot */
|
||||
private var box : AmmoBox = AmmoBox(tdef.AmmoTypes(ammoTypeIndex), fdef.Magazine)
|
||||
|
||||
def AmmoTypeIndex : Int = ammoTypeIndex
|
||||
|
||||
def AmmoTypeIndex_=(index : Int) : Int = {
|
||||
ammoTypeIndex = index
|
||||
ammoTypeIndex = index % fdef.AmmoTypeIndices.length
|
||||
AmmoTypeIndex
|
||||
}
|
||||
|
||||
def AmmoType : Ammo.Value = tool.Definition.AmmoTypes(ammoTypeIndex)
|
||||
/**
|
||||
* This is a reference to the `Ammo.Value` whose `AmmoBoxDefinition` should be loaded into `box`.
|
||||
* It may not be the correct `Ammo.Value` whose `AmmoBoxDefinition` is loaded into `box` such as is the case during ammunition swaps.
|
||||
* Generally, convert from this index, to the index in the fire mode's ammunition list, to the index in the `ToolDefinition`'s ammunition list.
|
||||
* @return the `Ammo` type that should be loaded into the magazine right now
|
||||
*/
|
||||
def AmmoType : Ammo.Value = tdef.AmmoTypes(fdef.AmmoTypeIndices(ammoTypeIndex)).AmmoType
|
||||
|
||||
def AllAmmoTypes : List[Ammo.Value] = {
|
||||
fdef.AmmoTypeIndices.map(index => tdef.AmmoTypes(fdef.AmmoTypeIndices(index)).AmmoType).toList
|
||||
}
|
||||
|
||||
def Magazine : Int = box.Capacity
|
||||
|
||||
|
|
@ -159,7 +166,7 @@ object Tool {
|
|||
}
|
||||
}
|
||||
|
||||
def Tool : Tool = tool
|
||||
def Tool : ToolDefinition = tdef
|
||||
|
||||
def Definition : FireModeDefinition = fdef
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects
|
|||
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
|
||||
import net.psforever.objects.inventory.GridInventory
|
||||
import net.psforever.objects.inventory.{GridInventory, InventoryTile}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -480,8 +480,12 @@ object Vehicle {
|
|||
vehicle.Utilities += Utility.Select(i, vehicle)
|
||||
}
|
||||
//trunk
|
||||
vehicle.trunk.Resize(vdef.TrunkSize.width, vdef.TrunkSize.height)
|
||||
vehicle.trunk.Offset = vdef.TrunkOffset
|
||||
vdef.TrunkSize match {
|
||||
case InventoryTile.None => ;
|
||||
case dim =>
|
||||
vehicle.trunk.Resize(dim.Width, dim.Height)
|
||||
vehicle.trunk.Offset = vdef.TrunkOffset
|
||||
}
|
||||
vehicle
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.definition.converter.ToolConverter
|
||||
import net.psforever.objects.equipment.{Ammo, FireModeDefinition}
|
||||
import net.psforever.objects.equipment.FireModeDefinition
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
|
||||
private val ammoTypes : mutable.ListBuffer[Ammo.Value] = new mutable.ListBuffer[Ammo.Value]
|
||||
private val ammoTypes : mutable.ListBuffer[AmmoBoxDefinition] = new mutable.ListBuffer[AmmoBoxDefinition]
|
||||
private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition]
|
||||
Name = "tool"
|
||||
Packet = new ToolConverter()
|
||||
|
||||
def AmmoTypes : mutable.ListBuffer[Ammo.Value] = ammoTypes
|
||||
def AmmoTypes : mutable.ListBuffer[AmmoBoxDefinition] = ammoTypes
|
||||
|
||||
def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ object Ammo extends Enumeration {
|
|||
final val fluxpod_ammo = Value(310)
|
||||
final val frag_cartridge = Value(327)
|
||||
final val frag_grenade_ammo = Value(331)
|
||||
final val gauss_cannon_ammo = Value(345)
|
||||
final val gauss_cannon_ammo = Value(347)
|
||||
final val grenade = Value(370)
|
||||
final val health_canister = Value(389)
|
||||
final val heavy_grenade_mortar = Value(391)
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ trait FireModeSwitch[Mode] {
|
|||
def FireMode : Mode
|
||||
|
||||
def NextFireMode : Mode
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class GridInventory {
|
|||
* @return the number of free cells
|
||||
*/
|
||||
def Capacity : Int = {
|
||||
TotalCapacity - items.values.foldLeft(0)((cnt, item) => cnt + (item.obj.Tile.width * item.obj.Tile.height))
|
||||
TotalCapacity - items.values.foldLeft(0)((cnt, item) => cnt + (item.obj.Tile.Width * item.obj.Tile.Height))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,7 +112,7 @@ class GridInventory {
|
|||
*/
|
||||
def CheckCollisions(start : Int, item : Equipment) : Try[List[Int]] = {
|
||||
val tile : InventoryTile = item.Tile
|
||||
CheckCollisions(start, tile.width, tile.height)
|
||||
CheckCollisions(start, tile.Width, tile.Height)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -185,8 +185,8 @@ class GridInventory {
|
|||
val itemx : Int = actualItemStart % width
|
||||
val itemy : Int = actualItemStart / width
|
||||
val tile = item.obj.Tile
|
||||
val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.width > startx } else { itemx <= startw }
|
||||
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 }
|
||||
val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.Height > starty } else { itemy <= starth }
|
||||
if(clipsOnX && clipsOnY) {
|
||||
collisions += item
|
||||
}
|
||||
|
|
@ -237,8 +237,8 @@ class GridInventory {
|
|||
* @return the grid index of the upper left corner where equipment to which the `tile` belongs should be placed
|
||||
*/
|
||||
def Fit(tile : InventoryTile) : Option[Int] = {
|
||||
val tWidth = tile.width
|
||||
val tHeight = tile.height
|
||||
val tWidth = tile.Width
|
||||
val tHeight = tile.Height
|
||||
val gridIter = (0 until (grid.length - (tHeight - 1) * width))
|
||||
.filter(cell => grid(cell) == -1 && (width - cell%width >= tWidth))
|
||||
.iterator
|
||||
|
|
@ -325,7 +325,7 @@ class GridInventory {
|
|||
val card = InventoryItem(obj, start)
|
||||
items += key -> card
|
||||
val tile = obj.Tile
|
||||
SetCells(start, tile.width, tile.height, key)
|
||||
SetCells(start, tile.Width, tile.Height, key)
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
|
|
@ -348,7 +348,7 @@ class GridInventory {
|
|||
items.remove(key) match {
|
||||
case Some(item) =>
|
||||
val tile = item.obj.Tile
|
||||
SetCells(item.start, tile.width, tile.height)
|
||||
SetCells(item.start, tile.Width, tile.Height)
|
||||
true
|
||||
case None =>
|
||||
false
|
||||
|
|
@ -362,7 +362,7 @@ class GridInventory {
|
|||
case Some(index) =>
|
||||
val item = items.remove(index).get
|
||||
val tile = item.obj.Tile
|
||||
SetCells(item.start, tile.width, tile.height)
|
||||
SetCells(item.start, tile.Width, tile.Height)
|
||||
true
|
||||
case None =>
|
||||
false
|
||||
|
|
@ -492,11 +492,11 @@ object GridInventory {
|
|||
(a, b) => {
|
||||
val aTile = a.obj.Tile
|
||||
val bTile = b.obj.Tile
|
||||
if(aTile.width == bTile.width) {
|
||||
aTile.height > bTile.height
|
||||
if(aTile.Width == bTile.Width) {
|
||||
aTile.Height > bTile.Height
|
||||
}
|
||||
else {
|
||||
aTile.width > bTile.width
|
||||
aTile.Width > bTile.Width
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -513,9 +513,9 @@ object GridInventory {
|
|||
private def sortKnapsack(list : List[InventoryItem], width : Int, height : Int) : Unit = {
|
||||
val root = new KnapsackNode(0, 0, width, height)
|
||||
list.foreach(item => {
|
||||
findKnapsackSpace(root, item.obj.Tile.width, item.obj.Tile.height) match {
|
||||
findKnapsackSpace(root, item.obj.Tile.Width, item.obj.Tile.Height) match {
|
||||
case Some(node) =>
|
||||
splitKnapsackSpace(node, item.obj.Tile.width, item.obj.Tile.height)
|
||||
splitKnapsackSpace(node, item.obj.Tile.Width, item.obj.Tile.Height)
|
||||
item.start = node.y * width + node.x
|
||||
case _ => ;
|
||||
item.start = -1
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ package net.psforever.objects.inventory
|
|||
* @param height the height of the tile
|
||||
* @throws IllegalArgumentException if either the width or the height are less than zero
|
||||
*/
|
||||
class InventoryTile(val width : Int, val height : Int) {
|
||||
class InventoryTile(private val width : Int, private val height : Int) {
|
||||
if(width < 0 || height < 0)
|
||||
throw new IllegalArgumentException(s"tile has no area - width: $width, height: $height")
|
||||
|
||||
|
|
@ -19,15 +19,27 @@ class InventoryTile(val width : Int, val height : Int) {
|
|||
|
||||
object InventoryTile {
|
||||
final val None = InventoryTile(0,0) //technically invalid; used to indicate a vehicle with no trunk
|
||||
final val Tile11 = InventoryTile(1,1) //placeholder size
|
||||
final val Tile11 = InventoryTile(1,1) //occasional placeholder
|
||||
final val Tile22 = InventoryTile(2,2) //grenades, boomer trigger
|
||||
final val Tile23 = InventoryTile(2,3) //canister ammo
|
||||
final val Tile42 = InventoryTile(4,2) //medkit
|
||||
final val Tile33 = InventoryTile(3,3) //ammo box, pistols, ace
|
||||
final val Tile44 = InventoryTile(4,4) //large ammo box
|
||||
final val Tile55 = InventoryTile(5,5) //bfr ammo box
|
||||
final val Tile66 = InventoryTile(6,6) //standard assault inventory
|
||||
final val Tile63 = InventoryTile(6,3) //rifles
|
||||
final val Tile93 = InventoryTile(9,3) //long-body weapons
|
||||
final val Tile96 = InventoryTile(9,6) //standard exo-suit
|
||||
final val Tile99 = InventoryTile(9,9) //agile exo-suit
|
||||
final val Tile1107 = InventoryTile(11, 7) //uncommon small trunk capacity - phantasm
|
||||
final val Tile1111 = InventoryTile(11,11) //common small trunk capacity
|
||||
final val Tile1209 = InventoryTile(12, 9) //reinforced exo-suit
|
||||
final val Tile1511 = InventoryTile(15,11) //common medium trunk capacity
|
||||
final val Tile1515 = InventoryTile(15,15) //common large trunk capacity
|
||||
final val Tile1611 = InventoryTile(16,11) //uncommon medium trunk capacity - vulture
|
||||
final val Tile1612 = InventoryTile(16,12) //MAX; uncommon medium trunk capacity - lodestar
|
||||
final val Tile1816 = InventoryTile(18,16) //uncommon massive trunk capacity - galaxy_gunship
|
||||
final val Tile2016 = InventoryTile(20,16) //uncommon massive trunk capacity - apc
|
||||
|
||||
def apply(w : Int, h : Int) : InventoryTile = {
|
||||
new InventoryTile(w, h)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ abstract class PlanetSideServerObject extends PlanetSideGameObject {
|
|||
* @return the current internal `ActorRef`
|
||||
*/
|
||||
def Actor_=(control : ActorRef) : ActorRef = {
|
||||
if(actor == ActorRef.noSender) {
|
||||
if(actor == ActorRef.noSender || control == ActorRef.noSender) {
|
||||
actor = control
|
||||
}
|
||||
actor
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
/**
|
||||
* Wrapper `Class` designed to instantiate a `VehicleSpawnPad` server object.
|
||||
* @param spdef an `ObjectDefinition` object ...
|
||||
* @param id the globally unique identifier to which this `VehicleSpawnPad` will be registered
|
||||
*/
|
||||
class VehicleSpawnPadObjectBuilder(private val spdef : ObjectDefinition, private val id : Int) extends ServerObjectBuilder[VehicleSpawnPad] {
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
def Build(implicit context : ActorContext, guid : NumberPoolHub) : VehicleSpawnPad = {
|
||||
val obj = VehicleSpawnPad(spdef)
|
||||
guid.register(obj, id) //non-Actor GUID registration
|
||||
obj.Actor = context.actorOf(Props(classOf[VehicleSpawnControl], obj), s"${spdef.Name}_${obj.GUID.guid}")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnPadObjectBuilder {
|
||||
/**
|
||||
* Overloaded constructor for a `DoorObjectBuilder`.
|
||||
* @param spdef an `ObjectDefinition` object
|
||||
* @param id a globally unique identifier
|
||||
* @return a `VehicleSpawnPadObjectBuilder` object
|
||||
*/
|
||||
def apply(spdef : ObjectDefinition, id : Int) : VehicleSpawnPadObjectBuilder = {
|
||||
new VehicleSpawnPadObjectBuilder(spdef, id)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, Player, Vehicle}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `VehicleSpawnPad`.<br>
|
||||
* <br>
|
||||
* A spawn pad receives vehicle orders from an attached `Terminal` object.
|
||||
* At the time when the order is received, the player who submitted the order is completely visible
|
||||
* and waiting back by the said `Terminal` from where the order was submitted.
|
||||
* Assuming no other orders are currently being processed, the repeated self message will retrieve this as the next order.
|
||||
* The player character is first made transparent with a `GenericObjectActionMessage` packet.
|
||||
* The vehicle model itself is then introduced to the game and three things happen with the following order, more or less:<br>
|
||||
* 1. the vehicle is attached to a lifting platform that is designed to introduce the vehicle;<br>
|
||||
* 2. the player is seated in the vehicle's driver seat (seat 0) and is thus declared the owner; <br>
|
||||
* 3. various properties of the player, the vehicle, and the spawn pad itself are set `PlanetsideAttributesMessage`.<br>
|
||||
* When this step is finished, the lifting platform raises the vehicle and the mounted player into the game world.
|
||||
* The vehicle detaches and is made to roll off the spawn pad a certain distance before being released to user control.
|
||||
* That is what is supposed to happen within a certain measure of timing.<br>
|
||||
* <br>
|
||||
* The orders that are submitted to the spawn pad must be composed of at least three elements:
|
||||
* 1. a player, specifically the one that submitted the order and will be declared the "owner;"
|
||||
* 2. a vehicle;
|
||||
* 3. a callback location for sending messages.
|
||||
* @param pad the `VehicleSpawnPad` object being governed
|
||||
*/
|
||||
class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor {
|
||||
/** an executor for progressing a vehicle order through the normal spawning logic */
|
||||
private var process : Cancellable = DefaultCancellable.obj
|
||||
/** a list of vehicle orders that have been submitted for this spawn pad */
|
||||
private var orders : List[VehicleSpawnControl.OrderEntry] = List.empty[VehicleSpawnControl.OrderEntry]
|
||||
/** the current vehicle order being acted upon */
|
||||
private var trackedOrder : Option[VehicleSpawnControl.OrderEntry] = None
|
||||
/** how many times a spawned vehicle (spatially) disrupted the next vehicle from being spawned */
|
||||
private var blockingViolations : Int = 0
|
||||
private[this] val log = org.log4s.getLogger
|
||||
private[this] def trace(msg : String) : Unit = log.trace(msg)
|
||||
|
||||
|
||||
def receive : Receive = {
|
||||
case VehicleSpawnPad.VehicleOrder(player, vehicle) =>
|
||||
trace(s"order from $player for $vehicle received")
|
||||
orders = orders :+ VehicleSpawnControl.OrderEntry(player, vehicle, sender)
|
||||
if(trackedOrder.isEmpty && orders.length == 1) {
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.GetOrder =>
|
||||
process.cancel
|
||||
blockingViolations = 0
|
||||
val (completeOrder, remainingOrders) : (Option[VehicleSpawnControl.OrderEntry], List[VehicleSpawnControl.OrderEntry]) = orders match {
|
||||
case x :: Nil =>
|
||||
(Some(x), Nil)
|
||||
case x :: b =>
|
||||
(Some(x), b)
|
||||
case Nil =>
|
||||
(None, Nil)
|
||||
}
|
||||
orders = remainingOrders
|
||||
completeOrder match {
|
||||
case Some(entry) =>
|
||||
trace(s"processing order $entry")
|
||||
trackedOrder = completeOrder
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.concealPlayerTimeout, self, VehicleSpawnControl.Process.ConcealPlayer)
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.ConcealPlayer =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.player.isAlive && entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender && entry.player.VehicleSeated.isEmpty) {
|
||||
trace(s"hiding player: ${entry.player}")
|
||||
entry.sendTo ! VehicleSpawnPad.ConcealPlayer
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.loadVehicleTimeout, self, VehicleSpawnControl.Process.LoadVehicle)
|
||||
}
|
||||
else {
|
||||
trace("integral component lost; abort order fulfillment")
|
||||
//TODO Unregister vehicle ... somehow
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.LoadVehicle =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender) {
|
||||
trace(s"loading vehicle: ${entry.vehicle} defined in order")
|
||||
entry.sendTo ! VehicleSpawnPad.LoadVehicle(entry.vehicle, pad)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated)
|
||||
}
|
||||
else {
|
||||
trace("owner or vehicle lost; abort order fulfillment")
|
||||
//TODO Unregister vehicle ... somehow
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case VehicleSpawnControl.Process.AwaitSeated =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.sendTo != ActorRef.noSender) {
|
||||
trace("owner seated in vehicle")
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = if(entry.player.VehicleOwned.contains(entry.vehicle.GUID)) {
|
||||
entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle)
|
||||
context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance)
|
||||
}
|
||||
else {
|
||||
context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated)
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace("owner lost; abort order fulfillment")
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
//TODO raise spawn pad rails from ground
|
||||
|
||||
//TODO start auto drive away
|
||||
|
||||
//TODO release auto drive away
|
||||
|
||||
case VehicleSpawnControl.Process.AwaitClearance =>
|
||||
process.cancel
|
||||
trackedOrder match {
|
||||
case Some(entry) =>
|
||||
if(entry.sendTo == ActorRef.noSender || entry.vehicle.Actor == ActorRef.noSender) {
|
||||
trace("integral component lost, but order fulfilled; process next order")
|
||||
trackedOrder = None
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
else if(Vector3.DistanceSquared(entry.vehicle.Position, pad.Position) > 100.0f) { //10m away from pad
|
||||
trace("pad cleared; process next order")
|
||||
trackedOrder = None
|
||||
entry.sendTo ! VehicleSpawnPad.SpawnPadUnblocked(entry.vehicle.GUID)
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
else {
|
||||
trace(s"pad blocked by ${entry.vehicle} ...")
|
||||
blockingViolations += 1
|
||||
entry.sendTo ! VehicleSpawnPad.SpawnPadBlockedWarning(entry.vehicle, blockingViolations)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance)
|
||||
}
|
||||
case None =>
|
||||
self ! VehicleSpawnControl.Process.GetOrder
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleSpawnControl {
|
||||
final val concealPlayerTimeout : FiniteDuration = 2000000000L nanoseconds //2s
|
||||
final val loadVehicleTimeout : FiniteDuration = 1000000000L nanoseconds //1s
|
||||
final val awaitSeatedTimeout : FiniteDuration = 1000000000L nanoseconds //1s
|
||||
final val awaitClearanceTimeout : FiniteDuration = 5000000000L nanoseconds //5s
|
||||
|
||||
/**
|
||||
* An `Enumeration` of the stages of a full vehicle spawning process, associated with certain messages passed.
|
||||
* Some stages are currently TEMPORARY.
|
||||
* @see VehicleSpawnPad
|
||||
*/
|
||||
object Process extends Enumeration {
|
||||
val
|
||||
GetOrder,
|
||||
ConcealPlayer,
|
||||
LoadVehicle,
|
||||
AwaitSeated,
|
||||
AwaitClearance
|
||||
= Value
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry that stores vehicle spawn pad spawning tasks (called "orders").
|
||||
* @param player the player
|
||||
* @param vehicle the vehicle
|
||||
* @param sendTo a callback `Actor` associated with the player (in other words, `WorldSessionActor`)
|
||||
*/
|
||||
private final case class OrderEntry(player : Player, vehicle : Vehicle, sendTo : ActorRef)
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.pad
|
||||
|
||||
import net.psforever.objects.{Player, Vehicle}
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
/**
|
||||
* A structure-owned server object that is a "spawn pad" for vehicles.<br>
|
||||
* <br>
|
||||
* Spawn pads have no purpose on their own but
|
||||
* maintain the operative queue that introduces the vehicle into the game world and applies initial activity to it and
|
||||
* maintain a position and a direction where the vehicle will be made to appear (as a `PlanetSideServerObject`).
|
||||
* The actual functionality managed by this object is wholly found on its accompanying `Actor`.
|
||||
* @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
* @see `VehicleSpawnControl`
|
||||
*/
|
||||
class VehicleSpawnPad(spDef : ObjectDefinition) extends PlanetSideServerObject {
|
||||
def Definition : ObjectDefinition = spDef
|
||||
}
|
||||
|
||||
object VehicleSpawnPad {
|
||||
|
||||
/**
|
||||
* Communicate to the spawn pad that it should enqueue the following vehicle.
|
||||
* This is the entry point to vehicle spawn pad functionality.
|
||||
* @param player the player who submitted the order (the "owner")
|
||||
* @param vehicle the vehicle produced from the order
|
||||
*/
|
||||
final case class VehicleOrder(player : Player, vehicle : Vehicle)
|
||||
|
||||
/**
|
||||
* The first callback step in spawning the vehicle.
|
||||
* An packet `GenericObjectActionMessage(/player/, 36)`, when used on a player character,
|
||||
* will cause that player character's model to fade into transparency.
|
||||
*/
|
||||
final case class ConcealPlayer()
|
||||
|
||||
/**
|
||||
* A callback step in spawning the vehicle.
|
||||
* The vehicle is properly introduced into the game world.
|
||||
* If information about the vehicle itself that is important to its spawning has not yet been set,
|
||||
* this callback is the last ideal situation to set that properties without having to adjust the vehicle visually.
|
||||
* The primary operation that should occur is a content-appropriate `ObjectCreateMessage` packet and
|
||||
* having the player sit down in the driver's seat (seat 0) of the vehicle.
|
||||
* @param vehicle the vehicle being spawned
|
||||
* @param pad the pad
|
||||
*/
|
||||
final case class LoadVehicle(vehicle : Vehicle, pad : VehicleSpawnPad)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in spawning the vehicle.
|
||||
* From a state of transparency, while the vehicle is attached to the lifting platform of the spawn pad,
|
||||
* the player designated the "owner" by callback is made to sit in the driver's seat (always seat 0).
|
||||
* This message is the next step after that.
|
||||
* @param vehicle the vehicle being spawned
|
||||
*/
|
||||
final case class PlayerSeatedInVehicle(vehicle : Vehicle)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in (successfully) spawning the vehicle.
|
||||
* While the vehicle is still occupying the pad just after being spawned and its driver seat mounted,
|
||||
* that vehicle is considered blocking the pad from being used for further spawning operations.
|
||||
* This message allows the user to be made known about this blockage.
|
||||
* @param vehicle the vehicle
|
||||
* @param warning_count the number of times a warning period has occurred
|
||||
*/
|
||||
final case class SpawnPadBlockedWarning(vehicle : Vehicle, warning_count : Int)
|
||||
|
||||
/**
|
||||
* A TEMPORARY callback step in (successfully) spawning the vehicle.
|
||||
* While the vehicle is still occupying the pad just after being spawned and its driver seat mounted,
|
||||
* that vehicle is considered blocking the pad from being used for further spawning operations.
|
||||
* A timeout will begin counting until the vehicle is despawned automatically for its driver's negligence.
|
||||
* This message is used to clear the deconstruction countdown, primarily.
|
||||
* @param vehicle_guid the vehicle
|
||||
*/
|
||||
final case class SpawnPadUnblocked(vehicle_guid : PlanetSideGUID)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param spDef the spawn pad's definition entry
|
||||
* @return a `VehicleSpawnPad` object
|
||||
*/
|
||||
def apply(spDef : ObjectDefinition) : VehicleSpawnPad = {
|
||||
new VehicleSpawnPad(spDef)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class AirVehicleTerminalDefinition extends TerminalDefinition(43) {
|
||||
Name = "air_vehicle_terminal"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
flight1Vehicles.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
Terminal.BuyVehicle(vehicle(), Nil)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class BFRTerminalDefinition extends TerminalDefinition(143) {
|
||||
Name = "bfr_terminal"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
bfrVehicles.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
//Terminal.BuyVehicle(vehicle, Nil)
|
||||
Terminal.NoDeal()
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ class CertTerminalDefinition extends TerminalDefinition(171) {
|
|||
* @param msg the original packet carrying the request
|
||||
* @return an actionable message that explains how to process the request
|
||||
*/
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
certificationList.get(msg.item_name) match {
|
||||
case Some((cert, cost)) =>
|
||||
Terminal.SellCertification(cert, cost)
|
||||
|
|
@ -89,12 +89,4 @@ class CertTerminalDefinition extends TerminalDefinition(171) {
|
|||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This action is not supported by this type of `Terminal`.
|
||||
* @param player the player
|
||||
* @param msg the original packet carrying the request
|
||||
* @return `Terminal.NoEvent` always
|
||||
*/
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class DropshipVehicleTerminalDefinition extends TerminalDefinition(263) {
|
||||
private val flightVehicles = flight1Vehicles ++ flight2Vehicles
|
||||
Name = "dropship_vehicle_terminal"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
flightVehicles.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
Terminal.BuyVehicle(vehicle(), Nil)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class GroundVehicleTerminalDefinition extends TerminalDefinition(386) {
|
||||
Name = "ground_vehicle_terminal"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
groundVehicles.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
Terminal.BuyVehicle(vehicle(), Nil)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.InfantryLoadout.Simplification
|
||||
import net.psforever.objects.Loadout.Simplification
|
||||
import net.psforever.objects.{Player, Tool}
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
|
|
@ -13,7 +13,7 @@ import scala.annotation.switch
|
|||
/**
|
||||
* The definition for any `Terminal` that is of a type "order_terminal".
|
||||
* `Buy` and `Sell` `Equipment` items and `AmmoBox` items.
|
||||
* Change `ExoSuitType` and retrieve `InfantryLoadout` entries.
|
||||
* Change `ExoSuitType` and retrieve `Loadout` entries.
|
||||
*/
|
||||
class OrderTerminalDefinition extends TerminalDefinition(612) {
|
||||
Name = "order_terminal"
|
||||
|
|
@ -75,19 +75,19 @@ class OrderTerminalDefinition extends TerminalDefinition(612) {
|
|||
* @param msg the original packet carrying the request
|
||||
* @return an actionable message that explains how to process the request
|
||||
*/
|
||||
def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
Terminal.SellEquipment()
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a `TransactionType.InfantryLoadout` action by the user.
|
||||
* `InfantryLoadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings.
|
||||
* `Loadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings.
|
||||
* If a valid loadout is found, its data is transformed back into actual `Equipment` for return to the user.
|
||||
* @param player the player
|
||||
* @param msg the original packet carrying the request
|
||||
* @return an actionable message that explains how to process the request
|
||||
*/
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
if(msg.item_page == 4) { //Favorites tab
|
||||
player.LoadLoadout(msg.unk1) match {
|
||||
case Some(loadout) =>
|
||||
|
|
@ -105,7 +105,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) {
|
|||
|
||||
/**
|
||||
* Accept a simplified blueprint for some piece of `Equipment` and create an actual piece of `Equipment` based on it.
|
||||
* Used specifically for the reconstruction of `Equipment` via an `InfantryLoadout`.
|
||||
* Used specifically for the reconstruction of `Equipment` via an `Loadout`.
|
||||
* @param entry the simplified blueprint
|
||||
* @return some `Equipment` object
|
||||
* @see `TerminalDefinition.MakeTool`<br>
|
||||
|
|
@ -115,7 +115,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) {
|
|||
* `TerminalDefinition.MakeKit`
|
||||
*/
|
||||
private def BuildSimplifiedPattern(entry : Simplification) : Equipment = {
|
||||
import net.psforever.objects.InfantryLoadout._
|
||||
import net.psforever.objects.Loadout._
|
||||
entry match {
|
||||
case obj : ShorthandTool =>
|
||||
val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef })
|
||||
|
|
|
|||
|
|
@ -136,10 +136,24 @@ object Terminal {
|
|||
final case class SellEquipment() extends Exchange
|
||||
|
||||
import net.psforever.types.CertificationType
|
||||
|
||||
/**
|
||||
* Provide the certification type unlocked by the player.
|
||||
* @param cert the certification unlocked
|
||||
* @param cost the certification point cost
|
||||
*/
|
||||
final case class LearnCertification(cert : CertificationType.Value, cost : Int) extends Exchange
|
||||
|
||||
/**
|
||||
* Provide the certification type freed-up by the player.
|
||||
* @param cert the certification returned
|
||||
* @param cost the certification point cost
|
||||
*/
|
||||
final case class SellCertification(cert : CertificationType.Value, cost : Int) extends Exchange
|
||||
|
||||
import net.psforever.objects.Vehicle
|
||||
final case class BuyVehicle(vehicle : Vehicle, loadout: List[Any]) extends Exchange
|
||||
|
||||
/**
|
||||
* Recover a former exo-suit and `Equipment` configuration that the `Player` possessed.
|
||||
* A result of a processed request.
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ import net.psforever.packet.game.ItemTransactionMessage
|
|||
import net.psforever.types.ExoSuitType
|
||||
|
||||
/**
|
||||
* The definition for any `Terminal`.
|
||||
* The basic definition for any `Terminal`.
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||
private[this] val log = org.log4s.getLogger("TerminalDefinition")
|
||||
Name = "terminal"
|
||||
|
||||
/**
|
||||
|
|
@ -22,12 +23,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
/**
|
||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.Sell` activity.
|
||||
*/
|
||||
def Sell(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
||||
def Sell(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
|
||||
/**
|
||||
* The unimplemented functionality for this `Terminal`'s `TransactionType.InfantryLoadout` activity.
|
||||
*/
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange
|
||||
def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()
|
||||
|
||||
/**
|
||||
* A `Map` of information for changing exo-suits.
|
||||
|
|
@ -128,42 +129,42 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val infantryWeapons : Map[String, ()=>Equipment] = Map(
|
||||
"ilc9" -> MakeTool(ilc9, bullet_9mm),
|
||||
"repeater" -> MakeTool(repeater, bullet_9mm),
|
||||
"isp" -> MakeTool(isp, shotgun_shell), //amp
|
||||
"beamer" -> MakeTool(beamer, energy_cell),
|
||||
"suppressor" -> MakeTool(suppressor, bullet_9mm),
|
||||
"anniversary_guna" -> MakeTool(anniversary_guna, anniversary_ammo), //tr stinger
|
||||
"anniversary_gun" -> MakeTool(anniversary_gun, anniversary_ammo), //nc spear
|
||||
"anniversary_gunb" -> MakeTool(anniversary_gunb, anniversary_ammo), //vs eraser
|
||||
"cycler" -> MakeTool(cycler, bullet_9mm),
|
||||
"gauss" -> MakeTool(gauss, bullet_9mm),
|
||||
"pulsar" -> MakeTool(pulsar, energy_cell),
|
||||
"punisher" -> MakeTool(punisher, List(bullet_9mm, rocket)),
|
||||
"flechette" -> MakeTool(flechette, shotgun_shell),
|
||||
"spiker" -> MakeTool(spiker, ancient_ammo_combo),
|
||||
"frag_grenade" -> MakeTool(frag_grenade, frag_grenade_ammo),
|
||||
"jammer_grenade" -> MakeTool(jammer_grenade, jammer_grenade_ammo),
|
||||
"plasma_grenade" -> MakeTool(plasma_grenade, plasma_grenade_ammo),
|
||||
"katana" -> MakeTool(katana, melee_ammo),
|
||||
"chainblade" -> MakeTool(chainblade, melee_ammo),
|
||||
"magcutter" -> MakeTool(magcutter, melee_ammo),
|
||||
"forceblade" -> MakeTool(forceblade, melee_ammo),
|
||||
"mini_chaingun" -> MakeTool(mini_chaingun, bullet_9mm),
|
||||
"r_shotgun" -> MakeTool(r_shotgun, shotgun_shell), //jackhammer
|
||||
"lasher" -> MakeTool(lasher, energy_cell),
|
||||
"maelstrom" -> MakeTool(maelstrom, maelstrom_ammo),
|
||||
"striker" -> MakeTool(striker, striker_missile_ammo),
|
||||
"hunterseeker" -> MakeTool(hunterseeker, hunter_seeker_missile), //phoenix
|
||||
"lancer" -> MakeTool(lancer, lancer_cartridge),
|
||||
"phoenix" -> MakeTool(phoenix, phoenix_missile), //decimator
|
||||
"rocklet" -> MakeTool(rocklet, rocket),
|
||||
"thumper" -> MakeTool(thumper, frag_cartridge),
|
||||
"radiator" -> MakeTool(radiator, ancient_ammo_combo),
|
||||
"heavy_sniper" -> MakeTool(heavy_sniper, bolt), //hsr
|
||||
"bolt_driver" -> MakeTool(bolt_driver, bolt),
|
||||
"oicw" -> MakeTool(oicw, oicw_ammo), //scorpion
|
||||
"flamethrower" -> MakeTool(flamethrower, flamethrower_ammo)
|
||||
"ilc9" -> MakeTool(ilc9),
|
||||
"repeater" -> MakeTool(repeater),
|
||||
"isp" -> MakeTool(isp), //amp
|
||||
"beamer" -> MakeTool(beamer),
|
||||
"suppressor" -> MakeTool(suppressor),
|
||||
"anniversary_guna" -> MakeTool(anniversary_guna), //tr stinger
|
||||
"anniversary_gun" -> MakeTool(anniversary_gun), //nc spear
|
||||
"anniversary_gunb" -> MakeTool(anniversary_gunb), //vs eraser
|
||||
"cycler" -> MakeTool(cycler),
|
||||
"gauss" -> MakeTool(gauss),
|
||||
"pulsar" -> MakeTool(pulsar),
|
||||
"punisher" -> MakeTool(punisher),
|
||||
"flechette" -> MakeTool(flechette),
|
||||
"spiker" -> MakeTool(spiker),
|
||||
"frag_grenade" -> MakeTool(frag_grenade),
|
||||
"jammer_grenade" -> MakeTool(jammer_grenade),
|
||||
"plasma_grenade" -> MakeTool(plasma_grenade),
|
||||
"katana" -> MakeTool(katana),
|
||||
"chainblade" -> MakeTool(chainblade),
|
||||
"magcutter" -> MakeTool(magcutter),
|
||||
"forceblade" -> MakeTool(forceblade),
|
||||
"mini_chaingun" -> MakeTool(mini_chaingun),
|
||||
"r_shotgun" -> MakeTool(r_shotgun), //jackhammer
|
||||
"lasher" -> MakeTool(lasher),
|
||||
"maelstrom" -> MakeTool(maelstrom),
|
||||
"striker" -> MakeTool(striker),
|
||||
"hunterseeker" -> MakeTool(hunterseeker), //phoenix
|
||||
"lancer" -> MakeTool(lancer),
|
||||
"phoenix" -> MakeTool(phoenix), //decimator
|
||||
"rocklet" -> MakeTool(rocklet),
|
||||
"thumper" -> MakeTool(thumper),
|
||||
"radiator" -> MakeTool(radiator),
|
||||
"heavy_sniper" -> MakeTool(heavy_sniper), //hsr
|
||||
"bolt_driver" -> MakeTool(bolt_driver),
|
||||
"oicw" -> MakeTool(oicw), //scorpion
|
||||
"flamethrower" -> MakeTool(flamethrower)
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -176,20 +177,98 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
"super_medkit" -> MakeKit(super_medkit),
|
||||
"super_armorkit" -> MakeKit(super_armorkit),
|
||||
"super_staminakit" -> MakeKit(super_staminakit),
|
||||
"medicalapplicator" -> MakeTool(medicalapplicator, health_canister),
|
||||
"medicalapplicator" -> MakeTool(medicalapplicator),
|
||||
"bank" -> MakeTool(bank, armor_canister),
|
||||
"nano_dispenser" -> MakeTool(nano_dispenser, armor_canister),
|
||||
"nano_dispenser" -> MakeTool(nano_dispenser),
|
||||
//TODO "ace" -> MakeConstructionItem(ace),
|
||||
//TODO "advanced_ace" -> MakeConstructionItem(advanced_ace),
|
||||
"remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
|
||||
"trek" -> MakeTool(trek, trek_ammo),
|
||||
"trek" -> MakeTool(trek),
|
||||
"command_detonater" -> MakeSimpleItem(command_detonater),
|
||||
"flail_targeting_laser" -> MakeSimpleItem(flail_targeting_laser)
|
||||
)
|
||||
|
||||
/**
|
||||
* A `Map` of operations for producing a ground-based `Vehicle`.
|
||||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val groundVehicles : Map[String, ()=>Vehicle] = Map(
|
||||
"quadassault" -> MakeVehicle(quadassault),
|
||||
"fury" -> MakeVehicle(fury),
|
||||
"quadstealth" -> MakeVehicle(quadstealth),
|
||||
"ant" -> MakeVehicle(ant),
|
||||
"ams" -> MakeVehicle(ams),
|
||||
"mediumtransport" -> MakeVehicle(mediumtransport),
|
||||
"two_man_assault_buggy" -> MakeVehicle(two_man_assault_buggy),
|
||||
"skyguard" -> MakeVehicle(skyguard),
|
||||
"lightning" -> MakeVehicle(lightning),
|
||||
"threemanheavybuggy" -> MakeVehicle(threemanheavybuggy),
|
||||
"battlewagon" -> MakeVehicle(battlewagon),
|
||||
"apc_tr" -> MakeVehicle(apc_tr),
|
||||
"prowler" -> MakeVehicle(prowler),
|
||||
"twomanheavybuggy" -> MakeVehicle(twomanheavybuggy),
|
||||
"thunderer" -> MakeVehicle(thunderer),
|
||||
"apc_nc" -> MakeVehicle(apc_nc),
|
||||
"vanguard" -> MakeVehicle(vanguard),
|
||||
"twomanhoverbuggy" -> MakeVehicle(twomanhoverbuggy),
|
||||
"aurora" -> MakeVehicle(aurora),
|
||||
"apc_vs" -> MakeVehicle(apc_vs),
|
||||
"magrider" -> MakeVehicle(magrider),
|
||||
"flail" -> MakeVehicle(flail),
|
||||
"switchblade" -> MakeVehicle(switchblade),
|
||||
"router" -> MakeVehicle(router)
|
||||
)
|
||||
|
||||
/**
|
||||
* A `Map` of operations for producing most flight-based `Vehicle`.
|
||||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val flight1Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
"mosquito" -> MakeVehicle(mosquito),
|
||||
"lightgunship" -> MakeVehicle(lightgunship),
|
||||
"wasp" -> MakeVehicle(wasp),
|
||||
"phantasm" -> MakeVehicle(phantasm),
|
||||
"vulture" -> MakeVehicle(vulture),
|
||||
"liberator" -> MakeVehicle(liberator)
|
||||
)
|
||||
|
||||
/**
|
||||
* A `Map` of operations for producing a flight-based `Vehicle` specific to the dropship terminal.
|
||||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val flight2Vehicles : Map[String, ()=>Vehicle] = Map(
|
||||
"dropship" -> MakeVehicle(dropship),
|
||||
"galaxy_gunship" -> MakeVehicle(galaxy_gunship),
|
||||
"lodestar" -> MakeVehicle(lodestar)
|
||||
)
|
||||
|
||||
/**
|
||||
* A `Map` of operations for producing a ground-based `Vehicle` specific to the bfr terminal.
|
||||
* key - an identification string sent by the client
|
||||
* value - a curried function that builds the object
|
||||
*/
|
||||
protected val bfrVehicles : Map[String, ()=>Vehicle] = Map(
|
||||
// "colossus_gunner" -> (()=>Unit),
|
||||
// "colossus_flight" -> (()=>Unit),
|
||||
// "peregrine_gunner" -> (()=>Unit),
|
||||
// "peregrine_flight" -> (()=>Unit),
|
||||
// "aphelion_gunner" -> (()=>Unit),
|
||||
// "aphelion_flight" -> (()=>Unit)
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a new `Tool` from provided `EquipmentDefinition` objects.
|
||||
* @param tdef the `ToolDefinition` objects
|
||||
* @param tdef the `ToolDefinition` object
|
||||
* @return a partial function that, when called, creates the piece of `Equipment`
|
||||
*/
|
||||
protected def MakeTool(tdef : ToolDefinition)() : Tool = MakeTool(tdef, Nil)
|
||||
|
||||
/**
|
||||
* Create a new `Tool` from provided `EquipmentDefinition` objects.
|
||||
* @param tdef the `ToolDefinition` object
|
||||
* @param adef an `AmmoBoxDefinition` object
|
||||
* @return a partial function that, when called, creates the piece of `Equipment`
|
||||
*/
|
||||
|
|
@ -200,24 +279,55 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
* Only use this function to create default `Tools` with the default parameters.
|
||||
* For example, loadouts can retain `Tool` information that utilizes alternate, valid ammunition types;
|
||||
* and, this method function will not construct a complete object if provided that information.
|
||||
* @param tdef the `ToolDefinition` objects
|
||||
* @param tdef the `ToolDefinition` object
|
||||
* @param adefs a `List` of `AmmoBoxDefinition` objects
|
||||
* @return a curried function that, when called, creates the piece of `Equipment`
|
||||
* @see `GlobalDefinitions`
|
||||
* @see `OrderTerminalDefinition.BuildSimplifiedPattern`
|
||||
*/
|
||||
protected def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = {
|
||||
protected def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = {
|
||||
val obj = Tool(tdef)
|
||||
(0 until obj.MaxAmmoSlot).foreach(index => {
|
||||
val aType = adefs(index)
|
||||
val ammo = MakeAmmoBox(aType, Some(obj.Definition.FireModes(index).Magazine)) //make internal magazine, full
|
||||
(obj.AmmoSlots(index).Box = ammo) match {
|
||||
case Some(_) => ; //this means it worked
|
||||
case None =>
|
||||
org.log4s.getLogger("TerminalDefinition").warn(s"plans do not match definition: trying to feed ${ammo.AmmoType} ammunition into Tool (${obj.Definition.ObjectId} @ $index)")
|
||||
adefs match {
|
||||
case _ :: _ =>
|
||||
LoadAmmunitionIntoWeapon(obj, adefs)
|
||||
case Nil => ; //as-is
|
||||
}
|
||||
obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a weapon, and custom ammunition profiles, attempt to load those boxes of ammunition into the weapon.<br>
|
||||
* <br>
|
||||
* This is a customization function that should normally go unused.
|
||||
* All of the information necessary to generate a `Tool` from a `Terminal` request should be available on the `ToolDefinition` object.
|
||||
* The ammunition information, regardless of "customization," must satisfy the type limits of the original definition.
|
||||
* As thus, to introduce very strange ammunition to a give `Tool`,
|
||||
* either the definition must be modified or a different definition must be used.
|
||||
* The custom ammunition is organized into order of ammunition slots based on the `FireModeDefinition` objects.
|
||||
* That is:
|
||||
* the first custom element is processed by the first ammunition slot;
|
||||
* the second custom element is processed by the second ammunition slot; and, so forth.
|
||||
* @param weapon the `Tool` object
|
||||
* @param adefs a sequential `List` of ammunition to be loaded into weapon
|
||||
* @see `AmmoBoxDefinition`
|
||||
* @see `FireModeDefinition`
|
||||
*/
|
||||
private def LoadAmmunitionIntoWeapon(weapon : Tool, adefs : List[AmmoBoxDefinition]) : Unit = {
|
||||
val definition = weapon.Definition
|
||||
(0 until math.min(weapon.MaxAmmoSlot, adefs.length)).foreach(index => {
|
||||
val ammoSlot = weapon.AmmoSlots(index)
|
||||
adefs.lift(index) match {
|
||||
case Some(aType) =>
|
||||
ammoSlot.AllAmmoTypes.indexOf(aType.AmmoType) match {
|
||||
case -1 =>
|
||||
log.warn(s"terminal plans do not match definition: can not feed ${aType.AmmoType} ammunition into Tool (${definition.ObjectId} @ ammo $index)")
|
||||
case n =>
|
||||
ammoSlot.AmmoTypeIndex = n
|
||||
ammoSlot.Box = MakeAmmoBox(aType, Some(definition.FireModes(index).Magazine)) //make new internal magazine, full
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
obj
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -229,11 +339,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
* @see `GlobalDefinitions`
|
||||
*/
|
||||
protected def MakeAmmoBox(adef : AmmoBoxDefinition, capacity : Option[Int] = None)() : AmmoBox = {
|
||||
val obj = AmmoBox(adef)
|
||||
if(capacity.isDefined) {
|
||||
obj.Capacity = capacity.get
|
||||
capacity match {
|
||||
case Some(cap) =>
|
||||
AmmoBox(adef, cap)
|
||||
case None =>
|
||||
AmmoBox(adef)
|
||||
}
|
||||
obj
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -259,4 +370,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec
|
|||
* @see `GlobalDefinitions`
|
||||
*/
|
||||
protected def MakeConstructionItem(cdef : ConstructionItemDefinition)() : ConstructionItem = ConstructionItem(cdef)
|
||||
|
||||
/**
|
||||
* Create a new `Vehicle` from provided `VehicleDefinition` objects.
|
||||
* @param vdef the `VehicleDefinition` object
|
||||
* @return a curried function that, when called, creates the `Vehicle`
|
||||
* @see `GlobalDefinitions`
|
||||
*/
|
||||
protected def MakeVehicle(vdef : VehicleDefinition)() : Vehicle = Vehicle(vdef)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
||||
class VehicleTerminalCombinedDefinition extends TerminalDefinition(952) {
|
||||
private val vehicles = groundVehicles ++ flight1Vehicles
|
||||
Name = "vehicle_terminal_combined"
|
||||
|
||||
def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = {
|
||||
vehicles.get(msg.item_name) match {
|
||||
case Some(vehicle) =>
|
||||
Terminal.BuyVehicle(vehicle(), Nil)
|
||||
case None =>
|
||||
Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
|
||||
/**
|
||||
* na
|
||||
|
|
@ -35,8 +34,9 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
})
|
||||
|
||||
//check door to locks association
|
||||
//check door to lock association
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
map.DoorToLock.foreach({ case((door_guid, lock_guid)) =>
|
||||
try {
|
||||
if(!guid(door_guid).get.isInstanceOf[Door]) {
|
||||
|
|
@ -45,7 +45,7 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a door, but looking for uninitialized object $door_guid")
|
||||
slog.error(s"expected a door at id $door_guid, but looking for uninitialized object")
|
||||
}
|
||||
try {
|
||||
if(!guid(lock_guid).get.isInstanceOf[IFFLock]) {
|
||||
|
|
@ -54,7 +54,31 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected an IFF locks, but looking for uninitialized object $lock_guid")
|
||||
slog.error(s"expected an IFF locks at id $lock_guid, but looking for uninitialized object")
|
||||
}
|
||||
})
|
||||
|
||||
//check vehicle terminal to spawn pad association
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
map.TerminalToSpawnPad.foreach({ case ((term_guid, pad_guid)) =>
|
||||
try {
|
||||
if(!guid(term_guid).get.isInstanceOf[Terminal]) { //TODO check is vehicle terminal
|
||||
slog.error(s"expected id $term_guid to be a terminal, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a terminal at id $term_guid, but looking for uninitialized object")
|
||||
}
|
||||
try {
|
||||
if(!guid(pad_guid).get.isInstanceOf[VehicleSpawnPad]) {
|
||||
slog.error(s"expected id $pad_guid to be a spawn pad, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a spawn pad at id $pad_guid, but looking for uninitialized object")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import net.psforever.objects.serverobject.builders.ServerObjectBuilder
|
|||
*/
|
||||
class ZoneMap(private val name : String) {
|
||||
private var localObjects : List[ServerObjectBuilder[_]] = List()
|
||||
private var linkTerminalPad : Map[Int, Int] = Map()
|
||||
private var linkDoorLock : Map[Int, Int] = Map()
|
||||
private var linkObjectBase : Map[Int, Int] = Map()
|
||||
private var numBases : Int = 0
|
||||
|
|
@ -64,7 +65,13 @@ class ZoneMap(private val name : String) {
|
|||
|
||||
def DoorToLock : Map[Int, Int] = linkDoorLock
|
||||
|
||||
def DoorToLock(door_guid : Int, lock_guid : Int) = {
|
||||
def DoorToLock(door_guid : Int, lock_guid : Int) : Unit = {
|
||||
linkDoorLock = linkDoorLock ++ Map(door_guid -> lock_guid)
|
||||
}
|
||||
|
||||
def TerminalToSpawnPad : Map[Int, Int] = linkTerminalPad
|
||||
|
||||
def TerminalToSpawnPad(terminal_guid : Int, pad_guid : Int) : Unit = {
|
||||
linkTerminalPad = linkTerminalPad ++ Map(terminal_guid -> pad_guid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x53 => noDecoder(DroppodLaunchRequestMessage)
|
||||
case 0x54 => game.HackMessage.decode
|
||||
case 0x55 => noDecoder(DroppodLaunchResponseMessage)
|
||||
case 0x56 => noDecoder(GenericObjectActionMessage)
|
||||
case 0x56 => game.GenericObjectActionMessage.decode
|
||||
case 0x57 => game.AvatarVehicleTimerMessage.decode
|
||||
// 0x58
|
||||
case 0x58 => game.AvatarImplantMessage.decode
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import scodec.bits.BitVector
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Dispatched by the server to enact an effect on some game object.
|
||||
* (Write more some other time.)
|
||||
* @param object_guid the target object
|
||||
* @param code the action code
|
||||
*/
|
||||
final case class GenericObjectActionMessage(object_guid : PlanetSideGUID,
|
||||
code : Int)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = GenericObjectActionMessage
|
||||
def opcode = GamePacketOpcode.GenericObjectActionMessage
|
||||
def encode = GenericObjectActionMessage.encode(this)
|
||||
}
|
||||
|
||||
object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] {
|
||||
implicit val codec : Codec[GenericObjectActionMessage] = (
|
||||
("object_guid" | PlanetSideGUID.codec) ::
|
||||
("code" | uint8L) ::
|
||||
("ex" | bits) //"code" may extract at odd sizes
|
||||
).exmap[GenericObjectActionMessage] (
|
||||
{
|
||||
case guid :: code :: _ :: HNil =>
|
||||
Attempt.Successful(GenericObjectActionMessage(guid, code))
|
||||
},
|
||||
{
|
||||
case GenericObjectActionMessage(guid, code) =>
|
||||
Attempt.Successful(guid :: code :: BitVector.empty :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ import scodec.codecs._
|
|||
* `106 - Custom Head`<br>
|
||||
* <br>
|
||||
* Vehicles:<br>
|
||||
* 0 - Vehicle health<br>
|
||||
* 0 - Vehicle base health<br>
|
||||
* 10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)<br>
|
||||
* 11 - Gunner seat(s) permissions (same)<br>
|
||||
* 12 - Passenger seat(s) permissions (same) <br>
|
||||
|
|
|
|||
|
|
@ -347,6 +347,8 @@ object ObjectClass {
|
|||
final val quadstealth_destroyed = 711
|
||||
final val router = 741
|
||||
final val router_destroyed = 742
|
||||
final val skyguard = 784
|
||||
final val skyguard_destroyed = 785
|
||||
final val switchblade = 847
|
||||
final val switchblade_destroyed = 848
|
||||
final val threemanheavybuggy = 862 //marauder
|
||||
|
|
@ -845,7 +847,7 @@ object ObjectClass {
|
|||
case ObjectClass.lancer => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.lasher => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.liberator_25mm_cannon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
|
||||
case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.liberator_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
|
||||
case ObjectClass.lightning_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
|
||||
|
|
@ -888,7 +890,7 @@ object ObjectClass {
|
|||
case ObjectClass.rocklet => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.scythe => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
|
||||
case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon")
|
||||
case ObjectClass.spiker => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.spitfire_aa_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
case ObjectClass.spitfire_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon")
|
||||
|
|
@ -1245,6 +1247,8 @@ object ObjectClass {
|
|||
case ObjectClass.quadstealth_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
|
||||
case ObjectClass.router => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
|
||||
case ObjectClass.router_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
|
||||
case ObjectClass.skyguard => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
|
||||
case ObjectClass.skyguard_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
|
||||
case ObjectClass.switchblade => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
|
||||
case ObjectClass.switchblade_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage")
|
||||
case ObjectClass.threemanheavybuggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle")
|
||||
|
|
|
|||
|
|
@ -27,6 +27,81 @@ object Prefab {
|
|||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility)
|
||||
}
|
||||
|
||||
def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_nc, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_tr, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_vs, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
|
|
@ -118,56 +193,6 @@ object Prefab {
|
|||
)(VehicleFormat.Variant)
|
||||
}
|
||||
|
||||
def juggernaut(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_tr, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def leviathan(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_vs, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
|
|
@ -187,8 +212,8 @@ object Prefab {
|
|||
def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
InventoryItemData(445, weapon_guid, 1,
|
||||
WeaponData(0x6, 0x8, 0, 16, ammo1_guid, 0, AmmoBoxData(8), 722, ammo2_guid,1, AmmoBoxData(8))
|
||||
InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Variant)
|
||||
|
|
@ -270,14 +295,6 @@ object Prefab {
|
|||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Variant)
|
||||
}
|
||||
|
||||
def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
|
|
@ -292,6 +309,24 @@ object Prefab {
|
|||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Variant)
|
||||
}
|
||||
|
||||
def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
|
|
@ -368,31 +403,6 @@ object Prefab {
|
|||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def vindicator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None,
|
||||
Some(InventoryData(
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_weapon_systemd_nc, weapon4_guid, 14,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo4_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8))
|
||||
) ::
|
||||
InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16,
|
||||
WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8))
|
||||
) :: Nil
|
||||
))
|
||||
)(VehicleFormat.Normal)
|
||||
}
|
||||
|
||||
def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = {
|
||||
VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)),
|
||||
Some(InventoryData(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,41 @@ package net.psforever.types
|
|||
import net.psforever.newcodecs._
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class Vector3(x : Float,
|
||||
y : Float,
|
||||
z : Float)
|
||||
z : Float) {
|
||||
/**
|
||||
* Operator override for vector addition, treating `Vector3` objects as actual mathematical vectors.
|
||||
* The application of this overload is "vector1 + vector2."
|
||||
* @param vec the other `Vector3` object
|
||||
* @return a new `Vector3` object with the summed values
|
||||
*/
|
||||
def +(vec : Vector3) : Vector3 = {
|
||||
new Vector3(x + vec.x, y + vec.y, z + vec.z)
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator override for vector subtraction, treating `Vector3` objects as actual mathematical vectors.
|
||||
* The application of this overload is "vector1 - vector2."
|
||||
* @param vec the other `Vector3` object
|
||||
* @return a new `Vector3` object with the difference values
|
||||
*/
|
||||
def -(vec : Vector3) : Vector3 = {
|
||||
new Vector3(x - vec.x, y - vec.y, z - vec.z)
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator override for vector scaling, treating `Vector3` objects as actual mathematical vectors.
|
||||
* The application of this overload is "vector * scalar" exclusively.
|
||||
* "scalar * vector" is invalid.
|
||||
* @param scalar the value to multiply this vector
|
||||
* @return a new `Vector3` object
|
||||
*/
|
||||
def *(scalar : Float) : Vector3 = {
|
||||
new Vector3(x*scalar, y*scalar, z*scalar)
|
||||
}
|
||||
}
|
||||
|
||||
object Vector3 {
|
||||
implicit val codec_pos : Codec[Vector3] = (
|
||||
|
|
@ -28,4 +58,48 @@ object Vector3 {
|
|||
("y" | floatL) ::
|
||||
("z" | floatL)
|
||||
).as[Vector3]
|
||||
|
||||
/**
|
||||
* Calculate the actual distance between two points.
|
||||
* @param pos1 the first point
|
||||
* @param pos2 the second point
|
||||
* @return the distance
|
||||
*/
|
||||
def Distance(pos1 : Vector3, pos2 : Vector3) : Float = {
|
||||
math.sqrt(DistanceSquared(pos1, pos2)).toFloat
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the squared distance between two points.
|
||||
* Though some time is saved care must be taken that any comparative distance is also squared.
|
||||
* @param pos1 the first point
|
||||
* @param pos2 the second point
|
||||
* @return the distance
|
||||
*/
|
||||
def DistanceSquared(pos1 : Vector3, pos2 : Vector3) : Float = {
|
||||
val dvec : Vector3 = pos1 - pos2
|
||||
(dvec.x * dvec.x) + (dvec.y * dvec.y) + (dvec.z * dvec.z)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the actual magnitude of a vector.
|
||||
* @param vec the vector
|
||||
* @return the magnitude
|
||||
*/
|
||||
def Magnitude(vec : Vector3) : Float = {
|
||||
math.sqrt(MagnitudeSquared(vec)).toFloat
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the squared magnitude of a vector.
|
||||
* Though some time is saved care must be taken that any comparative magnitude is also squared.
|
||||
* @param vec the vector
|
||||
* @return the magnitude
|
||||
*/
|
||||
def MagnitudeSquared(vec : Vector3) : Float = {
|
||||
val dx : Float = vec.x
|
||||
val dy : Float = vec.y
|
||||
val dz : Float = vec.z
|
||||
(dx * dx) + (dy * dy) + (dz * dz)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
68
common/src/test/scala/Vector3Test.scala
Normal file
68
common/src/test/scala/Vector3Test.scala
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class Vector3Test extends Specification {
|
||||
val vec = Vector3(1.3f, -2.6f, 3.9f)
|
||||
|
||||
"Vector3" should {
|
||||
"construct" in {
|
||||
vec.x mustEqual 1.3f
|
||||
vec.y mustEqual -2.6f
|
||||
vec.z mustEqual 3.9f
|
||||
}
|
||||
|
||||
"calculate magnitude (like a vector) 1" in {
|
||||
val obj = Vector3(2.0f, 0.0f, 0.0f)
|
||||
Vector3.Magnitude(obj) mustEqual 2.0f
|
||||
}
|
||||
|
||||
"calculate magnitude (like a vector) 2" in {
|
||||
val obj = Vector3(3.0f, 4.0f, 0.0f)
|
||||
Vector3.Magnitude(obj) mustEqual 5.0f
|
||||
}
|
||||
|
||||
"calculate magnitude (like a vector) 3" in {
|
||||
Vector3.Magnitude(vec) mustEqual 4.864155f
|
||||
}
|
||||
|
||||
"calculate square magnitude (like a vector)" in {
|
||||
Vector3.MagnitudeSquared(vec) mustEqual 23.66f
|
||||
}
|
||||
|
||||
"calculate distance 1" in {
|
||||
val obj1 = Vector3(0.0f, 0.0f, 0.0f)
|
||||
val obj2 = Vector3(2.0f, 0.0f, 0.0f)
|
||||
Vector3.Distance(obj1, obj2) mustEqual 2.0f
|
||||
}
|
||||
|
||||
"calculate distance 2" in {
|
||||
val obj1 = Vector3(0.0f, 0.0f, 0.0f)
|
||||
val obj2 = Vector3(2.0f, 0.0f, 0.0f)
|
||||
Vector3.Distance(obj1, obj2) mustEqual Vector3.Magnitude(obj2)
|
||||
}
|
||||
|
||||
"calculate distance 3" in {
|
||||
val obj1 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
val obj2 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
Vector3.Distance(obj1, obj2) mustEqual 0f
|
||||
}
|
||||
|
||||
"addition" in {
|
||||
val obj1 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
val obj2 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
obj1 + obj2 mustEqual Vector3(6f, 8f, 10f)
|
||||
}
|
||||
|
||||
"subtraction" in {
|
||||
val obj1 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
val obj2 = Vector3(3.0f, 4.0f, 5.0f)
|
||||
obj1 - obj2 mustEqual Vector3(0f, 0f, 0f)
|
||||
}
|
||||
|
||||
"scalar" in {
|
||||
vec * 3f mustEqual Vector3(3.8999999f, -7.7999997f, 11.700001f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class GenericObjectActionMessageTest extends Specification {
|
||||
val string = hex"56 B501 24"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case GenericObjectActionMessage(object_guid, action) =>
|
||||
object_guid mustEqual PlanetSideGUID(437)
|
||||
action mustEqual 36
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = GenericObjectActionMessage(PlanetSideGUID(437), 36)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
|
|
@ -40,16 +40,15 @@ class ConverterTest extends Specification {
|
|||
"convert to packet" in {
|
||||
val tdef = ToolDefinition(1076)
|
||||
tdef.Size = EquipmentSize.Rifle
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell_AP
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
|
||||
tdef.FireModes += new FireModeDefinition
|
||||
tdef.FireModes.head.AmmoTypeIndices += 0
|
||||
tdef.FireModes.head.AmmoTypeIndices += 1
|
||||
tdef.FireModes.head.AmmoSlotIndex = 0
|
||||
tdef.FireModes.head.Magazine = 30
|
||||
val obj : Tool = Tool(tdef)
|
||||
val box = AmmoBox(PlanetSideGUID(90), new AmmoBoxDefinition(Ammo.shotgun_shell.id))
|
||||
obj.AmmoSlots.head.Box = box
|
||||
obj.AmmoSlots.head.Magazine = 30
|
||||
obj.AmmoSlot.Box.GUID = PlanetSideGUID(90)
|
||||
|
||||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
|
|
@ -139,24 +138,22 @@ class ConverterTest extends Specification {
|
|||
Give the Player's Holster (2) the Tool
|
||||
Place the remaining AmmoBox into the Player's inventory in the third slot (8)
|
||||
*/
|
||||
val bullet_9mm = AmmoBoxDefinition(28)
|
||||
bullet_9mm.Capacity = 50
|
||||
val box1 = AmmoBox(PlanetSideGUID(90), bullet_9mm)
|
||||
val box2 = AmmoBox(PlanetSideGUID(91), bullet_9mm)
|
||||
val tdef = ToolDefinition(1076)
|
||||
tdef.Name = "sample_weapon"
|
||||
tdef.Size = EquipmentSize.Rifle
|
||||
tdef.AmmoTypes += Ammo.bullet_9mm
|
||||
tdef.AmmoTypes += GlobalDefinitions.bullet_9mm
|
||||
tdef.FireModes += new FireModeDefinition
|
||||
tdef.FireModes.head.AmmoTypeIndices += 0
|
||||
tdef.FireModes.head.AmmoSlotIndex = 0
|
||||
tdef.FireModes.head.Magazine = 18
|
||||
val tool = Tool(PlanetSideGUID(92), tdef)
|
||||
tool.AmmoSlots.head.Box = box1
|
||||
val tool = Tool(tdef)
|
||||
tool.GUID = PlanetSideGUID(92)
|
||||
tool.AmmoSlot.Box.GUID = PlanetSideGUID(90)
|
||||
val obj = Player(PlanetSideGUID(93), "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
|
||||
obj.Slot(2).Equipment = tool
|
||||
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(94)
|
||||
obj.Inventory += 8 -> box2
|
||||
obj.Inventory += 8 -> AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
obj.Slot(8).Equipment.get.GUID = PlanetSideGUID(91)
|
||||
obj
|
||||
}
|
||||
val converter = new CharacterSelectConverter
|
||||
|
|
@ -263,7 +260,7 @@ class ConverterTest extends Specification {
|
|||
|
||||
val fury_weapon_systema_def = ToolDefinition(ObjectClass.fury_weapon_systema)
|
||||
fury_weapon_systema_def.Size = EquipmentSize.VehicleWeapon
|
||||
fury_weapon_systema_def.AmmoTypes += Ammo.hellfire_ammo
|
||||
fury_weapon_systema_def.AmmoTypes += GlobalDefinitions.hellfire_ammo
|
||||
fury_weapon_systema_def.FireModes += new FireModeDefinition
|
||||
fury_weapon_systema_def.FireModes.head.AmmoTypeIndices += 0
|
||||
fury_weapon_systema_def.FireModes.head.AmmoSlotIndex = 0
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import net.psforever.objects.GlobalDefinitions._
|
|||
import org.specs2.mutable._
|
||||
|
||||
class EquipmentTest extends Specification {
|
||||
|
||||
"AmmoBox" should {
|
||||
"define" in {
|
||||
val obj = AmmoBoxDefinition(86)
|
||||
|
|
@ -19,8 +18,8 @@ class EquipmentTest extends Specification {
|
|||
|
||||
obj.AmmoType mustEqual Ammo.aphelion_immolation_cannon_ammo
|
||||
obj.Capacity mustEqual 300
|
||||
obj.Tile.width mustEqual InventoryTile.Tile44.width
|
||||
obj.Tile.height mustEqual InventoryTile.Tile44.height
|
||||
obj.Tile.Width mustEqual InventoryTile.Tile44.Width
|
||||
obj.Tile.Height mustEqual InventoryTile.Tile44.Height
|
||||
obj.ObjectId mustEqual 86
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +57,8 @@ class EquipmentTest extends Specification {
|
|||
val obj = ToolDefinition(1076)
|
||||
obj.Name = "sample_weapon"
|
||||
obj.Size = EquipmentSize.Rifle
|
||||
obj.AmmoTypes += Ammo.shotgun_shell
|
||||
obj.AmmoTypes += Ammo.shotgun_shell_AP
|
||||
obj.AmmoTypes += GlobalDefinitions.shotgun_shell
|
||||
obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
|
||||
obj.FireModes += new FireModeDefinition
|
||||
obj.FireModes.head.AmmoTypeIndices += 0
|
||||
obj.FireModes.head.AmmoTypeIndices += 1
|
||||
|
|
@ -74,9 +73,10 @@ class EquipmentTest extends Specification {
|
|||
obj.FireModes(1).Magazine = 18
|
||||
obj.Tile = InventoryTile.Tile93
|
||||
obj.ObjectId mustEqual 1076
|
||||
|
||||
obj.Name mustEqual "sample_weapon"
|
||||
obj.AmmoTypes.head mustEqual Ammo.shotgun_shell
|
||||
obj.AmmoTypes(1) mustEqual Ammo.shotgun_shell_AP
|
||||
obj.AmmoTypes.head mustEqual GlobalDefinitions.shotgun_shell
|
||||
obj.AmmoTypes(1) mustEqual GlobalDefinitions.shotgun_shell_AP
|
||||
obj.FireModes.head.AmmoTypeIndices.head mustEqual 0
|
||||
obj.FireModes.head.AmmoTypeIndices(1) mustEqual 1
|
||||
obj.FireModes.head.AmmoSlotIndex mustEqual 0
|
||||
|
|
@ -89,8 +89,8 @@ class EquipmentTest extends Specification {
|
|||
obj.FireModes(1).Chamber mustEqual 3
|
||||
obj.FireModes(1).Magazine mustEqual 18
|
||||
obj.FireModes(1).ResetAmmoIndexOnSwap mustEqual false
|
||||
obj.Tile.width mustEqual InventoryTile.Tile93.width
|
||||
obj.Tile.height mustEqual InventoryTile.Tile93.height
|
||||
obj.Tile.Width mustEqual InventoryTile.Tile93.Width
|
||||
obj.Tile.Height mustEqual InventoryTile.Tile93.Height
|
||||
}
|
||||
|
||||
"construct" in {
|
||||
|
|
@ -118,8 +118,8 @@ class EquipmentTest extends Specification {
|
|||
//explanation: sample_weapon has two fire modes; adjusting the FireMode changes between them
|
||||
val tdef = ToolDefinition(1076)
|
||||
tdef.Size = EquipmentSize.Rifle
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell_AP
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
|
||||
tdef.FireModes += new FireModeDefinition
|
||||
tdef.FireModes.head.AmmoTypeIndices += 0
|
||||
tdef.FireModes.head.AmmoSlotIndex = 0
|
||||
|
|
@ -149,8 +149,8 @@ class EquipmentTest extends Specification {
|
|||
//explanation: obj has one fire mode and two ammunitions; adjusting the AmmoType changes between them
|
||||
val tdef = ToolDefinition(1076)
|
||||
tdef.Size = EquipmentSize.Rifle
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell
|
||||
tdef.AmmoTypes += Ammo.shotgun_shell_AP
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell
|
||||
tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP
|
||||
tdef.FireModes += new FireModeDefinition
|
||||
tdef.FireModes.head.AmmoTypeIndices += 0
|
||||
tdef.FireModes.head.AmmoTypeIndices += 1
|
||||
|
|
@ -168,14 +168,54 @@ class EquipmentTest extends Specification {
|
|||
obj.AmmoTypeIndex mustEqual 0
|
||||
obj.AmmoType mustEqual Ammo.shotgun_shell
|
||||
}
|
||||
|
||||
"multiple ammo types and multiple fire modes, split (Punisher)" in {
|
||||
val obj = Tool(GlobalDefinitions.punisher)
|
||||
//ammo = 0, fmode = 0
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.AmmoTypeIndex mustEqual 0
|
||||
obj.AmmoType mustEqual Ammo.bullet_9mm
|
||||
//ammo = 2, fmode = 1
|
||||
obj.NextFireMode
|
||||
obj.FireModeIndex mustEqual 1
|
||||
obj.AmmoTypeIndex mustEqual 2
|
||||
obj.AmmoType mustEqual Ammo.rocket
|
||||
//ammo = 3, fmode = 1
|
||||
obj.NextAmmoType
|
||||
obj.AmmoTypeIndex mustEqual 3
|
||||
obj.AmmoType mustEqual Ammo.frag_cartridge
|
||||
//ammo = 4, fmode = 1
|
||||
obj.NextAmmoType
|
||||
obj.AmmoTypeIndex mustEqual 4
|
||||
obj.AmmoType mustEqual Ammo.jammer_cartridge
|
||||
//ammo = 0, fmode = 0
|
||||
obj.NextFireMode
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.AmmoTypeIndex mustEqual 0
|
||||
obj.AmmoType mustEqual Ammo.bullet_9mm
|
||||
//ammo = 1, fmode = 0
|
||||
obj.NextAmmoType
|
||||
obj.AmmoTypeIndex mustEqual 1
|
||||
obj.AmmoType mustEqual Ammo.bullet_9mm_AP
|
||||
//ammo = 5, fmode = 1
|
||||
obj.NextFireMode
|
||||
obj.NextAmmoType
|
||||
obj.FireModeIndex mustEqual 1
|
||||
obj.AmmoTypeIndex mustEqual 5
|
||||
obj.AmmoType mustEqual Ammo.plasma_cartridge
|
||||
//ammo = 2, fmode = 1
|
||||
obj.NextAmmoType
|
||||
obj.AmmoTypeIndex mustEqual 2
|
||||
obj.AmmoType mustEqual Ammo.rocket
|
||||
}
|
||||
}
|
||||
|
||||
"Kit" should {
|
||||
"define" in {
|
||||
val sample = KitDefinition(Kits.medkit)
|
||||
sample.ObjectId mustEqual medkit.ObjectId
|
||||
sample.Tile.width mustEqual medkit.Tile.width
|
||||
sample.Tile.height mustEqual medkit.Tile.height
|
||||
sample.Tile.Width mustEqual medkit.Tile.Width
|
||||
sample.Tile.Height mustEqual medkit.Tile.Height
|
||||
}
|
||||
|
||||
"construct" in {
|
||||
|
|
@ -200,8 +240,8 @@ class EquipmentTest extends Specification {
|
|||
sample.Modes.head mustEqual DeployedItem.tank_traps
|
||||
sample.Modes(1) mustEqual DeployedItem.portable_manned_turret_tr
|
||||
sample.Modes(2) mustEqual DeployedItem.deployable_shield_generator
|
||||
sample.Tile.width mustEqual InventoryTile.Tile63.width
|
||||
sample.Tile.height mustEqual InventoryTile.Tile63.height
|
||||
sample.Tile.Width mustEqual InventoryTile.Tile63.Width
|
||||
sample.Tile.Height mustEqual InventoryTile.Tile63.Height
|
||||
}
|
||||
|
||||
"construct" in {
|
||||
|
|
|
|||
|
|
@ -1,245 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import java.util.logging.LogManager
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.entity.IdentifiableEntity
|
||||
import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem}
|
||||
import net.psforever.objects.guid.selector.RandomSelector
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.guid.{GUIDTask, NumberPoolHub, Task, TaskResolver}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
|
||||
class GUIDTaskRegister1Test extends ActorTest() {
|
||||
"RegisterObjectTask" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = new GUIDTaskTest.TestObject
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskRegister2Test extends ActorTest() {
|
||||
"RegisterEquipment -> RegisterObjectTask" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskRegister3Test extends ActorTest() {
|
||||
"RegisterEquipment -> RegisterTool" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Tool(GlobalDefinitions.beamer)
|
||||
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj.AmmoSlots.head.Box.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj.AmmoSlots.head.Box.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskRegister4Test extends ActorTest() {
|
||||
"RegisterVehicle" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Vehicle(GlobalDefinitions.fury)
|
||||
val obj_wep = obj.WeaponControlledFromSeat(0).get
|
||||
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
|
||||
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
|
||||
val obj_trunk_ammo = obj.Trunk.Items(0).obj
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_trunk_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterVehicle(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_trunk_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskRegister5Test extends ActorTest() {
|
||||
"RegisterAvatar" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||
obj.Slot(0).Equipment = obj_wep
|
||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
|
||||
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj.Slot(6).Equipment = obj_inv_ammo
|
||||
val obj_locker = obj.Slot(5).Equipment.get
|
||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_inv_ammo.HasGUID)
|
||||
assert(!obj_locker.HasGUID)
|
||||
assert(!obj_locker_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterAvatar(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_inv_ammo.HasGUID)
|
||||
assert(obj_locker.HasGUID)
|
||||
assert(obj_locker_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskUnregister1Test extends ActorTest() {
|
||||
"UnregisterObjectTask" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = new GUIDTaskTest.TestObject
|
||||
guid.register(obj, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterObjectTask(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskUnregister2Test extends ActorTest() {
|
||||
"UnregisterEquipment -> UnregisterObjectTask" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
guid.register(obj, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskUnregister3Test extends ActorTest() {
|
||||
"UnregisterEquipment -> UnregisterTool" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Tool(GlobalDefinitions.beamer)
|
||||
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj.AmmoSlots.head.Box, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj.AmmoSlots.head.Box.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj.AmmoSlots.head.Box.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskUnregister4Test extends ActorTest() {
|
||||
"RegisterVehicle" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Vehicle(GlobalDefinitions.fury)
|
||||
val obj_wep = obj.WeaponControlledFromSeat(0).get
|
||||
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
|
||||
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
|
||||
val obj_trunk_ammo = obj.Trunk.Items(0).obj
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj_wep, "dynamic")
|
||||
guid.register(obj_wep_ammo, "dynamic")
|
||||
guid.register(obj_trunk_ammo, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_trunk_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterVehicle(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_trunk_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
class GUIDTaskUnregister5Test extends ActorTest() {
|
||||
"UnregisterAvatar" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||
obj.Slot(0).Equipment = obj_wep
|
||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
|
||||
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj.Slot(6).Equipment = obj_inv_ammo
|
||||
val obj_locker = obj.Slot(5).Equipment.get
|
||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj_wep, "dynamic")
|
||||
guid.register(obj_wep_ammo, "dynamic")
|
||||
guid.register(obj_inv_ammo, "dynamic")
|
||||
guid.register(obj_locker, "dynamic")
|
||||
guid.register(obj_locker_ammo, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_inv_ammo.HasGUID)
|
||||
assert(obj_locker.HasGUID)
|
||||
assert(obj_locker_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterAvatar(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_inv_ammo.HasGUID)
|
||||
assert(!obj_locker.HasGUID)
|
||||
assert(!obj_locker_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
||||
object GUIDTaskTest {
|
||||
class TestObject extends IdentifiableEntity
|
||||
|
||||
class RegisterTestTask(probe : ActorRef) extends Task {
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
probe ! scala.util.Success
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
}
|
||||
|
||||
def CommonTestSetup(implicit system : ActorSystem) : (NumberPoolHub, ActorRef, ActorRef, TestProbe) = {
|
||||
import akka.actor.Props
|
||||
import akka.routing.RandomPool
|
||||
import akka.testkit.TestProbe
|
||||
|
||||
val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110))
|
||||
guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now
|
||||
val uns = system.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))), "uns")
|
||||
val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]), "resolver")
|
||||
LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements
|
||||
(guid, uns, taskResolver, TestProbe())
|
||||
}
|
||||
|
||||
/**
|
||||
* @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
|
||||
*/
|
||||
def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = {
|
||||
poolSource.Pools.map({ case ((pname, pool)) =>
|
||||
pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
|
||||
}).toMap
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,8 @@ class InventoryTest extends Specification {
|
|||
val obj : GridInventory = GridInventory(9, 6)
|
||||
obj += 0 -> bullet9mmBox1
|
||||
obj.Capacity mustEqual 45
|
||||
val w = bullet9mmBox2.Tile.width
|
||||
val h = bullet9mmBox2.Tile.height
|
||||
val w = bullet9mmBox2.Tile.Width
|
||||
val h = bullet9mmBox2.Tile.Height
|
||||
val list0 = obj.CheckCollisionsAsList(0, w, h)
|
||||
list0 match {
|
||||
case scala.util.Success(list) => list.length mustEqual 1
|
||||
|
|
@ -91,8 +91,8 @@ class InventoryTest extends Specification {
|
|||
val obj : GridInventory = GridInventory(9, 6)
|
||||
obj += 3 -> bullet9mmBox1
|
||||
obj.Capacity mustEqual 45
|
||||
val w = bullet9mmBox2.Tile.width
|
||||
val h = bullet9mmBox2.Tile.height
|
||||
val w = bullet9mmBox2.Tile.Width
|
||||
val h = bullet9mmBox2.Tile.Height
|
||||
val list0 = obj.CheckCollisionsAsList(3, w, h)
|
||||
list0 match {
|
||||
case scala.util.Success(list) => list.length mustEqual 1
|
||||
|
|
@ -125,8 +125,8 @@ class InventoryTest extends Specification {
|
|||
val obj : GridInventory = GridInventory(9, 6)
|
||||
obj += 0 -> bullet9mmBox1
|
||||
obj.Capacity mustEqual 45
|
||||
val w = bullet9mmBox2.Tile.width
|
||||
val h = bullet9mmBox2.Tile.height
|
||||
val w = bullet9mmBox2.Tile.Width
|
||||
val h = bullet9mmBox2.Tile.Height
|
||||
val list0 = obj.CheckCollisionsAsList(0, w, h)
|
||||
list0 match {
|
||||
case scala.util.Success(list) => list.length mustEqual 1
|
||||
|
|
@ -159,8 +159,8 @@ class InventoryTest extends Specification {
|
|||
val obj : GridInventory = GridInventory(9, 6)
|
||||
obj += 27 -> bullet9mmBox1
|
||||
obj.Capacity mustEqual 45
|
||||
val w = bullet9mmBox2.Tile.width
|
||||
val h = bullet9mmBox2.Tile.height
|
||||
val w = bullet9mmBox2.Tile.Width
|
||||
val h = bullet9mmBox2.Tile.Height
|
||||
val list0 = obj.CheckCollisionsAsList(27, w, h)
|
||||
list0 match {
|
||||
case scala.util.Success(list) => list.length mustEqual 1
|
||||
|
|
@ -205,8 +205,8 @@ class InventoryTest extends Specification {
|
|||
val obj : GridInventory = GridInventory(12, 9)
|
||||
obj += 39 -> bullet9mmBox1
|
||||
obj.Capacity mustEqual 99 //108 - 9
|
||||
val w = bullet9mmBox2.Tile.width
|
||||
val h = bullet9mmBox2.Tile.height
|
||||
val w = bullet9mmBox2.Tile.Width
|
||||
val h = bullet9mmBox2.Tile.Height
|
||||
val list0 = obj.CheckCollisionsAsList(0, w, h)
|
||||
list0 match {
|
||||
case scala.util.Success(list) => list.isEmpty mustEqual true
|
||||
|
|
|
|||
110
common/src/test/scala/objects/VehicleSpawnPadTest.scala
Normal file
110
common/src/test/scala/objects/VehicleSpawnPadTest.scala
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class VehicleSpawnPadTest extends Specification {
|
||||
"VehicleSpawnPadDefinition" should {
|
||||
"define" in {
|
||||
GlobalDefinitions.spawn_pad.ObjectId mustEqual 800
|
||||
}
|
||||
}
|
||||
|
||||
"VehicleSpawnPad" should {
|
||||
"construct" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor mustEqual ActorRef.noSender
|
||||
obj.Definition mustEqual GlobalDefinitions.spawn_pad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl1Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"construct" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door")
|
||||
assert(obj.Actor != ActorRef.noSender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl2Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"spawn a vehicle" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door")
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
player.Spawn
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle")
|
||||
|
||||
obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply == VehicleSpawnPad.ConcealPlayer) //explicit: isInstanceOf does not work
|
||||
|
||||
val reply2 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply2.isInstanceOf[VehicleSpawnPad.LoadVehicle])
|
||||
assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].vehicle == vehicle)
|
||||
assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].pad == obj)
|
||||
|
||||
player.VehicleOwned = vehicle
|
||||
val reply3 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle])
|
||||
assert(reply3.asInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle].vehicle == vehicle)
|
||||
|
||||
val reply4 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply4.isInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning])
|
||||
assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].vehicle == vehicle)
|
||||
assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].warning_count > 0)
|
||||
|
||||
vehicle.Position = Vector3(11f, 0f, 0f) //greater than 10m
|
||||
val reply5 = receiveOne(Duration.create(10000, "ms"))
|
||||
assert(reply5.isInstanceOf[VehicleSpawnPad.SpawnPadUnblocked])
|
||||
assert(reply5.asInstanceOf[VehicleSpawnPad.SpawnPadUnblocked].vehicle_guid == vehicle.GUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl3Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"not spawn a vehicle if player is dead" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door")
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle")
|
||||
|
||||
obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(5000, "ms"))
|
||||
assert(reply == null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleSpawnControl4Test extends ActorTest() {
|
||||
"VehicleSpawnControl" should {
|
||||
"not spawn a vehicle if vehicle Actor is missing" in {
|
||||
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
|
||||
obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door")
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
player.Spawn
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
|
||||
obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle)
|
||||
val reply = receiveOne(Duration.create(5000, "ms"))
|
||||
assert(reply == null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,8 +44,8 @@ class VehicleTest extends Specification {
|
|||
fury.Weapons.size mustEqual 1
|
||||
fury.Weapons.get(0) mustEqual None
|
||||
fury.Weapons.get(1) mustEqual Some(GlobalDefinitions.fury_weapon_systema)
|
||||
fury.TrunkSize.width mustEqual 11
|
||||
fury.TrunkSize.height mustEqual 11
|
||||
fury.TrunkSize.Width mustEqual 11
|
||||
fury.TrunkSize.Height mustEqual 11
|
||||
fury.TrunkOffset mustEqual 30
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import akka.actor.{Actor, ActorSystem, Props}
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class GUIDTaskRegister1Test extends Specification {
|
||||
"RegisterObjectTask" should {
|
||||
"register (1)" in {
|
||||
val system = ActorSystem("sys")
|
||||
val test = system.actorOf(Props(classOf[GUIDTaskRegister1TestActor], system), "test")
|
||||
|
||||
implicit val timeout = Timeout(5 seconds)
|
||||
val future = test ? "test"
|
||||
val result = Await.result(future, timeout.duration).asInstanceOf[String]
|
||||
result mustEqual "success"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GUIDTaskRegister1TestActor(implicit system : ActorSystem) extends Actor {
|
||||
def receive : Receive = {
|
||||
case "test" =>
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = new GUIDTaskTest.TestObject
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
sender ! "success"
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
//class GUIDTaskRegister1Test extends ActorTest() {
|
||||
// "RegisterObjectTask" in {
|
||||
// val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
// val obj = new GUIDTaskTest.TestObject
|
||||
//
|
||||
// assert(!obj.HasGUID)
|
||||
// taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
|
||||
// probe.expectMsg(scala.util.Success)
|
||||
// assert(obj.HasGUID)
|
||||
// }
|
||||
//}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskRegister2Test extends ActorTest() {
|
||||
"RegisterEquipment -> RegisterObjectTask" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskRegister3Test extends ActorTest() {
|
||||
"RegisterEquipment -> RegisterTool" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Tool(GlobalDefinitions.beamer)
|
||||
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj.AmmoSlots.head.Box.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj.AmmoSlots.head.Box.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskRegister4Test extends ActorTest() {
|
||||
"RegisterVehicle" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Vehicle(GlobalDefinitions.fury)
|
||||
val obj_wep = obj.WeaponControlledFromSeat(0).get
|
||||
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
|
||||
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
|
||||
val obj_trunk_ammo = obj.Trunk.Items(0).obj
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_trunk_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterVehicle(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_trunk_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskRegister5Test extends ActorTest() {
|
||||
"RegisterAvatar" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||
obj.Slot(0).Equipment = obj_wep
|
||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
|
||||
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj.Slot(6).Equipment = obj_inv_ammo
|
||||
val obj_locker = obj.Slot(5).Equipment.get
|
||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_inv_ammo.HasGUID)
|
||||
assert(!obj_locker.HasGUID)
|
||||
assert(!obj_locker_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterAvatar(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_inv_ammo.HasGUID)
|
||||
assert(obj_locker.HasGUID)
|
||||
assert(obj_locker_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
45
common/src/test/scala/objects/guidtask/GUIDTaskTest.scala
Normal file
45
common/src/test/scala/objects/guidtask/GUIDTaskTest.scala
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import java.util.logging.LogManager
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.TestProbe
|
||||
import net.psforever.objects.entity.IdentifiableEntity
|
||||
import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem}
|
||||
import net.psforever.objects.guid.selector.RandomSelector
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.guid.{NumberPoolHub, Task, TaskResolver}
|
||||
|
||||
object GUIDTaskTest {
|
||||
class TestObject extends IdentifiableEntity
|
||||
|
||||
class RegisterTestTask(probe : ActorRef) extends Task {
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
probe ! scala.util.Success
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
}
|
||||
|
||||
def CommonTestSetup(implicit system : ActorSystem) : (NumberPoolHub, ActorRef, ActorRef, TestProbe) = {
|
||||
import akka.actor.Props
|
||||
import akka.routing.RandomPool
|
||||
import akka.testkit.TestProbe
|
||||
|
||||
val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110))
|
||||
guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now
|
||||
val uns = system.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))), "uns")
|
||||
val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]), "resolver")
|
||||
LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements
|
||||
(guid, uns, taskResolver, TestProbe())
|
||||
}
|
||||
|
||||
/**
|
||||
* @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
|
||||
*/
|
||||
def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = {
|
||||
poolSource.Pools.map({ case ((pname, pool)) =>
|
||||
pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
|
||||
}).toMap
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskUnregister1Test extends ActorTest() {
|
||||
"UnregisterObjectTask" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = new GUIDTaskTest.TestObject
|
||||
guid.register(obj, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterObjectTask(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskUnregister2Test extends ActorTest() {
|
||||
"UnregisterEquipment -> UnregisterObjectTask" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
guid.register(obj, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskUnregister3Test extends ActorTest() {
|
||||
"UnregisterEquipment -> UnregisterTool" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Tool(GlobalDefinitions.beamer)
|
||||
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj.AmmoSlots.head.Box, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj.AmmoSlots.head.Box.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj.AmmoSlots.head.Box.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskUnregister4Test extends ActorTest() {
|
||||
"RegisterVehicle" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Vehicle(GlobalDefinitions.fury)
|
||||
val obj_wep = obj.WeaponControlledFromSeat(0).get
|
||||
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get
|
||||
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
|
||||
val obj_trunk_ammo = obj.Trunk.Items(0).obj
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj_wep, "dynamic")
|
||||
guid.register(obj_wep_ammo, "dynamic")
|
||||
guid.register(obj_trunk_ammo, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_trunk_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterVehicle(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_trunk_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.guidtask
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
|
||||
import objects.ActorTest
|
||||
|
||||
class GUIDTaskUnregister5Test extends ActorTest() {
|
||||
"UnregisterAvatar" in {
|
||||
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val obj_wep = Tool(GlobalDefinitions.beamer)
|
||||
obj.Slot(0).Equipment = obj_wep
|
||||
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
|
||||
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj.Slot(6).Equipment = obj_inv_ammo
|
||||
val obj_locker = obj.Slot(5).Equipment.get
|
||||
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
|
||||
guid.register(obj, "dynamic")
|
||||
guid.register(obj_wep, "dynamic")
|
||||
guid.register(obj_wep_ammo, "dynamic")
|
||||
guid.register(obj_inv_ammo, "dynamic")
|
||||
guid.register(obj_locker, "dynamic")
|
||||
guid.register(obj_locker_ammo, "dynamic")
|
||||
|
||||
assert(obj.HasGUID)
|
||||
assert(obj_wep.HasGUID)
|
||||
assert(obj_wep_ammo.HasGUID)
|
||||
assert(obj_inv_ammo.HasGUID)
|
||||
assert(obj_locker.HasGUID)
|
||||
assert(obj_locker_ammo.HasGUID)
|
||||
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterAvatar(obj)(uns)))
|
||||
probe.expectMsg(scala.util.Success)
|
||||
assert(!obj.HasGUID)
|
||||
assert(!obj_wep.HasGUID)
|
||||
assert(!obj_wep_ammo.HasGUID)
|
||||
assert(!obj_inv_ammo.HasGUID)
|
||||
assert(!obj_locker.HasGUID)
|
||||
assert(!obj_locker_ammo.HasGUID)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class AirVehicleTerminalTest extends Specification {
|
||||
"Air_Vehicle_Terminal" should {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
|
||||
"construct" in {
|
||||
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
|
||||
terminal.Actor mustEqual ActorRef.noSender
|
||||
}
|
||||
|
||||
"player can buy a reaver ('lightgunship')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
|
||||
reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship
|
||||
reply2.loadout mustEqual Nil //TODO
|
||||
}
|
||||
|
||||
"player can not buy a fake vehicle ('reaver')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "reaver", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class DropshipVehicleTerminalTest extends Specification {
|
||||
"Dropship_Vehicle_Terminal" should {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
|
||||
"construct" in {
|
||||
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
|
||||
terminal.Actor mustEqual ActorRef.noSender
|
||||
}
|
||||
|
||||
"player can buy a galaxy ('dropship')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "dropship", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
|
||||
reply2.vehicle.Definition mustEqual GlobalDefinitions.dropship
|
||||
reply2.loadout mustEqual Nil //TODO
|
||||
}
|
||||
|
||||
"player can not buy a fake vehicle ('galaxy')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "galaxy", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class GroundVehicleTerminalTest extends Specification {
|
||||
"Ground_Vehicle_Terminal" should {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
|
||||
"construct" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
terminal.Actor mustEqual ActorRef.noSender
|
||||
}
|
||||
|
||||
"player can buy a harasser ('two_man_assault_buggy')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
|
||||
reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy
|
||||
reply2.loadout mustEqual Nil //TODO
|
||||
}
|
||||
|
||||
"player can not buy a fake vehicle ('harasser')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,3 +71,40 @@ class CertTerminalControl3Test extends ActorTest() {
|
|||
assert(reply2.response == Terminal.SellCertification(CertificationType.MediumAssault, 2))
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleTerminalControl1Test extends ActorTest() {
|
||||
"TerminalControl can be used to buy a vehicle ('two_man_assault_buggy')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Actor ! Terminal.Request(player, msg)
|
||||
val reply = receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply.isInstanceOf[Terminal.TerminalMessage])
|
||||
val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
|
||||
assert(reply2.player == player)
|
||||
assert(reply2.msg == msg)
|
||||
assert(reply2.response.isInstanceOf[Terminal.BuyVehicle])
|
||||
val reply3 = reply2.response.asInstanceOf[Terminal.BuyVehicle]
|
||||
assert(reply3.vehicle.Definition == GlobalDefinitions.two_man_assault_buggy)
|
||||
assert(reply3.loadout == Nil) //TODO
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleTerminalControl2Test extends ActorTest() {
|
||||
"TerminalControl can be used to warn about not buy a vehicle ('harasser')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
|
||||
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term")
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Actor ! Terminal.Request(player, msg)
|
||||
val reply = receiveOne(Duration.create(500, "ms"))
|
||||
assert(reply.isInstanceOf[Terminal.TerminalMessage])
|
||||
val reply2 = reply.asInstanceOf[Terminal.TerminalMessage]
|
||||
assert(reply2.player == player)
|
||||
assert(reply2.msg == msg)
|
||||
assert(reply2.response == Terminal.NoDeal())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package objects.terminal
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType}
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class VehicleTerminalCombinedTest extends Specification {
|
||||
"Ground_Vehicle_Terminal" should {
|
||||
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
|
||||
|
||||
"construct" in {
|
||||
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
||||
terminal.Actor mustEqual ActorRef.noSender
|
||||
}
|
||||
|
||||
"player can buy a ground vehicle, the harasser ('two_man_assault_buggy')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
|
||||
reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy
|
||||
reply2.loadout mustEqual Nil //TODO
|
||||
}
|
||||
|
||||
"player can buy a flying vehicle, the reaver ('lightgunship')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0))
|
||||
val reply = terminal.Request(player, msg)
|
||||
reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true
|
||||
val reply2 = reply.asInstanceOf[Terminal.BuyVehicle]
|
||||
reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship
|
||||
reply2.loadout mustEqual Nil //TODO
|
||||
}
|
||||
|
||||
"player can not buy a fake vehicle ('harasser')" in {
|
||||
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
|
||||
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0))
|
||||
|
||||
terminal.Request(player, msg) mustEqual Terminal.NoDeal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,8 @@ import com.typesafe.config.ConfigFactory
|
|||
import net.psforever.crypto.CryptoInterface
|
||||
import net.psforever.objects.zones._
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockObjectBuilder, TerminalObjectBuilder}
|
||||
import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockObjectBuilder, TerminalObjectBuilder, VehicleSpawnPadObjectBuilder}
|
||||
import net.psforever.types.Vector3
|
||||
import org.slf4j
|
||||
import org.fusesource.jansi.Ansi._
|
||||
import org.fusesource.jansi.Ansi.Color._
|
||||
|
|
@ -244,6 +245,10 @@ object PsLogin {
|
|||
LocalObject(TerminalObjectBuilder(order_terminal, 853))
|
||||
LocalObject(TerminalObjectBuilder(order_terminal, 855))
|
||||
LocalObject(TerminalObjectBuilder(order_terminal, 860))
|
||||
LocalObject(TerminalObjectBuilder(ground_vehicle_terminal, 1063))
|
||||
LocalObject(VehicleSpawnPadObjectBuilder(spawn_pad, 500)) //TODO guid not correct
|
||||
LocalObject(TerminalObjectBuilder(dropship_vehicle_terminal, 304))
|
||||
LocalObject(VehicleSpawnPadObjectBuilder(spawn_pad, 501)) //TODO guid not correct
|
||||
|
||||
LocalBases = 30
|
||||
|
||||
|
|
@ -251,8 +256,14 @@ object PsLogin {
|
|||
ObjectToBase(332, 29)
|
||||
ObjectToBase(556, 29)
|
||||
ObjectToBase(558, 29)
|
||||
ObjectToBase(1063, 29) //TODO unowned courtyard terminal?
|
||||
ObjectToBase(500, 29) //TODO unowned courtyard spawnpad?
|
||||
ObjectToBase(304, 29) //TODO unowned courtyard terminal?
|
||||
ObjectToBase(501, 29) //TODO unowned courtyard spawnpad?
|
||||
DoorToLock(330, 558)
|
||||
DoorToLock(332, 556)
|
||||
TerminalToSpawnPad(1063, 500)
|
||||
TerminalToSpawnPad(304, 501)
|
||||
}
|
||||
val home3 = new Zone("home3", map13, 13) {
|
||||
override def Init(implicit context : ActorContext) : Unit = {
|
||||
|
|
@ -261,6 +272,19 @@ object PsLogin {
|
|||
import net.psforever.types.PlanetSideEmpire
|
||||
Base(2).get.Faction = PlanetSideEmpire.VS //HART building C
|
||||
Base(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
|
||||
|
||||
GUID(500) match {
|
||||
case Some(pad) =>
|
||||
pad.Position = Vector3(3506.0f, 2820.0f, 92.0f)
|
||||
pad.Orientation = Vector3(0f, 0f, 270.0f)
|
||||
case None => ;
|
||||
}
|
||||
GUID(501) match {
|
||||
case Some(pad) =>
|
||||
pad.Position = Vector3(3508.9844f, 2895.961f, 92.296875f)
|
||||
pad.Orientation = Vector3(0f, 0f, 270.0f)
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
|||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, VehicleLockState}
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
|
||||
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
|
|
@ -45,25 +46,43 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var continent : Zone = null
|
||||
var progressBarValue : Option[Float] = None
|
||||
|
||||
var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable
|
||||
var progressBarUpdate : Cancellable = WorldSessionActor.DefaultCancellable
|
||||
var clientKeepAlive : Cancellable = DefaultCancellable.obj
|
||||
var progressBarUpdate : Cancellable = DefaultCancellable.obj
|
||||
|
||||
override def postStop() = {
|
||||
if(clientKeepAlive != null)
|
||||
clientKeepAlive.cancel()
|
||||
localService ! Service.Leave()
|
||||
vehicleService ! Service.Leave()
|
||||
avatarService ! Service.Leave()
|
||||
LivePlayerList.Remove(sessionId) match {
|
||||
case Some(tplayer) =>
|
||||
tplayer.VehicleSeated match {
|
||||
case Some(vehicle_guid) =>
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true))
|
||||
case None => ;
|
||||
}
|
||||
tplayer.VehicleOwned match {
|
||||
case Some(vehicle_guid) =>
|
||||
continent.GUID(vehicle_guid) match {
|
||||
case Some(vehicle : Vehicle) =>
|
||||
vehicle.Owner = None
|
||||
//TODO temporary solution; to un-own, permit driver seat to Empire access level
|
||||
vehicle.PermissionGroup(10, VehicleLockState.Empire.id)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(tplayer.GUID, vehicle_guid, 10, VehicleLockState.Empire.id))
|
||||
case _ => ;
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
|
||||
avatarService ! Service.Leave()
|
||||
localService ! Service.Leave()
|
||||
vehicleService ! Service.Leave()
|
||||
LivePlayerList.Remove(sessionId) match {
|
||||
case Some(tplayer) =>
|
||||
if(tplayer.HasGUID) {
|
||||
val guid = tplayer.GUID
|
||||
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID)
|
||||
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
|
||||
}
|
||||
case None => ;
|
||||
if(tplayer.HasGUID) {
|
||||
val guid = tplayer.GUID
|
||||
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID)
|
||||
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
|
||||
}
|
||||
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +143,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(guid, suit, subtype)))
|
||||
}
|
||||
|
||||
case AvatarResponse.ConcealPlayer() =>
|
||||
if(player.GUID != guid) {
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(guid, 36)))
|
||||
}
|
||||
|
||||
case AvatarResponse.EquipmentInHand(slot, item) =>
|
||||
if(player.GUID != guid) {
|
||||
val definition = item.Definition
|
||||
|
|
@ -186,7 +210,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
else {
|
||||
val before = player.lastSeenStreamMessage(guid.guid)
|
||||
val dist = WorldSessionActor.DistanceSquared(player.Position, msg.pos)
|
||||
val dist = Vector3.DistanceSquared(player.Position, msg.pos)
|
||||
(msg.pos, now - before, dist)
|
||||
}
|
||||
|
||||
|
|
@ -280,9 +304,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case VehicleResponse.MountVehicle(vehicle_guid, seat) =>
|
||||
if(player.GUID != guid) {
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, guid, seat)))
|
||||
if(player.VehicleOwned.contains(vehicle_guid)) { //simplistic vehicle ownership management
|
||||
player.VehicleOwned = None
|
||||
}
|
||||
}
|
||||
|
||||
case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) =>
|
||||
|
|
@ -524,6 +545,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false)))
|
||||
}
|
||||
|
||||
case Terminal.BuyVehicle(vehicle, loadout) =>
|
||||
continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match {
|
||||
case Some(pad_guid) =>
|
||||
val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad]
|
||||
vehicle.Faction = tplayer.Faction
|
||||
vehicle.Position = pad.Position
|
||||
vehicle.Orientation = pad.Orientation
|
||||
taskResolver ! RegisterNewVehicle(vehicle, pad)
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true)))
|
||||
case None =>
|
||||
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
|
||||
}
|
||||
|
||||
case Terminal.NoDeal() =>
|
||||
log.warn(s"$tplayer made a request but the terminal rejected the order $msg")
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false)))
|
||||
|
|
@ -533,9 +567,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
reply match {
|
||||
case Vehicle.CanSeatPlayer(vehicle, seat_num) =>
|
||||
log.info(s"MountVehicleMsg: ${player.GUID} mounts ${vehicle.GUID} @ $seat_num")
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle.GUID) //clear all deconstruction timers
|
||||
val vehicle_guid : PlanetSideGUID = vehicle.GUID
|
||||
tplayer.VehicleSeated = Some(vehicle_guid)
|
||||
if(seat_num == 0) { //simplistic vehicle ownership management
|
||||
vehicle.Owner match {
|
||||
case Some(owner_guid) =>
|
||||
continent.GUID(owner_guid) match {
|
||||
case Some(previous_owner : Player) =>
|
||||
if(previous_owner.VehicleOwned.contains(vehicle_guid)) {
|
||||
previous_owner.VehicleOwned = None //simplistic ownership management, player loses vehicle ownership
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
player.VehicleOwned = Some(vehicle_guid)
|
||||
vehicle.Owner = Some(player.GUID)
|
||||
}
|
||||
|
|
@ -558,12 +604,52 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, vehicle_guid, seat_num))
|
||||
|
||||
case Vehicle.CannotSeatPlayer(vehicle, seat_num) =>
|
||||
val seat : Seat = vehicle.Seat(seat_num).get
|
||||
log.warn(s"MountVehicleMsg: player $tplayer attempted to board vehicle ${vehicle.GUID}'s seat $seat_num, but was not allowed")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case VehicleSpawnPad.ConcealPlayer =>
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(player.GUID, 36)))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ConcealPlayer(player.GUID))
|
||||
|
||||
case VehicleSpawnPad.LoadVehicle(vehicle, _/*pad*/) =>
|
||||
val player_guid = player.GUID
|
||||
val definition = vehicle.Definition
|
||||
val objedtId = definition.ObjectId
|
||||
val vehicle_guid = vehicle.GUID
|
||||
val vdata = definition.Packet.ConstructorData(vehicle).get
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(objedtId, vehicle_guid, vdata)))
|
||||
continent.Transport ! Zone.SpawnVehicle(vehicle)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata))
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 22, 1L))) //mount points off?
|
||||
//sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid))) //fte and ownership?
|
||||
//sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, player_guid, 0)))
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //temporary drive away from pad delay
|
||||
vehicle.Actor ! Vehicle.TrySeatPlayer(0, player)
|
||||
|
||||
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) =>
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //sitting in the vehicle clears the drive away delay
|
||||
val vehicle_guid = vehicle.GUID
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 22, 0L))) //mount points on?
|
||||
//sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 0, vehicle.Definition.MaxHealth)))
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 68, 0L))) //???
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 113, 0L))) //???
|
||||
ReloadVehicleAccessPermissions(vehicle)
|
||||
|
||||
case VehicleSpawnPad.SpawnPadBlockedWarning(vehicle, warning_count) =>
|
||||
if(warning_count > 2) {
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, TriggerSoundMessage(TriggeredSound.Unknown14, vehicle.Position, 20, 1f)))
|
||||
sendResponse(PacketCoding.CreateGamePacket(0,
|
||||
ChatMsg(ChatMessageType.CMT_TELL, true, "", "\\#FYour vehicle is blocking the spawn pad, and will be deconstructed if not moved.", None))
|
||||
)
|
||||
}
|
||||
|
||||
case VehicleSpawnPad.SpawnPadUnblocked(vehicle_guid) =>
|
||||
//vehicle has moved away from spawn pad after initial spawn
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel temporary drive away from pad delay
|
||||
|
||||
case ListAccountCharacters =>
|
||||
import net.psforever.objects.definition.converter.CharacterSelectConverter
|
||||
val gen : AtomicInteger = new AtomicInteger(1)
|
||||
|
|
@ -605,15 +691,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
failWithError(s"$tplayer failed to load anywhere")
|
||||
}
|
||||
|
||||
case VehicleLoaded(vehicle) =>
|
||||
val definition = vehicle.Definition
|
||||
val objedtId = definition.ObjectId
|
||||
val vehicle_guid = vehicle.GUID
|
||||
val vdata = definition.Packet.ConstructorData(vehicle).get
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(objedtId, vehicle_guid, vdata)))
|
||||
ReloadVehicleAccessPermissions(vehicle)
|
||||
continent.Transport ! Zone.SpawnVehicle(vehicle)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player.GUID, vehicle, objedtId, vehicle_guid, vdata))
|
||||
case VehicleLoaded(_/*vehicle*/) => ;
|
||||
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
|
||||
|
||||
case Zone.ClientInitialization(/*initList*/_) =>
|
||||
//TODO iterate over initList; for now, just do this
|
||||
|
|
@ -795,7 +874,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
var player : Player = null
|
||||
var harasser : Vehicle = null //TODO used in testing
|
||||
|
||||
def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
|
||||
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
|
||||
|
|
@ -803,18 +881,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.info(s"New world login to $server with Token:$token. $clientVersion")
|
||||
//TODO begin temp player character auto-loading; remove later
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
val
|
||||
beamer1 = Tool(beamer)
|
||||
beamer1.AmmoSlots.head.Box = AmmoBox(energy_cell, 16)
|
||||
val
|
||||
suppressor1 = Tool(suppressor)
|
||||
suppressor1.AmmoSlots.head.Box = AmmoBox(bullet_9mm, 25)
|
||||
val
|
||||
forceblade1 = Tool(forceblade)
|
||||
forceblade1.AmmoSlots.head.Box = AmmoBox(melee_ammo)
|
||||
|
||||
player = Player("TestCharacter"+sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
|
||||
player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||
//player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||
player.Position = Vector3(3523.039f, 2855.5078f, 90.859375f)
|
||||
player.Orientation = Vector3(0f, 0f, 90f)
|
||||
player.Certifications += CertificationType.StandardAssault
|
||||
player.Certifications += CertificationType.MediumAssault
|
||||
|
|
@ -823,9 +892,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player.Certifications += CertificationType.ReinforcedExoSuit
|
||||
player.Certifications += CertificationType.ATV
|
||||
player.Certifications += CertificationType.Harasser
|
||||
player.Slot(0).Equipment = beamer1
|
||||
player.Slot(2).Equipment = suppressor1
|
||||
player.Slot(4).Equipment = forceblade1
|
||||
//
|
||||
player.Certifications += CertificationType.GroundSupport
|
||||
player.Certifications += CertificationType.GroundTransport
|
||||
player.Certifications += CertificationType.Flail
|
||||
player.Certifications += CertificationType.Switchblade
|
||||
player.Certifications += CertificationType.AssaultBuggy
|
||||
player.Certifications += CertificationType.ArmoredAssault1
|
||||
player.Certifications += CertificationType.ArmoredAssault2
|
||||
player.Certifications += CertificationType.AirCavalryScout
|
||||
player.Certifications += CertificationType.AirCavalryAssault
|
||||
player.Certifications += CertificationType.AirCavalryInterceptor
|
||||
player.Certifications += CertificationType.AirSupport
|
||||
player.Certifications += CertificationType.GalaxyGunship
|
||||
player.Certifications += CertificationType.Phantasm
|
||||
//player.ExoSuit = ExoSuitType.Infiltrator
|
||||
player.Slot(0).Equipment = Tool(beamer)
|
||||
player.Slot(2).Equipment = Tool(suppressor)
|
||||
player.Slot(4).Equipment = Tool(forceblade)
|
||||
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
|
||||
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
|
||||
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
|
||||
|
|
@ -917,21 +1001,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
})
|
||||
ReloadVehicleAccessPermissions(vehicle)
|
||||
})
|
||||
//TODO begin temp vehicle auto-loading
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
if(continent.Vehicles.isEmpty) {
|
||||
harasser = Vehicle(two_man_assault_buggy)
|
||||
harasser.Position = Vector3(3674.8438f, 2730.789f, 91.15625f)
|
||||
harasser.Faction = PlanetSideEmpire.VS
|
||||
harasser.Orientation = Vector3(0f, 0f, 90f)
|
||||
harasser.Weapons(2).Equipment.get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(bullet_12mm, 150)
|
||||
harasser.Trunk += 30 -> AmmoBox(bullet_12mm, 100)
|
||||
taskResolver ! RegisterNewVehicle(harasser)
|
||||
}
|
||||
else {
|
||||
harasser = continent.Vehicles.head //subsequent players after first
|
||||
}
|
||||
//TODO end temp vehicle auto-loading
|
||||
avatarService ! Service.Join(player.Continent)
|
||||
localService ! Service.Join(player.Continent)
|
||||
vehicleService ! Service.Join(player.Continent)
|
||||
|
|
@ -1129,6 +1198,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.GUID(object_guid) match {
|
||||
case Some(vehicle : Vehicle) =>
|
||||
if(player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) {
|
||||
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(object_guid)
|
||||
vehicleService ! VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent)
|
||||
log.info(s"RequestDestroy: vehicle $object_guid")
|
||||
}
|
||||
|
|
@ -1157,14 +1227,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(object_guid, 0)))
|
||||
log.info("ObjectDelete: " + msg)
|
||||
|
||||
case msg @ MoveItemMessage(item_guid, avatar_guid_1, avatar_guid_2, dest, unk1) =>
|
||||
case msg @ MoveItemMessage(item_guid, source_guid, destination_guid, dest, unk1) =>
|
||||
player.Find(item_guid) match {
|
||||
case Some(index) =>
|
||||
val indexSlot = player.Slot(index)
|
||||
var itemOpt = indexSlot.Equipment //use this to short circuit
|
||||
var itemOpt : Option[Equipment] = indexSlot.Equipment
|
||||
//use this to short circuit
|
||||
val item = itemOpt.get
|
||||
val destSlot = player.Slot(dest)
|
||||
|
||||
val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) {
|
||||
destSlot.Equipment match {
|
||||
case Some(found) =>
|
||||
|
|
@ -1181,20 +1251,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case Success(_) | scala.util.Failure(_) => itemOpt = None; None //abort item move altogether
|
||||
}
|
||||
}
|
||||
|
||||
if(itemOpt.isDefined) {
|
||||
log.info(s"MoveItem: $item_guid moved from $avatar_guid_1 @ $index to $avatar_guid_1 @ $dest")
|
||||
log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $source_guid @ $dest")
|
||||
indexSlot.Equipment = None
|
||||
destItem match { //do we have a swap item?
|
||||
destItem match {
|
||||
//do we have a swap item?
|
||||
case Some(entry) => //yes, swap
|
||||
val item2 = entry.obj
|
||||
player.Slot(entry.start).Equipment = None //remove item2 to make room for item
|
||||
destSlot.Equipment = item //in case dest and index could block each other
|
||||
(indexSlot.Equipment = entry.obj) match {
|
||||
case Some(_) => //item and item2 swapped places successfully
|
||||
log.info(s"MoveItem: ${item2.GUID} swapped to $avatar_guid_1 @ $index")
|
||||
log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index")
|
||||
//we must shuffle items around cleanly to avoid causing icons to "disappear"
|
||||
if(index == Player.FreeHandSlot) { //temporarily put in safe location, A -> C
|
||||
if(index == Player.FreeHandSlot) {
|
||||
//temporarily put in safe location, A -> C
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground
|
||||
}
|
||||
else {
|
||||
|
|
@ -1217,13 +1288,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case None => //just move item over
|
||||
destSlot.Equipment = item
|
||||
}
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(avatar_guid_1, item_guid, dest)))
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item_guid, dest)))
|
||||
if(0 <= dest && dest < 5) {
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
log.info(s"MoveItem: $avatar_guid_1 wanted to move the item $item_guid but could not find it")
|
||||
log.info(s"MoveItem: $source_guid wanted to move the item $item_guid but could not find it")
|
||||
}
|
||||
|
||||
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
|
||||
|
|
@ -1370,6 +1441,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
//TODO optimize this later
|
||||
log.info(s"DismountVehicleMsg: $msg")
|
||||
if(player.GUID == player_guid) {
|
||||
//common warning for this section
|
||||
def dismountWarning(msg : String) : Unit = {
|
||||
log.warn(s"$msg; a vehicle may not know that a player is no longer sitting it in")
|
||||
}
|
||||
//normally disembarking from a seat
|
||||
player.VehicleSeated match {
|
||||
case Some(vehicle_guid) =>
|
||||
|
|
@ -1377,23 +1452,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case Some(obj : Vehicle) =>
|
||||
obj.Seats.find(seat => seat.Occupant.contains(player)) match {
|
||||
case Some(seat) =>
|
||||
val vel = obj.Velocity.getOrElse(new Vector3(0f, 0f, 0f))
|
||||
val total_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt
|
||||
if(seat.Bailable || obj.Velocity.isEmpty || total_vel == 0) {
|
||||
val vel = obj.Velocity.getOrElse(Vector3(0f, 0f, 0f))
|
||||
val has_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt
|
||||
if(seat.Bailable || obj.Velocity.isEmpty || has_vel == 0) { //ugh, float comparison
|
||||
seat.Occupant = None
|
||||
player.VehicleSeated = None
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2))) //should be safe; replace with ObjectDetachMessage later
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
|
||||
if(obj.Seats.count(seat => seat.isOccupied) == 0) {
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
|
||||
dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
|
||||
}
|
||||
case _ =>
|
||||
log.warn(s"DismountVehicleMsg: can not find vehicle $vehicle_guid")
|
||||
dismountWarning(s"DismountVehicleMsg: can not find vehicle $vehicle_guid")
|
||||
}
|
||||
case None =>
|
||||
log.warn(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle")
|
||||
dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle")
|
||||
}
|
||||
//should be safe
|
||||
player.VehicleSeated = None
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2)))
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2))
|
||||
}
|
||||
else {
|
||||
//kicking someone else out of a seat; need to own that seat
|
||||
|
|
@ -1409,6 +1488,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
seat.Occupant = None
|
||||
tplayer.VehicleSeated = None
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2))
|
||||
if(obj.Seats.count(seat => seat.isOccupied) == 0) {
|
||||
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
|
||||
}
|
||||
case None =>
|
||||
log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
|
||||
}
|
||||
|
|
@ -1733,12 +1815,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
* @see `RegisterVehicle`
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def RegisterNewVehicle(obj : Vehicle) : TaskResolver.GiveTask = {
|
||||
def RegisterNewVehicle(obj : Vehicle, pad : VehicleSpawnPad) : TaskResolver.GiveTask = {
|
||||
TaskResolver.GiveTask(
|
||||
new Task() {
|
||||
private val localVehicle = obj
|
||||
private val localAnnounce = vehicleService
|
||||
private val localSession : String = sessionId.toString
|
||||
private val localPad = pad.Actor
|
||||
private val localPlayer = player
|
||||
private val localVehicleService = vehicleService
|
||||
private val localZone = continent
|
||||
|
||||
override def isComplete : Task.Resolution.Value = {
|
||||
if(localVehicle.Actor != ActorRef.noSender) {
|
||||
|
|
@ -1751,6 +1837,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
localAnnounce ! VehicleServiceMessage.GiveActorControl(obj, localSession)
|
||||
localPad ! VehicleSpawnPad.VehicleOrder(localPlayer, localVehicle)
|
||||
localVehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(localVehicle, localZone, 60L)
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
}, List(RegisterVehicle(obj)))
|
||||
|
|
@ -1945,35 +2033,4 @@ object WorldSessionActor {
|
|||
delta : Float,
|
||||
completeAction : () => Unit,
|
||||
tickAction : Option[() => Unit] = None)
|
||||
/**
|
||||
* A placeholder `Cancellable` object.
|
||||
*/
|
||||
private final val DefaultCancellable = new Cancellable() {
|
||||
def cancel : Boolean = true
|
||||
def isCancelled() : Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the actual distance between two points.
|
||||
* @param pos1 the first point
|
||||
* @param pos2 the second point
|
||||
* @return the distance
|
||||
*/
|
||||
def Distance(pos1 : Vector3, pos2 : Vector3) : Float = {
|
||||
math.sqrt(DistanceSquared(pos1, pos2)).toFloat
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the squared distance between two points.
|
||||
* Though some time is saved care must be taken that any comparative distance is also squared.
|
||||
* @param pos1 the first point
|
||||
* @param pos2 the second point
|
||||
* @return the distance
|
||||
*/
|
||||
def DistanceSquared(pos1 : Vector3, pos2 : Vector3) : Float = {
|
||||
val dx : Float = pos1.x - pos2.x
|
||||
val dy : Float = pos1.y - pos2.y
|
||||
val dz : Float = pos1.z - pos2.z
|
||||
(dx * dx) + (dy * dy) + (dz * dz)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ object AvatarAction {
|
|||
trait Action
|
||||
|
||||
final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action
|
||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Action
|
||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||
final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item : Equipment) extends Action
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ object AvatarResponse {
|
|||
trait Response
|
||||
|
||||
final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response
|
||||
final case class ConcealPlayer() extends Response
|
||||
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response
|
||||
final case class EquipmentInHand(slot : Int, item : Equipment) extends Response
|
||||
final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item : Equipment) extends Response
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype))
|
||||
)
|
||||
case AvatarAction.ConcealPlayer(player_guid) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
|
||||
)
|
||||
case AvatarAction.EquipmentInHand(player_guid, slot, obj) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(slot, obj))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -16,7 +17,7 @@ import scala.concurrent.duration._
|
|||
*/
|
||||
class DoorCloseActor() extends Actor {
|
||||
/** The periodic `Executor` that checks for doors to be closed */
|
||||
private var doorCloserTrigger : Cancellable = DoorCloseActor.DefaultCloser
|
||||
private var doorCloserTrigger : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently open doors */
|
||||
private var openDoors : List[DoorCloseActor.DoorEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
|
@ -98,11 +99,6 @@ object DoorCloseActor {
|
|||
/** The wait before an open door closes; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
private final val DefaultCloser : Cancellable = new Cancellable() {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Message that carries information about a door that has been opened.
|
||||
* @param door the door object
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -16,7 +17,7 @@ import scala.concurrent.duration._
|
|||
*/
|
||||
class HackClearActor() extends Actor {
|
||||
/** The periodic `Executor` that checks for server objects to be unhacked */
|
||||
private var clearTrigger : Cancellable = HackClearActor.DefaultClearer
|
||||
private var clearTrigger : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently hacked server objects */
|
||||
private var hackedObjects : List[HackClearActor.HackEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
|
@ -99,11 +100,6 @@ object HackClearActor {
|
|||
/** The wait before a server object is to unhack; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
private final val DefaultClearer : Cancellable = new Cancellable() {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Message that carries information about a server object that has been hacked.
|
||||
* @param target the server object
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
package services.vehicle
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.vehicle.support.{DeconstructionActor, VehicleContextActor}
|
||||
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor}
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class VehicleService extends Actor {
|
||||
private val vehicleContext : ActorRef = context.actorOf(Props[VehicleContextActor], "vehicle-context-root")
|
||||
private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent")
|
||||
private val vehicleDelayedDecon : ActorRef = context.actorOf(Props[DelayedDeconstructionActor], "vehicle-delayed-decon-agent")
|
||||
vehicleDecon ! DeconstructionActor.RequestTaskResolver
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
|
|
@ -79,6 +80,14 @@ class VehicleService extends Actor {
|
|||
case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
|
||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
|
||||
|
||||
//message to DelayedDeconstructionActor
|
||||
case VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, zone, timeAlive) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive)
|
||||
|
||||
//message to DelayedDeconstructionActor
|
||||
case VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid)
|
||||
|
||||
//response from DeconstructionActor
|
||||
case DeconstructionActor.DeleteVehicle(vehicle_guid, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package services.vehicle
|
|||
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action)
|
||||
|
||||
object VehicleServiceMessage {
|
||||
final case class DelayedVehicleDeconstruction(vehicle : Vehicle, continent : Zone, timeAlive : Long)
|
||||
final case class GiveActorControl(vehicle : Vehicle, actorName : String)
|
||||
final case class RevokeActorControl(vehicle : Vehicle)
|
||||
final case class RequestDeleteVehicle(vehicle : Vehicle, continent : Zone)
|
||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.{DefaultCancellable, Vehicle}
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.objects.zones.Zone
|
||||
|
|
@ -20,11 +20,13 @@ import scala.concurrent.duration._
|
|||
* A reference to a vehicle should be passed to this object as soon as it is going to be cleaned-up from the game world.
|
||||
* Once accepted, only a few seconds will remain before the vehicle is deleted.
|
||||
* To ensure that no players are lost in the deletion, all occupants of the vehicle are kicked out.
|
||||
* Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.
|
||||
* Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class DeconstructionActor extends Actor {
|
||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
||||
private var scrappingProcess : Cancellable = DeconstructionActor.DefaultProcess
|
||||
private var scrappingProcess : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently doomed vehicles */
|
||||
private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil
|
||||
/** The manager that helps unregister the vehicle from its current GUID scope */
|
||||
|
|
@ -59,7 +61,9 @@ class DeconstructionActor extends Actor {
|
|||
case Some(tplayer) =>
|
||||
seat.Occupant = None
|
||||
tplayer.VehicleSeated = None
|
||||
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
|
||||
if(tplayer.HasGUID) {
|
||||
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false))
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
|
|
@ -171,11 +175,6 @@ object DeconstructionActor {
|
|||
/** The wait before completely deleting a vehicle; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
private final val DefaultProcess : Cancellable = new Cancellable() {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
final case class RequestTaskResolver()
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, Vehicle}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Maintain and curate a list of timed `vehicle` object deconstruction tasks.<br>
|
||||
* <br>
|
||||
* These tasks are queued or dismissed by player activity but they are executed independent of player activity.
|
||||
* A common disconnected cause of deconstruction is neglect for an extended period of time.
|
||||
* At that point, the original owner of the vehicle no longer matters.
|
||||
* Deconstruction neglect, however, is averted by having someone become seated.
|
||||
* A realized deconstruction is entirely based on a fixed interval after an unresolved request has been received.
|
||||
* The actual process of deconstructing the vehicle and cleaning up its resources is performed by an external agent.<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class DelayedDeconstructionActor extends Actor {
|
||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
||||
private var monitor : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently doomed vehicles */
|
||||
private var vehicles : List[DelayedDeconstructionActor.VehicleEntry] = Nil
|
||||
private[this] val log = org.log4s.getLogger
|
||||
private[this] def trace(msg : String) : Unit = log.trace(msg)
|
||||
|
||||
|
||||
def receive : Receive = {
|
||||
case DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) =>
|
||||
trace(s"delayed deconstruction order for $vehicle in $timeAlive")
|
||||
vehicles = vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L)
|
||||
if(vehicles.size == 1) { //we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling)
|
||||
}
|
||||
|
||||
case DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) =>
|
||||
//all tasks for this vehicle are cleared from the queue
|
||||
//clear any task that is no longer valid by determination of unregistered GUID
|
||||
val before = vehicles.length
|
||||
vehicles = vehicles.filter(entry => { !entry.vehicle.HasGUID || entry.vehicle.GUID != vehicle_guid })
|
||||
trace(s"attempting to clear deconstruction order for vehicle $vehicle_guid, found ${before - vehicles.length}")
|
||||
if(vehicles.isEmpty) {
|
||||
monitor.cancel
|
||||
}
|
||||
|
||||
case DelayedDeconstructionActor.PeriodicTaskCulling =>
|
||||
//filter the list of deconstruction tasks for any that are need to be triggered
|
||||
monitor.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (vehiclesToDecon, vehiclesRemain) = vehicles.partition(entry => { now - entry.logTime >= entry.survivalTime })
|
||||
vehicles = vehiclesRemain
|
||||
trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found")
|
||||
vehiclesToDecon.foreach(entry => { context.parent ! VehicleServiceMessage.RequestDeleteVehicle(entry.vehicle, entry.zone) })
|
||||
if(vehiclesRemain.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object DelayedDeconstructionActor {
|
||||
/**
|
||||
* Timer for the repeating executor.
|
||||
*/
|
||||
private final val periodicTest : FiniteDuration = 5000000000L nanoseconds //5s
|
||||
|
||||
/**
|
||||
* Queue a future vehicle deconstruction action.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` that the vehicle currently occupies
|
||||
* @param survivalTime how long until the vehicle will be deconstructed in seconds
|
||||
*/
|
||||
final case class ScheduleDeconstruction(vehicle : Vehicle, zone : Zone, survivalTime : Long)
|
||||
|
||||
/**
|
||||
* Dequeue a vehicle from being deconstructed.
|
||||
* @param vehicle_guid the vehicle
|
||||
*/
|
||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
||||
|
||||
/**
|
||||
* A message the `Actor` sends to itself.
|
||||
* The trigger for the periodic deconstruction task.
|
||||
*/
|
||||
private final case class PeriodicTaskCulling()
|
||||
|
||||
/**
|
||||
* An entry that stores vehicle deconstruction tasks.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` that the vehicle currently occupies
|
||||
* @param survivalTime how long until the vehicle will be deconstructed in nanoseconds
|
||||
* @param logTime when this deconstruction request was initially created in nanoseconds;
|
||||
* initialized by default to a "now"
|
||||
*/
|
||||
private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, survivalTime : Long, logTime : Long = System.nanoTime())
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
|
|
@ -15,15 +15,18 @@ import services.vehicle.VehicleServiceMessage
|
|||
* <br>
|
||||
* The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation.
|
||||
* It is also be allowed to be responsible for cleaning up that context.
|
||||
* (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)
|
||||
* (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class VehicleContextActor() extends Actor {
|
||||
def receive : Receive = {
|
||||
case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
|
||||
vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName")
|
||||
vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName.${System.nanoTime()}")
|
||||
|
||||
case VehicleServiceMessage.RevokeActorControl(vehicle) =>
|
||||
vehicle.Actor ! akka.actor.PoisonPill
|
||||
vehicle.Actor = ActorRef.noSender
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue