mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 07:24:53 +00:00
Deployables (#230)
* functions for certifcation ui updates (that don't work) * initialization of combat engineering deployables ui on load and certification change * representation classes for ACE and FDU; ability to pull ACE and FDU from equipment terminals * ammo change functionality and fire mode change functionality for ConstructionItems refactored from Tool operations and supported properly (switch between deployable options) * zone-specific structure for keeping track of deployables; abaility to dismiss deployables from the map screen (previous functionality); local client creation of explosive-type deployables * refactored MannedTurret into FacilityTurret and lesser traits to be used in the deployable spitfires and the OMFT's; all ACE deployables are available for placement; partial management of the construction items after the deployable is placed; boomers create boomer triggers * Avatar-specific storage for deployables and for updating UI elements * refactored quite a bit of code in WSA for the benefit of deployable management; refinements to deployable creation; server messages about deployable quantities; corrected the FDU encoding pattern; lots of work dedicated just to synchronizing BoomerTrigger objects * added RemoverActor for deployables and redistributed deconstruction functionality away from WSA to new DeployableRemover; added events to facilitate activities not inheritable with this model * refactored and distributed Deployables classes; copious amounts of testing and document-writing * boomers now explode from trigger; support for deployables being destroyed by weapon discharge, including individual health, soure identification, and damage model; shuffled deployable classes to build different hierarchy * sensor_shield was skipped by accident * identified stray object in Hanish, Ishundar, and added Irkalla, Ishundar's capture console; fixed issue with Warp command and 'Irkalla'; modified building amenity setup and setup testing in Zone; players load and die properly when seated in an omft; reserve ammunition in omft properly registered * added local service channel, capture consoles, fixed tests as much as posible * fixed LocalService tests by booting the ServiceManager; added avatar and local tests * a simple attempt to refactor Actor messages in a way that is acceptable to Travis CI * making the explosive deployables vanish upon explosion; sensor health bars are now supported
This commit is contained in:
parent
6cd18c5623
commit
5f3e7e5df8
113 changed files with 8155 additions and 2312 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.DeployableToolbox
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
||||||
import net.psforever.objects.equipment.EquipmentSize
|
import net.psforever.objects.equipment.EquipmentSize
|
||||||
import net.psforever.objects.loadouts.Loadout
|
import net.psforever.objects.loadouts.Loadout
|
||||||
|
|
@ -37,6 +38,8 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val deployables : DeployableToolbox = new DeployableToolbox
|
||||||
|
|
||||||
def BEP : Long = bep
|
def BEP : Long = bep
|
||||||
|
|
||||||
def BEP_=(battleExperiencePoints : Long) : Long = {
|
def BEP_=(battleExperiencePoints : Long) : Long = {
|
||||||
|
|
@ -177,6 +180,8 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def Deployables : DeployableToolbox = deployables
|
||||||
|
|
||||||
def Definition : AvatarDefinition = GlobalDefinitions.avatar
|
def Definition : AvatarDefinition = GlobalDefinitions.avatar
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
|
||||||
|
class BoomerDeployable(cdef : DeployableDefinition) extends ExplosiveDeployable(cdef) {
|
||||||
|
private var trigger : Option[BoomerTrigger] = None
|
||||||
|
|
||||||
|
def Trigger : Option[BoomerTrigger] = trigger
|
||||||
|
|
||||||
|
def Trigger_=(item : BoomerTrigger) : Option[BoomerTrigger] = {
|
||||||
|
if(trigger.isEmpty) { //can only set trigger once
|
||||||
|
trigger = Some(item)
|
||||||
|
}
|
||||||
|
Trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
def Trigger_=(item : Option[BoomerTrigger]) : Option[BoomerTrigger] = {
|
||||||
|
if(item.isEmpty) {
|
||||||
|
trigger = None
|
||||||
|
}
|
||||||
|
Trigger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.equipment.RemoteUnit
|
||||||
|
|
||||||
|
class BoomerTrigger extends SimpleItem(GlobalDefinitions.boomer_trigger) with RemoteUnit
|
||||||
|
|
@ -1,26 +1,61 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.definition.ConstructionItemDefinition
|
import net.psforever.objects.ce.DeployedItem
|
||||||
import net.psforever.objects.equipment.{CItem, Equipment, FireModeSwitch}
|
import net.psforever.objects.definition.{ConstructionFireMode, ConstructionItemDefinition}
|
||||||
|
import net.psforever.objects.equipment.{Equipment, FireModeSwitch}
|
||||||
|
import net.psforever.types.CertificationType
|
||||||
|
|
||||||
class ConstructionItem(private val cItemDef : ConstructionItemDefinition) extends Equipment with FireModeSwitch[CItem.DeployedItem.Value] {
|
/**
|
||||||
|
* A type of `Equipment` that can be wielded and applied to the game world to produce other game objects.<br>
|
||||||
|
* <br>
|
||||||
|
* Functionally, `ConstructionItem` objects resemble `Tool` objects that have fire mode state and alternate "ammunition."
|
||||||
|
* Very much unlike `Tool` object counterparts, however,
|
||||||
|
* the alternate "ammunition" is also a type of fire mode state
|
||||||
|
* maintained in a two-dimensional grid of related states.
|
||||||
|
* These states represent output products called deployables or, in the common vernacular, CE.
|
||||||
|
* Also unlike `Tool` objects, whose ammunition is always available even when drawing the weapon is not permitted,
|
||||||
|
* the different states are not all available if just the equipment itself is available.
|
||||||
|
* Parameters along with these CE states
|
||||||
|
* indicate whether the current output product is something the player is permitted to utilize.
|
||||||
|
* @param cItemDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
|
||||||
|
*/
|
||||||
|
class ConstructionItem(private val cItemDef : ConstructionItemDefinition) extends Equipment
|
||||||
|
with FireModeSwitch[ConstructionFireMode] {
|
||||||
private var fireModeIndex : Int = 0
|
private var fireModeIndex : Int = 0
|
||||||
|
private var ammoTypeIndex : Int = 0
|
||||||
|
|
||||||
def FireModeIndex : Int = fireModeIndex
|
def FireModeIndex : Int = fireModeIndex
|
||||||
|
|
||||||
def FireModeIndex_=(index : Int) : Int = {
|
def FireModeIndex_=(index : Int) : Int = {
|
||||||
fireModeIndex = index % cItemDef.Modes.length
|
fireModeIndex = index % Definition.Modes.length
|
||||||
FireModeIndex
|
FireModeIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
def FireMode : CItem.DeployedItem.Value = cItemDef.Modes(fireModeIndex)
|
def FireMode : ConstructionFireMode = Definition.Modes(fireModeIndex)
|
||||||
|
|
||||||
def NextFireMode : CItem.DeployedItem.Value = {
|
def NextFireMode : ConstructionFireMode = {
|
||||||
FireModeIndex = FireModeIndex + 1
|
FireModeIndex = FireModeIndex + 1
|
||||||
|
ammoTypeIndex = 0
|
||||||
FireMode
|
FireMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def AmmoTypeIndex : Int = ammoTypeIndex
|
||||||
|
|
||||||
|
def AmmoTypeIndex_=(index : Int) : Int = {
|
||||||
|
ammoTypeIndex = index % FireMode.Deployables.length
|
||||||
|
AmmoTypeIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
def AmmoType : DeployedItem.Value = FireMode.Deployables(ammoTypeIndex)
|
||||||
|
|
||||||
|
def NextAmmoType : DeployedItem.Value = {
|
||||||
|
AmmoTypeIndex = AmmoTypeIndex + 1
|
||||||
|
FireMode.Deployables(ammoTypeIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
def ModePermissions : Set[CertificationType.Value] = FireMode.Permissions(ammoTypeIndex)
|
||||||
|
|
||||||
def Definition : ConstructionItemDefinition = cItemDef
|
def Definition : ConstructionItemDefinition = cItemDef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||||
|
|
||||||
|
object Deployables {
|
||||||
|
object Make {
|
||||||
|
def apply(item : DeployedItem.Value) : ()=>PlanetSideGameObject with Deployable = cemap(item)
|
||||||
|
|
||||||
|
private val cemap : Map[DeployedItem.Value, ()=>PlanetSideGameObject with Deployable] = Map(
|
||||||
|
DeployedItem.boomer -> { ()=> new BoomerDeployable(GlobalDefinitions.boomer) },
|
||||||
|
DeployedItem.he_mine -> { ()=> new ExplosiveDeployable(GlobalDefinitions.he_mine) },
|
||||||
|
DeployedItem.jammer_mine -> { ()=> new ExplosiveDeployable(GlobalDefinitions.jammer_mine) },
|
||||||
|
DeployedItem.spitfire_turret -> { ()=> new TurretDeployable(GlobalDefinitions.spitfire_turret) },
|
||||||
|
DeployedItem.spitfire_cloaked -> { ()=> new TurretDeployable(GlobalDefinitions.spitfire_cloaked) },
|
||||||
|
DeployedItem.spitfire_aa -> { ()=> new TurretDeployable(GlobalDefinitions.spitfire_aa) },
|
||||||
|
DeployedItem.motionalarmsensor -> { ()=> new SensorDeployable(GlobalDefinitions.motionalarmsensor) },
|
||||||
|
DeployedItem.sensor_shield -> { ()=> new SensorDeployable(GlobalDefinitions.sensor_shield) },
|
||||||
|
DeployedItem.tank_traps -> { ()=> new TrapDeployable(GlobalDefinitions.tank_traps) },
|
||||||
|
DeployedItem.portable_manned_turret -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret) },
|
||||||
|
DeployedItem.portable_manned_turret -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret) },
|
||||||
|
DeployedItem.portable_manned_turret_nc -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_nc) },
|
||||||
|
DeployedItem.portable_manned_turret_tr -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) },
|
||||||
|
DeployedItem.portable_manned_turret_vs -> { ()=> new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs) },
|
||||||
|
DeployedItem.deployable_shield_generator -> { ()=> new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator) }
|
||||||
|
).withDefaultValue( { ()=> new ExplosiveDeployable(GlobalDefinitions.boomer) } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.SimpleDeployable
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
|
||||||
|
class ExplosiveDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef) {
|
||||||
|
private var exploded : Boolean = false
|
||||||
|
|
||||||
|
def Exploded : Boolean = exploded
|
||||||
|
|
||||||
|
def Exploded_=(fuse : Boolean) : Boolean = {
|
||||||
|
exploded = fuse
|
||||||
|
Exploded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.Projectiles
|
import net.psforever.objects.ballistics.Projectiles
|
||||||
|
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
import net.psforever.objects.definition.converter._
|
import net.psforever.objects.definition.converter._
|
||||||
import net.psforever.objects.equipment.CItem.DeployedItem
|
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.serverobject.doors.DoorDefinition
|
import net.psforever.objects.serverobject.doors.DoorDefinition
|
||||||
|
|
@ -15,10 +15,10 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
|
||||||
import net.psforever.objects.serverobject.terminals._
|
import net.psforever.objects.serverobject.terminals._
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
|
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
|
||||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||||
import net.psforever.objects.serverobject.turret.{MannedTurretDefinition, TurretUpgrade}
|
import net.psforever.objects.serverobject.turret.{TurretDefinition, TurretUpgrade}
|
||||||
import net.psforever.objects.vehicles.{DestroyedVehicle, SeatArmorRestriction, UtilityType}
|
import net.psforever.objects.vehicles.{DestroyedVehicle, SeatArmorRestriction, UtilityType}
|
||||||
import net.psforever.objects.vital.DamageType
|
import net.psforever.objects.vital.{DamageType, StandardResolutions}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{CertificationType, PlanetSideEmpire}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -497,6 +497,12 @@ object GlobalDefinitions {
|
||||||
val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm)
|
val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm)
|
||||||
|
|
||||||
val phalanx_ammo = AmmoBoxDefinition(Ammo.phalanx_ammo)
|
val phalanx_ammo = AmmoBoxDefinition(Ammo.phalanx_ammo)
|
||||||
|
|
||||||
|
val spitfire_ammo = AmmoBoxDefinition(Ammo.spitfire_ammo)
|
||||||
|
|
||||||
|
val spitfire_aa_ammo = AmmoBoxDefinition(Ammo.spitfire_aa_ammo)
|
||||||
|
|
||||||
|
val energy_gun_ammo = AmmoBoxDefinition(Ammo.energy_gun_ammo)
|
||||||
init_ammo()
|
init_ammo()
|
||||||
|
|
||||||
val chainblade = ToolDefinition(ObjectClass.chainblade)
|
val chainblade = ToolDefinition(ObjectClass.chainblade)
|
||||||
|
|
@ -622,6 +628,8 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val bank = ToolDefinition(ObjectClass.bank)
|
val bank = ToolDefinition(ObjectClass.bank)
|
||||||
|
|
||||||
|
val boomer_trigger = SimpleItemDefinition(SItem.boomer_trigger)
|
||||||
|
|
||||||
val remote_electronics_kit = SimpleItemDefinition(SItem.remote_electronics_kit)
|
val remote_electronics_kit = SimpleItemDefinition(SItem.remote_electronics_kit)
|
||||||
|
|
||||||
val trek = ToolDefinition(ObjectClass.trek)
|
val trek = ToolDefinition(ObjectClass.trek)
|
||||||
|
|
@ -630,9 +638,9 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val command_detonater = SimpleItemDefinition(SItem.command_detonater)
|
val command_detonater = SimpleItemDefinition(SItem.command_detonater)
|
||||||
|
|
||||||
val ace = ConstructionItemDefinition(CItem.Unit.ace)
|
val ace = ConstructionItemDefinition(CItem.ace)
|
||||||
|
|
||||||
val advanced_ace = ConstructionItemDefinition(CItem.Unit.advanced_ace)
|
val advanced_ace = ConstructionItemDefinition(CItem.advanced_ace)
|
||||||
|
|
||||||
val fury_weapon_systema = ToolDefinition(ObjectClass.fury_weapon_systema)
|
val fury_weapon_systema = ToolDefinition(ObjectClass.fury_weapon_systema)
|
||||||
|
|
||||||
|
|
@ -737,6 +745,18 @@ object GlobalDefinitions {
|
||||||
val phalanx_avcombo = ToolDefinition(ObjectClass.phalanx_avcombo)
|
val phalanx_avcombo = ToolDefinition(ObjectClass.phalanx_avcombo)
|
||||||
|
|
||||||
val phalanx_flakcombo = ToolDefinition(ObjectClass.phalanx_flakcombo)
|
val phalanx_flakcombo = ToolDefinition(ObjectClass.phalanx_flakcombo)
|
||||||
|
|
||||||
|
val spitfire_weapon = ToolDefinition(ObjectClass.spitfire_weapon)
|
||||||
|
|
||||||
|
val spitfire_aa_weapon = ToolDefinition(ObjectClass.spitfire_aa_weapon)
|
||||||
|
|
||||||
|
val energy_gun = ToolDefinition(ObjectClass.energy_gun)
|
||||||
|
|
||||||
|
val energy_gun_nc = ToolDefinition(ObjectClass.energy_gun_nc)
|
||||||
|
|
||||||
|
val energy_gun_tr = ToolDefinition(ObjectClass.energy_gun_tr)
|
||||||
|
|
||||||
|
val energy_gun_vs = ToolDefinition(ObjectClass.energy_gun_vs)
|
||||||
init_tools()
|
init_tools()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -809,6 +829,38 @@ object GlobalDefinitions {
|
||||||
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
||||||
init_vehicles()
|
init_vehicles()
|
||||||
|
|
||||||
|
/*
|
||||||
|
combat engineering deployables
|
||||||
|
*/
|
||||||
|
val boomer = DeployableDefinition(DeployedItem.boomer)
|
||||||
|
|
||||||
|
val he_mine = DeployableDefinition(DeployedItem.he_mine)
|
||||||
|
|
||||||
|
val jammer_mine = DeployableDefinition(DeployedItem.jammer_mine)
|
||||||
|
|
||||||
|
val spitfire_turret = TurretDeployableDefinition(DeployedItem.spitfire_turret)
|
||||||
|
|
||||||
|
val spitfire_cloaked = TurretDeployableDefinition(DeployedItem.spitfire_cloaked)
|
||||||
|
|
||||||
|
val spitfire_aa = TurretDeployableDefinition(DeployedItem.spitfire_aa)
|
||||||
|
|
||||||
|
val motionalarmsensor = DeployableDefinition(DeployedItem.motionalarmsensor)
|
||||||
|
|
||||||
|
val sensor_shield = DeployableDefinition(DeployedItem.sensor_shield)
|
||||||
|
|
||||||
|
val tank_traps = DeployableDefinition(DeployedItem.tank_traps)
|
||||||
|
|
||||||
|
val portable_manned_turret = TurretDeployableDefinition(DeployedItem.portable_manned_turret)
|
||||||
|
|
||||||
|
val portable_manned_turret_nc = TurretDeployableDefinition(DeployedItem.portable_manned_turret_nc)
|
||||||
|
|
||||||
|
val portable_manned_turret_tr = TurretDeployableDefinition(DeployedItem.portable_manned_turret_tr)
|
||||||
|
|
||||||
|
val portable_manned_turret_vs = TurretDeployableDefinition(DeployedItem.portable_manned_turret_vs)
|
||||||
|
|
||||||
|
val deployable_shield_generator = new ShieldGeneratorDefinition
|
||||||
|
init_deployables()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
*/
|
*/
|
||||||
|
|
@ -868,7 +920,7 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val secondary_capture = new CaptureTerminalDefinition(751) // Tower CC
|
val secondary_capture = new CaptureTerminalDefinition(751) // Tower CC
|
||||||
|
|
||||||
val manned_turret = new MannedTurretDefinition(480) {
|
val manned_turret = new TurretDefinition(480) {
|
||||||
Name = "manned_turret"
|
Name = "manned_turret"
|
||||||
MaxHealth = 3600
|
MaxHealth = 3600
|
||||||
Weapons += 1 -> new mutable.HashMap()
|
Weapons += 1 -> new mutable.HashMap()
|
||||||
|
|
@ -1084,6 +1136,15 @@ object GlobalDefinitions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def PortableMannedTurret(faction :PlanetSideEmpire.Value) : TurretDeployableDefinition = {
|
||||||
|
faction match {
|
||||||
|
case PlanetSideEmpire.TR => portable_manned_turret_tr
|
||||||
|
case PlanetSideEmpire.NC => portable_manned_turret_nc
|
||||||
|
case PlanetSideEmpire.VS => portable_manned_turret_vs
|
||||||
|
case PlanetSideEmpire.NEUTRAL => portable_manned_turret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using the definition for a piece of `Equipment` determine if it is a grenade-type weapon.
|
* Using the definition for a piece of `Equipment` determine if it is a grenade-type weapon.
|
||||||
* Only the normal grenades count; the grenade packs are excluded.
|
* Only the normal grenades count; the grenade packs are excluded.
|
||||||
|
|
@ -1541,8 +1602,16 @@ object GlobalDefinitions {
|
||||||
bullet_150mm.Tile = InventoryTile.Tile44
|
bullet_150mm.Tile = InventoryTile.Tile44
|
||||||
|
|
||||||
phalanx_ammo.Name = "phalanx_ammo"
|
phalanx_ammo.Name = "phalanx_ammo"
|
||||||
phalanx_ammo.Capacity = 4000 //sufficient for a reload
|
|
||||||
phalanx_ammo.Size = EquipmentSize.Inventory
|
phalanx_ammo.Size = EquipmentSize.Inventory
|
||||||
|
|
||||||
|
spitfire_ammo.Name = "spitfire_ammo"
|
||||||
|
spitfire_ammo.Size = EquipmentSize.Inventory
|
||||||
|
|
||||||
|
spitfire_aa_ammo.Name = "spitfire_aa_ammo"
|
||||||
|
spitfire_aa_ammo.Size = EquipmentSize.Inventory
|
||||||
|
|
||||||
|
energy_gun_ammo.Name = "energy_gun_ammo"
|
||||||
|
energy_gun_ammo.Size = EquipmentSize.Inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -3938,6 +4007,10 @@ object GlobalDefinitions {
|
||||||
remote_electronics_kit.Packet = new REKConverter
|
remote_electronics_kit.Packet = new REKConverter
|
||||||
remote_electronics_kit.Tile = InventoryTile.Tile33
|
remote_electronics_kit.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
|
boomer_trigger.Name = "boomer_trigger"
|
||||||
|
boomer_trigger.Packet = new BoomerTriggerConverter
|
||||||
|
boomer_trigger.Tile = InventoryTile.Tile22
|
||||||
|
|
||||||
trek.Name = "trek"
|
trek.Name = "trek"
|
||||||
trek.Size = EquipmentSize.Pistol
|
trek.Size = EquipmentSize.Pistol
|
||||||
trek.AmmoTypes += trek_ammo
|
trek.AmmoTypes += trek_ammo
|
||||||
|
|
@ -3961,21 +4034,30 @@ object GlobalDefinitions {
|
||||||
command_detonater.Tile = InventoryTile.Tile33
|
command_detonater.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
ace.Name = "ace"
|
ace.Name = "ace"
|
||||||
ace.Modes += DeployedItem.boomer
|
ace.Size = EquipmentSize.Pistol
|
||||||
ace.Modes += DeployedItem.he_mine
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes += DeployedItem.jammer_mine
|
ace.Modes.head.Item(DeployedItem.boomer -> Set(CertificationType.CombatEngineering))
|
||||||
ace.Modes += DeployedItem.spitfire_turret
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes += DeployedItem.spitfire_cloaked
|
ace.Modes(1).Item(DeployedItem.he_mine -> Set(CertificationType.CombatEngineering))
|
||||||
ace.Modes += DeployedItem.spitfire_aa
|
ace.Modes(1).Item(DeployedItem.jammer_mine -> Set(CertificationType.AssaultEngineering))
|
||||||
ace.Modes += DeployedItem.motionalarmsensor
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes += DeployedItem.sensor_shield
|
ace.Modes(2).Item(DeployedItem.spitfire_turret -> Set(CertificationType.CombatEngineering))
|
||||||
|
ace.Modes(2).Item(DeployedItem.spitfire_cloaked -> Set(CertificationType.FortificationEngineering))
|
||||||
|
ace.Modes(2).Item(DeployedItem.spitfire_aa -> Set(CertificationType.FortificationEngineering))
|
||||||
|
ace.Modes += new ConstructionFireMode
|
||||||
|
ace.Modes(3).Item(DeployedItem.motionalarmsensor -> Set(CertificationType.CombatEngineering))
|
||||||
|
ace.Modes(3).Item(DeployedItem.sensor_shield -> Set(CertificationType.AdvancedHacking, CertificationType.CombatEngineering))
|
||||||
ace.Tile = InventoryTile.Tile33
|
ace.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
advanced_ace.Name = "advanced_ace"
|
advanced_ace.Name = "advanced_ace"
|
||||||
advanced_ace.Modes += DeployedItem.tank_traps
|
advanced_ace.Size = EquipmentSize.Rifle
|
||||||
advanced_ace.Modes += DeployedItem.portable_manned_turret
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
advanced_ace.Modes += DeployedItem.deployable_shield_generator
|
advanced_ace.Modes.head.Item(DeployedItem.tank_traps -> Set(CertificationType.FortificationEngineering))
|
||||||
advanced_ace.Tile = InventoryTile.Tile63
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
|
advanced_ace.Modes(1).Item(DeployedItem.portable_manned_turret -> Set(CertificationType.AssaultEngineering))
|
||||||
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
|
advanced_ace.Modes(2).Item(DeployedItem.deployable_shield_generator -> Set(CertificationType.AssaultEngineering))
|
||||||
|
advanced_ace.Tile = InventoryTile.Tile93
|
||||||
|
|
||||||
fury_weapon_systema.Name = "fury_weapon_systema"
|
fury_weapon_systema.Name = "fury_weapon_systema"
|
||||||
fury_weapon_systema.Size = EquipmentSize.VehicleWeapon
|
fury_weapon_systema.Size = EquipmentSize.VehicleWeapon
|
||||||
|
|
@ -4510,6 +4592,61 @@ object GlobalDefinitions {
|
||||||
phalanx_flakcombo.FireModes(1).ProjectileTypeIndices += 1
|
phalanx_flakcombo.FireModes(1).ProjectileTypeIndices += 1
|
||||||
phalanx_flakcombo.FireModes(1).AmmoSlotIndex = 0
|
phalanx_flakcombo.FireModes(1).AmmoSlotIndex = 0
|
||||||
phalanx_flakcombo.FireModes(1).Magazine = 4000
|
phalanx_flakcombo.FireModes(1).Magazine = 4000
|
||||||
|
|
||||||
|
spitfire_weapon.Name = "spitfire_weapon"
|
||||||
|
spitfire_weapon.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
spitfire_weapon.AmmoTypes += spitfire_ammo
|
||||||
|
spitfire_weapon.ProjectileTypes += spitfire_ammo_projectile
|
||||||
|
spitfire_weapon.FireModes += new InfiniteFireModeDefinition
|
||||||
|
spitfire_weapon.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
spitfire_weapon.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
spitfire_weapon.FireModes.head.Magazine = 4000
|
||||||
|
|
||||||
|
spitfire_aa_weapon.Name = "spitfire_aa_weapon"
|
||||||
|
spitfire_aa_weapon.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
spitfire_aa_weapon.AmmoTypes += spitfire_aa_ammo
|
||||||
|
spitfire_aa_weapon.ProjectileTypes += spitfire_aa_ammo_projectile
|
||||||
|
spitfire_aa_weapon.FireModes += new InfiniteFireModeDefinition
|
||||||
|
spitfire_aa_weapon.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
spitfire_aa_weapon.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
spitfire_aa_weapon.FireModes.head.Magazine = 4000
|
||||||
|
|
||||||
|
energy_gun.Name = "energy_gun"
|
||||||
|
energy_gun.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
energy_gun.AmmoTypes += energy_gun_ammo
|
||||||
|
energy_gun.ProjectileTypes += bullet_9mm_projectile //fallback
|
||||||
|
energy_gun.FireModes += new FireModeDefinition
|
||||||
|
energy_gun.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
energy_gun.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
energy_gun.FireModes.head.Magazine = 4000
|
||||||
|
|
||||||
|
energy_gun_nc.Name = "energy_gun_nc"
|
||||||
|
energy_gun_nc.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
energy_gun_nc.AmmoTypes += energy_gun_ammo
|
||||||
|
energy_gun_nc.ProjectileTypes += energy_gun_nc_projectile
|
||||||
|
energy_gun_nc.FireModes += new PelletFireModeDefinition
|
||||||
|
energy_gun_nc.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
energy_gun_nc.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
energy_gun_nc.FireModes.head.Magazine = 35
|
||||||
|
energy_gun_nc.FireModes.head.Chamber = 9
|
||||||
|
|
||||||
|
energy_gun_tr.Name = "energy_gun_tr"
|
||||||
|
energy_gun_tr.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
energy_gun_tr.AmmoTypes += energy_gun_ammo
|
||||||
|
energy_gun_tr.ProjectileTypes += energy_gun_tr_projectile
|
||||||
|
energy_gun_tr.FireModes += new FireModeDefinition
|
||||||
|
energy_gun_tr.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
energy_gun_tr.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
energy_gun_tr.FireModes.head.Magazine = 200
|
||||||
|
|
||||||
|
energy_gun_vs.Name = "energy_gun_vs"
|
||||||
|
energy_gun_vs.Size = EquipmentSize.BaseTurretWeapon
|
||||||
|
energy_gun_vs.AmmoTypes += energy_gun_ammo
|
||||||
|
energy_gun_vs.ProjectileTypes += energy_gun_tr_projectile
|
||||||
|
energy_gun_vs.FireModes += new FireModeDefinition
|
||||||
|
energy_gun_vs.FireModes.head.AmmoTypeIndices += 0
|
||||||
|
energy_gun_vs.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
energy_gun_vs.FireModes.head.Magazine = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -5238,4 +5375,145 @@ object GlobalDefinitions {
|
||||||
phantasm.Packet = variantConverter
|
phantasm.Packet = variantConverter
|
||||||
phantasm.DestroyedModel = None //the adb calls out a phantasm_destroyed but no such asset exists
|
phantasm.DestroyedModel = None //the adb calls out a phantasm_destroyed but no such asset exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize `Deployable` globals.
|
||||||
|
*/
|
||||||
|
private def init_deployables() : Unit = {
|
||||||
|
boomer.Name = "boomer"
|
||||||
|
boomer.Descriptor = "Boomers"
|
||||||
|
boomer.MaxHealth = 100
|
||||||
|
boomer.DeployCategory = DeployableCategory.Boomers
|
||||||
|
boomer.DeployTime = Duration.create(1000, "ms")
|
||||||
|
boomer.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
he_mine.Name = "he_mine"
|
||||||
|
he_mine.Descriptor = "Mines"
|
||||||
|
he_mine.MaxHealth = 100
|
||||||
|
he_mine.DeployCategory = DeployableCategory.Mines
|
||||||
|
he_mine.DeployTime = Duration.create(1000, "ms")
|
||||||
|
he_mine.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
jammer_mine.Name = "jammer_mine"
|
||||||
|
jammer_mine.Descriptor = "JammerMines"
|
||||||
|
jammer_mine.MaxHealth = 100
|
||||||
|
jammer_mine.DeployCategory = DeployableCategory.Mines
|
||||||
|
jammer_mine.DeployTime = Duration.create(1000, "ms")
|
||||||
|
jammer_mine.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
spitfire_turret.Name = "spitfire_turret"
|
||||||
|
spitfire_turret.Descriptor= "Spitfires"
|
||||||
|
spitfire_turret.MaxHealth = 100
|
||||||
|
spitfire_turret.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
spitfire_turret.Weapons(1) += TurretUpgrade.None -> spitfire_weapon
|
||||||
|
spitfire_turret.ReserveAmmunition = false
|
||||||
|
spitfire_turret.DeployCategory = DeployableCategory.SmallTurrets
|
||||||
|
spitfire_turret.DeployTime = Duration.create(5000, "ms")
|
||||||
|
spitfire_turret.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
spitfire_cloaked.Name = "spitfire_cloaked"
|
||||||
|
spitfire_cloaked.Descriptor= "CloakingSpitfires"
|
||||||
|
spitfire_cloaked.MaxHealth = 100
|
||||||
|
spitfire_cloaked.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
spitfire_cloaked.Weapons(1) += TurretUpgrade.None -> spitfire_weapon
|
||||||
|
spitfire_cloaked.ReserveAmmunition = false
|
||||||
|
spitfire_cloaked.DeployCategory = DeployableCategory.SmallTurrets
|
||||||
|
spitfire_cloaked.DeployTime = Duration.create(5000, "ms")
|
||||||
|
spitfire_cloaked.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
spitfire_aa.Name = "spitfire_aa"
|
||||||
|
spitfire_aa.Descriptor= "FlakSpitfires"
|
||||||
|
spitfire_aa.MaxHealth = 100
|
||||||
|
spitfire_aa.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
spitfire_aa.Weapons(1) += TurretUpgrade.None -> spitfire_aa_weapon
|
||||||
|
spitfire_aa.ReserveAmmunition = false
|
||||||
|
spitfire_aa.DeployCategory = DeployableCategory.SmallTurrets
|
||||||
|
spitfire_aa.DeployTime = Duration.create(5000, "ms")
|
||||||
|
spitfire_aa.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
motionalarmsensor.Name = "motionalarmsensor"
|
||||||
|
motionalarmsensor.Descriptor = "MotionSensors"
|
||||||
|
motionalarmsensor.MaxHealth = 100
|
||||||
|
motionalarmsensor.DeployCategory = DeployableCategory.Sensors
|
||||||
|
motionalarmsensor.DeployTime = Duration.create(1000, "ms")
|
||||||
|
motionalarmsensor.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
sensor_shield.Name = "sensor_shield"
|
||||||
|
sensor_shield.Descriptor = "SensorShields"
|
||||||
|
sensor_shield.MaxHealth = 100
|
||||||
|
sensor_shield.DeployCategory = DeployableCategory.Sensors
|
||||||
|
sensor_shield.DeployTime = Duration.create(5000, "ms")
|
||||||
|
sensor_shield.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
tank_traps.Name = "tank_traps"
|
||||||
|
tank_traps.Descriptor = "TankTraps"
|
||||||
|
tank_traps.MaxHealth = 5000
|
||||||
|
tank_traps.Packet = new TRAPConverter
|
||||||
|
tank_traps.DeployCategory = DeployableCategory.TankTraps
|
||||||
|
tank_traps.DeployTime = Duration.create(6000, "ms")
|
||||||
|
tank_traps.Model = StandardResolutions.SimpleDeployables
|
||||||
|
|
||||||
|
val fieldTurretConverter = new FieldTurretConverter
|
||||||
|
portable_manned_turret.Name = "portable_manned_turret"
|
||||||
|
portable_manned_turret.Descriptor = "FieldTurrets"
|
||||||
|
portable_manned_turret.MaxHealth = 1000
|
||||||
|
portable_manned_turret.MountPoints += 1 -> 0
|
||||||
|
portable_manned_turret.MountPoints += 2 -> 0
|
||||||
|
portable_manned_turret.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
portable_manned_turret.Weapons(1) += TurretUpgrade.None -> energy_gun
|
||||||
|
portable_manned_turret.ReserveAmmunition = true
|
||||||
|
portable_manned_turret.FactionLocked = true
|
||||||
|
portable_manned_turret.Packet = fieldTurretConverter
|
||||||
|
portable_manned_turret.DeployCategory = DeployableCategory.FieldTurrets
|
||||||
|
portable_manned_turret.DeployTime = Duration.create(6000, "ms")
|
||||||
|
portable_manned_turret.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
portable_manned_turret_nc.Name = "portable_manned_turret_nc"
|
||||||
|
portable_manned_turret_nc.Descriptor = "FieldTurrets"
|
||||||
|
portable_manned_turret_nc.MaxHealth = 1000
|
||||||
|
portable_manned_turret_nc.MountPoints += 1 -> 0
|
||||||
|
portable_manned_turret_nc.MountPoints += 2 -> 0
|
||||||
|
portable_manned_turret_nc.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
portable_manned_turret_nc.Weapons(1) += TurretUpgrade.None -> energy_gun_nc
|
||||||
|
portable_manned_turret_nc.ReserveAmmunition = true
|
||||||
|
portable_manned_turret_nc.FactionLocked = true
|
||||||
|
portable_manned_turret_nc.Packet = fieldTurretConverter
|
||||||
|
portable_manned_turret_nc.DeployCategory = DeployableCategory.FieldTurrets
|
||||||
|
portable_manned_turret_nc.DeployTime = Duration.create(6000, "ms")
|
||||||
|
portable_manned_turret_nc.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
portable_manned_turret_tr.Name = "portable_manned_turret_tr"
|
||||||
|
portable_manned_turret_tr.Descriptor = "FieldTurrets"
|
||||||
|
portable_manned_turret_tr.MaxHealth = 1000
|
||||||
|
portable_manned_turret_tr.MountPoints += 1 -> 0
|
||||||
|
portable_manned_turret_tr.MountPoints += 2 -> 0
|
||||||
|
portable_manned_turret_tr.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
portable_manned_turret_tr.Weapons(1) += TurretUpgrade.None -> energy_gun_tr
|
||||||
|
portable_manned_turret_tr.ReserveAmmunition = true
|
||||||
|
portable_manned_turret_tr.FactionLocked = true
|
||||||
|
portable_manned_turret_tr.Packet = fieldTurretConverter
|
||||||
|
portable_manned_turret_tr.DeployCategory = DeployableCategory.FieldTurrets
|
||||||
|
portable_manned_turret_tr.DeployTime = Duration.create(6000, "ms")
|
||||||
|
portable_manned_turret_tr.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
portable_manned_turret_vs.Name = "portable_manned_turret_vs"
|
||||||
|
portable_manned_turret_vs.Descriptor = "FieldTurrets"
|
||||||
|
portable_manned_turret_vs.MaxHealth = 1000
|
||||||
|
portable_manned_turret_vs.MountPoints += 1 -> 0
|
||||||
|
portable_manned_turret_vs.MountPoints += 2 -> 0
|
||||||
|
portable_manned_turret_vs.Weapons += 1 -> new mutable.HashMap()
|
||||||
|
portable_manned_turret_vs.Weapons(1) += TurretUpgrade.None -> energy_gun_vs
|
||||||
|
portable_manned_turret_vs.ReserveAmmunition = true
|
||||||
|
portable_manned_turret_vs.FactionLocked = true
|
||||||
|
portable_manned_turret_vs.Packet = fieldTurretConverter
|
||||||
|
portable_manned_turret_vs.DeployCategory = DeployableCategory.FieldTurrets
|
||||||
|
portable_manned_turret_vs.DeployTime = Duration.create(6000, "ms")
|
||||||
|
portable_manned_turret_vs.Model = StandardResolutions.ComplexDeployables
|
||||||
|
|
||||||
|
deployable_shield_generator.Name = "deployable_shield_generator"
|
||||||
|
deployable_shield_generator.Descriptor = "ShieldGenerators"
|
||||||
|
deployable_shield_generator.MaxHealth = 1700
|
||||||
|
deployable_shield_generator.DeployTime = Duration.create(6000, "ms")
|
||||||
|
deployable_shield_generator.Model = StandardResolutions.ComplexDeployables
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.SimpleDeployable
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
|
||||||
|
class SensorDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef)
|
||||||
|
with Hackable
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.{ComplexDeployable, DeployableCategory}
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
import net.psforever.objects.definition.converter.ShieldGeneratorConverter
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
|
||||||
|
class ShieldGeneratorDeployable(cdef : ShieldGeneratorDefinition) extends ComplexDeployable(cdef)
|
||||||
|
with Hackable
|
||||||
|
|
||||||
|
class ShieldGeneratorDefinition extends DeployableDefinition(240) {
|
||||||
|
Packet = new ShieldGeneratorConverter
|
||||||
|
DeployCategory = DeployableCategory.ShieldGenerators
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.ballistics.Projectiles
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type of utility that can be wielded and loaded with certain other game elements.<br>
|
* A type of `Equipment` that can be wielded and loaded with certain other game elements.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* "Tool" is a very mechanical name while this class is intended for various weapons and support items.
|
* "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,"
|
* The primary trait of a `Tool` is that it has something that counts as an "ammunition,"
|
||||||
|
|
@ -17,7 +17,8 @@ import scala.annotation.tailrec
|
||||||
* Some weapons Chainblade have ammunition but do not consume it.
|
* 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
|
* @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] {
|
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 */
|
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
|
||||||
private var fireModeIndex : Int = 0
|
private var fireModeIndex : Int = 0
|
||||||
/** current ammunition slot being used by this fire mode */
|
/** current ammunition slot being used by this fire mode */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.SimpleDeployable
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
|
||||||
|
class TrapDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef)
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorContext, Props}
|
||||||
|
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||||
|
import net.psforever.objects.definition.{BaseDeployableDefinition, DeployableDefinition}
|
||||||
|
import net.psforever.objects.definition.converter.SmallTurretConverter
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
import net.psforever.objects.serverobject.mount.MountableBehavior
|
||||||
|
import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret}
|
||||||
|
|
||||||
|
class TurretDeployable(tdef : TurretDeployableDefinition) extends PlanetSideServerObject
|
||||||
|
with Deployable
|
||||||
|
with WeaponTurret
|
||||||
|
with Hackable {
|
||||||
|
private var shields : Int = 0
|
||||||
|
|
||||||
|
WeaponTurret.LoadDefinition(this) //calls the equivalent of Health = Definition.MaxHealth
|
||||||
|
|
||||||
|
def MaxHealth : Int = Definition.MaxHealth
|
||||||
|
|
||||||
|
def Shields : Int = shields
|
||||||
|
|
||||||
|
def Shields_=(toShields : Int) : Int = {
|
||||||
|
shields = math.min(math.max(0, toShields), MaxShields)
|
||||||
|
Shields
|
||||||
|
}
|
||||||
|
|
||||||
|
def MaxShields : Int = {
|
||||||
|
0//Definition.MaxShields
|
||||||
|
}
|
||||||
|
|
||||||
|
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
|
||||||
|
|
||||||
|
//override to clarify inheritance conflict
|
||||||
|
override def Health : Int = super[Deployable].Health
|
||||||
|
//override to clarify inheritance conflict
|
||||||
|
override def Health_=(toHealth : Int) : Int = super[Deployable].Health_=(toHealth)
|
||||||
|
|
||||||
|
override def Definition = tdef
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretDeployableDefinition(private val objectId : Int) extends TurretDefinition(objectId)
|
||||||
|
with BaseDeployableDefinition {
|
||||||
|
private val item = DeployedItem(objectId) //let throw NoSuchElementException
|
||||||
|
Name = "turret_deployable"
|
||||||
|
Packet = new SmallTurretConverter
|
||||||
|
|
||||||
|
def Item : DeployedItem.Value = item
|
||||||
|
|
||||||
|
//override to clarify inheritance conflict
|
||||||
|
override def MaxHealth : Int = super[BaseDeployableDefinition].MaxHealth
|
||||||
|
//override to clarify inheritance conflict
|
||||||
|
override def MaxHealth_=(max : Int) : Int = super[BaseDeployableDefinition].MaxHealth_=(max)
|
||||||
|
|
||||||
|
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||||
|
obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||||
|
DeployableDefinition.SimpleUninitialize(obj, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TurretDeployableDefinition {
|
||||||
|
def apply(dtype : DeployedItem.Value) : TurretDeployableDefinition = {
|
||||||
|
new TurretDeployableDefinition(dtype.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** control actors */
|
||||||
|
|
||||||
|
class TurretControl(turret : TurretDeployable) extends Actor
|
||||||
|
with FactionAffinityBehavior.Check
|
||||||
|
with MountableBehavior.Mount
|
||||||
|
with MountableBehavior.Dismount {
|
||||||
|
def MountableObject = turret //do not add type!
|
||||||
|
|
||||||
|
def FactionObject : FactionAffinity = turret
|
||||||
|
|
||||||
|
def receive : Receive = checkBehavior
|
||||||
|
.orElse(dismountBehavior)
|
||||||
|
.orElse(turretMountBehavior)
|
||||||
|
.orElse {
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,668 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.CertificationType
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that keeps track - "manages" - deployables that are owned by the avatar.<br>
|
||||||
|
* <br>
|
||||||
|
* Deployables belong to the Engineering certification line of certifications.
|
||||||
|
* `CombatEngineering` and above certifications include permissions for different types of deployables,
|
||||||
|
* and one unique type of deployable is available through the `GroundSupport`
|
||||||
|
* and one that also requires `AdvancedHacking`.
|
||||||
|
* (They are collectively called "ce" for that reason.)
|
||||||
|
* Not only does the level of certification change the maximum number of deployables that can be managed by type
|
||||||
|
* but it also influences the maximum number of deployables that can be managed by category.
|
||||||
|
* Individual deployables are counted by type and category individually in special data structures
|
||||||
|
* to avoid having to probe the primary list of deployable references whenever a question of quantity is asked.
|
||||||
|
* As deployables are added and removed, and tracked certifications are added and removed,
|
||||||
|
* these structures are updated to reflect proper count.
|
||||||
|
*/
|
||||||
|
class DeployableToolbox {
|
||||||
|
/**
|
||||||
|
* a map of bins for keeping track of the quantities of deployables in a category
|
||||||
|
* keys: categories, values: quantity storage object
|
||||||
|
*/
|
||||||
|
private val categoryCounts = DeployableCategory.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
|
||||||
|
//)
|
||||||
|
/**
|
||||||
|
* a map of bins for keeping track of the quantities of individual deployables
|
||||||
|
* keys: deployable types, values: quantity storage object
|
||||||
|
*/
|
||||||
|
private val deployableCounts = DeployedItem.values.toSeq.map(value => { value -> new DeployableToolbox.Bin }).toMap
|
||||||
|
/**
|
||||||
|
* a map of tracked/owned individual deployables
|
||||||
|
* keys: categories, values: deployable objects
|
||||||
|
*/
|
||||||
|
private val deployableLists =
|
||||||
|
DeployableCategory.values.toSeq.map(value => { value -> mutable.ListBuffer[DeployableToolbox.AcceptableDeployable]() }).toMap
|
||||||
|
/**
|
||||||
|
* can only be initialized once
|
||||||
|
* set during the `Initialization` method primarily, and in `Add` and in `Remove` if not
|
||||||
|
*/
|
||||||
|
private var initialized : Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the initial deployable counts by providing certification values to be used in category and unit selection.
|
||||||
|
* @param certifications a group of certifications for the initial values
|
||||||
|
* @return `true`, if this is the first time and actual "initialization" is performed;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Initialize(certifications : Set[CertificationType.Value]) : Boolean = {
|
||||||
|
if(!initialized) {
|
||||||
|
DeployableToolbox.Initialize(deployableCounts, categoryCounts, certifications)
|
||||||
|
initialized = true
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the count of deployable units that can be tracked by providing a new certification.
|
||||||
|
* If the given certification is already factored into the quantities, no changes will occur.
|
||||||
|
* @param certification the new certification
|
||||||
|
* @param certificationSet the group of previous certifications being tracked;
|
||||||
|
* occasionally, important former certification values are required for additional configuration;
|
||||||
|
* the new certification should already have been added to this group
|
||||||
|
*/
|
||||||
|
def AddToDeployableQuantities(certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = {
|
||||||
|
initialized = true
|
||||||
|
DeployableToolbox.AddToDeployableQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the count of deployable units that can be tracked
|
||||||
|
* by designating a certification whose deployables will be removed.
|
||||||
|
* If the given certification is already factored out of the quantities, no changes will occur.
|
||||||
|
* @param certification the old certification
|
||||||
|
* @param certificationSet the group of previous certifications being tracked;
|
||||||
|
* occasionally, important former certification values are required for additional configuration;
|
||||||
|
* the new certification should already have been excluded from this group
|
||||||
|
*/
|
||||||
|
def RemoveFromDeployableQuantities(certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = {
|
||||||
|
initialized = true
|
||||||
|
DeployableToolbox.RemoveFromDeployablesQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given deployable can be managed by this toolbox.
|
||||||
|
* @see `Valid`
|
||||||
|
* @see `Available`
|
||||||
|
* @see `Contains`
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if it can be managed under the current conditions;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Accept(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
Valid(obj) && Available(obj) && !Contains(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given deployable can be managed by this toolbox
|
||||||
|
* by testing if the specific deployable maximum and the deployable category maximum is non-zero
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if both category maximum and deployable type maximum are positive non-zero integers;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Valid(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Max > 0 &&
|
||||||
|
categoryCounts(obj.Definition.DeployCategory).Max > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given deployable can be managed by this toolbox
|
||||||
|
* by testing if the specific deployable list and the deployable category list have available slots.
|
||||||
|
* In this case, a "slot" is merely the difference between the current count is less than the maximum count.
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if the deployable can be added to the support lists and counted;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Available(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Available &&
|
||||||
|
categoryCounts(obj.Definition.DeployCategory).Available
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this deployable is already being managed by the toolbox
|
||||||
|
* by determining whether or not it is already being managed by this toolbox.
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if the deployable can be found in one of the lists;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Contains(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
deployableLists(obj.Definition.DeployCategory).contains(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the provided deployable.<br>
|
||||||
|
* <br>
|
||||||
|
* Although proper testing should be performed prior to attempting to add the deployable to this toolbox,
|
||||||
|
* three tests are administered to determine whether space is available prior to insertion.
|
||||||
|
* The first two tests check for available space in the category count and in the unit count
|
||||||
|
* and the third test checks whether the deployable is already being managed by this toolbox.
|
||||||
|
* No changes should occur if the deployable is not properly added.
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if the deployable is added;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Add(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
val category = obj.Definition.DeployCategory
|
||||||
|
val dCategory = categoryCounts(category)
|
||||||
|
val dType = deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item))
|
||||||
|
val dList = deployableLists(category)
|
||||||
|
if(dCategory.Available() && dType.Available() && !dList.contains(obj)) {
|
||||||
|
dCategory.Current += 1
|
||||||
|
dType.Current += 1
|
||||||
|
dList += obj
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop managing the provided deployable.<br>
|
||||||
|
* <br>
|
||||||
|
* Although proper testing should be performed prior to attempting to remove the deployable to this toolbox,
|
||||||
|
* a single test is administered to determine whether the removal can take place.
|
||||||
|
* If the deployable is found to currently being managed by this toolbox, then it is properly removed.
|
||||||
|
* No changes should occur if the deployable is not properly removed.
|
||||||
|
* @param obj the deployable
|
||||||
|
* @return `true`, if the deployable is added;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def Remove(obj : DeployableToolbox.AcceptableDeployable) : Boolean = {
|
||||||
|
val category = obj.Definition.DeployCategory
|
||||||
|
val deployables = deployableLists(category)
|
||||||
|
if(deployables.contains(obj)) {
|
||||||
|
categoryCounts(category).Current -= 1
|
||||||
|
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Current -= 1
|
||||||
|
deployables -= obj
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the first managed deployable that matches the same type of deployable as the example.
|
||||||
|
* The explicit tests is defined to find the first deployable whose type matches.
|
||||||
|
* @param obj the example deployable
|
||||||
|
* @return any deployable that is found
|
||||||
|
*/
|
||||||
|
def DisplaceFirst(obj : DeployableToolbox.AcceptableDeployable) : Option[DeployableToolbox.AcceptableDeployable] = {
|
||||||
|
DisplaceFirst(obj, { d => d.Definition.Item == obj.Definition.Item })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the first managed deployable that satisfies a test and belongs to the same category as the example.
|
||||||
|
* The test in question is used to pinpoint the first qualifying deployable;
|
||||||
|
* but, if the test fails to find any valid targets,
|
||||||
|
* the first deployable in the list of managed deployables for that category is selected to be removed.
|
||||||
|
* The only test performed is whether there is any valid deployable managed for the category.
|
||||||
|
* @param obj the example deployable
|
||||||
|
* @param rule the testing rule for determining a valid deployable
|
||||||
|
* @return any deployable that is found
|
||||||
|
*/
|
||||||
|
def DisplaceFirst(obj : DeployableToolbox.AcceptableDeployable, rule : (Deployable)=> Boolean) : Option[DeployableToolbox.AcceptableDeployable] = {
|
||||||
|
val definition = obj.Definition
|
||||||
|
val category = definition.DeployCategory
|
||||||
|
val categoryList = deployableLists(category)
|
||||||
|
if(categoryList.nonEmpty) {
|
||||||
|
val found = categoryList.find(rule) match {
|
||||||
|
case Some(target) =>
|
||||||
|
categoryList.remove(categoryList.indexOf(target))
|
||||||
|
case None =>
|
||||||
|
categoryList.remove(0)
|
||||||
|
}
|
||||||
|
categoryCounts(category).Current -= 1
|
||||||
|
deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1
|
||||||
|
Some(found)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the first managed deployable from a category.
|
||||||
|
* The only test performed is whether there is any valid deployable managed for the category.
|
||||||
|
* @param category the target category
|
||||||
|
* @return any deployable that is found
|
||||||
|
*/
|
||||||
|
def DisplaceFirst(category : DeployableCategory.Value) : Option[DeployableToolbox.AcceptableDeployable] = {
|
||||||
|
val categoryList = deployableLists(category)
|
||||||
|
if(categoryList.nonEmpty) {
|
||||||
|
val found = categoryList.remove(0)
|
||||||
|
categoryCounts(category).Current -= 1
|
||||||
|
deployableCounts(DeployableToolbox.UnifiedType(found.Definition.Item)).Current -= 1
|
||||||
|
Some(found)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference all managed deployables of the same type as an example deployable.
|
||||||
|
* @param filter the example deployable
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def Deployables(filter : DeployableToolbox.AcceptableDeployable) : List[PlanetSideGUID] = {
|
||||||
|
Deployables(filter.Definition.Item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference all managed deployables of the same type.
|
||||||
|
* @param filter the type of deployable
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def Deployables(filter : DeployedItem.Value) : List[PlanetSideGUID] = {
|
||||||
|
deployableLists(Deployable.Category.Of(filter))
|
||||||
|
.filter(entry => { entry.Definition.Item == filter })
|
||||||
|
.map(_.GUID).toList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference all managed deployables in the same category as an example deployable.
|
||||||
|
* @param filter the example deployable
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def Category(filter : DeployableToolbox.AcceptableDeployable) : List[PlanetSideGUID] = {
|
||||||
|
Category(filter.Definition.DeployCategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference all managed deployables in the same category.
|
||||||
|
* @param filter the type of deployable
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def Category(filter : DeployableCategory.Value) : List[PlanetSideGUID] = {
|
||||||
|
deployableLists(filter).map(_.GUID).toList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the current capacity for the same type of deployable as the example.
|
||||||
|
* @param item the example deployable
|
||||||
|
* @return the current quantity of deployables and the maximum number
|
||||||
|
*/
|
||||||
|
def CountDeployable(item : DeployedItem.Value) : (Int, Int) = {
|
||||||
|
val dType = deployableCounts(DeployableToolbox.UnifiedType(item))
|
||||||
|
(dType.Current, dType.Max)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the current capacity for the same category of deployable as the example.
|
||||||
|
* @param item the example deployable
|
||||||
|
* @return the current quantity of deployables and the maximum number
|
||||||
|
*/
|
||||||
|
def CountCategory(item : DeployedItem.Value) : (Int, Int) = {
|
||||||
|
val dCat = categoryCounts(Deployable.Category.Of(DeployableToolbox.UnifiedType(item)))
|
||||||
|
(dCat.Current, dCat.Max)
|
||||||
|
}
|
||||||
|
|
||||||
|
def UpdateUIElement(entry : DeployedItem.Value) : List[(Int,Int,Int,Int)] = {
|
||||||
|
val toEntry = DeployableToolbox.UnifiedType(entry)
|
||||||
|
val (curr, max) = Deployable.UI(toEntry)
|
||||||
|
val dType = deployableCounts(toEntry)
|
||||||
|
List((curr, dType.Current, max, dType.Max))
|
||||||
|
}
|
||||||
|
|
||||||
|
def UpdateUI() : List[(Int,Int,Int,Int)] = DeployedItem.values flatMap UpdateUIElement toList
|
||||||
|
|
||||||
|
def UpdateUI(entry : CertificationType.Value) : List[(Int,Int,Int,Int)] = {
|
||||||
|
import CertificationType._
|
||||||
|
entry match {
|
||||||
|
case AdvancedHacking =>
|
||||||
|
UpdateUIElement(DeployedItem.sensor_shield)
|
||||||
|
|
||||||
|
case CombatEngineering =>
|
||||||
|
List(
|
||||||
|
DeployedItem.boomer, DeployedItem.he_mine, DeployedItem.spitfire_turret, DeployedItem.motionalarmsensor
|
||||||
|
) flatMap UpdateUIElement
|
||||||
|
|
||||||
|
case AssaultEngineering =>
|
||||||
|
List(
|
||||||
|
DeployedItem.jammer_mine, DeployedItem.portable_manned_turret, DeployedItem.deployable_shield_generator
|
||||||
|
) flatMap UpdateUIElement
|
||||||
|
|
||||||
|
case FortificationEngineering =>
|
||||||
|
List(
|
||||||
|
DeployedItem.boomer,
|
||||||
|
DeployedItem.he_mine,
|
||||||
|
DeployedItem.spitfire_turret, DeployedItem.spitfire_cloaked, DeployedItem.spitfire_aa,
|
||||||
|
DeployedItem.motionalarmsensor,
|
||||||
|
DeployedItem.tank_traps
|
||||||
|
) flatMap UpdateUIElement
|
||||||
|
|
||||||
|
case AdvancedEngineering =>
|
||||||
|
List(AssaultEngineering, FortificationEngineering) flatMap UpdateUI
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def UpdateUI(certifications : List[CertificationType.Value]) : List[(Int,Int,Int,Int)] = {
|
||||||
|
certifications flatMap UpdateUI
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all managed deployables that are the same type.
|
||||||
|
* @param item the deployable type
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def ClearDeployable(item : DeployedItem.Value) : List[PlanetSideGUID] = {
|
||||||
|
val uitem = DeployableToolbox.UnifiedType(item)
|
||||||
|
val category = Deployable.Category.Of(uitem)
|
||||||
|
val categoryList = deployableLists(category)
|
||||||
|
val (out, in) = categoryList.partition(_.Definition.Item == item)
|
||||||
|
|
||||||
|
categoryList.clear()
|
||||||
|
categoryList ++= in
|
||||||
|
categoryCounts(category).Current = in.size
|
||||||
|
deployableCounts(uitem).Current = 0
|
||||||
|
out.map(_.GUID).toList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all managed deployables that belong to the same category.
|
||||||
|
* @param item the deployable type belonging to a category
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def ClearCategory(item : DeployedItem.Value) : List[PlanetSideGUID] = {
|
||||||
|
val category = Deployable.Category.Of(DeployableToolbox.UnifiedType(item))
|
||||||
|
val out = deployableLists(category).map(_.GUID).toList
|
||||||
|
deployableLists(category).clear()
|
||||||
|
categoryCounts(category).Current = 0
|
||||||
|
(Deployable.Category.Includes(category) map DeployableToolbox.UnifiedType toSet)
|
||||||
|
.foreach({item : DeployedItem.Value => deployableCounts(item).Current = 0 })
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all managed deployables.
|
||||||
|
* @return a list of globally unique identifiers that should be valid for the current zone
|
||||||
|
*/
|
||||||
|
def Clear() : List[PlanetSideGUID] = {
|
||||||
|
val out = deployableLists.values.flatten.map(_.GUID).toList
|
||||||
|
deployableLists.values.foreach(_.clear())
|
||||||
|
deployableCounts.values.foreach(_.Current = 0)
|
||||||
|
categoryCounts.values.foreach(_.Current = 0)
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableToolbox {
|
||||||
|
/**
|
||||||
|
* A `type` intended to properly define the minimum acceptable conditions for a `Deployable` object.
|
||||||
|
*/
|
||||||
|
type AcceptableDeployable = PlanetSideGameObject with Deployable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal class to keep track of the quantity of deployables managed for a certain set of criteria.
|
||||||
|
* There are deployable numbers organized by deploybale type and by deployable category.
|
||||||
|
*/
|
||||||
|
private class Bin {
|
||||||
|
/** the maximum number of deployables for this criteria that can be managed */
|
||||||
|
private var max : Int = 0
|
||||||
|
/** the current number of deployables for this criteria that are being managed */
|
||||||
|
private var current : Int = 0
|
||||||
|
|
||||||
|
def Current : Int = current
|
||||||
|
|
||||||
|
def Current_=(curr : Int) : Int = {
|
||||||
|
current = curr
|
||||||
|
Current
|
||||||
|
}
|
||||||
|
|
||||||
|
def Max : Int = max
|
||||||
|
|
||||||
|
def Max_=(mx : Int) : Int = {
|
||||||
|
max = mx
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
def Available() : Boolean = current < max
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some deployable types, though unique themselves,
|
||||||
|
* resolve to the same deployable type for the purposes of categorization.
|
||||||
|
* @param item the type of deployable
|
||||||
|
* @return the corrected deployable type
|
||||||
|
*/
|
||||||
|
def UnifiedType(item : DeployedItem.Value) : DeployedItem.Value = item match {
|
||||||
|
case DeployedItem.portable_manned_turret_nc | DeployedItem.portable_manned_turret_tr | DeployedItem.portable_manned_turret_vs =>
|
||||||
|
DeployedItem.portable_manned_turret
|
||||||
|
case _ =>
|
||||||
|
item
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardcoded maximum values for the category and type initialization.
|
||||||
|
* @param counts a reference to the type `Bin` object
|
||||||
|
* @param categories a reference to the category `Bin` object
|
||||||
|
* @param certifications a group of certifications for the initial values
|
||||||
|
*/
|
||||||
|
private def Initialize(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certifications : Set[CertificationType.Value]) : Unit = {
|
||||||
|
import CertificationType._
|
||||||
|
if(certifications.contains(AdvancedEngineering)) {
|
||||||
|
counts(DeployedItem.boomer).Max = 25
|
||||||
|
counts(DeployedItem.he_mine).Max = 25
|
||||||
|
counts(DeployedItem.jammer_mine).Max = 20
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 15
|
||||||
|
counts(DeployedItem.spitfire_cloaked).Max = 5
|
||||||
|
counts(DeployedItem.spitfire_aa).Max = 5
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 25
|
||||||
|
counts(DeployedItem.tank_traps).Max = 5
|
||||||
|
counts(DeployedItem.portable_manned_turret).Max = 1 //the below turret types are unified
|
||||||
|
//counts(DeployedItem.portable_manned_turret_nc).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_tr).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_vs).Max = 1
|
||||||
|
counts(DeployedItem.deployable_shield_generator).Max = 1
|
||||||
|
categories(DeployableCategory.Boomers).Max = 25
|
||||||
|
categories(DeployableCategory.Mines).Max = 25
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 15
|
||||||
|
categories(DeployableCategory.Sensors).Max = 25
|
||||||
|
categories(DeployableCategory.TankTraps).Max = 5
|
||||||
|
categories(DeployableCategory.FieldTurrets).Max = 1
|
||||||
|
categories(DeployableCategory.ShieldGenerators).Max = 1
|
||||||
|
|
||||||
|
if(certifications.contains(AdvancedHacking)) {
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(certifications.contains(CombatEngineering)) {
|
||||||
|
if(certifications.contains(AssaultEngineering)) {
|
||||||
|
counts(DeployedItem.jammer_mine).Max = 20
|
||||||
|
counts(DeployedItem.portable_manned_turret).Max = 1 //the below turret types are unified
|
||||||
|
//counts(DeployedItem.portable_manned_turret_nc).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_tr).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_vs).Max = 1
|
||||||
|
counts(DeployedItem.deployable_shield_generator).Max = 1
|
||||||
|
categories(DeployableCategory.FieldTurrets).Max = 1
|
||||||
|
categories(DeployableCategory.ShieldGenerators).Max = 1
|
||||||
|
}
|
||||||
|
if(certifications.contains(FortificationEngineering)) {
|
||||||
|
counts(DeployedItem.boomer).Max = 25
|
||||||
|
counts(DeployedItem.he_mine).Max = 25
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 15
|
||||||
|
counts(DeployedItem.spitfire_cloaked).Max = 5
|
||||||
|
counts(DeployedItem.spitfire_aa).Max = 5
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 25
|
||||||
|
counts(DeployedItem.tank_traps).Max = 5
|
||||||
|
categories(DeployableCategory.Boomers).Max = 25
|
||||||
|
categories(DeployableCategory.Mines).Max = 25
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 15
|
||||||
|
categories(DeployableCategory.Sensors).Max = 25
|
||||||
|
categories(DeployableCategory.TankTraps).Max = 5
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
counts(DeployedItem.boomer).Max = 20
|
||||||
|
counts(DeployedItem.he_mine).Max = 20
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 10
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 20
|
||||||
|
categories(DeployableCategory.Boomers).Max = 20
|
||||||
|
categories(DeployableCategory.Mines).Max = 20
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 10
|
||||||
|
categories(DeployableCategory.Sensors).Max = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
if(certifications.contains(AdvancedHacking)) {
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(certifications.contains(CertificationType.GroundSupport)) {
|
||||||
|
counts(DeployedItem.router_telepad_deployable).Max = 1
|
||||||
|
categories(DeployableCategory.Telepads).Max = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardcoded maximum values for the category and type initialization upon providing a new certification.
|
||||||
|
* @param counts a reference to the type `Bin` object
|
||||||
|
* @param categories a reference to the category `Bin` object
|
||||||
|
* @param certification the new certification
|
||||||
|
* @param certificationSet the group of previous certifications being tracked
|
||||||
|
*/
|
||||||
|
def AddToDeployableQuantities(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = {
|
||||||
|
import CertificationType._
|
||||||
|
if(certificationSet contains certification) {
|
||||||
|
certification match {
|
||||||
|
case AdvancedHacking =>
|
||||||
|
if(certificationSet contains CombatEngineering) {
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
case CombatEngineering =>
|
||||||
|
counts(DeployedItem.boomer).Max = 20
|
||||||
|
counts(DeployedItem.he_mine).Max = 20
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 10
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 20
|
||||||
|
categories(DeployableCategory.Boomers).Max = 20
|
||||||
|
categories(DeployableCategory.Mines).Max = 20
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 10
|
||||||
|
categories(DeployableCategory.Sensors).Max = 20
|
||||||
|
if(certificationSet contains AdvancedHacking) {
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
case AssaultEngineering =>
|
||||||
|
counts(DeployedItem.jammer_mine).Max = 20
|
||||||
|
counts(DeployedItem.portable_manned_turret).Max = 1 //the below turret types are unified
|
||||||
|
//counts(DeployedItem.portable_manned_turret_nc).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_tr).Max = 1
|
||||||
|
//counts(DeployedItem.portable_manned_turret_vs).Max = 1
|
||||||
|
counts(DeployedItem.deployable_shield_generator).Max = 1
|
||||||
|
categories(DeployableCategory.FieldTurrets).Max = 1
|
||||||
|
categories(DeployableCategory.ShieldGenerators).Max = 1
|
||||||
|
|
||||||
|
case FortificationEngineering =>
|
||||||
|
counts(DeployedItem.boomer).Max = 25
|
||||||
|
counts(DeployedItem.he_mine).Max = 25
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 15
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 25
|
||||||
|
counts(DeployedItem.spitfire_cloaked).Max = 5
|
||||||
|
counts(DeployedItem.spitfire_aa).Max = 5
|
||||||
|
counts(DeployedItem.tank_traps).Max = 5
|
||||||
|
categories(DeployableCategory.Boomers).Max = 25
|
||||||
|
categories(DeployableCategory.Mines).Max = 25
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 15
|
||||||
|
categories(DeployableCategory.Sensors).Max = 25
|
||||||
|
categories(DeployableCategory.TankTraps).Max = 5
|
||||||
|
|
||||||
|
case AdvancedEngineering =>
|
||||||
|
if(!certificationSet.contains(AssaultEngineering)) {
|
||||||
|
AddToDeployableQuantities(counts, categories, AssaultEngineering, certificationSet ++ Set(AssaultEngineering))
|
||||||
|
}
|
||||||
|
if(!certificationSet.contains(FortificationEngineering)) {
|
||||||
|
AddToDeployableQuantities(counts, categories, FortificationEngineering, certificationSet ++ Set(FortificationEngineering))
|
||||||
|
}
|
||||||
|
|
||||||
|
case GroundSupport =>
|
||||||
|
counts(DeployedItem.router_telepad_deployable).Max = 1024
|
||||||
|
categories(DeployableCategory.Telepads).Max = 1024
|
||||||
|
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardcoded zero'd values for the category and type initialization upon ignoring a previous certification.
|
||||||
|
* @param counts a reference to the type `Bin` object
|
||||||
|
* @param categories a reference to the category `Bin` object
|
||||||
|
* @param certification the new certification
|
||||||
|
* @param certificationSet the group of previous certifications being tracked
|
||||||
|
*/
|
||||||
|
def RemoveFromDeployablesQuantities(counts : Map[DeployedItem.Value, DeployableToolbox.Bin], categories : Map[DeployableCategory.Value, DeployableToolbox.Bin], certification : CertificationType.Value, certificationSet : Set[CertificationType.Value]) : Unit = {
|
||||||
|
import CertificationType._
|
||||||
|
if(!certificationSet.contains(certification)) {
|
||||||
|
certification match {
|
||||||
|
case AdvancedHacking =>
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 0
|
||||||
|
|
||||||
|
case CombatEngineering =>
|
||||||
|
counts(DeployedItem.boomer).Max = 0
|
||||||
|
counts(DeployedItem.he_mine).Max = 0
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = 0
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = 0
|
||||||
|
counts(DeployedItem.sensor_shield).Max = 0
|
||||||
|
categories(DeployableCategory.Boomers).Max = 0
|
||||||
|
categories(DeployableCategory.Mines).Max = 0
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = 0
|
||||||
|
categories(DeployableCategory.Sensors).Max = 0
|
||||||
|
|
||||||
|
case AssaultEngineering =>
|
||||||
|
counts(DeployedItem.jammer_mine).Max = 0
|
||||||
|
counts(DeployedItem.portable_manned_turret).Max = 0 //the below turret types are unified
|
||||||
|
//counts(DeployedItem.portable_manned_turret_nc).Max = 0
|
||||||
|
//counts(DeployedItem.portable_manned_turret_tr).Max = 0
|
||||||
|
//counts(DeployedItem.portable_manned_turret_vs).Max = 0
|
||||||
|
counts(DeployedItem.deployable_shield_generator).Max = 0
|
||||||
|
categories(DeployableCategory.Sensors).Max = if(certificationSet contains CombatEngineering) 20 else 0
|
||||||
|
categories(DeployableCategory.FieldTurrets).Max = 0
|
||||||
|
categories(DeployableCategory.ShieldGenerators).Max = 0
|
||||||
|
|
||||||
|
case FortificationEngineering =>
|
||||||
|
val ce : Int = if(certificationSet contains CombatEngineering) 1 else 0 //true = 1, false = 0
|
||||||
|
counts(DeployedItem.boomer).Max = ce * 20
|
||||||
|
counts(DeployedItem.he_mine).Max = ce * 20
|
||||||
|
counts(DeployedItem.spitfire_turret).Max = ce * 10
|
||||||
|
counts(DeployedItem.motionalarmsensor).Max = ce * 20
|
||||||
|
counts(DeployedItem.spitfire_cloaked).Max = 0
|
||||||
|
counts(DeployedItem.spitfire_aa).Max = 0
|
||||||
|
counts(DeployedItem.tank_traps).Max = 0
|
||||||
|
categories(DeployableCategory.Boomers).Max = ce * 20
|
||||||
|
categories(DeployableCategory.Mines).Max = ce * 20
|
||||||
|
categories(DeployableCategory.SmallTurrets).Max = ce * 10
|
||||||
|
categories(DeployableCategory.Sensors).Max = ce * 20
|
||||||
|
categories(DeployableCategory.TankTraps).Max = 0
|
||||||
|
|
||||||
|
case AdvancedEngineering =>
|
||||||
|
if(!certificationSet.contains(AssaultEngineering)) {
|
||||||
|
RemoveFromDeployablesQuantities(counts, categories, AssaultEngineering, certificationSet)
|
||||||
|
}
|
||||||
|
if(!certificationSet.contains(FortificationEngineering)) {
|
||||||
|
RemoveFromDeployablesQuantities(counts, categories, FortificationEngineering, certificationSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
case GroundSupport =>
|
||||||
|
counts(DeployedItem.router_telepad_deployable).Max = 0
|
||||||
|
categories(DeployableCategory.Telepads).Max = 0
|
||||||
|
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
|
import net.psforever.objects.TurretDeployable
|
||||||
|
import net.psforever.objects.ce.ComplexDeployable
|
||||||
|
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class ComplexDeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition,
|
||||||
|
faction : PlanetSideEmpire.Value,
|
||||||
|
health : Int,
|
||||||
|
shields : Int,
|
||||||
|
ownerName : String,
|
||||||
|
position : Vector3,
|
||||||
|
orientation : Vector3) extends SourceEntry {
|
||||||
|
override def Name = SourceEntry.NameFormat(obj_def.Name)
|
||||||
|
override def Faction = faction
|
||||||
|
def Definition : ObjectDefinition with BaseDeployableDefinition = obj_def
|
||||||
|
def Health = health
|
||||||
|
def Shields = shields
|
||||||
|
def OwnerName = ownerName
|
||||||
|
def Position = position
|
||||||
|
def Orientation = orientation
|
||||||
|
def Velocity = None
|
||||||
|
}
|
||||||
|
|
||||||
|
object ComplexDeployableSource {
|
||||||
|
def apply(obj : ComplexDeployable) : ComplexDeployableSource = {
|
||||||
|
ComplexDeployableSource(
|
||||||
|
obj.Definition,
|
||||||
|
obj.Faction,
|
||||||
|
obj.Health,
|
||||||
|
obj.Shields,
|
||||||
|
obj.OwnerName.getOrElse(""),
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(obj : TurretDeployable) : ComplexDeployableSource = {
|
||||||
|
ComplexDeployableSource(
|
||||||
|
obj.Definition,
|
||||||
|
obj.Faction,
|
||||||
|
obj.Health,
|
||||||
|
obj.Shields,
|
||||||
|
obj.OwnerName.getOrElse(""),
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class DeployableSource(obj_def : ObjectDefinition with BaseDeployableDefinition,
|
||||||
|
faction : PlanetSideEmpire.Value,
|
||||||
|
health : Int,
|
||||||
|
ownerName : String,
|
||||||
|
position : Vector3,
|
||||||
|
orientation : Vector3) extends SourceEntry {
|
||||||
|
override def Name = SourceEntry.NameFormat(obj_def.Name)
|
||||||
|
override def Faction = faction
|
||||||
|
def Definition : ObjectDefinition with BaseDeployableDefinition = obj_def
|
||||||
|
def Health = health
|
||||||
|
def OwnerName = ownerName
|
||||||
|
def Position = position
|
||||||
|
def Orientation = orientation
|
||||||
|
def Velocity = None
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableSource {
|
||||||
|
def apply(obj : PlanetSideGameObject with Deployable) : DeployableSource = {
|
||||||
|
DeployableSource(
|
||||||
|
obj.Definition,
|
||||||
|
obj.Faction,
|
||||||
|
obj.Health,
|
||||||
|
obj.OwnerName.getOrElse(""),
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.ballistics
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle}
|
||||||
import net.psforever.objects.entity.WorldEntity
|
import net.psforever.objects.entity.WorldEntity
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
@ -29,6 +30,9 @@ object SourceEntry {
|
||||||
target match {
|
target match {
|
||||||
case obj : Player => PlayerSource(obj)
|
case obj : Player => PlayerSource(obj)
|
||||||
case obj : Vehicle => VehicleSource(obj)
|
case obj : Vehicle => VehicleSource(obj)
|
||||||
|
case obj : ComplexDeployable => ComplexDeployableSource(obj)
|
||||||
|
case obj : TurretDeployable => ComplexDeployableSource(obj)
|
||||||
|
case obj : SimpleDeployable => DeployableSource(obj)
|
||||||
case _ => ObjectSource(target)
|
case _ => ObjectSource(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
|
||||||
|
abstract class ComplexDeployable(cdef : DeployableDefinition) extends PlanetSideServerObject
|
||||||
|
with Deployable {
|
||||||
|
Health = Definition.MaxHealth
|
||||||
|
|
||||||
|
def MaxHealth : Int = Definition.MaxHealth
|
||||||
|
|
||||||
|
private var shields : Int = 0
|
||||||
|
|
||||||
|
def Shields : Int = shields
|
||||||
|
|
||||||
|
def Shields_=(toShields : Int) : Int = {
|
||||||
|
shields = math.min(math.max(0, toShields), MaxShields)
|
||||||
|
Shields
|
||||||
|
}
|
||||||
|
|
||||||
|
def MaxShields : Int = {
|
||||||
|
0//Definition.MaxShields
|
||||||
|
}
|
||||||
|
|
||||||
|
def Definition = cdef
|
||||||
|
}
|
||||||
151
common/src/main/scala/net/psforever/objects/ce/Deployable.scala
Normal file
151
common/src/main/scala/net/psforever/objects/ce/Deployable.scala
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
import net.psforever.objects._
|
||||||
|
import net.psforever.objects.definition.{BaseDeployableDefinition, ObjectDefinition}
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||||
|
import net.psforever.packet.game.{DeployableIcon, PlanetSideGUID}
|
||||||
|
import net.psforever.types.PlanetSideEmpire
|
||||||
|
|
||||||
|
trait Deployable extends FactionAffinity
|
||||||
|
with Vitality {
|
||||||
|
this : PlanetSideGameObject =>
|
||||||
|
private var health : Int = 1
|
||||||
|
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||||
|
private var owner : Option[PlanetSideGUID] = None
|
||||||
|
private var ownerName : Option[String] = None
|
||||||
|
|
||||||
|
def Health : Int = health
|
||||||
|
|
||||||
|
def Health_=(toHealth : Int) : Int = {
|
||||||
|
health = math.min(math.max(0, toHealth), MaxHealth)
|
||||||
|
Health
|
||||||
|
}
|
||||||
|
|
||||||
|
def MaxHealth : Int
|
||||||
|
|
||||||
|
def Faction : PlanetSideEmpire.Value = faction
|
||||||
|
|
||||||
|
override def Faction_=(toFaction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = {
|
||||||
|
faction = toFaction
|
||||||
|
Faction
|
||||||
|
}
|
||||||
|
|
||||||
|
def Owner : Option[PlanetSideGUID] = owner
|
||||||
|
|
||||||
|
def Owner_=(owner : PlanetSideGUID) : Option[PlanetSideGUID] = Owner_=(Some(owner))
|
||||||
|
|
||||||
|
def Owner_=(owner : Player) : Option[PlanetSideGUID] = Owner_=(Some(owner.GUID))
|
||||||
|
|
||||||
|
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
|
||||||
|
owner match {
|
||||||
|
case Some(_) =>
|
||||||
|
this.owner = owner
|
||||||
|
case None =>
|
||||||
|
this.owner = None
|
||||||
|
}
|
||||||
|
Owner
|
||||||
|
}
|
||||||
|
|
||||||
|
def OwnerName : Option[String] = ownerName
|
||||||
|
|
||||||
|
def OwnerName_=(owner : String) : Option[String] = OwnerName_=(Some(owner))
|
||||||
|
|
||||||
|
def OwnerName_=(owner : Player) : Option[String] = OwnerName_=(Some(owner.Name))
|
||||||
|
|
||||||
|
def OwnerName_=(owner : Option[String]) : Option[String] = {
|
||||||
|
owner match {
|
||||||
|
case Some(_) =>
|
||||||
|
ownerName = owner
|
||||||
|
case None =>
|
||||||
|
ownerName = None
|
||||||
|
}
|
||||||
|
OwnerName
|
||||||
|
}
|
||||||
|
|
||||||
|
def DamageModel : DamageResistanceModel = Definition.asInstanceOf[DamageResistanceModel]
|
||||||
|
|
||||||
|
def Definition : ObjectDefinition with BaseDeployableDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
object Deployable {
|
||||||
|
object Category {
|
||||||
|
def Of(item : DeployedItem.Value) : DeployableCategory.Value = deployablesToCategories(item)
|
||||||
|
|
||||||
|
def Includes(category : DeployableCategory.Value) : List[DeployedItem.Value] = {
|
||||||
|
(for {
|
||||||
|
(ce, cat) <- deployablesToCategories
|
||||||
|
if cat == category
|
||||||
|
} yield ce) toList
|
||||||
|
}
|
||||||
|
|
||||||
|
def OfAll() : Map[DeployedItem.Value, DeployableCategory.Value] = deployablesToCategories
|
||||||
|
|
||||||
|
private val deployablesToCategories : Map[DeployedItem.Value, DeployableCategory.Value] = Map(
|
||||||
|
DeployedItem.boomer -> DeployableCategory.Boomers,
|
||||||
|
DeployedItem.he_mine -> DeployableCategory.Mines,
|
||||||
|
DeployedItem.jammer_mine -> DeployableCategory.Mines,
|
||||||
|
DeployedItem.spitfire_turret -> DeployableCategory.SmallTurrets,
|
||||||
|
DeployedItem.motionalarmsensor -> DeployableCategory.Sensors,
|
||||||
|
DeployedItem.spitfire_cloaked -> DeployableCategory.SmallTurrets,
|
||||||
|
DeployedItem.spitfire_aa -> DeployableCategory.SmallTurrets,
|
||||||
|
DeployedItem.deployable_shield_generator -> DeployableCategory.ShieldGenerators,
|
||||||
|
DeployedItem.tank_traps -> DeployableCategory.TankTraps,
|
||||||
|
DeployedItem.portable_manned_turret -> DeployableCategory.FieldTurrets,
|
||||||
|
DeployedItem.portable_manned_turret_nc -> DeployableCategory.FieldTurrets,
|
||||||
|
DeployedItem.portable_manned_turret_tr -> DeployableCategory.FieldTurrets,
|
||||||
|
DeployedItem.portable_manned_turret_vs -> DeployableCategory.FieldTurrets,
|
||||||
|
DeployedItem.sensor_shield -> DeployableCategory.Sensors,
|
||||||
|
DeployedItem.router_telepad_deployable -> DeployableCategory.Telepads
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Icon {
|
||||||
|
def apply(item : DeployedItem.Value) : DeployableIcon.Value = ceicon(item.id)
|
||||||
|
|
||||||
|
private val ceicon : Map[Int, DeployableIcon.Value] = Map(
|
||||||
|
DeployedItem.boomer.id -> DeployableIcon.Boomer,
|
||||||
|
DeployedItem.he_mine.id -> DeployableIcon.HEMine,
|
||||||
|
DeployedItem.jammer_mine.id -> DeployableIcon.DisruptorMine,
|
||||||
|
DeployedItem.spitfire_turret.id -> DeployableIcon.SpitfireTurret,
|
||||||
|
DeployedItem.spitfire_cloaked.id -> DeployableIcon.ShadowTurret,
|
||||||
|
DeployedItem.spitfire_aa.id -> DeployableIcon.CerebusTurret,
|
||||||
|
DeployedItem.motionalarmsensor.id -> DeployableIcon.MotionAlarmSensor,
|
||||||
|
DeployedItem.sensor_shield.id -> DeployableIcon.SensorDisruptor,
|
||||||
|
DeployedItem.tank_traps.id -> DeployableIcon.TRAP,
|
||||||
|
DeployedItem.portable_manned_turret.id -> DeployableIcon.FieldTurret,
|
||||||
|
DeployedItem.portable_manned_turret_tr.id -> DeployableIcon.FieldTurret,
|
||||||
|
DeployedItem.portable_manned_turret_nc.id -> DeployableIcon.FieldTurret,
|
||||||
|
DeployedItem.portable_manned_turret_vs.id -> DeployableIcon.FieldTurret,
|
||||||
|
DeployedItem.deployable_shield_generator.id -> DeployableIcon.AegisShieldGenerator
|
||||||
|
).withDefaultValue(DeployableIcon.Boomer)
|
||||||
|
}
|
||||||
|
|
||||||
|
object UI {
|
||||||
|
def apply(item : DeployedItem.Value) : (Int, Int) = planetsideAttribute(item)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attribute values to be invoked in `PlanetsideAttributeMessage` packets
|
||||||
|
* in reference to a particular combat engineering deployable element on the UI.
|
||||||
|
* The first number is for the actual count field.
|
||||||
|
* The second number is for the maximum count field.
|
||||||
|
*/
|
||||||
|
private val planetsideAttribute : Map[DeployedItem.Value, (Int, Int)] = Map(
|
||||||
|
DeployedItem.boomer -> (94, 83),
|
||||||
|
DeployedItem.he_mine -> (95, 84),
|
||||||
|
DeployedItem.jammer_mine -> (96, 85),
|
||||||
|
DeployedItem.spitfire_turret -> (97, 86),
|
||||||
|
DeployedItem.motionalarmsensor -> (98, 87),
|
||||||
|
DeployedItem.spitfire_cloaked -> (99, 88),
|
||||||
|
DeployedItem.spitfire_aa -> (100, 89),
|
||||||
|
DeployedItem.deployable_shield_generator -> (101, 90),
|
||||||
|
DeployedItem.tank_traps -> (102, 91),
|
||||||
|
DeployedItem.portable_manned_turret -> (103, 92),
|
||||||
|
DeployedItem.portable_manned_turret_nc -> (103, 92),
|
||||||
|
DeployedItem.portable_manned_turret_tr -> (103, 92),
|
||||||
|
DeployedItem.portable_manned_turret_vs -> (103, 92),
|
||||||
|
DeployedItem.sensor_shield -> (104, 93)
|
||||||
|
).withDefaultValue((0,0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
object DeployableCategory extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
val
|
||||||
|
Boomers,
|
||||||
|
Mines,
|
||||||
|
SmallTurrets,
|
||||||
|
Sensors,
|
||||||
|
TankTraps,
|
||||||
|
FieldTurrets,
|
||||||
|
ShieldGenerators,
|
||||||
|
Telepads
|
||||||
|
= Value
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
object DeployedItem extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
final val boomer = Value(148)
|
||||||
|
final val deployable_shield_generator = Value(240)
|
||||||
|
final val he_mine = Value(388)
|
||||||
|
final val jammer_mine = Value(420) //disruptor mine
|
||||||
|
final val motionalarmsensor = Value(575)
|
||||||
|
final val sensor_shield = Value(752) //sensor disruptor
|
||||||
|
final val spitfire_aa = Value(819) //cerebus turret
|
||||||
|
final val spitfire_cloaked = Value(825) //shadow turret
|
||||||
|
final val spitfire_turret = Value(826)
|
||||||
|
final val tank_traps = Value(849) //trap
|
||||||
|
final val portable_manned_turret = Value(685)
|
||||||
|
final val portable_manned_turret_nc = Value(686)
|
||||||
|
final val portable_manned_turret_tr = Value(687)
|
||||||
|
final val portable_manned_turret_vs = Value(688)
|
||||||
|
final val router_telepad_deployable = Value(744)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.definition.DeployableDefinition
|
||||||
|
|
||||||
|
abstract class SimpleDeployable(cdef : DeployableDefinition) extends PlanetSideGameObject
|
||||||
|
with Deployable {
|
||||||
|
Health = Definition.MaxHealth
|
||||||
|
|
||||||
|
def MaxHealth : Int = Definition.MaxHealth
|
||||||
|
|
||||||
|
def Definition = cdef
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.definition
|
||||||
|
|
||||||
abstract class BasicDefinition {
|
abstract class BasicDefinition {
|
||||||
private var name : String = "definition"
|
private var name : String = "definition"
|
||||||
|
private var descriptor : Option[String] = None
|
||||||
|
|
||||||
def Name : String = name
|
def Name : String = name
|
||||||
|
|
||||||
|
|
@ -10,4 +11,13 @@ abstract class BasicDefinition {
|
||||||
this.name = name
|
this.name = name
|
||||||
Name
|
Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def Descriptor : String = descriptor.getOrElse(Name)
|
||||||
|
|
||||||
|
def Descriptor_=(description : String) : String = Descriptor_=(Some(description))
|
||||||
|
|
||||||
|
def Descriptor_=(description : Option[String]) : String = {
|
||||||
|
descriptor = description
|
||||||
|
Descriptor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.definition
|
package net.psforever.objects.definition
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.DeployedItem
|
||||||
|
import net.psforever.objects.definition.converter.ACEConverter
|
||||||
import net.psforever.objects.equipment.CItem
|
import net.psforever.objects.equipment.CItem
|
||||||
|
import net.psforever.types.CertificationType
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
class ConstructionItemDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
|
class ConstructionItemDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
|
||||||
CItem.Unit(objectId) //let throw NoSuchElementException
|
CItem(objectId) //let throw NoSuchElementException
|
||||||
private val modes : ListBuffer[CItem.DeployedItem.Value] = ListBuffer()
|
private val modes : ListBuffer[ConstructionFireMode] = ListBuffer()
|
||||||
|
Packet = new ACEConverter
|
||||||
|
|
||||||
def Modes : ListBuffer[CItem.DeployedItem.Value] = modes
|
def Modes : ListBuffer[ConstructionFireMode] = modes
|
||||||
}
|
}
|
||||||
|
|
||||||
object ConstructionItemDefinition {
|
object ConstructionItemDefinition {
|
||||||
|
|
@ -17,7 +21,29 @@ object ConstructionItemDefinition {
|
||||||
new ConstructionItemDefinition(objectId)
|
new ConstructionItemDefinition(objectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(cItem : CItem.Unit.Value) : ConstructionItemDefinition = {
|
def apply(cItem : CItem.Value) : ConstructionItemDefinition = {
|
||||||
new ConstructionItemDefinition(cItem.id)
|
new ConstructionItemDefinition(cItem.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConstructionFireMode {
|
||||||
|
private val deployables : ListBuffer[DeployedItem.Value] = ListBuffer.empty
|
||||||
|
private val permissions : ListBuffer[Set[CertificationType.Value]] = ListBuffer.empty
|
||||||
|
|
||||||
|
def Permissions : ListBuffer[Set[CertificationType.Value]] = permissions
|
||||||
|
|
||||||
|
def Deployables : ListBuffer[DeployedItem.Value] = deployables
|
||||||
|
|
||||||
|
def Item(deployable : DeployedItem.Value) : ListBuffer[DeployedItem.Value] = {
|
||||||
|
deployables += deployable
|
||||||
|
permissions += Set.empty[CertificationType.Value]
|
||||||
|
deployables
|
||||||
|
}
|
||||||
|
|
||||||
|
def Item(deployPair : (DeployedItem.Value, Set[CertificationType.Value])) : ListBuffer[DeployedItem.Value] = {
|
||||||
|
val (deployable, permission) = deployPair
|
||||||
|
deployables += deployable
|
||||||
|
permissions += permission
|
||||||
|
deployables
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition
|
||||||
|
|
||||||
|
import akka.actor.{ActorContext, ActorRef}
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
|
||||||
|
import net.psforever.objects.definition.converter.SmallDeployableConverter
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage}
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
trait BaseDeployableDefinition extends DamageResistanceModel {
|
||||||
|
private var category : DeployableCategory.Value = DeployableCategory.Boomers
|
||||||
|
private var deployTime : Long = (1 second).toMillis //ms
|
||||||
|
private var maxHealth : Int = 1
|
||||||
|
Damage = StandardDeployableDamage
|
||||||
|
Resistance = NoResistanceSelection
|
||||||
|
|
||||||
|
def Item : DeployedItem.Value
|
||||||
|
|
||||||
|
def MaxHealth : Int = maxHealth
|
||||||
|
|
||||||
|
def MaxHealth_=(toHealth : Int) : Int = {
|
||||||
|
maxHealth = toHealth
|
||||||
|
MaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
def DeployCategory : DeployableCategory.Value = category
|
||||||
|
|
||||||
|
def DeployCategory_=(cat : DeployableCategory.Value) : DeployableCategory.Value = {
|
||||||
|
category = cat
|
||||||
|
DeployCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
def DeployTime : Long = deployTime
|
||||||
|
|
||||||
|
def DeployTime_=(time : FiniteDuration) : Long = DeployTime_=(time.toMillis)
|
||||||
|
|
||||||
|
def DeployTime_=(time: Long) : Long = {
|
||||||
|
deployTime = time
|
||||||
|
DeployTime
|
||||||
|
}
|
||||||
|
|
||||||
|
def Initialize(obj : PlanetSideGameObject with Deployable, context : ActorContext) : Unit = { }
|
||||||
|
|
||||||
|
def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) : Unit = { }
|
||||||
|
|
||||||
|
def Uninitialize(obj : PlanetSideGameObject with Deployable, context : ActorContext) : Unit = { }
|
||||||
|
|
||||||
|
def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) : Unit = { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeployableDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||||
|
with BaseDeployableDefinition {
|
||||||
|
private val item = DeployedItem(objectId) //let throw NoSuchElementException
|
||||||
|
Packet = new SmallDeployableConverter
|
||||||
|
|
||||||
|
def Item : DeployedItem.Value = item
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableDefinition {
|
||||||
|
def apply(item : DeployedItem.Value) : DeployableDefinition =
|
||||||
|
new DeployableDefinition(item.id)
|
||||||
|
|
||||||
|
def SimpleUninitialize(obj : PlanetSideGameObject, context : ActorContext) : Unit = { }
|
||||||
|
|
||||||
|
def SimpleUninitialize(obj : PlanetSideServerObject, context : ActorContext) : Unit = {
|
||||||
|
obj.Actor ! akka.actor.PoisonPill
|
||||||
|
obj.Actor = ActorRef.noSender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.objects.serverobject.turret.WeaponTurret
|
||||||
|
import net.psforever.objects.TurretDeployable
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
||||||
|
override def ConstructorData(obj : TurretDeployable) : Try[OneMannedFieldTurretData] = {
|
||||||
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
|
if(health > 3) {
|
||||||
|
Success(
|
||||||
|
OneMannedFieldTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = false,
|
||||||
|
unk1 = 0,
|
||||||
|
obj.Jammered,
|
||||||
|
unk2 = false,
|
||||||
|
obj.Owner match {
|
||||||
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
health,
|
||||||
|
Some(InventoryData(FieldTurretConverter.MakeMountings(obj)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Success(
|
||||||
|
OneMannedFieldTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = true,
|
||||||
|
unk1 = 0,
|
||||||
|
jammered = false,
|
||||||
|
unk2 = false,
|
||||||
|
owner_guid = PlanetSideGUID(0)
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : TurretDeployable) : Try[OneMannedFieldTurretData] =
|
||||||
|
Failure(new Exception("converter should not be used to generate detailed OneMannedFieldTurretData"))
|
||||||
|
}
|
||||||
|
|
||||||
|
object FieldTurretConverter {
|
||||||
|
private def MakeMountings(obj : WeaponTurret) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
obj.Weapons.map({
|
||||||
|
case((index, slot)) =>
|
||||||
|
val equip : Equipment = slot.Equipment.get
|
||||||
|
val equipDef = equip.Definition
|
||||||
|
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||||
|
}).toList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.vehicles.Seat
|
||||||
|
import net.psforever.packet.game.objectcreate.{InventoryItemData, ObjectClass, PlayerData, VehicleData}
|
||||||
|
|
||||||
|
object SeatConverter {
|
||||||
|
def MakeSeat(player : Player, offset : Long) : PlayerData = {
|
||||||
|
VehicleData.PlayerData(
|
||||||
|
AvatarConverter.MakeAppearanceData(player),
|
||||||
|
AvatarConverter.MakeCharacterData(player),
|
||||||
|
AvatarConverter.MakeInventoryData(player),
|
||||||
|
AvatarConverter.GetDrawnSlot(player),
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO do not use for now; causes seat access permission issues with many passengers; may not mesh with workflows; GUID requirements
|
||||||
|
def MakeSeats(seats : Map[Int, Seat], initialOffset : Long) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
var offset = initialOffset
|
||||||
|
seats
|
||||||
|
.filter({ case (_, seat) => seat.isOccupied })
|
||||||
|
.map({case (index, seat) =>
|
||||||
|
val player = seat.Occupant.get
|
||||||
|
val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, SeatConverter.MakeSeat(player, offset))
|
||||||
|
offset += entry.bitsize
|
||||||
|
entry
|
||||||
|
}).toList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.ShieldGeneratorDeployable
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDeployable]() {
|
||||||
|
override def ConstructorData(obj : ShieldGeneratorDeployable) : Try[AegisShieldGeneratorData] = {
|
||||||
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
|
if(health > 0) {
|
||||||
|
Success(
|
||||||
|
AegisShieldGeneratorData(
|
||||||
|
CommonFieldData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = false,
|
||||||
|
unk = 0,
|
||||||
|
jammered = false,
|
||||||
|
obj.Owner match {
|
||||||
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
health
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Success(
|
||||||
|
AegisShieldGeneratorData(
|
||||||
|
CommonFieldData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = true,
|
||||||
|
unk = 0,
|
||||||
|
jammered = false,
|
||||||
|
player_guid = PlanetSideGUID(0)
|
||||||
|
),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : ShieldGeneratorDeployable) : Try[AegisShieldGeneratorData] =
|
||||||
|
Failure(new Exception("converter should not be used to generate detailed ShieldGeneratorDdata"))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate.{PlacementData, SmallDeployableData}
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObject with Deployable]() {
|
||||||
|
override def ConstructorData(obj : PlanetSideGameObject with Deployable) : Try[SmallDeployableData] = {
|
||||||
|
Success(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = false,
|
||||||
|
unk1 = 0,
|
||||||
|
jammered = false,
|
||||||
|
unk2 = false,
|
||||||
|
obj.Owner match {
|
||||||
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : PlanetSideGameObject with Deployable) : Try[SmallDeployableData] =
|
||||||
|
Failure(new Exception("converter should not be used to generate detailed SmallDeployableData"))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.objects.TurretDeployable
|
||||||
|
import net.psforever.objects.serverobject.turret.WeaponTurret
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
||||||
|
override def ConstructorData(obj : TurretDeployable) : Try[SmallTurretData] = {
|
||||||
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
|
if(health > 0) {
|
||||||
|
Success(
|
||||||
|
SmallTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = false,
|
||||||
|
unk1 = 0,
|
||||||
|
obj.Jammered,
|
||||||
|
unk2 = false,
|
||||||
|
obj.Owner match {
|
||||||
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
health,
|
||||||
|
Some(InventoryData(SmallTurretConverter.MakeMountings(obj)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Success(
|
||||||
|
SmallTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = true,
|
||||||
|
unk1 = 0,
|
||||||
|
jammered = false,
|
||||||
|
unk2 = false,
|
||||||
|
owner_guid = PlanetSideGUID(0)
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : TurretDeployable) : Try[SmallTurretData] =
|
||||||
|
Failure(new Exception("converter should not be used to generate detailed SmallTurretData"))
|
||||||
|
}
|
||||||
|
|
||||||
|
object SmallTurretConverter {
|
||||||
|
private def MakeMountings(obj : WeaponTurret) : List[InventoryItemData.InventoryItem] = {
|
||||||
|
obj.Weapons.map({
|
||||||
|
case((index, slot)) =>
|
||||||
|
val equip : Equipment = slot.Equipment.get
|
||||||
|
val equipDef = equip.Definition
|
||||||
|
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)
|
||||||
|
}).toList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.TrapDeployable
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
|
||||||
|
override def ConstructorData(obj : TrapDeployable) : Try[TRAPData] = {
|
||||||
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
|
if(health > 0) {
|
||||||
|
Success(
|
||||||
|
TRAPData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = false,
|
||||||
|
unk1 = 0,
|
||||||
|
jammered = false,
|
||||||
|
unk2 = false,
|
||||||
|
obj.Owner match {
|
||||||
|
case Some(owner) => owner
|
||||||
|
case None => PlanetSideGUID(0)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
health
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Success(
|
||||||
|
TRAPData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(obj.Position, obj.Orientation),
|
||||||
|
obj.Faction,
|
||||||
|
bops = false,
|
||||||
|
destroyed = true,
|
||||||
|
unk1 = 0,
|
||||||
|
jammered = false,
|
||||||
|
unk2 = false,
|
||||||
|
owner_guid = PlanetSideGUID(0)
|
||||||
|
),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : TrapDeployable) : Try[TRAPData] =
|
||||||
|
Failure(new Exception("converter should not be used to generate detailed TRAPData"))
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
|
|
||||||
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
override def ConstructorData(obj : Vehicle) : Try[VehicleData] = {
|
||||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||||
if(health > 3) { //active
|
if(health > 0) { //active
|
||||||
Success(
|
Success(
|
||||||
VehicleData(
|
VehicleData(
|
||||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||||
|
|
@ -72,39 +72,11 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
val offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
||||||
obj.Seats(0).Occupant match {
|
obj.Seats(0).Occupant match {
|
||||||
case Some(player) =>
|
case Some(player) =>
|
||||||
val mountedPlayer = VehicleData.PlayerData(
|
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, SeatConverter.MakeSeat(player, offset)))
|
||||||
AvatarConverter.MakeAppearanceData(player),
|
|
||||||
AvatarConverter.MakeCharacterData(player),
|
|
||||||
AvatarConverter.MakeInventoryData(player),
|
|
||||||
AvatarConverter.GetDrawnSlot(player),
|
|
||||||
offset
|
|
||||||
)
|
|
||||||
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer))
|
|
||||||
case None =>
|
case None =>
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO do not use for now; causes vehicle access permission issues; may not mesh with workflows; player GUID requirements
|
|
||||||
// private def MakeSeats(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
|
||||||
// var offset : Long = VehicleData.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
|
||||||
// obj.Seats
|
|
||||||
// .filter({ case (_, seat) => seat.isOccupied })
|
|
||||||
// .map({case (index, seat) =>
|
|
||||||
// val player = seat.Occupant.get
|
|
||||||
// val mountedPlayer = VehicleData.PlayerData(
|
|
||||||
// AvatarConverter.MakeAppearanceData(player),
|
|
||||||
// AvatarConverter.MakeCharacterData(player),
|
|
||||||
// AvatarConverter.MakeInventoryData(player),
|
|
||||||
// AvatarConverter.GetDrawnSlot(player),
|
|
||||||
// offset
|
|
||||||
// )
|
|
||||||
// val entry = InventoryItemData(ObjectClass.avatar, player.GUID, index, mountedPlayer)
|
|
||||||
// //println(s"seat 0 offset: $offset, size: ${entry.bitsize}, pad: ${mountedPlayer.basic_appearance.NamePadding}")
|
|
||||||
// offset += entry.bitsize
|
|
||||||
// entry
|
|
||||||
// }).toList
|
|
||||||
// }
|
|
||||||
|
|
||||||
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
private def MakeMountings(obj : Vehicle) : List[InventoryItemData.InventoryItem] = {
|
||||||
obj.Weapons.map({
|
obj.Weapons.map({
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,8 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.equipment
|
package net.psforever.objects.equipment
|
||||||
|
|
||||||
object CItem {
|
object CItem extends Enumeration {
|
||||||
object Unit extends Enumeration {
|
final val ace = Value(32)
|
||||||
final val ace = Value(32)
|
final val advanced_ace = Value(39) //fdu
|
||||||
final val advanced_ace = Value(39) //fdu
|
final val router_telepad = Value(743)
|
||||||
final val router_telepad = Value(743)
|
|
||||||
}
|
|
||||||
|
|
||||||
object DeployedItem extends Enumeration {
|
|
||||||
final val boomer = Value(148)
|
|
||||||
final val deployable_shield_generator = Value(240)
|
|
||||||
final val he_mine = Value(388)
|
|
||||||
final val jammer_mine = Value(420) //disruptor mine
|
|
||||||
final val motionalarmsensor = Value(575)
|
|
||||||
final val sensor_shield = Value(752) //sensor disruptor
|
|
||||||
final val spitfire_aa = Value(819) //cerebus turret
|
|
||||||
final val spitfire_cloaked = Value(825) //shadow turret
|
|
||||||
final val spitfire_turret = Value(826)
|
|
||||||
final val tank_traps = Value(849) //trap
|
|
||||||
final val portable_manned_turret = Value(685)
|
|
||||||
final val portable_manned_turret_nc = Value(686)
|
|
||||||
final val portable_manned_turret_tr = Value(687)
|
|
||||||
final val portable_manned_turret_vs = Value(688)
|
|
||||||
final val router_telepad_deployable = Value(744)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.equipment
|
||||||
|
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
||||||
|
trait RemoteUnit {
|
||||||
|
private var companion : Option[PlanetSideGUID] = None
|
||||||
|
|
||||||
|
def Companion : Option[PlanetSideGUID] = companion
|
||||||
|
|
||||||
|
def Companion_=(guid : PlanetSideGUID) : Option[PlanetSideGUID] = {
|
||||||
|
if(companion.isEmpty) {
|
||||||
|
companion = Some(guid)
|
||||||
|
}
|
||||||
|
Companion
|
||||||
|
}
|
||||||
|
|
||||||
|
def Companion_=(guid : Option[Any]) : Option[PlanetSideGUID] = {
|
||||||
|
if(guid.isEmpty) {
|
||||||
|
companion = None
|
||||||
|
}
|
||||||
|
Companion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,9 @@ package net.psforever.objects.guid
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import net.psforever.objects.entity.IdentifiableEntity
|
import net.psforever.objects.entity.IdentifiableEntity
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.{EquipmentSlot, LockerContainer, Player, Tool, Vehicle}
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.inventory.Container
|
import net.psforever.objects.inventory.Container
|
||||||
|
import net.psforever.objects.serverobject.turret.WeaponTurret
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
|
@ -191,6 +192,13 @@ object GUIDTask {
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def RegisterDeployableTurret(obj : PlanetSideGameObject with WeaponTurret)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
|
TaskResolver.GiveTask(
|
||||||
|
RegisterObjectTask(obj).task,
|
||||||
|
VisibleSlotTaskBuilding(obj.Weapons.values, GUIDTask.RegisterEquipment) ++ RegisterInventory(obj)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct tasking that unregisters an object from a globally unique identifier system.<br>
|
* Construct tasking that unregisters an object from a globally unique identifier system.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -329,6 +337,13 @@ object GUIDTask {
|
||||||
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def UnregisterDeployableTurret(obj : PlanetSideGameObject with WeaponTurret)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
|
||||||
|
TaskResolver.GiveTask(
|
||||||
|
UnregisterObjectTask(obj).task,
|
||||||
|
VisibleSlotTaskBuilding(obj.Weapons.values, GUIDTask.UnregisterEquipment) ++ UnregisterInventory(obj)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct tasking that allocates work upon encountered `Equipment` objects
|
* Construct tasking that allocates work upon encountered `Equipment` objects
|
||||||
* in reference to a globally unique identifier system of a pool of numbers.
|
* in reference to a globally unique identifier system of a pool of numbers.
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,12 @@ object Loadout {
|
||||||
* @param definition the `ConstructionItemDefinition` that describes this future object
|
* @param definition the `ConstructionItemDefinition` that describes this future object
|
||||||
*/
|
*/
|
||||||
final case class ShorthandConstructionItem(definition : ConstructionItemDefinition) extends Simplification
|
final case class ShorthandConstructionItem(definition : ConstructionItemDefinition) extends Simplification
|
||||||
|
/**
|
||||||
|
* The simplified form of a `BoomerTrigger`, a unique kind of `SimpleItem`.
|
||||||
|
* @param definition the `SimpleItemDefinition` that describes this future object;
|
||||||
|
* actually ignored, but retained for function definition consistency
|
||||||
|
*/
|
||||||
|
final case class ShorthandTriggerItem(definition : SimpleItemDefinition) extends Simplification
|
||||||
/**
|
/**
|
||||||
* The simplified form of a `SimpleItem`.
|
* The simplified form of a `SimpleItem`.
|
||||||
* @param definition the `SimpleItemDefinition` that describes this future object
|
* @param definition the `SimpleItemDefinition` that describes this future object
|
||||||
|
|
@ -223,6 +229,8 @@ object Loadout {
|
||||||
ShorthandAmmoBox(obj.Definition, obj.Capacity)
|
ShorthandAmmoBox(obj.Definition, obj.Capacity)
|
||||||
case obj : ConstructionItem =>
|
case obj : ConstructionItem =>
|
||||||
ShorthandConstructionItem(obj.Definition)
|
ShorthandConstructionItem(obj.Definition)
|
||||||
|
case obj : BoomerTrigger =>
|
||||||
|
ShorthandTriggerItem(obj.Definition)
|
||||||
case obj : SimpleItem =>
|
case obj : SimpleItem =>
|
||||||
ShorthandSimpleItem(obj.Definition)
|
ShorthandSimpleItem(obj.Definition)
|
||||||
case obj : Kit =>
|
case obj : Kit =>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
package net.psforever.objects.serverobject.mount
|
package net.psforever.objects.serverobject.mount
|
||||||
|
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.entity.{Identifiable, WorldEntity}
|
import net.psforever.objects.entity.{Identifiable, WorldEntity}
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.serverobject.turret.TurretDefinition
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
object MountableBehavior {
|
object MountableBehavior {
|
||||||
|
|
@ -16,7 +18,7 @@ object MountableBehavior {
|
||||||
trait Mount {
|
trait Mount {
|
||||||
this : Actor =>
|
this : Actor =>
|
||||||
|
|
||||||
def MountableObject : Mountable with Identifiable with WorldEntity with FactionAffinity
|
def MountableObject : PlanetSideGameObject with Mountable with FactionAffinity
|
||||||
|
|
||||||
val mountBehavior : Receive = {
|
val mountBehavior : Receive = {
|
||||||
case Mountable.TryMount(user, seat_num) =>
|
case Mountable.TryMount(user, seat_num) =>
|
||||||
|
|
@ -34,6 +36,25 @@ object MountableBehavior {
|
||||||
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val turretMountBehavior : Receive = {
|
||||||
|
case Mountable.TryMount(user, seat_num) =>
|
||||||
|
val obj = MountableObject
|
||||||
|
val definition = obj.Definition.asInstanceOf[TurretDefinition]
|
||||||
|
obj.Seat(seat_num) match {
|
||||||
|
case Some(seat) =>
|
||||||
|
if((!definition.FactionLocked || user.Faction == obj.Faction) &&
|
||||||
|
(seat.Occupant = user).contains(user)) {
|
||||||
|
user.VehicleSeated = obj.GUID
|
||||||
|
sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, seat_num))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -205,8 +205,8 @@ object EquipmentTerminalDefinition {
|
||||||
"medicalapplicator" -> MakeTool(medicalapplicator),
|
"medicalapplicator" -> MakeTool(medicalapplicator),
|
||||||
"bank" -> MakeTool(bank, armor_canister),
|
"bank" -> MakeTool(bank, armor_canister),
|
||||||
"nano_dispenser" -> MakeTool(nano_dispenser),
|
"nano_dispenser" -> MakeTool(nano_dispenser),
|
||||||
//TODO "ace" -> MakeConstructionItem(ace),
|
"ace" -> MakeConstructionItem(ace),
|
||||||
//TODO "advanced_ace" -> MakeConstructionItem(advanced_ace),
|
"advanced_ace" -> MakeConstructionItem(advanced_ace),
|
||||||
"remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
|
"remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
|
||||||
"trek" -> MakeTool(trek),
|
"trek" -> MakeTool(trek),
|
||||||
"command_detonater" -> MakeSimpleItem(command_detonater),
|
"command_detonater" -> MakeSimpleItem(command_detonater),
|
||||||
|
|
@ -309,6 +309,15 @@ object EquipmentTerminalDefinition {
|
||||||
*/
|
*/
|
||||||
private def MakeKit(kdef : KitDefinition)() : Kit = Kit(kdef)
|
private def MakeKit(kdef : KitDefinition)() : Kit = Kit(kdef)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new `BoomerTrigger`, a unique kind of `SimpleItem`.
|
||||||
|
* @param sdef the `SimpleItemDefinition` object;
|
||||||
|
* actually ignored, but retained for function definition consistency
|
||||||
|
* @return a curried function that, when called, creates the piece of `Equipment`
|
||||||
|
* @see `GlobalDefinitions`
|
||||||
|
*/
|
||||||
|
private def MakeTriggerItem(sdef : SimpleItemDefinition)() : SimpleItem = new BoomerTrigger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new `SimpleItem` from provided `EquipmentDefinition` objects.
|
* Create a new `SimpleItem` from provided `EquipmentDefinition` objects.
|
||||||
* @param sdef the `SimpleItemDefinition` object
|
* @param sdef the `SimpleItemDefinition` object
|
||||||
|
|
@ -356,6 +365,9 @@ object EquipmentTerminalDefinition {
|
||||||
case obj : ShorthandConstructionItem =>
|
case obj : ShorthandConstructionItem =>
|
||||||
MakeConstructionItem(obj.definition)
|
MakeConstructionItem(obj.definition)
|
||||||
|
|
||||||
|
case obj : ShorthandTriggerItem =>
|
||||||
|
MakeTriggerItem(obj.definition)
|
||||||
|
|
||||||
case obj : ShorthandSimpleItem =>
|
case obj : ShorthandSimpleItem =>
|
||||||
MakeSimpleItem(obj.definition)
|
MakeSimpleItem(obj.definition)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.turret
|
||||||
|
|
||||||
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
|
||||||
|
class FacilityTurret(tDef : TurretDefinition) extends Amenity
|
||||||
|
with WeaponTurret {
|
||||||
|
/** some turrets can be updated; they all start without updates */
|
||||||
|
private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None
|
||||||
|
|
||||||
|
WeaponTurret.LoadDefinition(this)
|
||||||
|
|
||||||
|
def MaxHealth : Int = Definition.MaxHealth
|
||||||
|
|
||||||
|
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
|
||||||
|
|
||||||
|
def Upgrade : TurretUpgrade.Value = upgradePath
|
||||||
|
|
||||||
|
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
|
||||||
|
upgradePath = upgrade
|
||||||
|
//upgrade each weapon as long as that weapon has a valid option for that upgrade
|
||||||
|
Definition.Weapons.foreach({ case(index, upgradePaths) =>
|
||||||
|
if(upgradePaths.contains(upgrade)) {
|
||||||
|
weapons(index).Equipment.get.asInstanceOf[TurretWeapon].Upgrade = upgrade
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Upgrade
|
||||||
|
}
|
||||||
|
|
||||||
|
def Definition : TurretDefinition = tDef
|
||||||
|
}
|
||||||
|
|
||||||
|
object FacilityTurret {
|
||||||
|
/**
|
||||||
|
* Overloaded constructor.
|
||||||
|
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||||
|
* @return a `FacilityTurret` object
|
||||||
|
*/
|
||||||
|
def apply(tDef : TurretDefinition) : FacilityTurret = {
|
||||||
|
new FacilityTurret(tDef)
|
||||||
|
}
|
||||||
|
|
||||||
|
import akka.actor.ActorContext
|
||||||
|
/**
|
||||||
|
* Instantiate and configure a `FacilityTurret` object
|
||||||
|
* @param id the unique id that will be assigned to this entity
|
||||||
|
* @param context a context to allow the object to properly set up `ActorSystem` functionality
|
||||||
|
* @return the `MannedTurret` object
|
||||||
|
*/
|
||||||
|
def Constructor(tdef : TurretDefinition)(id : Int, context : ActorContext) : FacilityTurret = {
|
||||||
|
import akka.actor.Props
|
||||||
|
val obj = FacilityTurret(tdef)
|
||||||
|
obj.Actor = context.actorOf(Props(classOf[FacilityTurretControl], obj), s"${tdef.Name}_$id")
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffi
|
||||||
* and faction-blind cavern sentry turrets.
|
* and faction-blind cavern sentry turrets.
|
||||||
* @param turret the `MannedTurret` object being governed
|
* @param turret the `MannedTurret` object being governed
|
||||||
*/
|
*/
|
||||||
class MannedTurretControl(turret : MannedTurret) extends Actor
|
class FacilityTurretControl(turret : FacilityTurret) extends Actor
|
||||||
with FactionAffinityBehavior.Check
|
with FactionAffinityBehavior.Check
|
||||||
with MountableBehavior.Dismount {
|
with MountableBehavior.Dismount {
|
||||||
def MountableObject = turret //do not add type!
|
def MountableObject = turret //do not add type!
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.serverobject.turret
|
|
||||||
|
|
||||||
import net.psforever.objects.{EquipmentSlot, Player}
|
|
||||||
import net.psforever.objects.definition.SeatDefinition
|
|
||||||
import net.psforever.objects.equipment.Equipment
|
|
||||||
import net.psforever.objects.inventory.{Container, GridInventory}
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
|
||||||
import net.psforever.objects.serverobject.turret.MannedTurret.MannedTurretWeapon
|
|
||||||
import net.psforever.objects.vehicles.{MountedWeapons, Seat => Chair}
|
|
||||||
|
|
||||||
class MannedTurret(tDef : MannedTurretDefinition) extends Amenity
|
|
||||||
with FactionAffinity
|
|
||||||
with Mountable
|
|
||||||
with MountedWeapons
|
|
||||||
with Container {
|
|
||||||
private var health : Int = 1
|
|
||||||
private var jammered : Boolean = false
|
|
||||||
/** manned turrets have just one seat; this is just standard interface */
|
|
||||||
private val seats : Map[Int, Chair] = Map(0 -> Chair(new SeatDefinition() { ControlledWeapon = Some(1) }))
|
|
||||||
/** manned turrets have just one weapon; this is just standard interface */
|
|
||||||
private var weapons : Map[Int, EquipmentSlot] = Map.empty
|
|
||||||
/** may or may not have inaccessible inventory space
|
|
||||||
* see `ReserveAmmunition` in the definition */
|
|
||||||
private val inventory : GridInventory = new GridInventory() {
|
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
|
||||||
override def Remove(index : Int) : Boolean = false
|
|
||||||
override def Remove(guid : PlanetSideGUID) : Boolean = false
|
|
||||||
}
|
|
||||||
/** some turrets can be updated; they all start without updates */
|
|
||||||
private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None
|
|
||||||
|
|
||||||
MannedTurret.LoadDefinition(this)
|
|
||||||
|
|
||||||
def Health : Int = {
|
|
||||||
health
|
|
||||||
}
|
|
||||||
|
|
||||||
def Health_=(toHealth : Int) : Int = {
|
|
||||||
health = toHealth
|
|
||||||
health
|
|
||||||
}
|
|
||||||
|
|
||||||
def MaxHealth : Int = {
|
|
||||||
Definition.MaxHealth
|
|
||||||
}
|
|
||||||
|
|
||||||
def Seats : Map[Int, Chair] = seats
|
|
||||||
|
|
||||||
def Seat(seatNum : Int) : Option[Chair] = seats.get(seatNum)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given the index of an entry mounting point, return the infantry-accessible `Seat` associated with it.
|
|
||||||
* @param mountPoint an index representing the seat position / mounting point
|
|
||||||
* @return a seat number, or `None`
|
|
||||||
*/
|
|
||||||
def GetSeatFromMountPoint(mountPoint : Int) : Option[Int] = {
|
|
||||||
Definition.MountPoints.get(mountPoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
|
|
||||||
|
|
||||||
def PassengerInSeat(user : Player) : Option[Int] = {
|
|
||||||
if(seats(0).Occupant.contains(user)) {
|
|
||||||
Some(0)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Weapons : Map[Int, EquipmentSlot] = weapons.filter({ case(index, _) => index < 2 })
|
|
||||||
|
|
||||||
def ControlledWeapon(wepNumber : Int) : Option[Equipment] = {
|
|
||||||
if(VisibleSlots.contains(wepNumber)) {
|
|
||||||
weapons(wepNumber).Equipment
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Inventory : GridInventory = inventory
|
|
||||||
|
|
||||||
def VisibleSlots : Set[Int] = Set(1)
|
|
||||||
|
|
||||||
def Upgrade : TurretUpgrade.Value = upgradePath
|
|
||||||
|
|
||||||
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
|
|
||||||
upgradePath = upgrade
|
|
||||||
//upgrade each weapon as long as that weapon has a valid option for that upgrade
|
|
||||||
Definition.Weapons.foreach({ case(index, upgradePaths) =>
|
|
||||||
if(upgradePaths.contains(upgrade)) {
|
|
||||||
weapons(index).Equipment.get.asInstanceOf[MannedTurretWeapon].Upgrade = upgrade
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Upgrade
|
|
||||||
}
|
|
||||||
|
|
||||||
def Jammered : Boolean = jammered
|
|
||||||
|
|
||||||
def Jammered_=(jamState : Boolean) : Boolean = {
|
|
||||||
jammered = jamState
|
|
||||||
Jammered
|
|
||||||
}
|
|
||||||
|
|
||||||
def Definition : MannedTurretDefinition = tDef
|
|
||||||
}
|
|
||||||
|
|
||||||
object MannedTurret {
|
|
||||||
/**
|
|
||||||
* Overloaded constructor.
|
|
||||||
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
|
||||||
* @return a `MannedTurret` object
|
|
||||||
*/
|
|
||||||
def apply(tDef : MannedTurretDefinition) : MannedTurret = {
|
|
||||||
new MannedTurret(tDef)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
|
||||||
* @param turret the `MannedTurret` being initialized
|
|
||||||
* @see `{object}.LoadDefinition`
|
|
||||||
*/
|
|
||||||
def LoadDefinition(turret : MannedTurret) : MannedTurret = {
|
|
||||||
import net.psforever.objects.equipment.EquipmentSize.BaseTurretWeapon
|
|
||||||
val tdef : MannedTurretDefinition = turret.Definition
|
|
||||||
//general stuff
|
|
||||||
turret.Health = tdef.MaxHealth
|
|
||||||
//create weapons; note the class
|
|
||||||
turret.weapons = tdef.Weapons.map({case (num, upgradePaths) =>
|
|
||||||
val slot = EquipmentSlot(BaseTurretWeapon)
|
|
||||||
slot.Equipment = new MannedTurretWeapon(tdef, upgradePaths.toMap)
|
|
||||||
num -> slot
|
|
||||||
}).toMap
|
|
||||||
//special inventory ammunition object(s)
|
|
||||||
if(tdef.ReserveAmmunition) {
|
|
||||||
val allAmmunitionTypes = tdef.Weapons.values.flatMap{ _.values.flatMap { _.AmmoTypes } }.toSet
|
|
||||||
if(allAmmunitionTypes.nonEmpty) {
|
|
||||||
turret.inventory.Resize(allAmmunitionTypes.size, 1)
|
|
||||||
var i : Int = 0
|
|
||||||
allAmmunitionTypes.foreach(ammotype => {
|
|
||||||
turret.inventory.InsertQuickly(i, new TurretAmmoBox(ammotype))
|
|
||||||
i += 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
turret
|
|
||||||
}
|
|
||||||
|
|
||||||
import net.psforever.objects.definition.ToolDefinition
|
|
||||||
import net.psforever.objects.Tool
|
|
||||||
/**
|
|
||||||
* A stateful weapon that is mounted in `MannedTurrets`
|
|
||||||
* and may maintains a group of upgraded forms that can by swapped
|
|
||||||
* without reconstructing the weapon object itself or managing object registration.
|
|
||||||
* @param mdef the turret's definition
|
|
||||||
* @param udefs a map of turret upgrades to tool definitions that would be constructed by this weapon
|
|
||||||
* @param default the default upgrade state;
|
|
||||||
* defaults to `None`
|
|
||||||
*/
|
|
||||||
private class MannedTurretWeapon(mdef : MannedTurretDefinition, udefs : Map[TurretUpgrade.Value, ToolDefinition], default : TurretUpgrade.Value = TurretUpgrade.None)
|
|
||||||
extends Tool(udefs(default)) {
|
|
||||||
private var upgradePath : TurretUpgrade.Value = default
|
|
||||||
|
|
||||||
def Upgrade : TurretUpgrade.Value = {
|
|
||||||
/*
|
|
||||||
Must check `not null` due to how this object's `Definition` will be called during `Tool`'s constructor
|
|
||||||
before the internal value can be set to default value `None`
|
|
||||||
*/
|
|
||||||
Option(upgradePath) match {
|
|
||||||
case Some(value) =>
|
|
||||||
value
|
|
||||||
case None =>
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
|
|
||||||
if(udefs.contains(upgrade)) {
|
|
||||||
val beforeUpgrade = upgradePath
|
|
||||||
upgradePath = upgrade
|
|
||||||
if(beforeUpgrade != upgradePath) {
|
|
||||||
Tool.LoadDefinition(this) //rebuild weapon internal structure
|
|
||||||
FireModeIndex = 0 //reset fire mode; this option is always valid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Upgrade
|
|
||||||
}
|
|
||||||
|
|
||||||
override def Definition = udefs(Upgrade)
|
|
||||||
}
|
|
||||||
|
|
||||||
import net.psforever.objects.definition.AmmoBoxDefinition
|
|
||||||
import net.psforever.objects.AmmoBox
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A special type of ammunition box contained within a `MannedTurret` for the purposes of infinite reloads.
|
|
||||||
* The original quantity of ammunition does not change.
|
|
||||||
* @param adef ammunition definition
|
|
||||||
*/
|
|
||||||
private class TurretAmmoBox(private val adef : AmmoBoxDefinition) extends AmmoBox(adef, Some(65535)) {
|
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
|
||||||
override def Tile = InventoryTile.Tile11
|
|
||||||
|
|
||||||
override def Capacity_=(toCapacity : Int) = Capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
import akka.actor.ActorContext
|
|
||||||
/**
|
|
||||||
* Instantiate an configure a `MannedTurret` object
|
|
||||||
* @param id the unique id that will be assigned to this entity
|
|
||||||
* @param context a context to allow the object to properly set up `ActorSystem` functionality
|
|
||||||
* @return the `MannedTurret` object
|
|
||||||
*/
|
|
||||||
def Constructor(tdef : MannedTurretDefinition)(id : Int, context : ActorContext) : MannedTurret = {
|
|
||||||
import akka.actor.Props
|
|
||||||
val obj = MannedTurret(tdef)
|
|
||||||
obj.Actor = context.actorOf(Props(classOf[MannedTurretControl], obj), s"${tdef.Name}_$id")
|
|
||||||
obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,7 @@ import scala.collection.mutable
|
||||||
* The definition for any `MannedTurret`.
|
* The definition for any `MannedTurret`.
|
||||||
* @param objectId the object's identifier number
|
* @param objectId the object's identifier number
|
||||||
*/
|
*/
|
||||||
class MannedTurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId) {
|
class TurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId) {
|
||||||
Turrets(objectId) //let throw NoSuchElementException
|
Turrets(objectId) //let throw NoSuchElementException
|
||||||
|
|
||||||
private var maxHealth : Int = 100
|
private var maxHealth : Int = 100
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.serverobject.turret
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.{AmmoBoxDefinition, SeatDefinition, ToolDefinition}
|
||||||
|
import net.psforever.objects._
|
||||||
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
import net.psforever.objects.inventory.{Container, GridInventory}
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.vehicles.{MountedWeapons, Seat => Chair}
|
||||||
|
|
||||||
|
trait WeaponTurret extends FactionAffinity
|
||||||
|
with Mountable
|
||||||
|
with MountedWeapons
|
||||||
|
with Container {
|
||||||
|
this : PlanetSideGameObject =>
|
||||||
|
|
||||||
|
private var health : Int = 1
|
||||||
|
private var jammered : Boolean = false
|
||||||
|
/** manned turrets have just one seat; this is just standard interface */
|
||||||
|
protected val seats : Map[Int, Chair] = Map(0 -> Chair(new SeatDefinition() { ControlledWeapon = Some(1) }))
|
||||||
|
/** turrets have just one weapon; this is just standard interface */
|
||||||
|
protected var weapons : Map[Int, EquipmentSlot] = Map.empty
|
||||||
|
/** may or may not have inaccessible inventory space
|
||||||
|
* see `ReserveAmmunition` in the definition */
|
||||||
|
protected val inventory : GridInventory = new GridInventory() {
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
override def Remove(index : Int) : Boolean = false
|
||||||
|
override def Remove(guid : PlanetSideGUID) : Boolean = false
|
||||||
|
}
|
||||||
|
|
||||||
|
def Health : Int = {
|
||||||
|
health
|
||||||
|
}
|
||||||
|
|
||||||
|
def Health_=(toHealth : Int) : Int = {
|
||||||
|
health = toHealth
|
||||||
|
health
|
||||||
|
}
|
||||||
|
|
||||||
|
def MaxHealth : Int
|
||||||
|
|
||||||
|
def Inventory : GridInventory = inventory
|
||||||
|
|
||||||
|
def VisibleSlots : Set[Int] = Set(1)
|
||||||
|
|
||||||
|
def Weapons : Map[Int, EquipmentSlot] = weapons
|
||||||
|
|
||||||
|
def MountPoints : Map[Int, Int]
|
||||||
|
|
||||||
|
def Seats : Map[Int, Chair] = seats
|
||||||
|
|
||||||
|
def Seat(seatNum : Int) : Option[Chair] = seats.get(seatNum)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the index of an entry mounting point, return the infantry-accessible `Seat` associated with it.
|
||||||
|
* @param mountPoint an index representing the seat position / mounting point
|
||||||
|
* @return a seat number, or `None`
|
||||||
|
*/
|
||||||
|
def GetSeatFromMountPoint(mountPoint : Int) : Option[Int] = {
|
||||||
|
MountPoints.get(mountPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
def PassengerInSeat(user : Player) : Option[Int] = {
|
||||||
|
if(seats(0).Occupant.contains(user)) {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def ControlledWeapon(wepNumber : Int) : Option[Equipment] = {
|
||||||
|
if(VisibleSlots.contains(wepNumber)) {
|
||||||
|
weapons(wepNumber).Equipment
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def Jammered : Boolean = jammered
|
||||||
|
|
||||||
|
def Jammered_=(jamState : Boolean) : Boolean = {
|
||||||
|
jammered = jamState
|
||||||
|
Jammered
|
||||||
|
}
|
||||||
|
|
||||||
|
def Definition : TurretDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
object WeaponTurret {
|
||||||
|
/**
|
||||||
|
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
||||||
|
* @see `{object}.LoadDefinition`
|
||||||
|
* @param turret the `MannedTurret` being initialized
|
||||||
|
*/
|
||||||
|
def LoadDefinition(turret : WeaponTurret) : WeaponTurret = {
|
||||||
|
LoadDefinition(turret, turret.Definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
|
||||||
|
* A default definition is provided to be used.
|
||||||
|
* @see `{object}.LoadDefinition`
|
||||||
|
* @param turret the `MannedTurret` being initialized
|
||||||
|
* @param tdef the object definition
|
||||||
|
*/
|
||||||
|
def LoadDefinition(turret : WeaponTurret, tdef : TurretDefinition) : WeaponTurret = {
|
||||||
|
import net.psforever.objects.equipment.EquipmentSize.BaseTurretWeapon
|
||||||
|
//general stuff
|
||||||
|
turret.Health = tdef.MaxHealth
|
||||||
|
//create weapons; note the class
|
||||||
|
turret.weapons = tdef.Weapons.map({case (num, upgradePaths) =>
|
||||||
|
val slot = EquipmentSlot(BaseTurretWeapon)
|
||||||
|
slot.Equipment = new TurretWeapon(tdef, upgradePaths.toMap)
|
||||||
|
num -> slot
|
||||||
|
}).toMap
|
||||||
|
//special inventory ammunition object(s)
|
||||||
|
if(tdef.ReserveAmmunition) {
|
||||||
|
val allAmmunitionTypes = tdef.Weapons.values.flatMap{ _.values.flatMap { _.AmmoTypes } }.toSet
|
||||||
|
if(allAmmunitionTypes.nonEmpty) {
|
||||||
|
turret.inventory.Resize(allAmmunitionTypes.size, 1)
|
||||||
|
var i : Int = 0
|
||||||
|
allAmmunitionTypes.foreach(ammotype => {
|
||||||
|
turret.inventory.InsertQuickly(i, new TurretAmmoBox(ammotype))
|
||||||
|
i += 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
turret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretWeapon(mdef : TurretDefinition, udefs : Map[TurretUpgrade.Value, ToolDefinition], default : TurretUpgrade.Value = TurretUpgrade.None)
|
||||||
|
extends Tool(udefs(default)) {
|
||||||
|
private var upgradePath : TurretUpgrade.Value = default
|
||||||
|
|
||||||
|
def Upgrade : TurretUpgrade.Value = {
|
||||||
|
/*
|
||||||
|
Must check `not null` due to how this object's `Definition` will be called during `Tool`'s constructor
|
||||||
|
before the internal value can be set to default value `None`
|
||||||
|
*/
|
||||||
|
Option(upgradePath) match {
|
||||||
|
case Some(value) =>
|
||||||
|
value
|
||||||
|
case None =>
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
|
||||||
|
if(udefs.contains(upgrade)) {
|
||||||
|
val beforeUpgrade = upgradePath
|
||||||
|
upgradePath = upgrade
|
||||||
|
if(beforeUpgrade != upgradePath) {
|
||||||
|
Tool.LoadDefinition(this) //rebuild weapon internal structure
|
||||||
|
FireModeIndex = 0 //reset fire mode; this option is always valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Upgrade
|
||||||
|
}
|
||||||
|
|
||||||
|
override def Definition = udefs(Upgrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special type of ammunition box contained within a `MannedTurret` for the purposes of infinite reloads.
|
||||||
|
* The original quantity of ammunition does not change.
|
||||||
|
* @param adef ammunition definition
|
||||||
|
*/
|
||||||
|
class TurretAmmoBox(private val adef : AmmoBoxDefinition) extends AmmoBox(adef, Some(65535)) {
|
||||||
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
|
override def Tile = InventoryTile.Tile11
|
||||||
|
|
||||||
|
override def Capacity_=(toCapacity : Int) = Capacity
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,16 @@
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `Enumeration` of all the turret type objectss in the game, paired with their object id as the `Value`.
|
* An `Enumeration` of all the turret type objects in the game, paired with their object id as the `Value`.
|
||||||
*/
|
*/
|
||||||
object Turrets extends Enumeration {
|
object Turrets extends Enumeration {
|
||||||
val manned_turret = Value(480)
|
final val manned_turret = Value(480)
|
||||||
val vanu_sentry_turret = Value(943)
|
final val portable_manned_turret = Value(685)
|
||||||
|
final val portable_manned_turret_nc = Value(686)
|
||||||
|
final val portable_manned_turret_tr = Value(687)
|
||||||
|
final val portable_manned_turret_vs = Value(688)
|
||||||
|
final val spitfire_aa = Value(819)
|
||||||
|
final val spitfire_cloaked = Value(825)
|
||||||
|
final val spitfire_turret = Value(826)
|
||||||
|
final val vanu_sentry_turret = Value(943)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,3 +111,9 @@ object StandardAircraftDamage extends DamageSelection {
|
||||||
def Splash = AircraftSplashDamage.Calculate
|
def Splash = AircraftSplashDamage.Calculate
|
||||||
def Lash = AircraftLashDamage.Calculate
|
def Lash = AircraftLashDamage.Calculate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object StandardDeployableDamage extends DamageSelection {
|
||||||
|
def Direct = VehicleHitDamage.Calculate
|
||||||
|
def Splash = VehicleSplashDamage.Calculate
|
||||||
|
def Lash = NoDamage.Calculate
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,21 @@ object VehicleResolutions extends DamageResistCalculations(
|
||||||
ResolutionCalculations.VehicleApplication
|
ResolutionCalculations.VehicleApplication
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object SimpleDeployableResolutions extends DamageResistCalculations(
|
||||||
|
ResolutionCalculations.VehicleDamageAfterResist,
|
||||||
|
ResolutionCalculations.SimpleDeployableApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
object ComplexDeployableResolutions extends DamageResistCalculations(
|
||||||
|
ResolutionCalculations.VehicleDamageAfterResist,
|
||||||
|
ResolutionCalculations.ComplexDeployableApplication
|
||||||
|
)
|
||||||
|
|
||||||
object StandardResolutions extends ResolutionSelection {
|
object StandardResolutions extends ResolutionSelection {
|
||||||
def Infantry : ResolutionCalculations.Form = InfantryResolutions.Calculate
|
def Infantry : ResolutionCalculations.Form = InfantryResolutions.Calculate
|
||||||
def Max : ResolutionCalculations.Form = MaxResolutions.Calculate
|
def Max : ResolutionCalculations.Form = MaxResolutions.Calculate
|
||||||
def Vehicle : ResolutionCalculations.Form = VehicleResolutions.Calculate
|
def Vehicle : ResolutionCalculations.Form = VehicleResolutions.Calculate
|
||||||
def Aircraft : ResolutionCalculations.Form = VehicleResolutions.Calculate
|
def Aircraft : ResolutionCalculations.Form = VehicleResolutions.Calculate
|
||||||
|
def SimpleDeployables : ResolutionCalculations.Form = SimpleDeployableResolutions.Calculate
|
||||||
|
def ComplexDeployables : ResolutionCalculations.Form = ComplexDeployableResolutions.Calculate
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,8 @@ object Vitality {
|
||||||
*/
|
*/
|
||||||
final case class Damage(func : (Any)=>Unit)
|
final case class Damage(func : (Any)=>Unit)
|
||||||
|
|
||||||
|
final case class DamageOn(obj : Vitality, func : (Any)=>Unit)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report that a vitals object must be updated due to damage.
|
* Report that a vitals object must be updated due to damage.
|
||||||
* @param obj the vital object
|
* @param obj the vital object
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,23 @@ abstract class DamageResistCalculations[A](calcFunc : (ResolvedProjectile)=>((In
|
||||||
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
||||||
extends ResolutionCalculations {
|
extends ResolutionCalculations {
|
||||||
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
||||||
|
val modDam = Sample(damages, resistances, data)
|
||||||
|
applyFunc(modDam, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An intermediate step of the normal `Calculate` operation that retrieves the damage values in their transitory form.
|
||||||
|
* @param damages the function that calculations raw damage values
|
||||||
|
* @param resistances the function that calculates resistance values
|
||||||
|
* @param data a historical projectile interaction;
|
||||||
|
* the origin of the data used to extract damage and resistance values
|
||||||
|
* @return the transitory form of the modified damage(s);
|
||||||
|
* usually, a single `Int` value or a tuple of `Int` values
|
||||||
|
*/
|
||||||
|
def Sample(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : A = {
|
||||||
val dam : Int = damages(data)
|
val dam : Int = damages(data)
|
||||||
val res : Int = resistances(data)
|
val res : Int = resistances(data)
|
||||||
val mod = calcFunc(data)
|
val mod = calcFunc(data)
|
||||||
val modDam = mod(dam, res)
|
mod(dam, res)
|
||||||
applyFunc(modDam, data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.vital.resolution
|
package net.psforever.objects.vital.resolution
|
||||||
|
|
||||||
import net.psforever.objects.{Player, Vehicle}
|
import net.psforever.objects.{Player, TurretDeployable, Vehicle}
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
||||||
|
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
|
||||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,4 +158,49 @@ object ResolutionCalculations {
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def SimpleDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||||
|
case ce : SimpleDeployable =>
|
||||||
|
if(ce.Health > 0) {
|
||||||
|
ce.Health -= damage
|
||||||
|
ce.History(data)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
|
||||||
|
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||||
|
case ce : ComplexDeployable =>
|
||||||
|
if(ce.Shields > 0) {
|
||||||
|
if(damage > ce.Shields) {
|
||||||
|
ce.Health -= (damage - ce.Shields)
|
||||||
|
ce.Shields = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ce.Shields -= damage
|
||||||
|
}
|
||||||
|
ce.History(data)
|
||||||
|
}
|
||||||
|
else if(ce.Health > 0) {
|
||||||
|
ce.Health -= damage
|
||||||
|
ce.History(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ce : TurretDeployable =>
|
||||||
|
if(ce.Shields > 0) {
|
||||||
|
if(damage > ce.Shields) {
|
||||||
|
ce.Health -= (damage - ce.Shields)
|
||||||
|
ce.Shields = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ce.Shields -= damage
|
||||||
|
}
|
||||||
|
ce.History(data)
|
||||||
|
}
|
||||||
|
else if(ce.Health > 0) {
|
||||||
|
ce.Health -= damage
|
||||||
|
ce.History(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,16 @@ import akka.actor.{ActorContext, ActorRef, Props}
|
||||||
import akka.routing.RandomPool
|
import akka.routing.RandomPool
|
||||||
import net.psforever.objects.ballistics.Projectile
|
import net.psforever.objects.ballistics.Projectile
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.guid.NumberPoolHub
|
import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.objects.guid.actor.UniqueNumberSystem
|
import net.psforever.objects.guid.actor.UniqueNumberSystem
|
||||||
import net.psforever.objects.guid.selector.RandomSelector
|
import net.psforever.objects.guid.selector.RandomSelector
|
||||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||||
|
import net.psforever.objects.inventory.Container
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.objects.serverobject.turret.MannedTurret
|
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
|
@ -54,6 +56,10 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
||||||
private var ground : ActorRef = ActorRef.noSender
|
private var ground : ActorRef = ActorRef.noSender
|
||||||
/** */
|
/** */
|
||||||
|
private val constructions : ListBuffer[PlanetSideGameObject with Deployable] = ListBuffer[PlanetSideGameObject with Deployable]()
|
||||||
|
/** */
|
||||||
|
private var deployables : ActorRef = ActorRef.noSender
|
||||||
|
/** */
|
||||||
private var transport : ActorRef = ActorRef.noSender
|
private var transport : ActorRef = ActorRef.noSender
|
||||||
/** */
|
/** */
|
||||||
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]()
|
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]()
|
||||||
|
|
@ -91,6 +97,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
SetupNumberPools()
|
SetupNumberPools()
|
||||||
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
||||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"$Id-ground")
|
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"$Id-ground")
|
||||||
|
deployables = context.actorOf(Props(classOf[ZoneDeployableActor], this, constructions), s"$Id-deployables")
|
||||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
||||||
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
||||||
|
|
||||||
|
|
@ -253,6 +260,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
*/
|
*/
|
||||||
def EquipmentOnGround : List[Equipment] = equipmentOnGround.toList
|
def EquipmentOnGround : List[Equipment] = equipmentOnGround.toList
|
||||||
|
|
||||||
|
def DeployableList : List[PlanetSideGameObject with Deployable] = constructions.toList
|
||||||
|
|
||||||
def Vehicles : List[Vehicle] = vehicles.toList
|
def Vehicles : List[Vehicle] = vehicles.toList
|
||||||
|
|
||||||
def Players : List[Avatar] = players.keys.toList
|
def Players : List[Avatar] = players.keys.toList
|
||||||
|
|
@ -271,6 +280,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
*/
|
*/
|
||||||
def Ground : ActorRef = ground
|
def Ground : ActorRef = ground
|
||||||
|
|
||||||
|
def Deployables : ActorRef = deployables
|
||||||
|
|
||||||
def Transport : ActorRef = transport
|
def Transport : ActorRef = transport
|
||||||
|
|
||||||
def Population : ActorRef = population
|
def Population : ActorRef = population
|
||||||
|
|
@ -290,7 +301,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
//turret to weapon
|
//turret to weapon
|
||||||
Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
||||||
((GUID(turret_guid) match {
|
((GUID(turret_guid) match {
|
||||||
case Some(obj : MannedTurret) =>
|
case Some(obj : FacilityTurret) =>
|
||||||
Some(obj)
|
Some(obj)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
None
|
None
|
||||||
|
|
@ -322,7 +333,11 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
|
|
||||||
private def AssignAmenities() : Unit = {
|
private def AssignAmenities() : Unit = {
|
||||||
Map.ObjectToBuilding.foreach({ case(object_guid, building_id) =>
|
Map.ObjectToBuilding.foreach({ case(object_guid, building_id) =>
|
||||||
buildings(building_id).Amenities = guid(object_guid).get.asInstanceOf[Amenity]
|
(buildings.get(building_id), guid(object_guid)) match {
|
||||||
|
case (Some(building), Some(amenity)) =>
|
||||||
|
building.Amenities = amenity.asInstanceOf[Amenity]
|
||||||
|
case (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,6 +401,17 @@ object Zone {
|
||||||
/** Default value, non-zone area. */
|
/** Default value, non-zone area. */
|
||||||
final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99)
|
final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded constructor.
|
||||||
|
* @param id the privileged name that can be used as the second parameter in the packet `LoadMapMessage`
|
||||||
|
* @param map the map of server objects upon which this `Zone` is based
|
||||||
|
* @param number the numerical index of the `Zone` as it is recognized in a variety of packets
|
||||||
|
* @return a `Zone` object
|
||||||
|
*/
|
||||||
|
def apply(id : String, map : ZoneMap, number : Int) : Zone = {
|
||||||
|
new Zone(id, map, number)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to initialize the `Zone`.
|
* Message to initialize the `Zone`.
|
||||||
* @see `Zone.Init(implicit ActorContext)`
|
* @see `Zone.Init(implicit ActorContext)`
|
||||||
|
|
@ -494,6 +520,14 @@ object Zone {
|
||||||
final case class RemoveItem(item_guid : PlanetSideGUID)
|
final case class RemoveItem(item_guid : PlanetSideGUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Deployable {
|
||||||
|
final case class Build(obj : PlanetSideGameObject with Deployable, withTool : ConstructionItem)
|
||||||
|
final case class DeployableIsBuilt(obj : PlanetSideGameObject with Deployable, withTool : ConstructionItem)
|
||||||
|
|
||||||
|
final case class Dismiss(obj : PlanetSideGameObject with Deployable)
|
||||||
|
final case class DeployableIsDismissed(obj : PlanetSideGameObject with Deployable)
|
||||||
|
}
|
||||||
|
|
||||||
object Vehicle {
|
object Vehicle {
|
||||||
final case class Spawn(vehicle : Vehicle)
|
final case class Spawn(vehicle : Vehicle)
|
||||||
|
|
||||||
|
|
@ -512,14 +546,71 @@ object Zone {
|
||||||
*/
|
*/
|
||||||
final case class ClientInitialization(zone : Zone)
|
final case class ClientInitialization(zone : Zone)
|
||||||
|
|
||||||
/**
|
object EquipmentIs {
|
||||||
* Overloaded constructor.
|
/**
|
||||||
* @param id the privileged name that can be used as the second parameter in the packet `LoadMapMessage`
|
* Tha base `trait` connecting all `Equipment` object location tokens.
|
||||||
* @param map the map of server objects upon which this `Zone` is based
|
*/
|
||||||
* @param number the numerical index of the `Zone` as it is recognized in a variety of packets
|
sealed trait ItemLocation
|
||||||
* @return a `Zone` object
|
/**
|
||||||
*/
|
* The target item is contained within another object.
|
||||||
def apply(id : String, map : ZoneMap, number : Int) : Zone = {
|
* @see `GridInventory`<br>
|
||||||
new Zone(id, map, number)
|
* `Container`
|
||||||
|
* @param obj the containing object
|
||||||
|
* @param index the slot where the target is located
|
||||||
|
*/
|
||||||
|
final case class InContainer(obj : Container, index : Int) extends ItemLocation
|
||||||
|
/**
|
||||||
|
* The target item is found on the Ground.
|
||||||
|
* @see `ZoneGroundActor`
|
||||||
|
*/
|
||||||
|
final case class OnGround() extends ItemLocation
|
||||||
|
/**
|
||||||
|
* The target item exists but could not be found belonging to any expected region of the location.
|
||||||
|
*/
|
||||||
|
final case class Orphaned() extends ItemLocation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exhaustive search of the provided zone is conducted in search of the target `Equipment` object
|
||||||
|
* and a token that qualifies the current location of the object in the zone is returned.
|
||||||
|
* The following groups of objects are searched:
|
||||||
|
* the inventories of all players and all corpses,
|
||||||
|
* all vehicles trunks,
|
||||||
|
* the lockers of all players and corpses;
|
||||||
|
* and, if still not found, the ground is scoured too.
|
||||||
|
* @see `ItemLocation`<br>
|
||||||
|
* `LockerContainer`
|
||||||
|
* @param equipment the target object
|
||||||
|
* @param guid that target object's globally unique identifier
|
||||||
|
* @param continent the zone whose objects to search
|
||||||
|
* @return a token that explains where the object is, if it is found in this zone;
|
||||||
|
* `None` is the token that is used to indicate not having been found
|
||||||
|
*/
|
||||||
|
def Where(equipment : Equipment, guid : PlanetSideGUID, continent : Zone) : Option[Zone.EquipmentIs.ItemLocation] = {
|
||||||
|
continent.GUID(guid) match {
|
||||||
|
case Some(_) =>
|
||||||
|
((continent.LivePlayers ++ continent.Corpses).find(_.Find(guid).nonEmpty) match {
|
||||||
|
case Some(tplayer) => Some((tplayer, tplayer.Find(guid)))
|
||||||
|
case _ => None
|
||||||
|
}).orElse(continent.Vehicles.find(_.Find(guid).nonEmpty) match {
|
||||||
|
case Some(vehicle) => Some((vehicle, vehicle.Find(guid)))
|
||||||
|
case _ => None
|
||||||
|
}).orElse(continent.Players.find(_.Locker.Find(guid).nonEmpty) match {
|
||||||
|
case Some(avatar) => Some((avatar.Locker, avatar.Locker.Find(guid)))
|
||||||
|
case _ => None
|
||||||
|
}) match {
|
||||||
|
case Some((obj, Some(index))) =>
|
||||||
|
Some(Zone.EquipmentIs.InContainer(obj, index))
|
||||||
|
case _ =>
|
||||||
|
continent.EquipmentOnGround.find(_.GUID == guid) match {
|
||||||
|
case Some(_) =>
|
||||||
|
Some(Zone.EquipmentIs.OnGround())
|
||||||
|
case None =>
|
||||||
|
Some(Zone.EquipmentIs.Orphaned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,13 @@ class ZoneActor(zone : Zone) extends Actor {
|
||||||
case msg @ Zone.Ground.PickupItem =>
|
case msg @ Zone.Ground.PickupItem =>
|
||||||
zone.Ground forward msg
|
zone.Ground forward msg
|
||||||
|
|
||||||
|
//frwd to Deployable Actor
|
||||||
|
case msg @ Zone.Deployable.Build =>
|
||||||
|
zone.Deployables forward msg
|
||||||
|
|
||||||
|
case msg @ Zone.Deployable.Dismiss =>
|
||||||
|
zone.Deployables forward msg
|
||||||
|
|
||||||
//frwd to Vehicle Actor
|
//frwd to Vehicle Actor
|
||||||
case msg @ Zone.Vehicle.Spawn =>
|
case msg @ Zone.Vehicle.Spawn =>
|
||||||
zone.Transport forward msg
|
zone.Transport forward msg
|
||||||
|
|
@ -142,17 +149,21 @@ class ZoneActor(zone : Zone) extends Actor {
|
||||||
val errors = new AtomicInteger(0)
|
val errors = new AtomicInteger(0)
|
||||||
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
|
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
|
||||||
|
|
||||||
//check base to object associations
|
//check bases
|
||||||
map.ObjectToBuilding.foreach({ case((object_guid, building_id)) =>
|
map.ObjectToBuilding.values.toSet[Int].foreach(building_id =>
|
||||||
if(zone.Building(building_id).isEmpty) {
|
if(zone.Building(building_id).isEmpty) {
|
||||||
slog.error(s"expected a building at id #$building_id")
|
slog.error(s"expected a building for id #$building_id")
|
||||||
errors.incrementAndGet()
|
errors.incrementAndGet()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//check base to object associations
|
||||||
|
map.ObjectToBuilding.keys.foreach(object_guid =>
|
||||||
if(guid(object_guid).isEmpty) {
|
if(guid(object_guid).isEmpty) {
|
||||||
slog.error(s"expected object id $object_guid to exist, but it did not")
|
slog.error(s"expected object id $object_guid to exist, but it did not")
|
||||||
errors.incrementAndGet()
|
errors.incrementAndGet()
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
//check door to lock association
|
//check door to lock association
|
||||||
map.DoorToLock.foreach({ case((door_guid, lock_guid)) =>
|
map.DoorToLock.foreach({ case((door_guid, lock_guid)) =>
|
||||||
|
|
@ -174,8 +185,8 @@ class ZoneActor(zone : Zone) extends Actor {
|
||||||
|
|
||||||
//check manned turret to weapon association
|
//check manned turret to weapon association
|
||||||
map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
||||||
validateObject(turret_guid, MannedTurretCheck, "manned turret mount")
|
validateObject(turret_guid, FacilityTurretCheck, "facility turret mount")
|
||||||
if(validateObject(weapon_guid, WeaponCheck, "manned turret weapon")) {
|
if(validateObject(weapon_guid, WeaponCheck, "facility turret weapon")) {
|
||||||
if(guid(weapon_guid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
|
if(guid(weapon_guid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
|
||||||
slog.error(s"expected weapon $weapon_guid has an unregistered ammunition unit")
|
slog.error(s"expected weapon $weapon_guid has an unregistered ammunition unit")
|
||||||
errors.incrementAndGet()
|
errors.incrementAndGet()
|
||||||
|
|
@ -247,9 +258,9 @@ object ZoneActor {
|
||||||
obj.isInstanceOf[VehicleSpawnPad]
|
obj.isInstanceOf[VehicleSpawnPad]
|
||||||
}
|
}
|
||||||
|
|
||||||
def MannedTurretCheck(obj : PlanetSideGameObject) : Boolean = {
|
def FacilityTurretCheck(obj : PlanetSideGameObject) : Boolean = {
|
||||||
import net.psforever.objects.serverobject.turret.MannedTurret
|
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
obj.isInstanceOf[MannedTurret]
|
obj.isInstanceOf[FacilityTurret]
|
||||||
}
|
}
|
||||||
|
|
||||||
def WeaponCheck(obj : PlanetSideGameObject) : Boolean = {
|
def WeaponCheck(obj : PlanetSideGameObject) : Boolean = {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.zones
|
||||||
|
|
||||||
|
import akka.actor.Actor
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param zone the `Zone` object
|
||||||
|
*/
|
||||||
|
class ZoneDeployableActor(zone : Zone, deployableList : ListBuffer[PlanetSideGameObject with Deployable]) extends Actor {
|
||||||
|
import ZoneDeployableActor._
|
||||||
|
|
||||||
|
def receive : Receive = {
|
||||||
|
case Zone.Deployable.Build(obj, tool) =>
|
||||||
|
if(DeployableBuild(obj, deployableList)) {
|
||||||
|
obj match {
|
||||||
|
case o : PlanetSideServerObject =>
|
||||||
|
obj.Definition.Initialize(o, context)
|
||||||
|
case _ =>
|
||||||
|
obj.Definition.Initialize(obj, context)
|
||||||
|
}
|
||||||
|
sender ! Zone.Deployable.DeployableIsBuilt(obj, tool)
|
||||||
|
}
|
||||||
|
|
||||||
|
case Zone.Deployable.Dismiss(obj) =>
|
||||||
|
if(DeployableDismiss(obj, deployableList)) {
|
||||||
|
obj match {
|
||||||
|
case o : PlanetSideServerObject =>
|
||||||
|
obj.Definition.Uninitialize(o, context)
|
||||||
|
case _ =>
|
||||||
|
obj.Definition.Uninitialize(obj, context)
|
||||||
|
}
|
||||||
|
sender ! Zone.Deployable.DeployableIsDismissed(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ZoneDeployableActor {
|
||||||
|
def DeployableBuild(obj : PlanetSideGameObject with Deployable, deployableList : ListBuffer[PlanetSideGameObject with Deployable]) : Boolean = {
|
||||||
|
deployableList.find(d => d == obj) match {
|
||||||
|
case Some(_) =>
|
||||||
|
false
|
||||||
|
case None =>
|
||||||
|
deployableList += obj
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def DeployableDismiss(obj : PlanetSideGameObject with Deployable, deployableList : ListBuffer[PlanetSideGameObject with Deployable]) : Boolean = {
|
||||||
|
recursiveFindDeployable(deployableList.iterator, obj) match {
|
||||||
|
case None =>
|
||||||
|
false
|
||||||
|
case Some(index) =>
|
||||||
|
deployableList.remove(index)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@tailrec final def recursiveFindDeployable(iter : Iterator[PlanetSideGameObject with Deployable], target : PlanetSideGameObject with Deployable, index : Int = 0) : Option[Int] = {
|
||||||
|
if(!iter.hasNext) {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(iter.next == target) {
|
||||||
|
Some(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
recursiveFindDeployable(iter, target, index + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Add an `avatar` as the key of an `Avatar` to `Player` object pair in the given collection.
|
||||||
|
// * @param avatar an `Avatar` object
|
||||||
|
// * @param playerMap the mapping of `Avatar` objects to `Player` objects
|
||||||
|
// * @return true, if the mapping is for a new key;
|
||||||
|
// * false, if the key already exists
|
||||||
|
// */
|
||||||
|
// def PopulationJoin(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Boolean = {
|
||||||
|
// playerMap.get(avatar) match {
|
||||||
|
// case Some(_) =>
|
||||||
|
// false
|
||||||
|
// case None =>
|
||||||
|
// playerMap += avatar -> None
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// /**
|
||||||
|
// * Remove an `avatar` from the key of an `Avatar` to `Player` object pair in the given collection.
|
||||||
|
// * If a `Player` object is associated at the time, return it safely.
|
||||||
|
// * @param avatar an `Avatar` object
|
||||||
|
// * @param playerMap the mapping of `Avatar` objects to `Player` objects
|
||||||
|
// * @return any `Player` object that was associated at the time the `avatar` was removed
|
||||||
|
// */
|
||||||
|
// def PopulationLeave(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
|
||||||
|
// playerMap.remove(avatar) match {
|
||||||
|
// case None =>
|
||||||
|
// None
|
||||||
|
// case Some(tplayer) =>
|
||||||
|
// tplayer
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Associate a `Player` object as a value to an existing `Avatar` object that will be its key.
|
||||||
|
// * Do not overwrite players that are already associated.
|
||||||
|
// * @param avatar an `Avatar` object
|
||||||
|
// * @param player a `Player` object
|
||||||
|
// * @param playerMap the mapping of `Avatar` objects to `Player` objects
|
||||||
|
// * @return the `Player` object that is associated with the `Avatar` key
|
||||||
|
// */
|
||||||
|
// def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
|
||||||
|
// playerMap.get(avatar) match {
|
||||||
|
// case None =>
|
||||||
|
// None
|
||||||
|
// case Some(tplayer) =>
|
||||||
|
// tplayer match {
|
||||||
|
// case Some(aplayer) =>
|
||||||
|
// Some(aplayer)
|
||||||
|
// case None =>
|
||||||
|
// playerMap(avatar) = Some(player)
|
||||||
|
// Some(player)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Disassociate a `Player` object from an existing `Avatar` object that was be its key.
|
||||||
|
// * @param avatar an `Avatar` object
|
||||||
|
// * @param playerMap the mapping of `Avatar` objects to `Player` objects
|
||||||
|
// * @return any `Player` object that is associated at the time
|
||||||
|
// */
|
||||||
|
// def PopulationRelease(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
|
||||||
|
// playerMap.get(avatar) match {
|
||||||
|
// case None =>
|
||||||
|
// None
|
||||||
|
// case Some(tplayer) =>
|
||||||
|
// playerMap(avatar) = None
|
||||||
|
// tplayer
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * If the given `player` passes a condition check, add it to the list.
|
||||||
|
// * @param player a `Player` object
|
||||||
|
// * @param corpseList a list of `Player` objects
|
||||||
|
// * @return true, if the `player` was added to the list;
|
||||||
|
// * false, otherwise
|
||||||
|
// */
|
||||||
|
// def CorpseAdd(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
|
||||||
|
// if(player.isBackpack) {
|
||||||
|
// corpseList += player
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Remove the given `player` from the list.
|
||||||
|
// * @param player a `Player` object
|
||||||
|
// * @param corpseList a list of `Player` objects
|
||||||
|
// */
|
||||||
|
// def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = {
|
||||||
|
// recursiveFindCorpse(corpseList.iterator, player) match {
|
||||||
|
// case None => ;
|
||||||
|
// case Some(index) =>
|
||||||
|
// corpseList.remove(index)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * A recursive function that finds and removes a specific player from a list of players.
|
||||||
|
// * @param iter an `Iterator` of `Player` objects
|
||||||
|
// * @param player the target `Player`
|
||||||
|
// * @param index the index of the discovered `Player` object
|
||||||
|
// * @return the index of the `Player` object in the list to be removed;
|
||||||
|
// * `None`, otherwise
|
||||||
|
// */
|
||||||
|
// @tailrec final def recursiveFindCorpse(iter : Iterator[Player], player : Player, index : Int = 0) : Option[Int] = {
|
||||||
|
// if(!iter.hasNext) {
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// if(iter.next == player) {
|
||||||
|
// Some(index)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// recursiveFindCorpse(iter, player, index + 1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.{Angular, Vector3}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatched from the client to request that an object be deployed.<br>
|
* Dispatched from the client to request that an object be deployed.<br>
|
||||||
|
|
@ -15,17 +16,13 @@ import scodec.codecs._
|
||||||
* @param object_guid the object
|
* @param object_guid the object
|
||||||
* @param unk1 na
|
* @param unk1 na
|
||||||
* @param pos the location where the object is to be deployed
|
* @param pos the location where the object is to be deployed
|
||||||
* @param roll the amount of roll that affects orientation
|
* @param orient the angle of orientation
|
||||||
* @param pitch the amount of pitch that affects orientation
|
|
||||||
* @param yaw the amount of yaw that affects orientation
|
|
||||||
* @param unk2 na
|
* @param unk2 na
|
||||||
*/
|
*/
|
||||||
final case class DeployObjectMessage(object_guid : PlanetSideGUID,
|
final case class DeployObjectMessage(object_guid : PlanetSideGUID,
|
||||||
unk1 : Long,
|
unk1 : Long,
|
||||||
pos : Vector3,
|
pos : Vector3,
|
||||||
roll : Int,
|
orient : Vector3,
|
||||||
pitch : Int,
|
|
||||||
yaw : Int,
|
|
||||||
unk2 : Long)
|
unk2 : Long)
|
||||||
extends PlanetSideGamePacket {
|
extends PlanetSideGamePacket {
|
||||||
type Packet = DeployObjectMessage
|
type Packet = DeployObjectMessage
|
||||||
|
|
@ -38,9 +35,19 @@ object DeployObjectMessage extends Marshallable[DeployObjectMessage] {
|
||||||
("object_guid" | PlanetSideGUID.codec) ::
|
("object_guid" | PlanetSideGUID.codec) ::
|
||||||
("unk1" | uint32L) ::
|
("unk1" | uint32L) ::
|
||||||
("pos" | Vector3.codec_pos) ::
|
("pos" | Vector3.codec_pos) ::
|
||||||
("roll" | uint8L) ::
|
(("roll" | Angular.codec_roll) ::
|
||||||
("pitch" | uint8L) ::
|
("pitch" | Angular.codec_pitch) ::
|
||||||
("yaw" | uint8L) ::
|
("yaw" | Angular.codec_yaw())
|
||||||
|
).xmap[Vector3] (
|
||||||
|
{
|
||||||
|
case x :: y :: z :: HNil =>
|
||||||
|
Vector3(x, y, z)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case Vector3(x, y, z) =>
|
||||||
|
x :: y :: z :: HNil
|
||||||
|
}
|
||||||
|
) ::
|
||||||
("unk2" | uint32L)
|
("unk2" | uint32L)
|
||||||
).as[DeployObjectMessage]
|
).as[DeployObjectMessage]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import shapeless.{::, HNil}
|
||||||
/**
|
/**
|
||||||
* An `Enumeration` for the forms of the event chat message produced by this packet.
|
* An `Enumeration` for the forms of the event chat message produced by this packet.
|
||||||
*/
|
*/
|
||||||
object DeploymentOutcome extends Enumeration(1) {
|
object DeployOutcome extends Enumeration(1) {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
|
|
||||||
val Failure = Value(2)
|
val Failure = Value(2)
|
||||||
|
|
@ -47,7 +47,7 @@ object DeploymentOutcome extends Enumeration(1) {
|
||||||
*/
|
*/
|
||||||
final case class ObjectDeployedMessage(unk : Int,
|
final case class ObjectDeployedMessage(unk : Int,
|
||||||
desc : String,
|
desc : String,
|
||||||
action : DeploymentOutcome.Value,
|
action : DeployOutcome.Value,
|
||||||
count : Long,
|
count : Long,
|
||||||
max : Long)
|
max : Long)
|
||||||
extends PlanetSideGamePacket {
|
extends PlanetSideGamePacket {
|
||||||
|
|
@ -65,13 +65,31 @@ object ObjectDeployedMessage extends Marshallable[ObjectDeployedMessage] {
|
||||||
* @param max the maximum number of this type of object that can be deployed
|
* @param max the maximum number of this type of object that can be deployed
|
||||||
* @return an `ObjectDeployedMessage` object
|
* @return an `ObjectDeployedMessage` object
|
||||||
*/
|
*/
|
||||||
def apply(desc : String, action : DeploymentOutcome.Value, count : Long, max : Long) : ObjectDeployedMessage =
|
def apply(desc : String, action : DeployOutcome.Value, count : Long, max : Long) : ObjectDeployedMessage =
|
||||||
new ObjectDeployedMessage(0, desc, action, count, max)
|
new ObjectDeployedMessage(0, desc, action, count, max)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param desc descriptive text of what kind of object is being deployed
|
||||||
|
* @param count the number of this type of object deployed
|
||||||
|
* @param max the maximum number of this type of object that can be deployed
|
||||||
|
* @return an `ObjectDeployedMessage` object
|
||||||
|
*/
|
||||||
|
def Success(desc : String, count : Int, max : Int) : ObjectDeployedMessage =
|
||||||
|
new ObjectDeployedMessage(0, desc, DeployOutcome.Success, count, max)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param desc descriptive text of what kind of object failed to be deployed
|
||||||
|
* @return an `ObjectDeployedMessage` object
|
||||||
|
*/
|
||||||
|
def Failure(desc : String) : ObjectDeployedMessage =
|
||||||
|
new ObjectDeployedMessage(0, desc, DeployOutcome.Failure, 0, 0)
|
||||||
|
|
||||||
implicit val codec : Codec[ObjectDeployedMessage] = (
|
implicit val codec : Codec[ObjectDeployedMessage] = (
|
||||||
("unk" | uint16L) ::
|
("unk" | uint16L) ::
|
||||||
("desc" | PacketHelpers.encodedString) ::
|
("desc" | PacketHelpers.encodedString) ::
|
||||||
("action" | DeploymentOutcome.codec) ::
|
("action" | DeployOutcome.codec) ::
|
||||||
("count" | uint32L) ::
|
("count" | uint32L) ::
|
||||||
("max" | uint32L)
|
("max" | uint32L)
|
||||||
).xmap[ObjectDeployedMessage] (
|
).xmap[ObjectDeployedMessage] (
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import scodec.codecs._
|
||||||
* `67 - ???`<br>
|
* `67 - ???`<br>
|
||||||
* <br>
|
* <br>
|
||||||
* Global (GUID=0)<br>
|
* Global (GUID=0)<br>
|
||||||
* `82 - ???`
|
* `75 - Russian client region check` (value checks with bitmask `& 8`)<br>
|
||||||
|
* `82 - ???`<br>
|
||||||
* `83 - max boomers`<br>
|
* `83 - max boomers`<br>
|
||||||
* `84 - max he mines`<br>
|
* `84 - max he mines`<br>
|
||||||
* `85 - max disruptor mines`<br>
|
* `85 - max disruptor mines`<br>
|
||||||
|
|
@ -114,10 +115,10 @@ import scodec.codecs._
|
||||||
* `36 - CR. Value is the CR`<br>
|
* `36 - CR. Value is the CR`<br>
|
||||||
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`<br>
|
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`<br>
|
||||||
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`<br>
|
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`<br>
|
||||||
* 47 - Sets base NTU level to CRITICAL. MUST use base modelId not base GUID
|
* `47 - Sets base NTU level to CRITICAL. MUST use base modelId not base GUID`<br>
|
||||||
* 48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base modelId not base GUID
|
* `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base modelId not base GUID`<br>
|
||||||
* 49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)
|
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`<br>
|
||||||
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)
|
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`<br>
|
||||||
* `53 - LFS. Value is 1 to flag LFS`<br>
|
* `53 - LFS. Value is 1 to flag LFS`<br>
|
||||||
* `54 - Player "Aura". Values can be expressed in the first byte's lower nibble:`<br>
|
* `54 - Player "Aura". Values can be expressed in the first byte's lower nibble:`<br>
|
||||||
* - 0 is nothing<br>
|
* - 0 is nothing<br>
|
||||||
|
|
@ -151,7 +152,7 @@ import scodec.codecs._
|
||||||
* `79 - ???`<br>
|
* `79 - ???`<br>
|
||||||
* `80 - Damage vehicle (unknown value)`<br>
|
* `80 - Damage vehicle (unknown value)`<br>
|
||||||
* `81 - ???`<br>
|
* `81 - ???`<br>
|
||||||
* `113 - `Vehicle capacitor - e.g. Leviathan EMP charge`
|
* `113 - Vehicle capacitor - e.g. Leviathan EMP charge`
|
||||||
*
|
*
|
||||||
* @param player_guid the player
|
* @param player_guid the player
|
||||||
* @param attribute_type na
|
* @param attribute_type na
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.{Angular, Vector3}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
|
|
@ -19,14 +20,11 @@ final case class TriggeredEffect(unk1 : Boolean,
|
||||||
* Activate an effect that is not directly associated with an existing game object.
|
* Activate an effect that is not directly associated with an existing game object.
|
||||||
* Without a game object from which to inherit position and orientation, those explicit parameters must be provided.
|
* Without a game object from which to inherit position and orientation, those explicit parameters must be provided.
|
||||||
* @param pos the position in the game world
|
* @param pos the position in the game world
|
||||||
* @param roll the amount of roll that affects orientation
|
* @param orient the angle of orientation
|
||||||
* @param pitch the amount of pitch that affects orientation
|
|
||||||
* @param yaw the amount of yaw that affects orientation
|
|
||||||
*/
|
*/
|
||||||
|
//TODO must find proper North-corrective angle for orientation
|
||||||
final case class TriggeredEffectLocation(pos : Vector3,
|
final case class TriggeredEffectLocation(pos : Vector3,
|
||||||
roll : Int,
|
orient : Vector3)
|
||||||
pitch : Int,
|
|
||||||
yaw : Int)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatched by the server to cause a client to display a special graphical effect.<br>
|
* Dispatched by the server to cause a client to display a special graphical effect.<br>
|
||||||
|
|
@ -38,14 +36,14 @@ final case class TriggeredEffectLocation(pos : Vector3,
|
||||||
* For example, the effect "on" will only work on objects that accept "on" normally, like a deployed `motionalarmsensor`.
|
* For example, the effect "on" will only work on objects that accept "on" normally, like a deployed `motionalarmsensor`.
|
||||||
* The effect "spawn_object_effect" can be applied anywhere in the environment;
|
* The effect "spawn_object_effect" can be applied anywhere in the environment;
|
||||||
* but, it can not be activated in conjunction with an existing object.
|
* but, it can not be activated in conjunction with an existing object.
|
||||||
* @param obj an object that accepts the effect
|
* @param object_guid an object that accepts the effect
|
||||||
* @param effect the name of the effect
|
* @param effect the name of the effect
|
||||||
* @param unk na;
|
* @param unk na;
|
||||||
* when activating an effect on an existing object
|
* when activating an effect on an existing object
|
||||||
* @param location an optional position where the effect will be displayed;
|
* @param location an optional position where the effect will be displayed;
|
||||||
* when activating an effect independently
|
* when activating an effect independently
|
||||||
*/
|
*/
|
||||||
final case class TriggerEffectMessage(obj : PlanetSideGUID,
|
final case class TriggerEffectMessage(object_guid : PlanetSideGUID,
|
||||||
effect : String,
|
effect : String,
|
||||||
unk : Option[TriggeredEffect] = None,
|
unk : Option[TriggeredEffect] = None,
|
||||||
location : Option[TriggeredEffectLocation] = None
|
location : Option[TriggeredEffectLocation] = None
|
||||||
|
|
@ -56,6 +54,15 @@ final case class TriggerEffectMessage(obj : PlanetSideGUID,
|
||||||
}
|
}
|
||||||
|
|
||||||
object TriggerEffectMessage extends Marshallable[TriggerEffectMessage] {
|
object TriggerEffectMessage extends Marshallable[TriggerEffectMessage] {
|
||||||
|
def apply(object_guid : PlanetSideGUID, effect : String) : TriggerEffectMessage =
|
||||||
|
TriggerEffectMessage(object_guid, effect, None, None)
|
||||||
|
|
||||||
|
def apply(object_guid : PlanetSideGUID, effect : String, unk1 : Boolean, unk2 : Long) : TriggerEffectMessage =
|
||||||
|
TriggerEffectMessage(object_guid, effect, Some(TriggeredEffect(unk1, unk2)), None)
|
||||||
|
|
||||||
|
def apply(effect : String, position : Vector3, orientation : Vector3) : TriggerEffectMessage =
|
||||||
|
TriggerEffectMessage(PlanetSideGUID(0), effect, None, Some(TriggeredEffectLocation(position, orientation)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `Codec` for `TriggeredEffect` data.
|
* A `Codec` for `TriggeredEffect` data.
|
||||||
*/
|
*/
|
||||||
|
|
@ -69,15 +76,24 @@ object TriggerEffectMessage extends Marshallable[TriggerEffectMessage] {
|
||||||
*/
|
*/
|
||||||
private val effect_location_codec : Codec[TriggeredEffectLocation] = (
|
private val effect_location_codec : Codec[TriggeredEffectLocation] = (
|
||||||
("pos" | Vector3.codec_pos) ::
|
("pos" | Vector3.codec_pos) ::
|
||||||
("roll" | uint8L) ::
|
(("roll" | Angular.codec_roll) ::
|
||||||
("pitch" | uint8L) ::
|
("pitch" | Angular.codec_pitch) ::
|
||||||
("yaw" | uint8L)
|
("yaw" | Angular.codec_yaw())).xmap[Vector3] (
|
||||||
|
{
|
||||||
|
case x :: y :: z :: HNil =>
|
||||||
|
Vector3(x, y, z)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case Vector3(x, y, z) =>
|
||||||
|
x :: y :: z :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
).as[TriggeredEffectLocation]
|
).as[TriggeredEffectLocation]
|
||||||
|
|
||||||
implicit val codec : Codec[TriggerEffectMessage] = (
|
implicit val codec : Codec[TriggerEffectMessage] = (
|
||||||
("obj" | PlanetSideGUID.codec) >>:~ { obj =>
|
("object_guid" | PlanetSideGUID.codec) >>:~ { guid =>
|
||||||
("effect" | PacketHelpers.encodedString) ::
|
("effect" | PacketHelpers.encodedString) ::
|
||||||
optional(bool, "unk" | effect_codec) ::
|
optional(bool, "unk" | effect_codec) ::
|
||||||
conditional(obj.guid == 0, "location" | effect_location_codec)
|
conditional(guid.guid == 0, "location" | effect_location_codec)
|
||||||
}).as[TriggerEffectMessage]
|
}).as[TriggerEffectMessage]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.packet.Marshallable
|
||||||
|
import scodec.codecs._
|
||||||
|
import scodec.{Attempt, Codec, Err}
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of the Spitfire-based small turrets deployed using an adaptive construction engine.<br>
|
||||||
|
* <br>
|
||||||
|
* The turret may contain substructure defining a weapon is a turret weapon contained within the turret itself.
|
||||||
|
* Furthermore, that turret-like weapon is loaded with turret-like ammunition.
|
||||||
|
* In other words, this outer turret can be considered a weapons platform for the inner turret weapon.<br>
|
||||||
|
* <br>
|
||||||
|
* If the turret has no `health`, it is rendered as destroyed.
|
||||||
|
* If the turret has no internal weapon, it is safest rendered as destroyed.
|
||||||
|
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
||||||
|
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||||
|
* @param internals data regarding the mounted weapon
|
||||||
|
*/
|
||||||
|
final case class LargeDeployableData(deploy : SmallDeployableData,
|
||||||
|
health : Int,
|
||||||
|
internals : Option[InventoryData] = None
|
||||||
|
) extends ConstructorData {
|
||||||
|
override def bitsize : Long = {
|
||||||
|
val deploySize = deploy.bitsize
|
||||||
|
val internalSize = internals match {
|
||||||
|
case Some(inv) =>
|
||||||
|
inv.bitsize
|
||||||
|
case None =>
|
||||||
|
0
|
||||||
|
}
|
||||||
|
22L + deploySize + internalSize //8u + 7u + 4u + 2u + 1u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LargeDeployableData extends Marshallable[LargeDeployableData] {
|
||||||
|
implicit val codec : Codec[LargeDeployableData] = (
|
||||||
|
("deploy" | SmallDeployableData.codec) ::
|
||||||
|
("health" | uint8L) ::
|
||||||
|
uintL(7) ::
|
||||||
|
uint4L ::
|
||||||
|
uint2L ::
|
||||||
|
optional(bool, "internals" | InventoryData.codec)
|
||||||
|
).exmap[LargeDeployableData] (
|
||||||
|
{
|
||||||
|
case deploy :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
||||||
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
|
(0, None)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(health, internals)
|
||||||
|
}
|
||||||
|
Attempt.successful(LargeDeployableData(deploy, newHealth, newInternals))
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
Attempt.failure(Err("invalid large deployable data format"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case LargeDeployableData(deploy, health, internals) =>
|
||||||
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
|
(0, None)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(health, internals)
|
||||||
|
}
|
||||||
|
Attempt.successful(deploy :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -955,7 +955,7 @@ object ObjectClass {
|
||||||
case ObjectClass.trek => ConstructorData.genericCodec(WeaponData.codec, "tool")
|
case ObjectClass.trek => ConstructorData.genericCodec(WeaponData.codec, "tool")
|
||||||
//ace deployables
|
//ace deployables
|
||||||
case ObjectClass.ace => ConstructorData.genericCodec(ACEData.codec, "ace")
|
case ObjectClass.ace => ConstructorData.genericCodec(ACEData.codec, "ace")
|
||||||
case ObjectClass.advanced_ace => ConstructorData.genericCodec(CommandDetonaterData.codec, "advanced ace")
|
case ObjectClass.advanced_ace => ConstructorData.genericCodec(ACEData.codec, "advanced ace")
|
||||||
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
|
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
|
||||||
//vehicles?
|
//vehicles?
|
||||||
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
|
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
|
||||||
|
|
@ -1186,7 +1186,7 @@ object ObjectClass {
|
||||||
case ObjectClass.trek => DroppedItemData.genericCodec(WeaponData.codec, "tool")
|
case ObjectClass.trek => DroppedItemData.genericCodec(WeaponData.codec, "tool")
|
||||||
//ace deployables
|
//ace deployables
|
||||||
case ObjectClass.ace => DroppedItemData.genericCodec(ACEData.codec, "ace")
|
case ObjectClass.ace => DroppedItemData.genericCodec(ACEData.codec, "ace")
|
||||||
case ObjectClass.advanced_ace => DroppedItemData.genericCodec(CommandDetonaterData.codec, "advanced ace") //todo temporary?
|
case ObjectClass.advanced_ace => DroppedItemData.genericCodec(ACEData.codec, "advanced ace") //todo temporary?
|
||||||
case ObjectClass.boomer_trigger => DroppedItemData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
|
case ObjectClass.boomer_trigger => DroppedItemData.genericCodec(BoomerTriggerData.codec, "boomer trigger")
|
||||||
case ObjectClass.boomer => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
case ObjectClass.boomer => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
||||||
case ObjectClass.he_mine => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
case ObjectClass.he_mine => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ object ObjectCreateBase {
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
case ex : Exception =>
|
case ex : Exception =>
|
||||||
log.error(s"${ex.getClass.toString} - ${ex.toString}")
|
log.error(s"${ex.getClass.toString} - ${ex.toString} ($objectClass)")
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ object ObjectCreateBase {
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
case ex : Exception =>
|
case ex : Exception =>
|
||||||
log.error(s"${ex.getClass.toString} - ${ex.toString}")
|
log.error(s"${ex.getClass.toString} - ${ex.toString} ($objectClass)")
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,19 @@ import shapeless.{::, HNil}
|
||||||
* @param health the amount of health the object has, as a percentage of a filled bar
|
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||||
* @param internals data regarding the mountable weapon
|
* @param internals data regarding the mountable weapon
|
||||||
*/
|
*/
|
||||||
final case class OneMannedFieldTurretData(deploy : CommonFieldData,
|
final case class OneMannedFieldTurretData(deploy : SmallDeployableData,
|
||||||
health : Int,
|
health : Int,
|
||||||
internals : Option[InternalSlot] = None
|
internals : Option[InventoryData] = None
|
||||||
) extends ConstructorData {
|
) extends ConstructorData {
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
val deploySize = deploy.bitsize
|
val deploySize = deploy.bitsize
|
||||||
val internalSize = if(internals.isDefined) { CommonFieldData.internalWeapon_bitsize + internals.get.bitsize } else { 0L }
|
val internalSize = internals match {
|
||||||
38L + deploySize + internalSize //16u + 8u + 8u + 2u + 4u
|
case Some(inv) =>
|
||||||
|
inv.bitsize
|
||||||
|
case None =>
|
||||||
|
0
|
||||||
|
}
|
||||||
|
37L + deploySize + internalSize //16u + 1u + 8u + 5u + 4u + 2u + 1u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,7 +45,7 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
|
||||||
* @param internals data regarding the mountable weapon
|
* @param internals data regarding the mountable weapon
|
||||||
* @return a `OneMannedFieldTurretData` object
|
* @return a `OneMannedFieldTurretData` object
|
||||||
*/
|
*/
|
||||||
def apply(deploy : CommonFieldData, health : Int, internals : InternalSlot) : OneMannedFieldTurretData =
|
def apply(deploy : SmallDeployableData, health : Int, internals : InventoryData) : OneMannedFieldTurretData =
|
||||||
new OneMannedFieldTurretData(deploy, health, Some(internals))
|
new OneMannedFieldTurretData(deploy, health, Some(internals))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -122,40 +127,44 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
|
||||||
)
|
)
|
||||||
|
|
||||||
implicit val codec : Codec[OneMannedFieldTurretData] = (
|
implicit val codec : Codec[OneMannedFieldTurretData] = (
|
||||||
("deploy" | CommonFieldData.codec) ::
|
("deploy" | SmallDeployableData.codec) ::
|
||||||
bool ::
|
PlanetSideGUID.codec :: //hoist/extract with the deploy.owner_guid in field above
|
||||||
PlanetSideGUID.codec :: //hoist/extract with the CommonFieldData above
|
|
||||||
bool ::
|
bool ::
|
||||||
("health" | uint8L) ::
|
("health" | uint8L) ::
|
||||||
uint2L ::
|
uint(5) ::
|
||||||
uint8L ::
|
uint4 ::
|
||||||
bool ::
|
uint2 ::
|
||||||
optional(bool, "internals" | CommonFieldData.internalWeaponCodec)
|
optional(bool, "internals" | InventoryData.codec)
|
||||||
).exmap[OneMannedFieldTurretData] (
|
).exmap[OneMannedFieldTurretData] (
|
||||||
{
|
{
|
||||||
case deploy :: false :: player :: false :: health :: 0 :: 0x1E :: false :: internals :: HNil =>
|
case deploy :: player :: false :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
||||||
var newHealth : Int = health
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
var newInternals : Option[InternalSlot] = internals
|
(0, None)
|
||||||
if(health == 0 || internals.isEmpty) {
|
|
||||||
newHealth = 0
|
|
||||||
newInternals = None
|
|
||||||
}
|
}
|
||||||
val newDeploy = CommonFieldData(deploy.pos, deploy.faction, deploy.unk, player)
|
else {
|
||||||
Attempt.successful(OneMannedFieldTurretData(newDeploy, newHealth, newInternals))
|
(health, internals)
|
||||||
|
}
|
||||||
|
Attempt.successful(
|
||||||
|
OneMannedFieldTurretData(
|
||||||
|
SmallDeployableData(deploy.pos, deploy.faction, deploy.bops, deploy.destroyed, deploy.unk1, deploy.jammered, deploy.unk2, player),
|
||||||
|
newHealth,
|
||||||
|
newInternals
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.failure(Err("invalid omft data format"))
|
Attempt.failure(Err("invalid omft data format"))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case OneMannedFieldTurretData(deploy, health, internals) =>
|
case OneMannedFieldTurretData(deploy, health, internals) =>
|
||||||
var newHealth : Int = health
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
var newInternals : Option[InternalSlot] = internals
|
(0, None)
|
||||||
if(health == 0 || internals.isEmpty) {
|
|
||||||
newHealth = 0
|
|
||||||
newInternals = None
|
|
||||||
}
|
}
|
||||||
val newDeploy = CommonFieldData(deploy.pos, deploy.faction, deploy.unk)
|
else {
|
||||||
Attempt.successful(newDeploy :: false :: deploy.player_guid :: false :: newHealth :: 0 :: 0x1E :: false :: newInternals :: HNil)
|
(health, internals)
|
||||||
|
}
|
||||||
|
val newDeploy = SmallDeployableData(deploy.pos, deploy.faction, deploy.bops, deploy.destroyed, deploy.unk1, deploy.jammered, deploy.unk2, PlanetSideGUID(0))
|
||||||
|
Attempt.successful(newDeploy :: deploy.owner_guid :: false :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.failure(Err("invalid omft data format"))
|
Attempt.failure(Err("invalid omft data format"))
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,42 @@
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
import net.psforever.packet.Marshallable
|
import net.psforever.packet.Marshallable
|
||||||
import scodec.{Attempt, Codec, Err}
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.PlanetSideEmpire
|
||||||
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless.{::, HNil}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of simple objects that are spawned by the adaptive construction engine.
|
* A representation of simple objects that are spawned by the adaptive construction engine.
|
||||||
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
* //@param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
||||||
*/
|
*/
|
||||||
final case class SmallDeployableData(deploy : CommonFieldData) extends ConstructorData {
|
final case class SmallDeployableData(pos : PlacementData,
|
||||||
override def bitsize : Long = deploy.bitsize + 1L
|
faction : PlanetSideEmpire.Value,
|
||||||
|
bops : Boolean,
|
||||||
|
destroyed : Boolean,
|
||||||
|
unk1 : Int,
|
||||||
|
jammered : Boolean,
|
||||||
|
unk2 : Boolean,
|
||||||
|
owner_guid : PlanetSideGUID) extends ConstructorData {
|
||||||
|
override def bitsize : Long = {
|
||||||
|
val posSize = pos.bitsize
|
||||||
|
24 + posSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object SmallDeployableData extends Marshallable[SmallDeployableData] {
|
object SmallDeployableData extends Marshallable[SmallDeployableData] {
|
||||||
implicit val codec : Codec[SmallDeployableData] = (
|
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk1 : Int, jammered : Boolean, unk2 : Boolean) : SmallDeployableData = {
|
||||||
("deploy" | CommonFieldData.codec) ::
|
SmallDeployableData(pos, faction, false, false, unk1, jammered, unk2, PlanetSideGUID(0))
|
||||||
bool
|
}
|
||||||
).exmap[SmallDeployableData] (
|
|
||||||
{
|
|
||||||
case deploy :: false :: HNil =>
|
|
||||||
Attempt.successful(SmallDeployableData(deploy))
|
|
||||||
|
|
||||||
case _ =>
|
implicit val codec : Codec[SmallDeployableData] = (
|
||||||
Attempt.failure(Err("invalid small deployable data format"))
|
("pos" | PlacementData.codec) ::
|
||||||
},
|
("faction" | PlanetSideEmpire.codec) ::
|
||||||
{
|
("bops" | bool) ::
|
||||||
case SmallDeployableData(deploy) =>
|
("destroyed" | bool) ::
|
||||||
Attempt.successful(deploy :: false :: HNil)
|
("unk1" | uint2L) :: //3 - na, 2 - common, 1 - na, 0 - common?
|
||||||
}
|
("jammered" | bool) ::
|
||||||
)
|
("unk2" | bool) ::
|
||||||
|
("owner_guid" | PlanetSideGUID.codec)
|
||||||
|
).as[SmallDeployableData]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,19 @@ import shapeless.{::, HNil}
|
||||||
* @param health the amount of health the object has, as a percentage of a filled bar
|
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||||
* @param internals data regarding the mounted weapon
|
* @param internals data regarding the mounted weapon
|
||||||
*/
|
*/
|
||||||
final case class SmallTurretData(deploy : CommonFieldData,
|
final case class SmallTurretData(deploy : SmallDeployableData,
|
||||||
health : Int,
|
health : Int,
|
||||||
internals : Option[InternalSlot] = None
|
internals : Option[InventoryData] = None
|
||||||
) extends ConstructorData {
|
) extends ConstructorData {
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
val deploySize = deploy.bitsize
|
val deploySize = deploy.bitsize
|
||||||
val internalSize = if(internals.isDefined) { CommonFieldData.internalWeapon_bitsize + internals.get.bitsize } else { 0L }
|
val internalSize = internals match {
|
||||||
23L + deploySize + internalSize //1u + 8u + 7u + 4u + 2u + 1u
|
case Some(inv) =>
|
||||||
|
inv.bitsize
|
||||||
|
case None =>
|
||||||
|
0
|
||||||
|
}
|
||||||
|
22L + deploySize + internalSize //8u + 7u + 4u + 2u + 1u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +44,7 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
|
||||||
* @param internals data regarding the mounted weapon
|
* @param internals data regarding the mounted weapon
|
||||||
* @return a `SmallTurretData` object
|
* @return a `SmallTurretData` object
|
||||||
*/
|
*/
|
||||||
def apply(deploy : CommonFieldData, health : Int, internals : InternalSlot) : SmallTurretData =
|
def apply(deploy : SmallDeployableData, health : Int, internals : InventoryData) : SmallTurretData =
|
||||||
new SmallTurretData(deploy, health, Some(internals))
|
new SmallTurretData(deploy, health, Some(internals))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -81,21 +86,20 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
|
||||||
)
|
)
|
||||||
|
|
||||||
implicit val codec : Codec[SmallTurretData] = (
|
implicit val codec : Codec[SmallTurretData] = (
|
||||||
("deploy" | CommonFieldData.codec) ::
|
("deploy" | SmallDeployableData.codec) ::
|
||||||
bool ::
|
|
||||||
("health" | uint8L) ::
|
("health" | uint8L) ::
|
||||||
uintL(7) ::
|
uintL(7) ::
|
||||||
uint4L ::
|
uint4L ::
|
||||||
uint2L ::
|
uint2L ::
|
||||||
optional(bool, "internals" | CommonFieldData.internalWeaponCodec)
|
optional(bool, "internals" | InventoryData.codec)
|
||||||
).exmap[SmallTurretData] (
|
).exmap[SmallTurretData] (
|
||||||
{
|
{
|
||||||
case deploy :: false :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
case deploy :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
||||||
var newHealth : Int = health
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
var newInternals : Option[InternalSlot] = internals
|
(0, None)
|
||||||
if(health == 0 || internals.isEmpty) {
|
}
|
||||||
newHealth = 0
|
else {
|
||||||
newInternals = None
|
(health, internals)
|
||||||
}
|
}
|
||||||
Attempt.successful(SmallTurretData(deploy, newHealth, newInternals))
|
Attempt.successful(SmallTurretData(deploy, newHealth, newInternals))
|
||||||
|
|
||||||
|
|
@ -104,13 +108,13 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case SmallTurretData(deploy, health, internals) =>
|
case SmallTurretData(deploy, health, internals) =>
|
||||||
var newHealth : Int = health
|
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||||
var newInternals : Option[InternalSlot] = internals
|
(0, None)
|
||||||
if(health == 0 || internals.isEmpty) {
|
|
||||||
newHealth = 0
|
|
||||||
newInternals = None
|
|
||||||
}
|
}
|
||||||
Attempt.successful(deploy :: false :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil)
|
else {
|
||||||
|
(health, internals)
|
||||||
|
}
|
||||||
|
Attempt.successful(deploy :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,24 @@ import shapeless.{::, HNil}
|
||||||
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine
|
||||||
* @param health the amount of health the object has, as a percentage of a filled bar
|
* @param health the amount of health the object has, as a percentage of a filled bar
|
||||||
*/
|
*/
|
||||||
final case class TRAPData(deploy : CommonFieldData,
|
final case class TRAPData(deploy : SmallDeployableData,
|
||||||
health : Int
|
health : Int
|
||||||
) extends ConstructorData {
|
) extends ConstructorData {
|
||||||
override def bitsize : Long = {
|
override def bitsize : Long = {
|
||||||
23L + deploy.bitsize //8u + 7u + 4u + 3u + 1u
|
22L + deploy.bitsize //8u + 7u + 4u + 3u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object TRAPData extends Marshallable[TRAPData] {
|
object TRAPData extends Marshallable[TRAPData] {
|
||||||
implicit val codec : Codec[TRAPData] = (
|
implicit val codec : Codec[TRAPData] = (
|
||||||
("deploy" | CommonFieldData.codec) ::
|
("deploy" | SmallDeployableData.codec) ::
|
||||||
bool ::
|
|
||||||
("health" | uint8L) ::
|
("health" | uint8L) ::
|
||||||
uint(7) ::
|
uint(7) ::
|
||||||
uint4L ::
|
uint4L ::
|
||||||
uint(3)
|
uint(3)
|
||||||
).exmap[TRAPData] (
|
).exmap[TRAPData] (
|
||||||
{
|
{
|
||||||
case deploy :: false :: health :: 0 :: 15 :: 0 :: HNil =>
|
case deploy :: health :: 0 :: 15 :: 0 :: HNil =>
|
||||||
Attempt.successful(TRAPData(deploy, health))
|
Attempt.successful(TRAPData(deploy, health))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -38,7 +37,7 @@ object TRAPData extends Marshallable[TRAPData] {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case TRAPData(deploy, health) =>
|
case TRAPData(deploy, health) =>
|
||||||
Attempt.successful(deploy :: false :: health :: 0 :: 15 :: 0 :: HNil)
|
Attempt.successful(deploy :: health :: 0 :: 15 :: 0 :: HNil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,13 @@ object Vector3 {
|
||||||
("z" | floatL)
|
("z" | floatL)
|
||||||
).as[Vector3]
|
).as[Vector3]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common vector object that only concerns itself with rotation around the world-up axis.
|
||||||
|
* @param yaw the angle of rotation
|
||||||
|
* @return a `Vector3` object
|
||||||
|
*/
|
||||||
|
def z(yaw : Float) : Vector3 = Vector3(0, 0, yaw)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the actual distance between two points.
|
* Calculate the actual distance between two points.
|
||||||
* @param pos1 the first point
|
* @param pos1 the first point
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
|
||||||
*/
|
*/
|
||||||
var secondHeap : List[RemoverActor.Entry] = List()
|
var secondHeap : List[RemoverActor.Entry] = List()
|
||||||
|
|
||||||
private var taskResolver : ActorRef = Actor.noSender
|
protected var taskResolver : ActorRef = Actor.noSender
|
||||||
|
|
||||||
val sameEntryComparator = new SimilarityComparator[RemoverActor.Entry]() {
|
val sameEntryComparator = new SimilarityComparator[RemoverActor.Entry]() {
|
||||||
def Test(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = {
|
def Test(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = {
|
||||||
|
|
@ -101,7 +101,16 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
|
||||||
val entry = RemoverActor.Entry(obj, zone, duration.getOrElse(FirstStandardDuration).toNanos)
|
val entry = RemoverActor.Entry(obj, zone, duration.getOrElse(FirstStandardDuration).toNanos)
|
||||||
if(InclusionTest(entry) && !secondHeap.exists(test => sameEntryComparator.Test(test, entry) )) {
|
if(InclusionTest(entry) && !secondHeap.exists(test => sameEntryComparator.Test(test, entry) )) {
|
||||||
InitialJob(entry)
|
InitialJob(entry)
|
||||||
if(firstHeap.isEmpty) {
|
if(entry.duration == 0) {
|
||||||
|
//skip the first queue altogether
|
||||||
|
FirstJob(entry)
|
||||||
|
secondHeap = secondHeap ++ List(RepackageEntry(entry))
|
||||||
|
if(secondHeap.size == 1) {
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(firstHeap.isEmpty) {
|
||||||
//we were the only entry so the event must be started from scratch
|
//we were the only entry so the event must be started from scratch
|
||||||
firstHeap = List(entry)
|
firstHeap = List(entry)
|
||||||
trace(s"a remover task has been added: $entry")
|
trace(s"a remover task has been added: $entry")
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.ballistics.SourceEntry
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.Container
|
import net.psforever.objects.inventory.Container
|
||||||
|
|
@ -9,7 +10,7 @@ import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||||
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
|
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
|
@ -23,20 +24,23 @@ object AvatarAction {
|
||||||
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : (Any)=>Unit) extends Action
|
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : (Any)=>Unit) extends Action
|
||||||
|
final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action
|
||||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
||||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
||||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
||||||
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
|
||||||
final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
|
final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
|
||||||
|
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
||||||
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
||||||
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||||
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||||
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
||||||
|
final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action
|
||||||
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
||||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
|
final case class SetEmpire(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Action
|
||||||
final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import net.psforever.objects.Player
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.ballistics.SourceEntry
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
import net.psforever.packet.game._
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
object AvatarResponse {
|
object AvatarResponse {
|
||||||
trait Response
|
trait Response
|
||||||
|
|
@ -30,8 +30,10 @@ object AvatarResponse {
|
||||||
final case class ObjectHeld(slot : Int) extends Response
|
final case class ObjectHeld(slot : Int) extends Response
|
||||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||||
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
|
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
|
||||||
|
final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response
|
||||||
final case class Release(player : Player) extends Response
|
final case class Release(player : Player) extends Response
|
||||||
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
|
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
|
||||||
|
final case class SetEmpire(object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Response
|
||||||
final case class StowEquipment(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response
|
final case class StowEquipment(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response
|
||||||
final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
|
final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import services.{GenericEventBus, RemoverActor, Service}
|
||||||
class AvatarService extends Actor {
|
class AvatarService extends Actor {
|
||||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||||
private val janitor = context.actorOf(Props[DroppedItemRemover], "item-remover-agent")
|
private val janitor = context.actorOf(Props[DroppedItemRemover], "item-remover-agent")
|
||||||
//undertaker ! "startup"
|
|
||||||
|
|
||||||
private [this] val log = org.log4s.getLogger
|
private [this] val log = org.log4s.getLogger
|
||||||
|
|
||||||
|
|
@ -69,6 +68,14 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.DamageResolution(target, resolution_function))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.DamageResolution(target, resolution_function))
|
||||||
)
|
)
|
||||||
|
case AvatarAction.DeployItem(player_guid, item) =>
|
||||||
|
val definition = item.Definition
|
||||||
|
val objectData = definition.Packet.ConstructorData(item).get
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid,
|
||||||
|
AvatarResponse.DropItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData))
|
||||||
|
)
|
||||||
|
)
|
||||||
case AvatarAction.Destroy(victim, killer, weapon, pos) =>
|
case AvatarAction.Destroy(victim, killer, weapon, pos) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos))
|
AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos))
|
||||||
|
|
@ -148,6 +155,10 @@ class AvatarService extends Actor {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
case AvatarAction.PutDownFDU(player_guid) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.PutDownFDU(player_guid))
|
||||||
|
)
|
||||||
case AvatarAction.Release(player, zone, time) =>
|
case AvatarAction.Release(player, zone, time) =>
|
||||||
undertaker forward RemoverActor.AddTask(player, zone, time)
|
undertaker forward RemoverActor.AddTask(player, zone, time)
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
|
|
@ -157,6 +168,10 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
|
||||||
)
|
)
|
||||||
|
case AvatarAction.SetEmpire(player_guid, target_guid, faction) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.SetEmpire(target_guid, faction))
|
||||||
|
)
|
||||||
case AvatarAction.StowEquipment(player_guid, target_guid, slot, obj) =>
|
case AvatarAction.StowEquipment(player_guid, target_guid, slot, obj) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.StowEquipment(target_guid, slot, obj))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.StowEquipment(target_guid, slot, obj))
|
||||||
|
|
@ -177,6 +192,7 @@ class AvatarService extends Actor {
|
||||||
case AvatarServiceMessage.Corpse(msg) =>
|
case AvatarServiceMessage.Corpse(msg) =>
|
||||||
undertaker forward msg
|
undertaker forward msg
|
||||||
|
|
||||||
|
//message to Janitor
|
||||||
case AvatarServiceMessage.Ground(msg) =>
|
case AvatarServiceMessage.Ground(msg) =>
|
||||||
janitor forward msg
|
janitor forward msg
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class DroppedItemRemover extends RemoverActor {
|
||||||
|
|
||||||
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
entry.zone.Ground ! Zone.Ground.PickupItem(entry.obj.GUID)
|
entry.zone.Ground ! Zone.Ground.RemoveItem(entry.obj.GUID)
|
||||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.galaxy
|
package services.galaxy
|
||||||
|
|
||||||
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
|
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||||
|
|
||||||
object GalaxyAction {
|
object GalaxyAction {
|
||||||
trait Action
|
trait Action
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.galaxy
|
package services.galaxy
|
||||||
|
|
||||||
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
|
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||||
|
|
||||||
object GalaxyResponse {
|
object GalaxyResponse {
|
||||||
trait Response
|
trait Response
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.local
|
package services.local
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.doors.Door
|
import net.psforever.objects.serverobject.doors.Door
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
import net.psforever.packet.game._
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
object LocalAction {
|
object LocalAction {
|
||||||
trait Action
|
trait Action
|
||||||
|
|
||||||
|
final case class AlertDestroyDeployable(player_guid : PlanetSideGUID, obj : PlanetSideGameObject with Deployable) extends Action
|
||||||
|
final case class DeployableMapIcon(player_guid : PlanetSideGUID, behavior : DeploymentAction.Value, deployInfo : DeployableInfo) extends Action
|
||||||
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
|
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
|
||||||
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
||||||
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||||
|
|
@ -19,6 +23,9 @@ object LocalAction {
|
||||||
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action
|
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action
|
||||||
final case class HackCaptureTerminal(player_guid : PlanetSideGUID, continent : Zone, target : CaptureTerminal, unk1 : Long, unk2 : Long = 8L, isResecured : Boolean) extends Action
|
final case class HackCaptureTerminal(player_guid : PlanetSideGUID, continent : Zone, target : CaptureTerminal, unk1 : Long, unk2 : Long = 8L, isResecured : Boolean) extends Action
|
||||||
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||||
|
final case class TriggerEffect(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID) extends Action
|
||||||
|
final case class TriggerEffectInfo(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID, unk1 : Boolean, unk2 : Long) extends Action
|
||||||
|
final case class TriggerEffectLocation(player_guid : PlanetSideGUID, effect : String, pos : Vector3, orient : Vector3) extends Action
|
||||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||||
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
|
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package services.local
|
package services.local
|
||||||
|
|
||||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.packet.game._
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
object LocalResponse {
|
object LocalResponse {
|
||||||
trait Response
|
trait Response
|
||||||
|
|
||||||
|
final case class AlertDestroyDeployable(obj : PlanetSideGameObject with Deployable) extends Response
|
||||||
|
final case class DeployableMapIcon(action : DeploymentAction.Value, deployInfo : DeployableInfo) extends Response
|
||||||
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
|
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
|
||||||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||||
|
final case class EliminateDeployable(obj : PlanetSideGameObject with Deployable, object_guid : PlanetSideGUID, pos : Vector3) extends Response
|
||||||
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||||
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||||
final case class HackCaptureTerminal(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long, isResecured: Boolean) extends Response
|
final case class HackCaptureTerminal(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long, isResecured: Boolean) extends Response
|
||||||
|
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||||
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
||||||
|
final case class TriggerEffect(target: PlanetSideGUID, effect: String, effectInfo: Option[TriggeredEffect] = None, triggeredLocation: Option[TriggeredEffectLocation] = None) extends Response
|
||||||
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
||||||
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
|
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,30 @@
|
||||||
package services.local
|
package services.local
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||||
|
import net.psforever.objects.serverobject.structures.Building
|
||||||
|
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||||
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
||||||
import net.psforever.objects.zones.InterstellarCluster.GetWorld
|
import net.psforever.objects.{BoomerDeployable, GlobalDefinitions, PlanetSideGameObject, TurretDeployable}
|
||||||
import services.local.support.{DoorCloseActor, HackCaptureActor, HackClearActor}
|
import net.psforever.packet.game.{PlanetSideGUID, TriggeredEffect, TriggeredEffectLocation}
|
||||||
|
import net.psforever.objects.vital.Vitality
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import services.local.support.{DeployableRemover, DoorCloseActor, HackClearActor, HackCaptureActor}
|
||||||
|
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
import services.{GenericEventBus, Service, ServiceManager}
|
import services.{GenericEventBus, Service, ServiceManager}
|
||||||
import services.local.support.{DoorCloseActor, HackClearActor}
|
|
||||||
|
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import net.psforever.objects.GlobalDefinitions
|
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
|
||||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
|
||||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
|
||||||
import services.ServiceManager.Lookup
|
import services.ServiceManager.Lookup
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
class LocalService extends Actor {
|
class LocalService extends Actor {
|
||||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||||
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
||||||
private val hackCapturer = context.actorOf(Props[HackCaptureActor], "local-hack-capturer")
|
private val hackCapturer = context.actorOf(Props[HackCaptureActor], "local-hack-capturer")
|
||||||
|
private val engineer = context.actorOf(Props[DeployableRemover], "deployable-remover-agent")
|
||||||
private [this] val log = org.log4s.getLogger
|
private [this] val log = org.log4s.getLogger
|
||||||
var cluster : ActorRef = Actor.noSender
|
var cluster : ActorRef = Actor.noSender
|
||||||
|
|
||||||
|
|
@ -59,6 +61,14 @@ class LocalService extends Actor {
|
||||||
|
|
||||||
case LocalServiceMessage(forChannel, action) =>
|
case LocalServiceMessage(forChannel, action) =>
|
||||||
action match {
|
action match {
|
||||||
|
case LocalAction.AlertDestroyDeployable(_, obj) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.AlertDestroyDeployable(obj))
|
||||||
|
)
|
||||||
|
case LocalAction.DeployableMapIcon(player_guid, behavior, deployInfo) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DeployableMapIcon(behavior, deployInfo))
|
||||||
|
)
|
||||||
case LocalAction.DoorOpens(player_guid, zone, door) =>
|
case LocalAction.DoorOpens(player_guid, zone, door) =>
|
||||||
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
|
|
@ -77,7 +87,7 @@ class LocalService extends Actor {
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
||||||
)
|
)
|
||||||
case LocalAction.ClearTemporaryHack(player_guid, target) =>
|
case LocalAction.ClearTemporaryHack(_, target) =>
|
||||||
hackClearer ! HackClearActor.ObjectIsResecured(target)
|
hackClearer ! HackClearActor.ObjectIsResecured(target)
|
||||||
case LocalAction.HackCaptureTerminal(player_guid, zone, target, unk1, unk2, isResecured) =>
|
case LocalAction.HackCaptureTerminal(player_guid, zone, target, unk1, unk2, isResecured) =>
|
||||||
|
|
||||||
|
|
@ -101,14 +111,26 @@ class LocalService extends Actor {
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
|
||||||
)
|
)
|
||||||
|
case LocalAction.SetEmpire(object_guid, empire) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.SetEmpire(object_guid, empire))
|
||||||
|
)
|
||||||
|
case LocalAction.TriggerEffect(player_guid, effect, target) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerEffect(target, effect))
|
||||||
|
)
|
||||||
|
case LocalAction.TriggerEffectLocation(player_guid, effect, pos, orient) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerEffect(PlanetSideGUID(0), effect, None, Some(TriggeredEffectLocation(pos, orient))))
|
||||||
|
)
|
||||||
|
case LocalAction.TriggerEffectInfo(player_guid, effect, target, unk1, unk2) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerEffect(target, effect, Some(TriggeredEffect(unk1, unk2))))
|
||||||
|
)
|
||||||
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
|
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
||||||
)
|
)
|
||||||
case LocalAction.SetEmpire(object_guid, empire) =>
|
|
||||||
LocalEvents.publish(
|
|
||||||
LocalServiceResponse(s"/$forChannel/Local", PlanetSideGUID(-1), LocalResponse.SetEmpire(object_guid, empire))
|
|
||||||
)
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,21 +142,21 @@ class LocalService extends Actor {
|
||||||
|
|
||||||
//response from HackClearActor
|
//response from HackClearActor
|
||||||
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
|
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
|
||||||
log.warn(s"Clearing hack for ${target_guid}")
|
log.warn(s"Clearing hack for $target_guid")
|
||||||
LocalEvents.publish(
|
LocalEvents.publish(
|
||||||
LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.HackClear(target_guid, unk1, unk2))
|
LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.HackClear(target_guid, unk1, unk2))
|
||||||
)
|
)
|
||||||
|
|
||||||
case HackCaptureActor.HackTimeoutReached(capture_terminal_guid, zone_id, unk1, unk2, hackedByFaction) =>
|
case HackCaptureActor.HackTimeoutReached(capture_terminal_guid, zone_id, _, _, hackedByFaction) =>
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
ask(cluster, InterstellarCluster.GetWorld(zone_id))(1 seconds).onComplete {
|
ask(cluster, InterstellarCluster.GetWorld(zone_id))(1 seconds).onComplete {
|
||||||
case Success(InterstellarCluster.GiveWorld(zoneId, zone)) =>
|
case Success(InterstellarCluster.GiveWorld(_, zone)) =>
|
||||||
val terminal = zone.asInstanceOf[Zone].GUID(capture_terminal_guid).get.asInstanceOf[CaptureTerminal]
|
val terminal = zone.asInstanceOf[Zone].GUID(capture_terminal_guid).get.asInstanceOf[CaptureTerminal]
|
||||||
val building = terminal.Owner.asInstanceOf[Building]
|
val building = terminal.Owner.asInstanceOf[Building]
|
||||||
|
|
||||||
// todo: Move this to a function for Building
|
// todo: Move this to a function for Building
|
||||||
var ntuLevel = 0
|
var ntuLevel = 0
|
||||||
building.Amenities.filter(x => (x.Definition == GlobalDefinitions.resource_silo)).headOption.asInstanceOf[Option[ResourceSilo]] match {
|
building.Amenities.find(_.Definition == GlobalDefinitions.resource_silo).asInstanceOf[Option[ResourceSilo]] match {
|
||||||
case Some(obj: ResourceSilo) =>
|
case Some(obj: ResourceSilo) =>
|
||||||
ntuLevel = obj.CapacitorDisplay.toInt
|
ntuLevel = obj.CapacitorDisplay.toInt
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -143,7 +165,7 @@ class LocalService extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ntuLevel > 0) {
|
if(ntuLevel > 0) {
|
||||||
log.info(s"Setting base ${building.ModelId} as owned by ${hackedByFaction}")
|
log.info(s"Setting base ${building.ModelId} as owned by $hackedByFaction")
|
||||||
|
|
||||||
building.Faction = hackedByFaction
|
building.Faction = hackedByFaction
|
||||||
self ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(PlanetSideGUID(building.ModelId), hackedByFaction))
|
self ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(PlanetSideGUID(building.ModelId), hackedByFaction))
|
||||||
|
|
@ -160,9 +182,86 @@ class LocalService extends Actor {
|
||||||
|
|
||||||
case scala.util.Failure(_) => log.warn(s"LocalService Failed to get zone when hack timeout was reached")
|
case scala.util.Failure(_) => log.warn(s"LocalService Failed to get zone when hack timeout was reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) =>
|
case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) =>
|
||||||
hackCapturer forward HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid)
|
hackCapturer forward HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid)
|
||||||
|
|
||||||
|
//message to Engineer
|
||||||
|
case LocalServiceMessage.Deployables(msg) =>
|
||||||
|
engineer forward msg
|
||||||
|
|
||||||
|
//message(s) from Engineer
|
||||||
|
case msg @ DeployableRemover.EliminateDeployable(obj : TurretDeployable, guid, pos, zone) =>
|
||||||
|
val seats = obj.Seats.values
|
||||||
|
if(seats.count(_.isOccupied) > 0) {
|
||||||
|
val wasKickedByDriver = false //TODO yeah, I don't know
|
||||||
|
seats.foreach(seat => {
|
||||||
|
seat.Occupant match {
|
||||||
|
case Some(tplayer) =>
|
||||||
|
seat.Occupant = None
|
||||||
|
tplayer.VehicleSeated = None
|
||||||
|
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.KickPassenger(tplayer.GUID, 4, wasKickedByDriver, obj.GUID))
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
context.system.scheduler.scheduleOnce(Duration.create(2, "seconds"), self, msg)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EliminateDeployable(obj, guid, pos, zone.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
case DeployableRemover.EliminateDeployable(obj : BoomerDeployable, guid, pos, zone) =>
|
||||||
|
EliminateDeployable(obj, guid, pos, zone.Id)
|
||||||
|
obj.Trigger match {
|
||||||
|
case Some(trigger) =>
|
||||||
|
log.warn(s"LocalService: deconstructing boomer in ${zone.Id}, but trigger@${trigger.GUID.guid} still exists")
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DeployableRemover.EliminateDeployable(obj, guid, pos, zone) =>
|
||||||
|
EliminateDeployable(obj, guid, pos, zone.Id)
|
||||||
|
|
||||||
|
case DeployableRemover.DeleteTrigger(trigger_guid, zone) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/${zone.Id}/Local", Service.defaultPlayerGUID, LocalResponse.ObjectDelete(trigger_guid, 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
//synchronized damage calculations
|
||||||
|
case Vitality.DamageOn(target : Deployable, func) =>
|
||||||
|
func(target)
|
||||||
|
sender ! Vitality.DamageResolution(target)
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.info(s"Unhandled message $msg from $sender")
|
log.warn(s"Unhandled message $msg from $sender")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common behavior for distributing information about a deployable's destruction or deconstruction.<br>
|
||||||
|
* <br>
|
||||||
|
* The primary distribution task instructs all clients to eliminate the target deployable.
|
||||||
|
* This is a cosmetic exercise as the deployable should already be unregistered from its zone and
|
||||||
|
* functionally removed from its zone's list of deployable objects by external operations.
|
||||||
|
* The other distribution is a targeted message sent to the former owner of the deployable
|
||||||
|
* if he still exists on the server
|
||||||
|
* to clean up any leftover ownership-specific knowledge about the deployable.
|
||||||
|
* @see `DeployableRemover`
|
||||||
|
* @param obj the deployable object
|
||||||
|
* @param guid the deployable objects globally unique identifier;
|
||||||
|
* may be a former identifier
|
||||||
|
* @param position the deployable's position
|
||||||
|
* @param zoneId the zone where the deployable is currently placed
|
||||||
|
*/
|
||||||
|
def EliminateDeployable(obj : PlanetSideGameObject with Deployable, guid : PlanetSideGUID, position : Vector3, zoneId : String) : Unit = {
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$zoneId/Local", Service.defaultPlayerGUID, LocalResponse.EliminateDeployable(obj, guid, position))
|
||||||
|
)
|
||||||
|
obj.OwnerName match {
|
||||||
|
case Some(name) =>
|
||||||
|
LocalEvents.publish(
|
||||||
|
LocalServiceResponse(s"/$name/Local", Service.defaultPlayerGUID, LocalResponse.AlertDestroyDeployable(obj))
|
||||||
|
)
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,7 @@
|
||||||
package services.local
|
package services.local
|
||||||
|
|
||||||
final case class LocalServiceMessage(forChannel : String, actionMessage : LocalAction.Action)
|
final case class LocalServiceMessage(forChannel : String, actionMessage : LocalAction.Action)
|
||||||
|
|
||||||
|
object LocalServiceMessage {
|
||||||
|
final case class Deployables(msg : Any)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package services.local.support
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
import net.psforever.objects.zones.Zone
|
||||||
|
import net.psforever.objects.{BoomerDeployable, PlanetSideGameObject, TurretDeployable}
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import services.RemoverActor
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class DeployableRemover extends RemoverActor {
|
||||||
|
final val FirstStandardDuration : FiniteDuration = 3 minutes
|
||||||
|
|
||||||
|
final val SecondStandardDuration : FiniteDuration = 2 seconds
|
||||||
|
|
||||||
|
def InclusionTest(entry : RemoverActor.Entry) : Boolean = entry.obj.isInstanceOf[Deployable]
|
||||||
|
|
||||||
|
def InitialJob(entry : RemoverActor.Entry) : Unit = { }
|
||||||
|
|
||||||
|
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||||
|
val obj = entry.obj
|
||||||
|
obj match {
|
||||||
|
case boomer : BoomerDeployable =>
|
||||||
|
FirstJobBoomer(boomer, entry)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
entry.zone.Deployables ! Zone.Deployable.Dismiss(obj.asInstanceOf[PlanetSideGameObject with Deployable])
|
||||||
|
}
|
||||||
|
|
||||||
|
def FirstJobBoomer(obj : BoomerDeployable, entry : RemoverActor.Entry) : Unit = {
|
||||||
|
obj.Trigger match {
|
||||||
|
case Some(trigger) =>
|
||||||
|
if(trigger.HasGUID) {
|
||||||
|
val guid = trigger.GUID
|
||||||
|
Zone.EquipmentIs.Where(trigger, guid, entry.zone) match {
|
||||||
|
case Some(Zone.EquipmentIs.InContainer(container, index)) =>
|
||||||
|
container.Slot(index).Equipment = None
|
||||||
|
case Some(Zone.EquipmentIs.OnGround()) =>
|
||||||
|
entry.zone.Ground ! Zone.Ground.RemoveItem(guid)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
context.parent ! DeployableRemover.DeleteTrigger(guid, entry.zone)
|
||||||
|
}
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def SecondJob(entry : RemoverActor.Entry) : Unit = {
|
||||||
|
val obj = entry.obj.asInstanceOf[PlanetSideGameObject with Deployable]
|
||||||
|
info(s"Deleting a ${obj.Definition.Name} deployable")
|
||||||
|
context.parent ! DeployableRemover.EliminateDeployable(obj, obj.GUID, obj.Position, entry.zone)
|
||||||
|
super.SecondJob(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = !entry.zone.DeployableList.contains(entry.obj)
|
||||||
|
|
||||||
|
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
|
||||||
|
entry.obj match {
|
||||||
|
case turret : TurretDeployable =>
|
||||||
|
GUIDTask.UnregisterDeployableTurret(turret)(entry.zone.GUID)
|
||||||
|
case boomer : BoomerDeployable =>
|
||||||
|
boomer.Trigger match {
|
||||||
|
case Some(trigger) =>
|
||||||
|
boomer.Trigger = None
|
||||||
|
taskResolver ! GUIDTask.UnregisterObjectTask(trigger)(entry.zone.GUID)
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
GUIDTask.UnregisterObjectTask(boomer)(entry.zone.GUID)
|
||||||
|
case obj =>
|
||||||
|
GUIDTask.UnregisterObjectTask(obj)(entry.zone.GUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableRemover {
|
||||||
|
final case class EliminateDeployable(obj : PlanetSideGameObject with Deployable,
|
||||||
|
guid : PlanetSideGUID,
|
||||||
|
position : Vector3,
|
||||||
|
zone : Zone)
|
||||||
|
|
||||||
|
final case class DeleteTrigger(trigger_guid : PlanetSideGUID,
|
||||||
|
zone : Zone)
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ package services.vehicle.support
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||||
import net.psforever.objects.{AmmoBox, DefaultCancellable, PlanetSideGameObject, Tool}
|
import net.psforever.objects.{AmmoBox, DefaultCancellable, PlanetSideGameObject, Tool}
|
||||||
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
||||||
import net.psforever.objects.serverobject.turret.{MannedTurret, TurretUpgrade}
|
import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade}
|
||||||
import net.psforever.objects.vehicles.MountedWeapons
|
import net.psforever.objects.vehicles.MountedWeapons
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
@ -50,7 +50,7 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
|
||||||
|
|
||||||
def CreateEntry(obj : PlanetSideGameObject, zone : Zone, upgrade : TurretUpgrade.Value, duration : Long) = TurretUpgrader.Entry(obj, zone, upgrade, duration)
|
def CreateEntry(obj : PlanetSideGameObject, zone : Zone, upgrade : TurretUpgrade.Value, duration : Long) = TurretUpgrader.Entry(obj, zone, upgrade, duration)
|
||||||
|
|
||||||
def InclusionTest(entry : TurretUpgrader.Entry) : Boolean = entry.obj.isInstanceOf[MannedTurret]
|
def InclusionTest(entry : TurretUpgrader.Entry) : Boolean = entry.obj.isInstanceOf[FacilityTurret]
|
||||||
|
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case Service.Startup() =>
|
case Service.Startup() =>
|
||||||
|
|
@ -157,7 +157,7 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
|
||||||
* @param entry na
|
* @param entry na
|
||||||
*/
|
*/
|
||||||
def UpgradeTurretAmmo(entry : TurretUpgrader.Entry) : Unit = {
|
def UpgradeTurretAmmo(entry : TurretUpgrader.Entry) : Unit = {
|
||||||
val target = entry.obj.asInstanceOf[MannedTurret]
|
val target = entry.obj.asInstanceOf[FacilityTurret]
|
||||||
val zone = entry.zone
|
val zone = entry.zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.Id
|
||||||
val upgrade = entry.upgrade
|
val upgrade = entry.upgrade
|
||||||
|
|
@ -222,7 +222,7 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
|
||||||
* @param entry na
|
* @param entry na
|
||||||
*/
|
*/
|
||||||
def FinishUpgradingTurret(entry : TurretUpgrader.Entry)() : Unit = {
|
def FinishUpgradingTurret(entry : TurretUpgrader.Entry)() : Unit = {
|
||||||
val target = entry.obj.asInstanceOf[MannedTurret]
|
val target = entry.obj.asInstanceOf[FacilityTurret]
|
||||||
val zone = entry.zone
|
val zone = entry.zone
|
||||||
info(s"Wall turret finished ${target.Upgrade} upgrade")
|
info(s"Wall turret finished ${target.Upgrade} upgrade")
|
||||||
val targetGUID = target.GUID
|
val targetGUID = target.GUID
|
||||||
|
|
@ -250,7 +250,7 @@ object TurretUpgrader extends SupportActorCaseConversions {
|
||||||
*/
|
*/
|
||||||
case class Entry(_obj : PlanetSideGameObject, _zone : Zone, upgrade : TurretUpgrade.Value, _duration : Long) extends SupportActor.Entry(_obj, _zone, _duration)
|
case class Entry(_obj : PlanetSideGameObject, _zone : Zone, upgrade : TurretUpgrade.Value, _duration : Long) extends SupportActor.Entry(_obj, _zone, _duration)
|
||||||
|
|
||||||
final case class AddTask(turret : MannedTurret, zone : Zone, upgrade : TurretUpgrade.Value, duration : Option[FiniteDuration] = None)
|
final case class AddTask(turret : FacilityTurret, zone : Zone, upgrade : TurretUpgrade.Value, duration : Option[FiniteDuration] = None)
|
||||||
|
|
||||||
final case class Downgrade()
|
final case class Downgrade()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,14 @@ class Vector3Test extends Specification {
|
||||||
vec.z mustEqual 3.9f
|
vec.z mustEqual 3.9f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"isolate x,y components" in {
|
||||||
|
vec.xy mustEqual Vector3(1.3f, -2.6f, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
"promote float values into a specific z-format" in {
|
||||||
|
Vector3.z(3.9f) mustEqual Vector3(0, 0, 3.9f)
|
||||||
|
}
|
||||||
|
|
||||||
"calculate magnitude (like a vector) 1" in {
|
"calculate magnitude (like a vector) 1" in {
|
||||||
val obj = Vector3(2.0f, 0.0f, 0.0f)
|
val obj = Vector3(2.0f, 0.0f, 0.0f)
|
||||||
Vector3.Magnitude(obj) mustEqual 2.0f
|
Vector3.Magnitude(obj) mustEqual 2.0f
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,11 @@ class DeployObjectMessageTest extends Specification {
|
||||||
|
|
||||||
"decode" in {
|
"decode" in {
|
||||||
PacketCoding.DecodePacket(string).require match {
|
PacketCoding.DecodePacket(string).require match {
|
||||||
case DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
|
case DeployObjectMessage(guid, unk1, pos, orient, unk2) =>
|
||||||
guid mustEqual PlanetSideGUID(2932)
|
guid mustEqual PlanetSideGUID(2932)
|
||||||
unk1 mustEqual 1000L
|
unk1 mustEqual 1000L
|
||||||
pos.x mustEqual 5769.297f
|
pos mustEqual Vector3(5769.297f, 3192.8594f, 97.96875f)
|
||||||
pos.y mustEqual 3192.8594f
|
orient mustEqual Vector3.z(272.8125f)
|
||||||
pos.z mustEqual 97.96875f
|
|
||||||
roll mustEqual 0
|
|
||||||
pitch mustEqual 0
|
|
||||||
yaw mustEqual 63
|
|
||||||
unk2 mustEqual 1L
|
unk2 mustEqual 1L
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
|
|
@ -28,7 +24,7 @@ class DeployObjectMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val msg = DeployObjectMessage(PlanetSideGUID(2932), 1000L, Vector3(5769.297f, 3192.8594f, 97.96875f), 0, 0, 63, 1L)
|
val msg = DeployObjectMessage(PlanetSideGUID(2932), 1000L, Vector3(5769.297f, 3192.8594f, 97.96875f), Vector3.z(272.8125f), 1L)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string
|
pkt mustEqual string
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ class ObjectDeployedMessageTest extends Specification {
|
||||||
|
|
||||||
"decode" in {
|
"decode" in {
|
||||||
PacketCoding.DecodePacket(string_boomer).require match {
|
PacketCoding.DecodePacket(string_boomer).require match {
|
||||||
case ObjectDeployedMessage(unk : Int, desc : String, act : DeploymentOutcome.Value, count : Long, max : Long) =>
|
case ObjectDeployedMessage(unk : Int, desc : String, act : DeployOutcome.Value, count : Long, max : Long) =>
|
||||||
unk mustEqual 0
|
unk mustEqual 0
|
||||||
desc mustEqual "boomer"
|
desc mustEqual "boomer"
|
||||||
act mustEqual DeploymentOutcome.Success
|
act mustEqual DeployOutcome.Success
|
||||||
count mustEqual 1
|
count mustEqual 1
|
||||||
max mustEqual 25
|
max mustEqual 25
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -23,7 +23,7 @@ class ObjectDeployedMessageTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val msg = ObjectDeployedMessage("boomer", DeploymentOutcome.Success, 1, 25)
|
val msg = ObjectDeployedMessage("boomer", DeployOutcome.Success, 1, 25)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string_boomer
|
pkt mustEqual string_boomer
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import scodec.bits._
|
||||||
class TriggerEffectMessageTest extends Specification {
|
class TriggerEffectMessageTest extends Specification {
|
||||||
val string_motionalarmsensor = hex"51 970B 82 6F6E FA00C00000"
|
val string_motionalarmsensor = hex"51 970B 82 6F6E FA00C00000"
|
||||||
val string_boomer = hex"51 0000 93 737061776E5F6F626A6563745F656666656374 417BB2CB3B4F8E00000000"
|
val string_boomer = hex"51 0000 93 737061776E5F6F626A6563745F656666656374 417BB2CB3B4F8E00000000"
|
||||||
|
val string_boomer_explode = hex"51 DF09 8F 6465746F6E6174655F626F6F6D6572 00"
|
||||||
|
|
||||||
"decode (motion alarm sensor)" in {
|
"decode (motion alarm sensor)" in {
|
||||||
PacketCoding.DecodePacket(string_motionalarmsensor).require match {
|
PacketCoding.DecodePacket(string_motionalarmsensor).require match {
|
||||||
|
|
@ -32,42 +33,44 @@ class TriggerEffectMessageTest extends Specification {
|
||||||
effect mustEqual "spawn_object_effect"
|
effect mustEqual "spawn_object_effect"
|
||||||
unk.isDefined mustEqual false
|
unk.isDefined mustEqual false
|
||||||
location.isDefined mustEqual true
|
location.isDefined mustEqual true
|
||||||
location.get.pos.x mustEqual 3567.0156f
|
location.get.pos mustEqual Vector3(3567.0156f, 3278.6953f, 114.484375f)
|
||||||
location.get.pos.y mustEqual 3278.6953f
|
location.get.orient mustEqual Vector3(0, 0, 90)
|
||||||
location.get.pos.z mustEqual 114.484375f
|
case _ =>
|
||||||
location.get.roll mustEqual 0
|
ko
|
||||||
location.get.pitch mustEqual 0
|
}
|
||||||
location.get.yaw mustEqual 0
|
}
|
||||||
|
|
||||||
|
"decode (boomer explode)" in {
|
||||||
|
PacketCoding.DecodePacket(string_boomer_explode).require match {
|
||||||
|
case TriggerEffectMessage(guid, effect, unk, location) =>
|
||||||
|
guid mustEqual PlanetSideGUID(2527)
|
||||||
|
effect mustEqual "detonate_boomer"
|
||||||
|
unk.isDefined mustEqual false
|
||||||
|
location.isDefined mustEqual false
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (motion alarm sensor)" in {
|
"encode (motion alarm sensor)" in {
|
||||||
val msg = TriggerEffectMessage(
|
val msg = TriggerEffectMessage(PlanetSideGUID(2967), "on", true, 1000L)
|
||||||
PlanetSideGUID(2967),
|
|
||||||
"on",
|
|
||||||
Some(TriggeredEffect(true, 1000L)),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string_motionalarmsensor
|
pkt mustEqual string_motionalarmsensor
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (boomer)" in {
|
"encode (boomer)" in {
|
||||||
val msg = TriggerEffectMessage(
|
val msg = TriggerEffectMessage("spawn_object_effect", Vector3(3567.0156f, 3278.6953f, 114.484375f), Vector3(0, 0, 90))
|
||||||
PlanetSideGUID(0),
|
|
||||||
"spawn_object_effect",
|
|
||||||
None,
|
|
||||||
Some(TriggeredEffectLocation(
|
|
||||||
Vector3(3567.0156f, 3278.6953f, 114.484375f),
|
|
||||||
0, 0, 0
|
|
||||||
))
|
|
||||||
)
|
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string_boomer
|
pkt mustEqual string_boomer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"encode (boomer explode)" in {
|
||||||
|
val msg = TriggerEffectMessage(PlanetSideGUID(2527), "detonate_boomer")
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_boomer_explode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -22,12 +22,8 @@ class AegisShieldGeneratorDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[AegisShieldGeneratorData] mustEqual true
|
data.get.isInstanceOf[AegisShieldGeneratorData] mustEqual true
|
||||||
val aegis = data.get.asInstanceOf[AegisShieldGeneratorData]
|
val aegis = data.get.asInstanceOf[AegisShieldGeneratorData]
|
||||||
aegis.deploy.pos.coord.x mustEqual 3571.2266f
|
aegis.deploy.pos.coord mustEqual Vector3(3571.2266f, 3278.0938f, 114.0f)
|
||||||
aegis.deploy.pos.coord.y mustEqual 3278.0938f
|
aegis.deploy.pos.orient mustEqual Vector3(0, 0, 90)
|
||||||
aegis.deploy.pos.coord.z mustEqual 114.0f
|
|
||||||
aegis.deploy.pos.orient.x mustEqual 0f
|
|
||||||
aegis.deploy.pos.orient.y mustEqual 0f
|
|
||||||
aegis.deploy.pos.orient.z mustEqual 90.0f
|
|
||||||
aegis.deploy.faction mustEqual PlanetSideEmpire.VS
|
aegis.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||||
aegis.deploy.unk mustEqual 2
|
aegis.deploy.unk mustEqual 2
|
||||||
aegis.health mustEqual 255
|
aegis.health mustEqual 255
|
||||||
|
|
@ -40,7 +36,7 @@ class AegisShieldGeneratorDataTest extends Specification {
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val obj = AegisShieldGeneratorData(
|
val obj = AegisShieldGeneratorData(
|
||||||
CommonFieldData(
|
CommonFieldData(
|
||||||
PlacementData(3571.2266f, 3278.0938f, 114.0f, 0f, 0f, 90.0f),
|
PlacementData(Vector3(3571.2266f, 3278.0938f, 114.0f), Vector3(0, 0, 90)),
|
||||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(2366)
|
PlanetSideEmpire.VS, 2, PlanetSideGUID(2366)
|
||||||
),
|
),
|
||||||
255
|
255
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -22,23 +22,19 @@ class OneMannedFieldTurretDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[OneMannedFieldTurretData] mustEqual true
|
data.get.isInstanceOf[OneMannedFieldTurretData] mustEqual true
|
||||||
val omft = data.get.asInstanceOf[OneMannedFieldTurretData]
|
val omft = data.get.asInstanceOf[OneMannedFieldTurretData]
|
||||||
omft.deploy.pos.coord.x mustEqual 3567.1406f
|
omft.deploy.pos.coord mustEqual Vector3(3567.1406f, 2988.0078f, 71.84375f)
|
||||||
omft.deploy.pos.coord.y mustEqual 2988.0078f
|
omft.deploy.pos.orient mustEqual Vector3(0, 0, 185.625f)
|
||||||
omft.deploy.pos.coord.z mustEqual 71.84375f
|
|
||||||
omft.deploy.pos.orient.x mustEqual 0f
|
|
||||||
omft.deploy.pos.orient.y mustEqual 0f
|
|
||||||
omft.deploy.pos.orient.z mustEqual 185.625f
|
|
||||||
omft.deploy.faction mustEqual PlanetSideEmpire.VS
|
omft.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||||
omft.deploy.unk mustEqual 2
|
omft.deploy.unk1 mustEqual 2
|
||||||
omft.deploy.player_guid mustEqual PlanetSideGUID(2502)
|
omft.deploy.owner_guid mustEqual PlanetSideGUID(2502)
|
||||||
omft.health mustEqual 255
|
omft.health mustEqual 255
|
||||||
omft.internals.isDefined mustEqual true
|
omft.internals.isDefined mustEqual true
|
||||||
val internals = omft.internals.get
|
val internals = omft.internals.get.contents
|
||||||
internals.objectClass mustEqual ObjectClass.energy_gun_vs
|
internals.head.objectClass mustEqual ObjectClass.energy_gun_vs
|
||||||
internals.guid mustEqual PlanetSideGUID(2615)
|
internals.head.guid mustEqual PlanetSideGUID(2615)
|
||||||
internals.parentSlot mustEqual 1
|
internals.head.parentSlot mustEqual 1
|
||||||
internals.obj.isInstanceOf[WeaponData] mustEqual true
|
internals.head.obj.isInstanceOf[WeaponData] mustEqual true
|
||||||
val wep = internals.obj.asInstanceOf[WeaponData]
|
val wep = internals.head.obj.asInstanceOf[WeaponData]
|
||||||
wep.unk1 mustEqual 0x6
|
wep.unk1 mustEqual 0x6
|
||||||
wep.unk2 mustEqual 0x8
|
wep.unk2 mustEqual 0x8
|
||||||
wep.fire_mode mustEqual 0
|
wep.fire_mode mustEqual 0
|
||||||
|
|
@ -55,20 +51,16 @@ class OneMannedFieldTurretDataTest extends Specification {
|
||||||
|
|
||||||
"encode (orion)" in {
|
"encode (orion)" in {
|
||||||
val obj = OneMannedFieldTurretData(
|
val obj = OneMannedFieldTurretData(
|
||||||
CommonFieldData(
|
SmallDeployableData(
|
||||||
PlacementData(3567.1406f, 2988.0078f, 71.84375f, 0f, 0f, 185.625f),
|
PlacementData(Vector3(3567.1406f, 2988.0078f, 71.84375f), Vector3(0, 0, 185.625f)),
|
||||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(2502)
|
PlanetSideEmpire.VS, false, false, 2, false, false, PlanetSideGUID(2502)
|
||||||
),
|
),
|
||||||
255,
|
255,
|
||||||
OneMannedFieldTurretData.orion(PlanetSideGUID(2615), 0x6, 0x8, PlanetSideGUID(2510), 8)
|
InventoryData(List(OneMannedFieldTurretData.orion(PlanetSideGUID(2615), 0x6, 0x8, PlanetSideGUID(2510), 8)))
|
||||||
)
|
)
|
||||||
val msg = ObjectCreateMessage(ObjectClass.portable_manned_turret_vs, PlanetSideGUID(2916), obj)
|
val msg = ObjectCreateMessage(ObjectClass.portable_manned_turret_vs, PlanetSideGUID(2916), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
val pkt_bitv = pkt.toBitVector
|
pkt mustEqual string_orion
|
||||||
val ori_bitv = string_orion.toBitVector
|
|
||||||
pkt_bitv.take(189) mustEqual ori_bitv.take(189)
|
|
||||||
pkt_bitv.drop(200) mustEqual ori_bitv.drop(200)
|
|
||||||
//TODO work on OneMannedFieldTurretData to make this pass as a single stream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"avenger" in {
|
"avenger" in {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -22,14 +22,14 @@ class SmallDeployableDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[SmallDeployableData] mustEqual true
|
data.get.isInstanceOf[SmallDeployableData] mustEqual true
|
||||||
val boomer = data.get.asInstanceOf[SmallDeployableData]
|
val boomer = data.get.asInstanceOf[SmallDeployableData]
|
||||||
boomer.deploy.pos.coord.x mustEqual 4704.172f
|
boomer.pos.coord.x mustEqual 4704.172f
|
||||||
boomer.deploy.pos.coord.y mustEqual 5546.4375f
|
boomer.pos.coord.y mustEqual 5546.4375f
|
||||||
boomer.deploy.pos.coord.z mustEqual 82.234375f
|
boomer.pos.coord.z mustEqual 82.234375f
|
||||||
boomer.deploy.pos.orient.x mustEqual 0f
|
boomer.pos.orient.x mustEqual 0f
|
||||||
boomer.deploy.pos.orient.y mustEqual 0f
|
boomer.pos.orient.y mustEqual 0f
|
||||||
boomer.deploy.pos.orient.z mustEqual 272.8125f
|
boomer.pos.orient.z mustEqual 272.8125f
|
||||||
boomer.deploy.unk mustEqual 0
|
boomer.unk1 mustEqual 0
|
||||||
boomer.deploy.player_guid mustEqual PlanetSideGUID(4145)
|
boomer.owner_guid mustEqual PlanetSideGUID(8290)
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -37,10 +37,8 @@ class SmallDeployableDataTest extends Specification {
|
||||||
|
|
||||||
"encode (boomer)" in {
|
"encode (boomer)" in {
|
||||||
val obj = SmallDeployableData(
|
val obj = SmallDeployableData(
|
||||||
CommonFieldData(
|
PlacementData(Vector3(4704.172f, 5546.4375f, 82.234375f), Vector3.z(272.8125f)),
|
||||||
PlacementData(4704.172f, 5546.4375f, 82.234375f, 0f, 0f, 272.8125f),
|
PlanetSideEmpire.TR, false, false, 0, false, false, PlanetSideGUID(8290)
|
||||||
PlanetSideEmpire.TR, 0, PlanetSideGUID(4145)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
val msg = ObjectCreateMessage(ObjectClass.boomer, PlanetSideGUID(3840), obj)
|
val msg = ObjectCreateMessage(ObjectClass.boomer, PlanetSideGUID(3840), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package game.objectcreate
|
||||||
|
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate.{SmallDeployableData, _}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -23,16 +23,12 @@ class SmallTurretDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
||||||
val turret = data.get.asInstanceOf[SmallTurretData]
|
val turret = data.get.asInstanceOf[SmallTurretData]
|
||||||
turret.deploy.pos.coord.x mustEqual 4577.7812f
|
turret.deploy.pos.coord mustEqual Vector3(4577.7812f, 5624.828f, 72.046875f)
|
||||||
turret.deploy.pos.coord.y mustEqual 5624.828f
|
turret.deploy.pos.orient mustEqual Vector3(0, 2.8125f, 264.375f)
|
||||||
turret.deploy.pos.coord.z mustEqual 72.046875f
|
|
||||||
turret.deploy.pos.orient.x mustEqual 0f
|
|
||||||
turret.deploy.pos.orient.y mustEqual 2.8125f
|
|
||||||
turret.deploy.pos.orient.z mustEqual 264.375f
|
|
||||||
turret.deploy.faction mustEqual PlanetSideEmpire.NC
|
turret.deploy.faction mustEqual PlanetSideEmpire.NC
|
||||||
turret.deploy.destroyed mustEqual true
|
turret.deploy.destroyed mustEqual true
|
||||||
turret.deploy.unk mustEqual 2
|
turret.deploy.unk1 mustEqual 2
|
||||||
turret.deploy.player_guid mustEqual PlanetSideGUID(3871)
|
turret.deploy.owner_guid mustEqual PlanetSideGUID(7742)
|
||||||
turret.health mustEqual 0
|
turret.health mustEqual 0
|
||||||
turret.internals.isDefined mustEqual false
|
turret.internals.isDefined mustEqual false
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -50,23 +46,19 @@ class SmallTurretDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
||||||
val turret = data.get.asInstanceOf[SmallTurretData]
|
val turret = data.get.asInstanceOf[SmallTurretData]
|
||||||
turret.deploy.pos.coord.x mustEqual 4527.633f
|
turret.deploy.pos.coord mustEqual Vector3(4527.633f, 6271.3594f, 70.265625f)
|
||||||
turret.deploy.pos.coord.y mustEqual 6271.3594f
|
turret.deploy.pos.orient mustEqual Vector3(0, 0, 154.6875f)
|
||||||
turret.deploy.pos.coord.z mustEqual 70.265625f
|
|
||||||
turret.deploy.pos.orient.x mustEqual 0f
|
|
||||||
turret.deploy.pos.orient.y mustEqual 0f
|
|
||||||
turret.deploy.pos.orient.z mustEqual 154.6875f
|
|
||||||
turret.deploy.faction mustEqual PlanetSideEmpire.VS
|
turret.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||||
turret.deploy.unk mustEqual 2
|
turret.deploy.unk1 mustEqual 2
|
||||||
turret.deploy.player_guid mustEqual PlanetSideGUID(4232)
|
turret.deploy.owner_guid mustEqual PlanetSideGUID(8208)
|
||||||
turret.health mustEqual 255
|
turret.health mustEqual 255
|
||||||
turret.internals.isDefined mustEqual true
|
turret.internals.isDefined mustEqual true
|
||||||
val internals = turret.internals.get
|
val internals = turret.internals.get.contents
|
||||||
internals.objectClass mustEqual ObjectClass.spitfire_weapon
|
internals.head.objectClass mustEqual ObjectClass.spitfire_weapon
|
||||||
internals.guid mustEqual PlanetSideGUID(3064)
|
internals.head.guid mustEqual PlanetSideGUID(3064)
|
||||||
internals.parentSlot mustEqual 0
|
internals.head.parentSlot mustEqual 0
|
||||||
internals.obj.isInstanceOf[WeaponData] mustEqual true
|
internals.head.obj.isInstanceOf[WeaponData] mustEqual true
|
||||||
val wep = internals.obj.asInstanceOf[WeaponData]
|
val wep = internals.head.obj.asInstanceOf[WeaponData]
|
||||||
wep.unk1 mustEqual 0x6
|
wep.unk1 mustEqual 0x6
|
||||||
wep.unk2 mustEqual 0x8
|
wep.unk2 mustEqual 0x8
|
||||||
wep.fire_mode mustEqual 0
|
wep.fire_mode mustEqual 0
|
||||||
|
|
@ -83,37 +75,29 @@ class SmallTurretDataTest extends Specification {
|
||||||
|
|
||||||
"encode (spitfire, short)" in {
|
"encode (spitfire, short)" in {
|
||||||
val obj = SmallTurretData(
|
val obj = SmallTurretData(
|
||||||
CommonFieldData(
|
SmallDeployableData(
|
||||||
PlacementData(4577.7812f, 5624.828f, 72.046875f, 0f, 2.8125f, 264.375f),
|
PlacementData(Vector3(4577.7812f, 5624.828f, 72.046875f), Vector3(0, 2.8125f, 264.375f)),
|
||||||
PlanetSideEmpire.NC, true, 2, PlanetSideGUID(3871)
|
PlanetSideEmpire.NC, false, true, 2, false, false, PlanetSideGUID(7742)
|
||||||
),
|
),
|
||||||
255 //sets to 0
|
255 //sets to 0
|
||||||
)
|
)
|
||||||
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4208), obj)
|
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4208), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
val pkt_bitv = pkt.toBitVector
|
pkt mustEqual string_spitfire_short
|
||||||
val ori_bitv = string_spitfire_short.toBitVector
|
|
||||||
pkt_bitv.take(173) mustEqual ori_bitv.take(173)
|
|
||||||
pkt_bitv.drop(185) mustEqual ori_bitv.drop(185)
|
|
||||||
//TODO work on SmallTurretData to make this pass as a single stream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode (spitfire)" in {
|
"encode (spitfire)" in {
|
||||||
val obj = SmallTurretData(
|
val obj = SmallTurretData(
|
||||||
CommonFieldData(
|
SmallDeployableData(
|
||||||
PlacementData(4527.633f, 6271.3594f, 70.265625f, 0f, 0f, 154.6875f),
|
PlacementData(Vector3(4527.633f, 6271.3594f, 70.265625f), Vector3(0, 0, 154.6875f)),
|
||||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(4232)
|
PlanetSideEmpire.VS, false, false, 2, false, true, PlanetSideGUID(8208)
|
||||||
),
|
),
|
||||||
255,
|
255,
|
||||||
SmallTurretData.spitfire(PlanetSideGUID(3064), 0x6, 0x8, PlanetSideGUID(3694), 8)
|
InventoryData(List(SmallTurretData.spitfire(PlanetSideGUID(3064), 0x6, 0x8, PlanetSideGUID(3694), 8)))
|
||||||
)
|
)
|
||||||
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4265), obj)
|
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4265), obj)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
val pkt_bitv = pkt.toBitVector
|
pkt mustEqual string_spitfire
|
||||||
val ori_bitv = string_spitfire.toBitVector
|
|
||||||
pkt_bitv.take(173) mustEqual ori_bitv.take(173)
|
|
||||||
pkt_bitv.drop(185) mustEqual ori_bitv.drop(185)
|
|
||||||
//TODO work on SmallTurretData to make this pass as a single stream
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -22,16 +22,11 @@ class TRAPDataTest extends Specification {
|
||||||
data.isDefined mustEqual true
|
data.isDefined mustEqual true
|
||||||
data.get.isInstanceOf[TRAPData] mustEqual true
|
data.get.isInstanceOf[TRAPData] mustEqual true
|
||||||
val trap = data.get.asInstanceOf[TRAPData]
|
val trap = data.get.asInstanceOf[TRAPData]
|
||||||
trap.deploy.pos.coord.x mustEqual 3572.4453f
|
trap.deploy.pos.coord mustEqual Vector3(3572.4453f, 3277.9766f, 114.0f)
|
||||||
trap.deploy.pos.coord.y mustEqual 3277.9766f
|
trap.deploy.pos.orient mustEqual Vector3(0, 0, 90)
|
||||||
trap.deploy.pos.coord.z mustEqual 114.0f
|
|
||||||
trap.deploy.pos.orient.x mustEqual 0f
|
|
||||||
trap.deploy.pos.orient.y mustEqual 0f
|
|
||||||
trap.deploy.pos.orient.z mustEqual 90.0f
|
|
||||||
trap.deploy.faction mustEqual PlanetSideEmpire.VS
|
trap.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||||
trap.deploy.unk mustEqual 2
|
trap.deploy.owner_guid mustEqual PlanetSideGUID(4748)
|
||||||
trap.health mustEqual 255
|
trap.health mustEqual 255
|
||||||
trap.deploy.player_guid mustEqual PlanetSideGUID(2502)
|
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -39,9 +34,9 @@ class TRAPDataTest extends Specification {
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val obj = TRAPData(
|
val obj = TRAPData(
|
||||||
CommonFieldData(
|
SmallDeployableData(
|
||||||
PlacementData(3572.4453f, 3277.9766f, 114.0f, 0f, 0f, 90.0f),
|
PlacementData(3572.4453f, 3277.9766f, 114.0f, 0f, 0f, 90.0f),
|
||||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(2502)
|
PlanetSideEmpire.VS, false, false, 2, false, true, PlanetSideGUID(4748)
|
||||||
),
|
),
|
||||||
255
|
255
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import net.psforever.objects.definition.converter.{ACEConverter, CharacterSelectConverter, DestroyedVehicleConverter, REKConverter}
|
import net.psforever.objects.definition.converter.{CharacterSelectConverter, DestroyedVehicleConverter, REKConverter}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.objects.vehicles.DestroyedVehicle
|
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||||
|
|
@ -109,13 +107,7 @@ class ConverterTest extends Specification {
|
||||||
|
|
||||||
"ConstructionItem" should {
|
"ConstructionItem" should {
|
||||||
"convert to packet" in {
|
"convert to packet" in {
|
||||||
val cdef = ConstructionItemDefinition(Unit.advanced_ace)
|
val obj = ConstructionItem(GlobalDefinitions.ace)
|
||||||
cdef.Modes += DeployedItem.tank_traps
|
|
||||||
cdef.Modes += DeployedItem.portable_manned_turret_tr
|
|
||||||
cdef.Modes += DeployedItem.deployable_shield_generator
|
|
||||||
cdef.Tile = InventoryTile.Tile63
|
|
||||||
cdef.Packet = new ACEConverter()
|
|
||||||
val obj = ConstructionItem(cdef)
|
|
||||||
obj.GUID = PlanetSideGUID(90)
|
obj.GUID = PlanetSideGUID(90)
|
||||||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||||
case Success(pkt) =>
|
case Success(pkt) =>
|
||||||
|
|
@ -123,6 +115,7 @@ class ConverterTest extends Specification {
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Definition.Packet.ConstructorData(obj) match {
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
case Success(pkt) =>
|
case Success(pkt) =>
|
||||||
pkt mustEqual ACEData(0,0)
|
pkt mustEqual ACEData(0,0)
|
||||||
|
|
@ -154,6 +147,158 @@ class ConverterTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"BoomerTrigger" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new BoomerTrigger
|
||||||
|
obj.GUID = PlanetSideGUID(90)
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual DetailedBoomerTriggerData()
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual BoomerTriggerData()
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"SmallDeployable" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual SmallDeployableData(
|
||||||
|
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"SmallTurret" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.GUID = PlanetSideGUID(90)
|
||||||
|
obj.Weapons(1).Equipment.get.GUID = PlanetSideGUID(91)
|
||||||
|
obj.Weapons(1).Equipment.get.asInstanceOf[Tool].AmmoSlot.Box.GUID = PlanetSideGUID(92)
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual SmallTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
255,
|
||||||
|
InventoryData(
|
||||||
|
List(InternalSlot(ObjectClass.spitfire_weapon, PlanetSideGUID(91), 1,
|
||||||
|
WeaponData(4, 8, ObjectClass.spitfire_ammo, PlanetSideGUID(92), 0, AmmoBoxData()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"FieldTurret" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr)
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.GUID = PlanetSideGUID(90)
|
||||||
|
obj.Weapons(1).Equipment.get.GUID = PlanetSideGUID(91)
|
||||||
|
obj.Weapons(1).Equipment.get.asInstanceOf[Tool].AmmoSlot.Box.GUID = PlanetSideGUID(92)
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual OneMannedFieldTurretData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
255,
|
||||||
|
InventoryData(
|
||||||
|
List(InternalSlot(ObjectClass.energy_gun_tr, PlanetSideGUID(91), 1,
|
||||||
|
WeaponData(4, 8, ObjectClass.energy_gun_ammo, PlanetSideGUID(92), 0, AmmoBoxData()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"TRAP" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new TrapDeployable(GlobalDefinitions.tank_traps)
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.GUID = PlanetSideGUID(90)
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual TRAPData(
|
||||||
|
SmallDeployableData(
|
||||||
|
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
255
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"ShieldGenerator" should {
|
||||||
|
"convert" in {
|
||||||
|
val obj = new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator)
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.GUID = PlanetSideGUID(90)
|
||||||
|
obj.Definition.Packet.DetailedConstructorData(obj).isFailure mustEqual true
|
||||||
|
|
||||||
|
obj.Definition.Packet.ConstructorData(obj) match {
|
||||||
|
case Success(pkt) =>
|
||||||
|
pkt mustEqual AegisShieldGeneratorData(
|
||||||
|
CommonFieldData(
|
||||||
|
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
0
|
||||||
|
),
|
||||||
|
255
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"Player" should {
|
"Player" should {
|
||||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||||
val obj : Player = {
|
val obj : Player = {
|
||||||
|
|
|
||||||
330
common/src/test/scala/objects/DeployableTest.scala
Normal file
330
common/src/test/scala/objects/DeployableTest.scala
Normal file
|
|
@ -0,0 +1,330 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
|
import base.ActorTest
|
||||||
|
import net.psforever.objects.ce.DeployedItem
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.{TurretDeployable, _}
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class DeployableTest extends Specification {
|
||||||
|
"Deployable" should {
|
||||||
|
"know its owner by GUID" in {
|
||||||
|
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
|
||||||
|
obj.Owner mustEqual None
|
||||||
|
obj.Owner = PlanetSideGUID(10)
|
||||||
|
obj.Owner mustEqual Some(PlanetSideGUID(10))
|
||||||
|
}
|
||||||
|
|
||||||
|
"know its owner by GUID" in {
|
||||||
|
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
|
||||||
|
obj.OwnerName mustEqual None
|
||||||
|
obj.OwnerName = "TestCharacter"
|
||||||
|
obj.OwnerName mustEqual Some("TestCharacter")
|
||||||
|
}
|
||||||
|
|
||||||
|
"know its faction allegiance" in {
|
||||||
|
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
|
||||||
|
obj.Faction mustEqual PlanetSideEmpire.NEUTRAL
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Faction mustEqual PlanetSideEmpire.TR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SensorDeployableTest extends Specification {
|
||||||
|
"SensorDeployable" should {
|
||||||
|
"construct" in {
|
||||||
|
new SensorDeployable(GlobalDefinitions.motionalarmsensor)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExplosiveDeployableTest extends Specification {
|
||||||
|
"ExplosiveDeployable" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
|
||||||
|
obj.Exploded mustEqual false
|
||||||
|
}
|
||||||
|
|
||||||
|
"explode" in {
|
||||||
|
val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
|
||||||
|
obj.Exploded mustEqual false
|
||||||
|
obj.Exploded = true
|
||||||
|
obj.Exploded mustEqual true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoomerDeployableTest extends Specification {
|
||||||
|
"BoomerDeployable" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj = new BoomerDeployable(GlobalDefinitions.boomer)
|
||||||
|
obj.Exploded mustEqual false
|
||||||
|
obj.Trigger mustEqual None
|
||||||
|
}
|
||||||
|
|
||||||
|
"explode" in {
|
||||||
|
val obj = new BoomerDeployable(GlobalDefinitions.boomer)
|
||||||
|
obj.Exploded mustEqual false
|
||||||
|
obj.Exploded = true
|
||||||
|
obj.Exploded mustEqual true
|
||||||
|
}
|
||||||
|
|
||||||
|
"manage its trigger" in {
|
||||||
|
val obj = new BoomerDeployable(GlobalDefinitions.boomer)
|
||||||
|
obj.Trigger mustEqual None
|
||||||
|
|
||||||
|
val trigger = new BoomerTrigger
|
||||||
|
obj.Trigger = trigger
|
||||||
|
obj.Trigger mustEqual Some(trigger)
|
||||||
|
|
||||||
|
obj.Trigger = None
|
||||||
|
obj.Trigger mustEqual None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrapDeployableTest extends Specification {
|
||||||
|
"SensorDeployable" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj = new TrapDeployable(GlobalDefinitions.tank_traps)
|
||||||
|
obj.Health mustEqual GlobalDefinitions.tank_traps.MaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
"update health values" in {
|
||||||
|
val obj = new TrapDeployable(GlobalDefinitions.tank_traps)
|
||||||
|
obj.Health mustEqual GlobalDefinitions.tank_traps.MaxHealth
|
||||||
|
obj.Health = 0
|
||||||
|
obj.Health mustEqual 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretDeployableTest extends Specification {
|
||||||
|
"TurretDeployable" should {
|
||||||
|
"define (valid turret objects)" in {
|
||||||
|
List(
|
||||||
|
DeployedItem.spitfire_turret.id, DeployedItem.spitfire_cloaked.id, DeployedItem.spitfire_aa.id,
|
||||||
|
DeployedItem.portable_manned_turret.id, DeployedItem.portable_manned_turret_tr.id,
|
||||||
|
DeployedItem.portable_manned_turret_nc.id, DeployedItem.portable_manned_turret_vs.id
|
||||||
|
).foreach(id => {
|
||||||
|
try { new TurretDeployableDefinition(id) } catch { case _ : Exception => ko }
|
||||||
|
})
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"define (invalid object)" in {
|
||||||
|
new TurretDeployableDefinition(5) must throwA[NoSuchElementException] //wrong object id altogether
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
obj.Health mustEqual obj.MaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
"update health values" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
obj.Health mustEqual GlobalDefinitions.spitfire_turret.MaxHealth
|
||||||
|
obj.Health = 0
|
||||||
|
obj.Health mustEqual 0
|
||||||
|
}
|
||||||
|
|
||||||
|
"may have mount point" in {
|
||||||
|
new TurretDeployable(GlobalDefinitions.spitfire_turret).MountPoints mustEqual Map()
|
||||||
|
new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs).MountPoints mustEqual Map(1 -> 0, 2 -> 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShieldGeneratorDeployableTest extends Specification {
|
||||||
|
"ShieldGeneratorDeployable" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj = new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator)
|
||||||
|
obj.Health mustEqual obj.MaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
"update health values" in {
|
||||||
|
val obj = new ShieldGeneratorDeployable(GlobalDefinitions.deployable_shield_generator)
|
||||||
|
obj.Health mustEqual GlobalDefinitions.deployable_shield_generator.MaxHealth
|
||||||
|
obj.Health = 0
|
||||||
|
obj.Health mustEqual 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlConstructTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlInitializeTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"initialize" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
obj.GUID = PlanetSideGUID(1)
|
||||||
|
assert(obj.Actor == ActorRef.noSender)
|
||||||
|
val init = system.actorOf(Props(classOf[DeployableTest.TurretInitializer], obj), "init_turret_test")
|
||||||
|
init ! "initialize"
|
||||||
|
expectNoMsg(200 milliseconds)
|
||||||
|
assert(obj.Actor != ActorRef.noSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlUninitializeTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"uninitialize" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret)
|
||||||
|
val init = system.actorOf(Props(classOf[DeployableTest.TurretInitializer], obj), "init_turret_test")
|
||||||
|
obj.GUID = PlanetSideGUID(1)
|
||||||
|
init ! "initialize"
|
||||||
|
expectNoMsg(200 milliseconds)
|
||||||
|
assert(obj.Actor != ActorRef.noSender)
|
||||||
|
|
||||||
|
init ! "uninitialize"
|
||||||
|
expectNoMsg(200 milliseconds)
|
||||||
|
assert(obj.Actor == ActorRef.noSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlMountTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"control mounting" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) }
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
obj.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
val reply1a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply1b = reply1a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply1b.player == player1)
|
||||||
|
assert(reply1b.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
assert(obj.Seats(0).Occupant.contains(player1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlBlockMountTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"block mounting by others if already mounted by someone" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) }
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
obj.Actor ! Mountable.TryMount(player1, 0)
|
||||||
|
val reply1a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply1b = reply1a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply1b.player == player1)
|
||||||
|
assert(reply1b.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
assert(obj.Seats(0).Occupant.contains(player1))
|
||||||
|
|
||||||
|
val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
obj.Actor ! Mountable.TryMount(player2, 0)
|
||||||
|
val reply2a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply2a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply2b = reply2a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply2b.player == player2)
|
||||||
|
assert(reply2b.response.isInstanceOf[Mountable.CanNotMount])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlBlockBetrayalMountTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"block mounting by players of another faction" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) }
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
val player = Player(Avatar("test", PlanetSideEmpire.VS, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
obj.Actor ! Mountable.TryMount(player, 0)
|
||||||
|
val reply1a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply1b = reply1a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply1b.player == player)
|
||||||
|
assert(reply1b.response.isInstanceOf[Mountable.CanNotMount])
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlDismountTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"control dismounting" in {
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) }
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
obj.Actor ! Mountable.TryMount(player, 0)
|
||||||
|
val reply1a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply1b = reply1a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply1b.player == player)
|
||||||
|
assert(reply1b.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
assert(obj.Seats(0).Occupant.contains(player))
|
||||||
|
|
||||||
|
obj.Actor ! Mountable.TryDismount(player, 0)
|
||||||
|
val reply2a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply2a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply2b = reply2a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply2b.player == player)
|
||||||
|
assert(reply2b.response.isInstanceOf[Mountable.CanDismount])
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TurretControlBetrayalMountTest extends ActorTest {
|
||||||
|
"TurretControl" should {
|
||||||
|
"allow all allegiances" in {
|
||||||
|
val obj = new TurretDeployable(
|
||||||
|
new TurretDeployableDefinition(685) { FactionLocked = false } //required (defaults to true)
|
||||||
|
) { GUID = PlanetSideGUID(1) }
|
||||||
|
obj.Faction = PlanetSideEmpire.TR
|
||||||
|
obj.Actor = system.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_test")
|
||||||
|
|
||||||
|
assert(obj.Seats(0).Occupant.isEmpty)
|
||||||
|
val player = Player(Avatar("test", PlanetSideEmpire.NC, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
assert(player.Faction != obj.Faction)
|
||||||
|
obj.Actor ! Mountable.TryMount(player, 0)
|
||||||
|
val reply1a = receiveOne(200 milliseconds)
|
||||||
|
assert(reply1a.isInstanceOf[Mountable.MountMessages])
|
||||||
|
val reply1b = reply1a.asInstanceOf[Mountable.MountMessages]
|
||||||
|
assert(reply1b.player == player)
|
||||||
|
assert(reply1b.response.isInstanceOf[Mountable.CanMount])
|
||||||
|
assert(obj.Seats(0).Occupant.contains(player))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableTest {
|
||||||
|
class TurretInitializer(obj : TurretDeployable) extends Actor {
|
||||||
|
def receive : Receive = {
|
||||||
|
case "initialize" =>
|
||||||
|
obj.Definition.Initialize(obj, context)
|
||||||
|
case "uninitialize" =>
|
||||||
|
obj.Definition.Uninitialize(obj, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1054
common/src/test/scala/objects/DeployableToolboxTest.scala
Normal file
1054
common/src/test/scala/objects/DeployableToolboxTest.scala
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,11 +2,13 @@
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.GlobalDefinitions._
|
import net.psforever.objects.GlobalDefinitions._
|
||||||
|
import net.psforever.objects.ce.DeployedItem
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.types.CertificationType
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
class EquipmentTest extends Specification {
|
class EquipmentTest extends Specification {
|
||||||
|
|
@ -329,62 +331,57 @@ class EquipmentTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"ConstructionItem" should {
|
"ConstructionItem" should {
|
||||||
val advanced_ace_tr = ConstructionItemDefinition(39)
|
|
||||||
advanced_ace_tr.Modes += DeployedItem.tank_traps
|
|
||||||
advanced_ace_tr.Modes += DeployedItem.portable_manned_turret_tr
|
|
||||||
advanced_ace_tr.Modes += DeployedItem.deployable_shield_generator
|
|
||||||
advanced_ace_tr.Tile = InventoryTile.Tile63
|
|
||||||
|
|
||||||
"define" in {
|
|
||||||
val sample = ConstructionItemDefinition(Unit.advanced_ace)
|
|
||||||
sample.Modes += DeployedItem.tank_traps
|
|
||||||
sample.Modes += DeployedItem.portable_manned_turret_tr
|
|
||||||
sample.Modes += DeployedItem.deployable_shield_generator
|
|
||||||
sample.Tile = InventoryTile.Tile63
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
"construct" in {
|
"construct" in {
|
||||||
val obj : ConstructionItem = ConstructionItem(advanced_ace_tr)
|
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||||
obj.Definition.ObjectId mustEqual advanced_ace_tr.ObjectId
|
obj.Definition.ObjectId mustEqual GlobalDefinitions.ace.ObjectId
|
||||||
}
|
}
|
||||||
|
|
||||||
"fire mode" in {
|
"fire modes" in {
|
||||||
//explanation: router_telepad has one fire mode and that fire mode is our only option
|
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||||
val router_telepad : ConstructionItemDefinition = ConstructionItemDefinition(Unit.router_telepad)
|
obj.AmmoType mustEqual DeployedItem.boomer
|
||||||
router_telepad.Modes += DeployedItem.router_telepad_deployable
|
obj.NextFireMode
|
||||||
val obj : ConstructionItem = ConstructionItem(router_telepad)
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
//fmode = 0
|
obj.NextFireMode
|
||||||
obj.FireModeIndex mustEqual 0
|
obj.AmmoType mustEqual DeployedItem.spitfire_turret
|
||||||
obj.FireMode mustEqual DeployedItem.router_telepad_deployable
|
obj.NextFireMode
|
||||||
//fmode -> 1 (0)
|
obj.AmmoType mustEqual DeployedItem.motionalarmsensor
|
||||||
obj.FireModeIndex = 1
|
obj.NextFireMode
|
||||||
obj.FireModeIndex mustEqual 0
|
obj.AmmoType mustEqual DeployedItem.boomer
|
||||||
obj.FireMode mustEqual DeployedItem.router_telepad_deployable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"multiple fire modes" in {
|
"ammo types" in {
|
||||||
//explanation: advanced_ace_tr has three fire modes; adjusting the FireMode changes between them
|
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||||
val obj : ConstructionItem = ConstructionItem(advanced_ace_tr)
|
|
||||||
//fmode = 0
|
|
||||||
obj.FireModeIndex mustEqual 0
|
|
||||||
obj.FireMode mustEqual DeployedItem.tank_traps
|
|
||||||
//fmode -> 1
|
|
||||||
obj.NextFireMode
|
obj.NextFireMode
|
||||||
obj.FireModeIndex mustEqual 1
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
obj.FireMode mustEqual DeployedItem.portable_manned_turret_tr
|
obj.NextAmmoType
|
||||||
//fmode -> 2
|
obj.AmmoType mustEqual DeployedItem.jammer_mine
|
||||||
|
obj.NextAmmoType
|
||||||
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
|
}
|
||||||
|
|
||||||
|
"when switching fire modes, ammo mode resets to the first entry" in {
|
||||||
|
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||||
obj.NextFireMode
|
obj.NextFireMode
|
||||||
obj.FireModeIndex mustEqual 2
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
obj.FireMode mustEqual DeployedItem.deployable_shield_generator
|
obj.NextAmmoType
|
||||||
//fmode -> 0
|
obj.AmmoType mustEqual DeployedItem.jammer_mine
|
||||||
|
obj.NextFireMode //spitfire_turret
|
||||||
|
obj.NextFireMode //motionalarmsensor
|
||||||
|
obj.NextFireMode //boomer
|
||||||
obj.NextFireMode
|
obj.NextFireMode
|
||||||
obj.FireModeIndex mustEqual 0
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
obj.FireMode mustEqual DeployedItem.tank_traps
|
}
|
||||||
|
|
||||||
|
"qualify certifications that must be met before ammo types may be used" in {
|
||||||
|
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||||
|
obj.AmmoType mustEqual DeployedItem.boomer
|
||||||
|
obj.ModePermissions mustEqual Set(CertificationType.CombatEngineering)
|
||||||
|
obj.NextFireMode
|
||||||
|
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||||
|
obj.ModePermissions mustEqual Set(CertificationType.CombatEngineering)
|
||||||
|
obj.NextAmmoType
|
||||||
|
obj.AmmoType mustEqual DeployedItem.jammer_mine
|
||||||
|
obj.ModePermissions mustEqual Set(CertificationType.AssaultEngineering)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,4 +396,21 @@ class EquipmentTest extends Specification {
|
||||||
obj.Definition.ObjectId mustEqual remote_electronics_kit.ObjectId
|
obj.Definition.ObjectId mustEqual remote_electronics_kit.ObjectId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"BoomerTrigger" should {
|
||||||
|
"construct" in {
|
||||||
|
val obj : BoomerTrigger = new BoomerTrigger
|
||||||
|
obj.Definition.ObjectId mustEqual boomer_trigger.ObjectId
|
||||||
|
obj.Companion mustEqual None
|
||||||
|
}
|
||||||
|
|
||||||
|
"boomer trigger has a companion object referenced by GUID" in {
|
||||||
|
val obj : BoomerTrigger = new BoomerTrigger
|
||||||
|
obj.Companion mustEqual None
|
||||||
|
obj.Companion = PlanetSideGUID(1)
|
||||||
|
obj.Companion.contains(PlanetSideGUID(1)) mustEqual true
|
||||||
|
obj.Companion = None
|
||||||
|
obj.Companion mustEqual None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,8 @@
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.GlobalDefinitions._
|
|
||||||
import net.psforever.objects.definition._
|
|
||||||
import net.psforever.types.ExoSuitType
|
import net.psforever.types.ExoSuitType
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool}
|
||||||
import net.psforever.objects.definition.ToolDefinition
|
import net.psforever.objects.definition.ToolDefinition
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||||
import net.psforever.objects.serverobject.turret.{MannedTurret, MannedTurretControl, MannedTurretDefinition, TurretUpgrade}
|
import net.psforever.objects.serverobject.turret._
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
|
|
@ -16,10 +16,10 @@ import org.specs2.mutable.Specification
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class MannedTurretTest extends Specification {
|
class FacilityTurretTest extends Specification {
|
||||||
"MannedTurretTest" should {
|
"FacilityTurretTest" should {
|
||||||
"define" in {
|
"define" in {
|
||||||
val obj = new MannedTurretDefinition(480)
|
val obj = new TurretDefinition(480)
|
||||||
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
|
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
|
||||||
obj.ReserveAmmunition mustEqual false
|
obj.ReserveAmmunition mustEqual false
|
||||||
obj.FactionLocked mustEqual true
|
obj.FactionLocked mustEqual true
|
||||||
|
|
@ -28,7 +28,7 @@ class MannedTurretTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"construct" in {
|
"construct" in {
|
||||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||||
obj.Weapons.size mustEqual 1
|
obj.Weapons.size mustEqual 1
|
||||||
obj.Weapons(1).Equipment match {
|
obj.Weapons(1).Equipment match {
|
||||||
case Some(tool : Tool) =>
|
case Some(tool : Tool) =>
|
||||||
|
|
@ -51,7 +51,7 @@ class MannedTurretTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
"upgrade to a different weapon" in {
|
"upgrade to a different weapon" in {
|
||||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||||
obj.Upgrade = TurretUpgrade.None
|
obj.Upgrade = TurretUpgrade.None
|
||||||
obj.Weapons(1).Equipment match {
|
obj.Weapons(1).Equipment match {
|
||||||
case Some(tool : Tool) =>
|
case Some(tool : Tool) =>
|
||||||
|
|
@ -87,26 +87,26 @@ class MannedTurretTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MannedTurretControl1Test extends ActorTest {
|
class FacilityTurretControl1Test extends ActorTest {
|
||||||
"MannedTurretControl" should {
|
"FacilityTurretControl" should {
|
||||||
"construct" in {
|
"construct" in {
|
||||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||||
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
|
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
|
||||||
assert(obj.Actor != ActorRef.noSender)
|
assert(obj.Actor != ActorRef.noSender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MannedTurretControl2Test extends ActorTest {
|
class FacilityTurretControl2Test extends ActorTest {
|
||||||
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||||
obj.GUID = PlanetSideGUID(1)
|
obj.GUID = PlanetSideGUID(1)
|
||||||
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
|
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
|
||||||
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
bldg.Amenities = obj
|
bldg.Amenities = obj
|
||||||
bldg.Faction = PlanetSideEmpire.TR
|
bldg.Faction = PlanetSideEmpire.TR
|
||||||
|
|
||||||
"MannedTurretControl" should {
|
"FacilityTurretControl" should {
|
||||||
"seat on faction affiliation when FactionLock is true" in {
|
"seat on faction affiliation when FactionLock is true" in {
|
||||||
assert(player.Faction == PlanetSideEmpire.TR)
|
assert(player.Faction == PlanetSideEmpire.TR)
|
||||||
assert(obj.Faction == PlanetSideEmpire.TR)
|
assert(obj.Faction == PlanetSideEmpire.TR)
|
||||||
|
|
@ -124,15 +124,15 @@ class MannedTurretControl2Test extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MannedTurretControl3Test extends ActorTest {
|
class FacilityTurretControl3Test extends ActorTest {
|
||||||
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||||
obj.GUID = PlanetSideGUID(1)
|
obj.GUID = PlanetSideGUID(1)
|
||||||
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
|
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
|
||||||
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
bldg.Amenities = obj
|
bldg.Amenities = obj
|
||||||
|
|
||||||
"MannedTurretControl" should {
|
"FacilityTurretControl" should {
|
||||||
"block seating on mismatched faction affiliation when FactionLock is true" in {
|
"block seating on mismatched faction affiliation when FactionLock is true" in {
|
||||||
assert(player.Faction == PlanetSideEmpire.TR)
|
assert(player.Faction == PlanetSideEmpire.TR)
|
||||||
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
|
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
|
||||||
|
|
@ -150,17 +150,17 @@ class MannedTurretControl3Test extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MannedTurretControl4Test extends ActorTest {
|
class FacilityTurretControl4Test extends ActorTest {
|
||||||
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
val objDef = new MannedTurretDefinition(480)
|
val objDef = new TurretDefinition(480)
|
||||||
objDef.FactionLocked = false
|
objDef.FactionLocked = false
|
||||||
val obj = MannedTurret(objDef)
|
val obj = FacilityTurret(objDef)
|
||||||
obj.GUID = PlanetSideGUID(1)
|
obj.GUID = PlanetSideGUID(1)
|
||||||
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
|
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
|
||||||
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
|
||||||
bldg.Amenities = obj
|
bldg.Amenities = obj
|
||||||
|
|
||||||
"MannedTurretControl" should {
|
"FacilityTurretControl" should {
|
||||||
"seating even with mismatched faction affiliation when FactionLock is false" in {
|
"seating even with mismatched faction affiliation when FactionLock is false" in {
|
||||||
assert(player.Faction == PlanetSideEmpire.TR)
|
assert(player.Faction == PlanetSideEmpire.TR)
|
||||||
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
|
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
|
||||||
|
|
@ -191,7 +191,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
||||||
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 0)
|
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 0)
|
||||||
|
|
||||||
val reply4 = probe1.receiveOne(500 milliseconds)
|
val reply4 = probe1.receiveOne(500 milliseconds)
|
||||||
assert(obj.LowNtuWarningOn == false)
|
assert(!obj.LowNtuWarningOn)
|
||||||
assert(reply4.isInstanceOf[AvatarServiceMessage])
|
assert(reply4.isInstanceOf[AvatarServiceMessage])
|
||||||
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||||
assert(reply4.asInstanceOf[AvatarServiceMessage]
|
assert(reply4.asInstanceOf[AvatarServiceMessage]
|
||||||
|
|
|
||||||
|
|
@ -246,20 +246,20 @@ class SpawnTubeObjectBuilderTest extends ActorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MannedTurretObjectBuilderTest extends ActorTest {
|
class FacilityTurretObjectBuilderTest extends ActorTest {
|
||||||
import net.psforever.objects.GlobalDefinitions.manned_turret
|
import net.psforever.objects.GlobalDefinitions.manned_turret
|
||||||
import net.psforever.objects.serverobject.turret.MannedTurret
|
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
"MannedTurretObjectBuilder" should {
|
"FacilityTurretObjectBuilder" should {
|
||||||
"build" in {
|
"build" in {
|
||||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
|
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
|
||||||
MannedTurret.Constructor(manned_turret)), hub), "spawn-tube")
|
FacilityTurret.Constructor(manned_turret)), hub), "spawn-tube")
|
||||||
actor ! "!"
|
actor ! "!"
|
||||||
|
|
||||||
val reply = receiveOne(Duration.create(1000, "ms"))
|
val reply = receiveOne(Duration.create(1000, "ms"))
|
||||||
assert(reply.isInstanceOf[MannedTurret])
|
assert(reply.isInstanceOf[FacilityTurret])
|
||||||
assert(reply.asInstanceOf[MannedTurret].HasGUID)
|
assert(reply.asInstanceOf[FacilityTurret].HasGUID)
|
||||||
assert(reply.asInstanceOf[MannedTurret].GUID == PlanetSideGUID(1))
|
assert(reply.asInstanceOf[FacilityTurret].GUID == PlanetSideGUID(1))
|
||||||
assert(reply == hub(1).get)
|
assert(reply == hub(1).get)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import base.ActorTest
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
|
||||||
class GUIDTaskRegister2Test extends ActorTest {
|
class GUIDTaskRegisterAmmoTest extends ActorTest {
|
||||||
"RegisterEquipment -> RegisterObjectTask" in {
|
"RegisterEquipment -> RegisterObjectTask" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
|
|
||||||
class GUIDTaskRegister5Test extends ActorTest {
|
class GUIDTaskRegisterAvatarTest extends ActorTest {
|
||||||
"RegisterAvatar" in {
|
"RegisterAvatar" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
|
@ -4,7 +4,7 @@ package objects.guidtask
|
||||||
import base.ActorTest
|
import base.ActorTest
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
|
||||||
class GUIDTaskRegister1Test extends ActorTest {
|
class GUIDTaskRegisterObjectTest extends ActorTest {
|
||||||
"RegisterObjectTask" in {
|
"RegisterObjectTask" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = new GUIDTaskTest.TestObject
|
val obj = new GUIDTaskTest.TestObject
|
||||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||||
|
|
||||||
class GUIDTaskRegister6Test extends ActorTest {
|
class GUIDTaskRegisterPlayerTest extends ActorTest {
|
||||||
"RegisterPlayer" in {
|
"RegisterPlayer" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||||
|
|
@ -5,7 +5,7 @@ import base.ActorTest
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
|
||||||
class GUIDTaskRegister3Test extends ActorTest {
|
class GUIDTaskRegisterToolTest extends ActorTest {
|
||||||
"RegisterEquipment -> RegisterTool" in {
|
"RegisterEquipment -> RegisterTool" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Tool(GlobalDefinitions.beamer)
|
val obj = Tool(GlobalDefinitions.beamer)
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package objects.guidtask
|
||||||
|
|
||||||
|
import base.ActorTest
|
||||||
|
import net.psforever.objects._
|
||||||
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
|
||||||
|
class GUIDTaskRegisterTurretTest extends ActorTest {
|
||||||
|
"RegisterDeployableTurret" in {
|
||||||
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
|
val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
|
||||||
|
val obj_wep = obj.Weapons(1).Equipment.get
|
||||||
|
val obj_ammo = obj_wep.asInstanceOf[Tool].AmmoSlot.Box
|
||||||
|
val obj_res = obj.Inventory.Items.map(_.obj)
|
||||||
|
|
||||||
|
assert(!obj.HasGUID)
|
||||||
|
assert(!obj_wep.HasGUID)
|
||||||
|
assert(!obj_ammo.HasGUID)
|
||||||
|
obj_res.foreach(box => !box.HasGUID)
|
||||||
|
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterDeployableTurret(obj)(uns)))
|
||||||
|
probe.expectMsg(scala.util.Success)
|
||||||
|
assert(obj.HasGUID)
|
||||||
|
assert(obj_wep.HasGUID)
|
||||||
|
assert(obj_ammo.HasGUID)
|
||||||
|
obj_res.foreach(box => box.HasGUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import base.ActorTest
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||||
|
|
||||||
class GUIDTaskRegister4Test extends ActorTest {
|
class GUIDTaskRegisterVehicleTest extends ActorTest {
|
||||||
"RegisterVehicle" in {
|
"RegisterVehicle" in {
|
||||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||||
val obj = Vehicle(GlobalDefinitions.fury)
|
val obj = Vehicle(GlobalDefinitions.fury)
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue