mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +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
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.avatar.DeployableToolbox
|
||||
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
||||
import net.psforever.objects.equipment.EquipmentSize
|
||||
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_=(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
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.definition.ConstructionItemDefinition
|
||||
import net.psforever.objects.equipment.{CItem, Equipment, FireModeSwitch}
|
||||
import net.psforever.objects.ce.DeployedItem
|
||||
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 ammoTypeIndex : Int = 0
|
||||
|
||||
def FireModeIndex : Int = fireModeIndex
|
||||
|
||||
def FireModeIndex_=(index : Int) : Int = {
|
||||
fireModeIndex = index % cItemDef.Modes.length
|
||||
fireModeIndex = index % Definition.Modes.length
|
||||
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
|
||||
ammoTypeIndex = 0
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
import net.psforever.objects.ballistics.Projectiles
|
||||
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.definition.converter._
|
||||
import net.psforever.objects.equipment.CItem.DeployedItem
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
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.tube.SpawnTubeDefinition
|
||||
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.vital.DamageType
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.objects.vital.{DamageType, StandardResolutions}
|
||||
import net.psforever.types.{CertificationType, PlanetSideEmpire}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -497,6 +497,12 @@ object GlobalDefinitions {
|
|||
val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm)
|
||||
|
||||
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()
|
||||
|
||||
val chainblade = ToolDefinition(ObjectClass.chainblade)
|
||||
|
|
@ -622,6 +628,8 @@ object GlobalDefinitions {
|
|||
|
||||
val bank = ToolDefinition(ObjectClass.bank)
|
||||
|
||||
val boomer_trigger = SimpleItemDefinition(SItem.boomer_trigger)
|
||||
|
||||
val remote_electronics_kit = SimpleItemDefinition(SItem.remote_electronics_kit)
|
||||
|
||||
val trek = ToolDefinition(ObjectClass.trek)
|
||||
|
|
@ -630,9 +638,9 @@ object GlobalDefinitions {
|
|||
|
||||
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)
|
||||
|
||||
|
|
@ -737,6 +745,18 @@ object GlobalDefinitions {
|
|||
val phalanx_avcombo = ToolDefinition(ObjectClass.phalanx_avcombo)
|
||||
|
||||
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()
|
||||
|
||||
/*
|
||||
|
|
@ -809,6 +829,38 @@ object GlobalDefinitions {
|
|||
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
||||
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
|
||||
*/
|
||||
|
|
@ -868,7 +920,7 @@ object GlobalDefinitions {
|
|||
|
||||
val secondary_capture = new CaptureTerminalDefinition(751) // Tower CC
|
||||
|
||||
val manned_turret = new MannedTurretDefinition(480) {
|
||||
val manned_turret = new TurretDefinition(480) {
|
||||
Name = "manned_turret"
|
||||
MaxHealth = 3600
|
||||
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.
|
||||
* Only the normal grenades count; the grenade packs are excluded.
|
||||
|
|
@ -1541,8 +1602,16 @@ object GlobalDefinitions {
|
|||
bullet_150mm.Tile = InventoryTile.Tile44
|
||||
|
||||
phalanx_ammo.Name = "phalanx_ammo"
|
||||
phalanx_ammo.Capacity = 4000 //sufficient for a reload
|
||||
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.Tile = InventoryTile.Tile33
|
||||
|
||||
boomer_trigger.Name = "boomer_trigger"
|
||||
boomer_trigger.Packet = new BoomerTriggerConverter
|
||||
boomer_trigger.Tile = InventoryTile.Tile22
|
||||
|
||||
trek.Name = "trek"
|
||||
trek.Size = EquipmentSize.Pistol
|
||||
trek.AmmoTypes += trek_ammo
|
||||
|
|
@ -3961,21 +4034,30 @@ object GlobalDefinitions {
|
|||
command_detonater.Tile = InventoryTile.Tile33
|
||||
|
||||
ace.Name = "ace"
|
||||
ace.Modes += DeployedItem.boomer
|
||||
ace.Modes += DeployedItem.he_mine
|
||||
ace.Modes += DeployedItem.jammer_mine
|
||||
ace.Modes += DeployedItem.spitfire_turret
|
||||
ace.Modes += DeployedItem.spitfire_cloaked
|
||||
ace.Modes += DeployedItem.spitfire_aa
|
||||
ace.Modes += DeployedItem.motionalarmsensor
|
||||
ace.Modes += DeployedItem.sensor_shield
|
||||
ace.Size = EquipmentSize.Pistol
|
||||
ace.Modes += new ConstructionFireMode
|
||||
ace.Modes.head.Item(DeployedItem.boomer -> Set(CertificationType.CombatEngineering))
|
||||
ace.Modes += new ConstructionFireMode
|
||||
ace.Modes(1).Item(DeployedItem.he_mine -> Set(CertificationType.CombatEngineering))
|
||||
ace.Modes(1).Item(DeployedItem.jammer_mine -> Set(CertificationType.AssaultEngineering))
|
||||
ace.Modes += new ConstructionFireMode
|
||||
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
|
||||
|
||||
advanced_ace.Name = "advanced_ace"
|
||||
advanced_ace.Modes += DeployedItem.tank_traps
|
||||
advanced_ace.Modes += DeployedItem.portable_manned_turret
|
||||
advanced_ace.Modes += DeployedItem.deployable_shield_generator
|
||||
advanced_ace.Tile = InventoryTile.Tile63
|
||||
advanced_ace.Size = EquipmentSize.Rifle
|
||||
advanced_ace.Modes += new ConstructionFireMode
|
||||
advanced_ace.Modes.head.Item(DeployedItem.tank_traps -> Set(CertificationType.FortificationEngineering))
|
||||
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.Size = EquipmentSize.VehicleWeapon
|
||||
|
|
@ -4510,6 +4592,61 @@ object GlobalDefinitions {
|
|||
phalanx_flakcombo.FireModes(1).ProjectileTypeIndices += 1
|
||||
phalanx_flakcombo.FireModes(1).AmmoSlotIndex = 0
|
||||
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.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
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* "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,"
|
||||
|
|
@ -17,7 +17,8 @@ import scala.annotation.tailrec
|
|||
* Some weapons Chainblade have ammunition but do not consume it.
|
||||
* @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields
|
||||
*/
|
||||
class Tool(private val toolDef : ToolDefinition) extends Equipment with FireModeSwitch[FireModeDefinition] {
|
||||
class Tool(private val toolDef : ToolDefinition) extends Equipment
|
||||
with FireModeSwitch[FireModeDefinition] {
|
||||
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
|
||||
private var fireModeIndex : Int = 0
|
||||
/** 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
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
|
||||
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.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
|
|
@ -29,6 +30,9 @@ object SourceEntry {
|
|||
target match {
|
||||
case obj : Player => PlayerSource(obj)
|
||||
case obj : Vehicle => VehicleSource(obj)
|
||||
case obj : ComplexDeployable => ComplexDeployableSource(obj)
|
||||
case obj : TurretDeployable => ComplexDeployableSource(obj)
|
||||
case obj : SimpleDeployable => DeployableSource(obj)
|
||||
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 {
|
||||
private var name : String = "definition"
|
||||
private var descriptor : Option[String] = None
|
||||
|
||||
def Name : String = name
|
||||
|
||||
|
|
@ -10,4 +11,13 @@ abstract class BasicDefinition {
|
|||
this.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
|
||||
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.types.CertificationType
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
class ConstructionItemDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
|
||||
CItem.Unit(objectId) //let throw NoSuchElementException
|
||||
private val modes : ListBuffer[CItem.DeployedItem.Value] = ListBuffer()
|
||||
CItem(objectId) //let throw NoSuchElementException
|
||||
private val modes : ListBuffer[ConstructionFireMode] = ListBuffer()
|
||||
Packet = new ACEConverter
|
||||
|
||||
def Modes : ListBuffer[CItem.DeployedItem.Value] = modes
|
||||
def Modes : ListBuffer[ConstructionFireMode] = modes
|
||||
}
|
||||
|
||||
object ConstructionItemDefinition {
|
||||
|
|
@ -17,7 +21,29 @@ object ConstructionItemDefinition {
|
|||
new ConstructionItemDefinition(objectId)
|
||||
}
|
||||
|
||||
def apply(cItem : CItem.Unit.Value) : ConstructionItemDefinition = {
|
||||
def apply(cItem : CItem.Value) : ConstructionItemDefinition = {
|
||||
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] = {
|
||||
val health = 255 * obj.Health / obj.MaxHealth //TODO not precise
|
||||
if(health > 3) { //active
|
||||
if(health > 0) { //active
|
||||
Success(
|
||||
VehicleData(
|
||||
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)
|
||||
obj.Seats(0).Occupant match {
|
||||
case Some(player) =>
|
||||
val mountedPlayer = VehicleData.PlayerData(
|
||||
AvatarConverter.MakeAppearanceData(player),
|
||||
AvatarConverter.MakeCharacterData(player),
|
||||
AvatarConverter.MakeInventoryData(player),
|
||||
AvatarConverter.GetDrawnSlot(player),
|
||||
offset
|
||||
)
|
||||
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, mountedPlayer))
|
||||
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, SeatConverter.MakeSeat(player, offset)))
|
||||
case None =>
|
||||
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] = {
|
||||
obj.Weapons.map({
|
||||
|
|
|
|||
|
|
@ -1,28 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
object CItem {
|
||||
object Unit extends Enumeration {
|
||||
final val ace = Value(32)
|
||||
final val advanced_ace = Value(39) //fdu
|
||||
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)
|
||||
}
|
||||
object CItem extends Enumeration {
|
||||
final val ace = Value(32)
|
||||
final val advanced_ace = Value(39) //fdu
|
||||
final val router_telepad = Value(743)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 net.psforever.objects.entity.IdentifiableEntity
|
||||
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.serverobject.turret.WeaponTurret
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
@ -191,6 +192,13 @@ object GUIDTask {
|
|||
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>
|
||||
* <br>
|
||||
|
|
@ -329,6 +337,13 @@ object GUIDTask {
|
|||
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
|
||||
* 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
|
||||
*/
|
||||
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`.
|
||||
* @param definition the `SimpleItemDefinition` that describes this future object
|
||||
|
|
@ -223,6 +229,8 @@ object Loadout {
|
|||
ShorthandAmmoBox(obj.Definition, obj.Capacity)
|
||||
case obj : ConstructionItem =>
|
||||
ShorthandConstructionItem(obj.Definition)
|
||||
case obj : BoomerTrigger =>
|
||||
ShorthandTriggerItem(obj.Definition)
|
||||
case obj : SimpleItem =>
|
||||
ShorthandSimpleItem(obj.Definition)
|
||||
case obj : Kit =>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
package net.psforever.objects.serverobject.mount
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.entity.{Identifiable, WorldEntity}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.turret.TurretDefinition
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object MountableBehavior {
|
||||
|
|
@ -16,7 +18,7 @@ object MountableBehavior {
|
|||
trait Mount {
|
||||
this : Actor =>
|
||||
|
||||
def MountableObject : Mountable with Identifiable with WorldEntity with FactionAffinity
|
||||
def MountableObject : PlanetSideGameObject with Mountable with FactionAffinity
|
||||
|
||||
val mountBehavior : Receive = {
|
||||
case Mountable.TryMount(user, seat_num) =>
|
||||
|
|
@ -34,6 +36,25 @@ object MountableBehavior {
|
|||
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),
|
||||
"bank" -> MakeTool(bank, armor_canister),
|
||||
"nano_dispenser" -> MakeTool(nano_dispenser),
|
||||
//TODO "ace" -> MakeConstructionItem(ace),
|
||||
//TODO "advanced_ace" -> MakeConstructionItem(advanced_ace),
|
||||
"ace" -> MakeConstructionItem(ace),
|
||||
"advanced_ace" -> MakeConstructionItem(advanced_ace),
|
||||
"remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit),
|
||||
"trek" -> MakeTool(trek),
|
||||
"command_detonater" -> MakeSimpleItem(command_detonater),
|
||||
|
|
@ -309,6 +309,15 @@ object EquipmentTerminalDefinition {
|
|||
*/
|
||||
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.
|
||||
* @param sdef the `SimpleItemDefinition` object
|
||||
|
|
@ -356,6 +365,9 @@ object EquipmentTerminalDefinition {
|
|||
case obj : ShorthandConstructionItem =>
|
||||
MakeConstructionItem(obj.definition)
|
||||
|
||||
case obj : ShorthandTriggerItem =>
|
||||
MakeTriggerItem(obj.definition)
|
||||
|
||||
case obj : ShorthandSimpleItem =>
|
||||
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.
|
||||
* @param turret the `MannedTurret` object being governed
|
||||
*/
|
||||
class MannedTurretControl(turret : MannedTurret) extends Actor
|
||||
class FacilityTurretControl(turret : FacilityTurret) extends Actor
|
||||
with FactionAffinityBehavior.Check
|
||||
with MountableBehavior.Dismount {
|
||||
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`.
|
||||
* @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
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
val manned_turret = Value(480)
|
||||
val vanu_sentry_turret = Value(943)
|
||||
final val manned_turret = Value(480)
|
||||
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 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
|
||||
)
|
||||
|
||||
object SimpleDeployableResolutions extends DamageResistCalculations(
|
||||
ResolutionCalculations.VehicleDamageAfterResist,
|
||||
ResolutionCalculations.SimpleDeployableApplication
|
||||
)
|
||||
|
||||
object ComplexDeployableResolutions extends DamageResistCalculations(
|
||||
ResolutionCalculations.VehicleDamageAfterResist,
|
||||
ResolutionCalculations.ComplexDeployableApplication
|
||||
)
|
||||
|
||||
object StandardResolutions extends ResolutionSelection {
|
||||
def Infantry : ResolutionCalculations.Form = InfantryResolutions.Calculate
|
||||
def Max : ResolutionCalculations.Form = MaxResolutions.Calculate
|
||||
def Vehicle : 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 DamageOn(obj : Vitality, func : (Any)=>Unit)
|
||||
|
||||
/**
|
||||
* Report that a vitals object must be updated due to damage.
|
||||
* @param obj the vital object
|
||||
|
|
|
|||
|
|
@ -18,10 +18,23 @@ abstract class DamageResistCalculations[A](calcFunc : (ResolvedProjectile)=>((In
|
|||
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
||||
extends ResolutionCalculations {
|
||||
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 res : Int = resistances(data)
|
||||
val mod = calcFunc(data)
|
||||
val modDam = mod(dam, res)
|
||||
applyFunc(modDam, data)
|
||||
mod(dam, res)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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.ce.{ComplexDeployable, SimpleDeployable}
|
||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
|
||||
/**
|
||||
|
|
@ -157,4 +158,49 @@ object ResolutionCalculations {
|
|||
}
|
||||
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 net.psforever.objects.ballistics.Projectile
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.actor.UniqueNumberSystem
|
||||
import net.psforever.objects.guid.selector.RandomSelector
|
||||
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.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.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. */
|
||||
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 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()
|
||||
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")
|
||||
deployables = context.actorOf(Props(classOf[ZoneDeployableActor], this, constructions), s"$Id-deployables")
|
||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
||||
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 DeployableList : List[PlanetSideGameObject with Deployable] = constructions.toList
|
||||
|
||||
def Vehicles : List[Vehicle] = vehicles.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 Deployables : ActorRef = deployables
|
||||
|
||||
def Transport : ActorRef = transport
|
||||
|
||||
def Population : ActorRef = population
|
||||
|
|
@ -290,7 +301,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
//turret to weapon
|
||||
Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
||||
((GUID(turret_guid) match {
|
||||
case Some(obj : MannedTurret) =>
|
||||
case Some(obj : FacilityTurret) =>
|
||||
Some(obj)
|
||||
case _ => ;
|
||||
None
|
||||
|
|
@ -322,7 +333,11 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
|
||||
private def AssignAmenities() : Unit = {
|
||||
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. */
|
||||
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`.
|
||||
* @see `Zone.Init(implicit ActorContext)`
|
||||
|
|
@ -494,6 +520,14 @@ object Zone {
|
|||
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 {
|
||||
final case class Spawn(vehicle : Vehicle)
|
||||
|
||||
|
|
@ -512,14 +546,71 @@ object Zone {
|
|||
*/
|
||||
final case class ClientInitialization(zone : Zone)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
object EquipmentIs {
|
||||
/**
|
||||
* Tha base `trait` connecting all `Equipment` object location tokens.
|
||||
*/
|
||||
sealed trait ItemLocation
|
||||
/**
|
||||
* The target item is contained within another object.
|
||||
* @see `GridInventory`<br>
|
||||
* `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 =>
|
||||
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
|
||||
case msg @ Zone.Vehicle.Spawn =>
|
||||
zone.Transport forward msg
|
||||
|
|
@ -142,17 +149,21 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
val errors = new AtomicInteger(0)
|
||||
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
|
||||
|
||||
//check base to object associations
|
||||
map.ObjectToBuilding.foreach({ case((object_guid, building_id)) =>
|
||||
//check bases
|
||||
map.ObjectToBuilding.values.toSet[Int].foreach(building_id =>
|
||||
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()
|
||||
}
|
||||
)
|
||||
|
||||
//check base to object associations
|
||||
map.ObjectToBuilding.keys.foreach(object_guid =>
|
||||
if(guid(object_guid).isEmpty) {
|
||||
slog.error(s"expected object id $object_guid to exist, but it did not")
|
||||
errors.incrementAndGet()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
//check door to lock association
|
||||
map.DoorToLock.foreach({ case((door_guid, lock_guid)) =>
|
||||
|
|
@ -174,8 +185,8 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
|
||||
//check manned turret to weapon association
|
||||
map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
|
||||
validateObject(turret_guid, MannedTurretCheck, "manned turret mount")
|
||||
if(validateObject(weapon_guid, WeaponCheck, "manned turret weapon")) {
|
||||
validateObject(turret_guid, FacilityTurretCheck, "facility turret mount")
|
||||
if(validateObject(weapon_guid, WeaponCheck, "facility turret weapon")) {
|
||||
if(guid(weapon_guid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
|
||||
slog.error(s"expected weapon $weapon_guid has an unregistered ammunition unit")
|
||||
errors.incrementAndGet()
|
||||
|
|
@ -247,9 +258,9 @@ object ZoneActor {
|
|||
obj.isInstanceOf[VehicleSpawnPad]
|
||||
}
|
||||
|
||||
def MannedTurretCheck(obj : PlanetSideGameObject) : Boolean = {
|
||||
import net.psforever.objects.serverobject.turret.MannedTurret
|
||||
obj.isInstanceOf[MannedTurret]
|
||||
def FacilityTurretCheck(obj : PlanetSideGameObject) : Boolean = {
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
obj.isInstanceOf[FacilityTurret]
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.types.{Angular, Vector3}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* 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 unk1 na
|
||||
* @param pos the location where the object is to be deployed
|
||||
* @param roll the amount of roll that affects orientation
|
||||
* @param pitch the amount of pitch that affects orientation
|
||||
* @param yaw the amount of yaw that affects orientation
|
||||
* @param orient the angle of orientation
|
||||
* @param unk2 na
|
||||
*/
|
||||
final case class DeployObjectMessage(object_guid : PlanetSideGUID,
|
||||
unk1 : Long,
|
||||
pos : Vector3,
|
||||
roll : Int,
|
||||
pitch : Int,
|
||||
yaw : Int,
|
||||
orient : Vector3,
|
||||
unk2 : Long)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = DeployObjectMessage
|
||||
|
|
@ -38,9 +35,19 @@ object DeployObjectMessage extends Marshallable[DeployObjectMessage] {
|
|||
("object_guid" | PlanetSideGUID.codec) ::
|
||||
("unk1" | uint32L) ::
|
||||
("pos" | Vector3.codec_pos) ::
|
||||
("roll" | uint8L) ::
|
||||
("pitch" | uint8L) ::
|
||||
("yaw" | uint8L) ::
|
||||
(("roll" | Angular.codec_roll) ::
|
||||
("pitch" | Angular.codec_pitch) ::
|
||||
("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)
|
||||
).as[DeployObjectMessage]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import shapeless.{::, HNil}
|
|||
/**
|
||||
* 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
|
||||
|
||||
val Failure = Value(2)
|
||||
|
|
@ -47,7 +47,7 @@ object DeploymentOutcome extends Enumeration(1) {
|
|||
*/
|
||||
final case class ObjectDeployedMessage(unk : Int,
|
||||
desc : String,
|
||||
action : DeploymentOutcome.Value,
|
||||
action : DeployOutcome.Value,
|
||||
count : Long,
|
||||
max : Long)
|
||||
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
|
||||
* @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)
|
||||
|
||||
/**
|
||||
* 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] = (
|
||||
("unk" | uint16L) ::
|
||||
("desc" | PacketHelpers.encodedString) ::
|
||||
("action" | DeploymentOutcome.codec) ::
|
||||
("action" | DeployOutcome.codec) ::
|
||||
("count" | uint32L) ::
|
||||
("max" | uint32L)
|
||||
).xmap[ObjectDeployedMessage] (
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import scodec.codecs._
|
|||
* `67 - ???`<br>
|
||||
* <br>
|
||||
* Global (GUID=0)<br>
|
||||
* `82 - ???`
|
||||
* `75 - Russian client region check` (value checks with bitmask `& 8`)<br>
|
||||
* `82 - ???`<br>
|
||||
* `83 - max boomers`<br>
|
||||
* `84 - max he mines`<br>
|
||||
* `85 - max disruptor mines`<br>
|
||||
|
|
@ -114,10 +115,10 @@ import scodec.codecs._
|
|||
* `36 - CR. Value is the CR`<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>
|
||||
* 47 - Sets base NTU level to CRITICAL. 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
|
||||
* 49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)
|
||||
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)
|
||||
* `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`<br>
|
||||
* `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?)`<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>
|
||||
* - 0 is nothing<br>
|
||||
|
|
@ -151,7 +152,7 @@ import scodec.codecs._
|
|||
* `79 - ???`<br>
|
||||
* `80 - Damage vehicle (unknown value)`<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 attribute_type na
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.types.{Angular, Vector3}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* na
|
||||
|
|
@ -19,14 +20,11 @@ final case class TriggeredEffect(unk1 : Boolean,
|
|||
* 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.
|
||||
* @param pos the position in the game world
|
||||
* @param roll the amount of roll that affects orientation
|
||||
* @param pitch the amount of pitch that affects orientation
|
||||
* @param yaw the amount of yaw that affects orientation
|
||||
* @param orient the angle of orientation
|
||||
*/
|
||||
//TODO must find proper North-corrective angle for orientation
|
||||
final case class TriggeredEffectLocation(pos : Vector3,
|
||||
roll : Int,
|
||||
pitch : Int,
|
||||
yaw : Int)
|
||||
orient : Vector3)
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* The effect "spawn_object_effect" can be applied anywhere in the environment;
|
||||
* 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 unk na;
|
||||
* when activating an effect on an existing object
|
||||
* @param location an optional position where the effect will be displayed;
|
||||
* when activating an effect independently
|
||||
*/
|
||||
final case class TriggerEffectMessage(obj : PlanetSideGUID,
|
||||
final case class TriggerEffectMessage(object_guid : PlanetSideGUID,
|
||||
effect : String,
|
||||
unk : Option[TriggeredEffect] = None,
|
||||
location : Option[TriggeredEffectLocation] = None
|
||||
|
|
@ -56,6 +54,15 @@ final case class TriggerEffectMessage(obj : PlanetSideGUID,
|
|||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
|
@ -69,15 +76,24 @@ object TriggerEffectMessage extends Marshallable[TriggerEffectMessage] {
|
|||
*/
|
||||
private val effect_location_codec : Codec[TriggeredEffectLocation] = (
|
||||
("pos" | Vector3.codec_pos) ::
|
||||
("roll" | uint8L) ::
|
||||
("pitch" | uint8L) ::
|
||||
("yaw" | uint8L)
|
||||
(("roll" | Angular.codec_roll) ::
|
||||
("pitch" | Angular.codec_pitch) ::
|
||||
("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]
|
||||
|
||||
implicit val codec : Codec[TriggerEffectMessage] = (
|
||||
("obj" | PlanetSideGUID.codec) >>:~ { obj =>
|
||||
("object_guid" | PlanetSideGUID.codec) >>:~ { guid =>
|
||||
("effect" | PacketHelpers.encodedString) ::
|
||||
optional(bool, "unk" | effect_codec) ::
|
||||
conditional(obj.guid == 0, "location" | effect_location_codec)
|
||||
conditional(guid.guid == 0, "location" | effect_location_codec)
|
||||
}).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")
|
||||
//ace deployables
|
||||
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")
|
||||
//vehicles?
|
||||
case ObjectClass.orbital_shuttle => ConstructorData.genericCodec(OrbitalShuttleData.codec, "HART")
|
||||
|
|
@ -1186,7 +1186,7 @@ object ObjectClass {
|
|||
case ObjectClass.trek => DroppedItemData.genericCodec(WeaponData.codec, "tool")
|
||||
//ace deployables
|
||||
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 => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
||||
case ObjectClass.he_mine => ConstructorData.genericCodec(SmallDeployableData.codec, "ace deployable")
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ object ObjectCreateBase {
|
|||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
log.error(s"${ex.getClass.toString} - ${ex.toString}")
|
||||
log.error(s"${ex.getClass.toString} - ${ex.toString} ($objectClass)")
|
||||
}
|
||||
out
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ object ObjectCreateBase {
|
|||
}
|
||||
catch {
|
||||
case ex : Exception =>
|
||||
log.error(s"${ex.getClass.toString} - ${ex.toString}")
|
||||
log.error(s"${ex.getClass.toString} - ${ex.toString} ($objectClass)")
|
||||
}
|
||||
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 internals data regarding the mountable weapon
|
||||
*/
|
||||
final case class OneMannedFieldTurretData(deploy : CommonFieldData,
|
||||
final case class OneMannedFieldTurretData(deploy : SmallDeployableData,
|
||||
health : Int,
|
||||
internals : Option[InternalSlot] = None
|
||||
internals : Option[InventoryData] = None
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val deploySize = deploy.bitsize
|
||||
val internalSize = if(internals.isDefined) { CommonFieldData.internalWeapon_bitsize + internals.get.bitsize } else { 0L }
|
||||
38L + deploySize + internalSize //16u + 8u + 8u + 2u + 4u
|
||||
val internalSize = internals match {
|
||||
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
|
||||
* @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))
|
||||
|
||||
/**
|
||||
|
|
@ -122,40 +127,44 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
|
|||
)
|
||||
|
||||
implicit val codec : Codec[OneMannedFieldTurretData] = (
|
||||
("deploy" | CommonFieldData.codec) ::
|
||||
bool ::
|
||||
PlanetSideGUID.codec :: //hoist/extract with the CommonFieldData above
|
||||
("deploy" | SmallDeployableData.codec) ::
|
||||
PlanetSideGUID.codec :: //hoist/extract with the deploy.owner_guid in field above
|
||||
bool ::
|
||||
("health" | uint8L) ::
|
||||
uint2L ::
|
||||
uint8L ::
|
||||
bool ::
|
||||
optional(bool, "internals" | CommonFieldData.internalWeaponCodec)
|
||||
uint(5) ::
|
||||
uint4 ::
|
||||
uint2 ::
|
||||
optional(bool, "internals" | InventoryData.codec)
|
||||
).exmap[OneMannedFieldTurretData] (
|
||||
{
|
||||
case deploy :: false :: player :: false :: health :: 0 :: 0x1E :: false :: internals :: HNil =>
|
||||
var newHealth : Int = health
|
||||
var newInternals : Option[InternalSlot] = internals
|
||||
if(health == 0 || internals.isEmpty) {
|
||||
newHealth = 0
|
||||
newInternals = None
|
||||
case deploy :: player :: false :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
||||
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||
(0, None)
|
||||
}
|
||||
val newDeploy = CommonFieldData(deploy.pos, deploy.faction, deploy.unk, player)
|
||||
Attempt.successful(OneMannedFieldTurretData(newDeploy, newHealth, newInternals))
|
||||
else {
|
||||
(health, internals)
|
||||
}
|
||||
Attempt.successful(
|
||||
OneMannedFieldTurretData(
|
||||
SmallDeployableData(deploy.pos, deploy.faction, deploy.bops, deploy.destroyed, deploy.unk1, deploy.jammered, deploy.unk2, player),
|
||||
newHealth,
|
||||
newInternals
|
||||
)
|
||||
)
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid omft data format"))
|
||||
},
|
||||
{
|
||||
case OneMannedFieldTurretData(deploy, health, internals) =>
|
||||
var newHealth : Int = health
|
||||
var newInternals : Option[InternalSlot] = internals
|
||||
if(health == 0 || internals.isEmpty) {
|
||||
newHealth = 0
|
||||
newInternals = None
|
||||
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||
(0, None)
|
||||
}
|
||||
val newDeploy = CommonFieldData(deploy.pos, deploy.faction, deploy.unk)
|
||||
Attempt.successful(newDeploy :: false :: deploy.player_guid :: false :: newHealth :: 0 :: 0x1E :: false :: newInternals :: HNil)
|
||||
else {
|
||||
(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 _ =>
|
||||
Attempt.failure(Err("invalid omft data format"))
|
||||
|
|
|
|||
|
|
@ -2,33 +2,42 @@
|
|||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
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 shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
override def bitsize : Long = deploy.bitsize + 1L
|
||||
final case class SmallDeployableData(pos : PlacementData,
|
||||
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] {
|
||||
implicit val codec : Codec[SmallDeployableData] = (
|
||||
("deploy" | CommonFieldData.codec) ::
|
||||
bool
|
||||
).exmap[SmallDeployableData] (
|
||||
{
|
||||
case deploy :: false :: HNil =>
|
||||
Attempt.successful(SmallDeployableData(deploy))
|
||||
def apply(pos : PlacementData, faction : PlanetSideEmpire.Value, unk1 : Int, jammered : Boolean, unk2 : Boolean) : SmallDeployableData = {
|
||||
SmallDeployableData(pos, faction, false, false, unk1, jammered, unk2, PlanetSideGUID(0))
|
||||
}
|
||||
|
||||
case _ =>
|
||||
Attempt.failure(Err("invalid small deployable data format"))
|
||||
},
|
||||
{
|
||||
case SmallDeployableData(deploy) =>
|
||||
Attempt.successful(deploy :: false :: HNil)
|
||||
}
|
||||
)
|
||||
implicit val codec : Codec[SmallDeployableData] = (
|
||||
("pos" | PlacementData.codec) ::
|
||||
("faction" | PlanetSideEmpire.codec) ::
|
||||
("bops" | bool) ::
|
||||
("destroyed" | bool) ::
|
||||
("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 internals data regarding the mounted weapon
|
||||
*/
|
||||
final case class SmallTurretData(deploy : CommonFieldData,
|
||||
final case class SmallTurretData(deploy : SmallDeployableData,
|
||||
health : Int,
|
||||
internals : Option[InternalSlot] = None
|
||||
internals : Option[InventoryData] = None
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
val deploySize = deploy.bitsize
|
||||
val internalSize = if(internals.isDefined) { CommonFieldData.internalWeapon_bitsize + internals.get.bitsize } else { 0L }
|
||||
23L + deploySize + internalSize //1u + 8u + 7u + 4u + 2u + 1u
|
||||
val internalSize = internals match {
|
||||
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
|
||||
* @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))
|
||||
|
||||
/**
|
||||
|
|
@ -81,21 +86,20 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
|
|||
)
|
||||
|
||||
implicit val codec : Codec[SmallTurretData] = (
|
||||
("deploy" | CommonFieldData.codec) ::
|
||||
bool ::
|
||||
("deploy" | SmallDeployableData.codec) ::
|
||||
("health" | uint8L) ::
|
||||
uintL(7) ::
|
||||
uint4L ::
|
||||
uint2L ::
|
||||
optional(bool, "internals" | CommonFieldData.internalWeaponCodec)
|
||||
optional(bool, "internals" | InventoryData.codec)
|
||||
).exmap[SmallTurretData] (
|
||||
{
|
||||
case deploy :: false :: health :: 0 :: 0xF :: 0 :: internals :: HNil =>
|
||||
var newHealth : Int = health
|
||||
var newInternals : Option[InternalSlot] = internals
|
||||
if(health == 0 || internals.isEmpty) {
|
||||
newHealth = 0
|
||||
newInternals = None
|
||||
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(SmallTurretData(deploy, newHealth, newInternals))
|
||||
|
||||
|
|
@ -104,13 +108,13 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
|
|||
},
|
||||
{
|
||||
case SmallTurretData(deploy, health, internals) =>
|
||||
var newHealth : Int = health
|
||||
var newInternals : Option[InternalSlot] = internals
|
||||
if(health == 0 || internals.isEmpty) {
|
||||
newHealth = 0
|
||||
newInternals = None
|
||||
val (newHealth, newInternals) = if(health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
|
||||
(0, 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 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
|
||||
) extends ConstructorData {
|
||||
override def bitsize : Long = {
|
||||
23L + deploy.bitsize //8u + 7u + 4u + 3u + 1u
|
||||
22L + deploy.bitsize //8u + 7u + 4u + 3u
|
||||
}
|
||||
}
|
||||
|
||||
object TRAPData extends Marshallable[TRAPData] {
|
||||
implicit val codec : Codec[TRAPData] = (
|
||||
("deploy" | CommonFieldData.codec) ::
|
||||
bool ::
|
||||
("deploy" | SmallDeployableData.codec) ::
|
||||
("health" | uint8L) ::
|
||||
uint(7) ::
|
||||
uint4L ::
|
||||
uint(3)
|
||||
).exmap[TRAPData] (
|
||||
{
|
||||
case deploy :: false :: health :: 0 :: 15 :: 0 :: HNil =>
|
||||
case deploy :: health :: 0 :: 15 :: 0 :: HNil =>
|
||||
Attempt.successful(TRAPData(deploy, health))
|
||||
|
||||
case _ =>
|
||||
|
|
@ -38,7 +37,7 @@ object TRAPData extends Marshallable[TRAPData] {
|
|||
},
|
||||
{
|
||||
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)
|
||||
).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.
|
||||
* @param pos1 the first point
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
|
|||
*/
|
||||
var secondHeap : List[RemoverActor.Entry] = List()
|
||||
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
protected var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
val sameEntryComparator = new SimilarityComparator[RemoverActor.Entry]() {
|
||||
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)
|
||||
if(InclusionTest(entry) && !secondHeap.exists(test => sameEntryComparator.Test(test, 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
|
||||
firstHeap = List(entry)
|
||||
trace(s"a remover task has been added: $entry")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package services.avatar
|
||||
|
||||
import net.psforever.objects.ballistics.SourceEntry
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
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.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
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
|
||||
|
||||
|
|
@ -23,20 +24,23 @@ object AvatarAction {
|
|||
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 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 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 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 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 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 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 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 PutDownFDU(player_guid : PlanetSideGUID) 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 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 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.equipment.Equipment
|
||||
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.types.{ExoSuitType, Vector3}
|
||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
|
||||
|
||||
object AvatarResponse {
|
||||
trait Response
|
||||
|
|
@ -30,8 +30,10 @@ object AvatarResponse {
|
|||
final case class ObjectHeld(slot : Int) 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 PutDownFDU(target_guid : PlanetSideGUID) extends Response
|
||||
final case class Release(player : Player) 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 WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import services.{GenericEventBus, RemoverActor, Service}
|
|||
class AvatarService extends Actor {
|
||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||
private val janitor = context.actorOf(Props[DroppedItemRemover], "item-remover-agent")
|
||||
//undertaker ! "startup"
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
|
|
@ -69,6 +68,14 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
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) =>
|
||||
AvatarEvents.publish(
|
||||
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) =>
|
||||
undertaker forward RemoverActor.AddTask(player, zone, time)
|
||||
AvatarEvents.publish(
|
||||
|
|
@ -157,6 +168,10 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
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) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.StowEquipment(target_guid, slot, obj))
|
||||
|
|
@ -177,6 +192,7 @@ class AvatarService extends Actor {
|
|||
case AvatarServiceMessage.Corpse(msg) =>
|
||||
undertaker forward msg
|
||||
|
||||
//message to Janitor
|
||||
case AvatarServiceMessage.Ground(msg) =>
|
||||
janitor forward msg
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class DroppedItemRemover extends RemoverActor {
|
|||
|
||||
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.galaxy
|
||||
|
||||
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
|
||||
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||
|
||||
object GalaxyAction {
|
||||
trait Action
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.galaxy
|
||||
|
||||
import net.psforever.packet.game.{BuildingInfoUpdateMessage}
|
||||
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||
|
||||
object GalaxyResponse {
|
||||
trait Response
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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.doors.Door
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
|
||||
object LocalAction {
|
||||
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 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
|
||||
|
|
@ -19,6 +23,9 @@ object LocalAction {
|
|||
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action
|
||||
final case class HackCaptureTerminal(player_guid : PlanetSideGUID, continent : Zone, target : CaptureTerminal, unk1 : Long, unk2 : Long = 8L, isResecured : Boolean) extends Action
|
||||
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||
final case class TriggerEffect(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID) extends Action
|
||||
final case class TriggerEffectInfo(player_guid : PlanetSideGUID, effect : String, target : PlanetSideGUID, unk1 : Boolean, unk2 : Long) extends Action
|
||||
final case class TriggerEffectLocation(player_guid : PlanetSideGUID, effect : String, pos : Vector3, orient : Vector3) extends Action
|
||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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}
|
||||
|
||||
object LocalResponse {
|
||||
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 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 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 ObjectDelete(item_guid : PlanetSideGUID, unk : Int) 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 SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,28 +2,30 @@
|
|||
package services.local
|
||||
|
||||
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.GetWorld
|
||||
import services.local.support.{DoorCloseActor, HackCaptureActor, HackClearActor}
|
||||
import net.psforever.objects.{BoomerDeployable, GlobalDefinitions, PlanetSideGameObject, TurretDeployable}
|
||||
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.local.support.{DoorCloseActor, HackClearActor}
|
||||
|
||||
import scala.util.Success
|
||||
import scala.concurrent.duration._
|
||||
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 scala.concurrent.duration.Duration
|
||||
|
||||
class LocalService extends Actor {
|
||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
||||
private val hackCapturer = context.actorOf(Props[HackCaptureActor], "local-hack-capturer")
|
||||
private val engineer = context.actorOf(Props[DeployableRemover], "deployable-remover-agent")
|
||||
private [this] val log = org.log4s.getLogger
|
||||
var cluster : ActorRef = Actor.noSender
|
||||
|
||||
|
|
@ -59,6 +61,14 @@ class LocalService extends Actor {
|
|||
|
||||
case LocalServiceMessage(forChannel, action) =>
|
||||
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) =>
|
||||
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
||||
LocalEvents.publish(
|
||||
|
|
@ -77,7 +87,7 @@ class LocalService extends Actor {
|
|||
LocalEvents.publish(
|
||||
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)
|
||||
case LocalAction.HackCaptureTerminal(player_guid, zone, target, unk1, unk2, isResecured) =>
|
||||
|
||||
|
|
@ -101,14 +111,26 @@ class LocalService extends Actor {
|
|||
LocalEvents.publish(
|
||||
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) =>
|
||||
LocalEvents.publish(
|
||||
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 _ => ;
|
||||
}
|
||||
|
||||
|
|
@ -120,21 +142,21 @@ class LocalService extends Actor {
|
|||
|
||||
//response from HackClearActor
|
||||
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(
|
||||
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
|
||||
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 building = terminal.Owner.asInstanceOf[Building]
|
||||
|
||||
// todo: Move this to a function for Building
|
||||
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) =>
|
||||
ntuLevel = obj.CapacitorDisplay.toInt
|
||||
case _ =>
|
||||
|
|
@ -143,7 +165,7 @@ class LocalService extends Actor {
|
|||
}
|
||||
|
||||
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
|
||||
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 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 =>
|
||||
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
|
||||
|
||||
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 net.psforever.objects.{AmmoBox, DefaultCancellable, PlanetSideGameObject, Tool}
|
||||
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.zones.Zone
|
||||
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 InclusionTest(entry : TurretUpgrader.Entry) : Boolean = entry.obj.isInstanceOf[MannedTurret]
|
||||
def InclusionTest(entry : TurretUpgrader.Entry) : Boolean = entry.obj.isInstanceOf[FacilityTurret]
|
||||
|
||||
def receive : Receive = {
|
||||
case Service.Startup() =>
|
||||
|
|
@ -157,7 +157,7 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
|
|||
* @param entry na
|
||||
*/
|
||||
def UpgradeTurretAmmo(entry : TurretUpgrader.Entry) : Unit = {
|
||||
val target = entry.obj.asInstanceOf[MannedTurret]
|
||||
val target = entry.obj.asInstanceOf[FacilityTurret]
|
||||
val zone = entry.zone
|
||||
val zoneId = zone.Id
|
||||
val upgrade = entry.upgrade
|
||||
|
|
@ -222,7 +222,7 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
|
|||
* @param entry na
|
||||
*/
|
||||
def FinishUpgradingTurret(entry : TurretUpgrader.Entry)() : Unit = {
|
||||
val target = entry.obj.asInstanceOf[MannedTurret]
|
||||
val target = entry.obj.asInstanceOf[FacilityTurret]
|
||||
val zone = entry.zone
|
||||
info(s"Wall turret finished ${target.Upgrade} upgrade")
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@ class Vector3Test extends Specification {
|
|||
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 {
|
||||
val obj = Vector3(2.0f, 0.0f, 0.0f)
|
||||
Vector3.Magnitude(obj) mustEqual 2.0f
|
||||
|
|
|
|||
|
|
@ -12,15 +12,11 @@ class DeployObjectMessageTest extends Specification {
|
|||
|
||||
"decode" in {
|
||||
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)
|
||||
unk1 mustEqual 1000L
|
||||
pos.x mustEqual 5769.297f
|
||||
pos.y mustEqual 3192.8594f
|
||||
pos.z mustEqual 97.96875f
|
||||
roll mustEqual 0
|
||||
pitch mustEqual 0
|
||||
yaw mustEqual 63
|
||||
pos mustEqual Vector3(5769.297f, 3192.8594f, 97.96875f)
|
||||
orient mustEqual Vector3.z(272.8125f)
|
||||
unk2 mustEqual 1L
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -28,7 +24,7 @@ class DeployObjectMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"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
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ class ObjectDeployedMessageTest extends Specification {
|
|||
|
||||
"decode" in {
|
||||
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
|
||||
desc mustEqual "boomer"
|
||||
act mustEqual DeploymentOutcome.Success
|
||||
act mustEqual DeployOutcome.Success
|
||||
count mustEqual 1
|
||||
max mustEqual 25
|
||||
case _ =>
|
||||
|
|
@ -23,7 +23,7 @@ class ObjectDeployedMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"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
|
||||
|
||||
pkt mustEqual string_boomer
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import scodec.bits._
|
|||
class TriggerEffectMessageTest extends Specification {
|
||||
val string_motionalarmsensor = hex"51 970B 82 6F6E FA00C00000"
|
||||
val string_boomer = hex"51 0000 93 737061776E5F6F626A6563745F656666656374 417BB2CB3B4F8E00000000"
|
||||
val string_boomer_explode = hex"51 DF09 8F 6465746F6E6174655F626F6F6D6572 00"
|
||||
|
||||
"decode (motion alarm sensor)" in {
|
||||
PacketCoding.DecodePacket(string_motionalarmsensor).require match {
|
||||
|
|
@ -32,42 +33,44 @@ class TriggerEffectMessageTest extends Specification {
|
|||
effect mustEqual "spawn_object_effect"
|
||||
unk.isDefined mustEqual false
|
||||
location.isDefined mustEqual true
|
||||
location.get.pos.x mustEqual 3567.0156f
|
||||
location.get.pos.y mustEqual 3278.6953f
|
||||
location.get.pos.z mustEqual 114.484375f
|
||||
location.get.roll mustEqual 0
|
||||
location.get.pitch mustEqual 0
|
||||
location.get.yaw mustEqual 0
|
||||
location.get.pos mustEqual Vector3(3567.0156f, 3278.6953f, 114.484375f)
|
||||
location.get.orient mustEqual Vector3(0, 0, 90)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"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 _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (motion alarm sensor)" in {
|
||||
val msg = TriggerEffectMessage(
|
||||
PlanetSideGUID(2967),
|
||||
"on",
|
||||
Some(TriggeredEffect(true, 1000L)),
|
||||
None
|
||||
)
|
||||
val msg = TriggerEffectMessage(PlanetSideGUID(2967), "on", true, 1000L)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_motionalarmsensor
|
||||
}
|
||||
|
||||
"encode (boomer)" in {
|
||||
val msg = TriggerEffectMessage(
|
||||
PlanetSideGUID(0),
|
||||
"spawn_object_effect",
|
||||
None,
|
||||
Some(TriggeredEffectLocation(
|
||||
Vector3(3567.0156f, 3278.6953f, 114.484375f),
|
||||
0, 0, 0
|
||||
))
|
||||
)
|
||||
val msg = TriggerEffectMessage("spawn_object_effect", Vector3(3567.0156f, 3278.6953f, 114.484375f), Vector3(0, 0, 90))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
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.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -22,12 +22,8 @@ class AegisShieldGeneratorDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[AegisShieldGeneratorData] mustEqual true
|
||||
val aegis = data.get.asInstanceOf[AegisShieldGeneratorData]
|
||||
aegis.deploy.pos.coord.x mustEqual 3571.2266f
|
||||
aegis.deploy.pos.coord.y mustEqual 3278.0938f
|
||||
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.pos.coord mustEqual Vector3(3571.2266f, 3278.0938f, 114.0f)
|
||||
aegis.deploy.pos.orient mustEqual Vector3(0, 0, 90)
|
||||
aegis.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||
aegis.deploy.unk mustEqual 2
|
||||
aegis.health mustEqual 255
|
||||
|
|
@ -40,7 +36,7 @@ class AegisShieldGeneratorDataTest extends Specification {
|
|||
"encode" in {
|
||||
val obj = AegisShieldGeneratorData(
|
||||
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)
|
||||
),
|
||||
255
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
|||
import net.psforever.packet.PacketCoding
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -22,23 +22,19 @@ class OneMannedFieldTurretDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[OneMannedFieldTurretData] mustEqual true
|
||||
val omft = data.get.asInstanceOf[OneMannedFieldTurretData]
|
||||
omft.deploy.pos.coord.x mustEqual 3567.1406f
|
||||
omft.deploy.pos.coord.y mustEqual 2988.0078f
|
||||
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.pos.coord mustEqual Vector3(3567.1406f, 2988.0078f, 71.84375f)
|
||||
omft.deploy.pos.orient mustEqual Vector3(0, 0, 185.625f)
|
||||
omft.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||
omft.deploy.unk mustEqual 2
|
||||
omft.deploy.player_guid mustEqual PlanetSideGUID(2502)
|
||||
omft.deploy.unk1 mustEqual 2
|
||||
omft.deploy.owner_guid mustEqual PlanetSideGUID(2502)
|
||||
omft.health mustEqual 255
|
||||
omft.internals.isDefined mustEqual true
|
||||
val internals = omft.internals.get
|
||||
internals.objectClass mustEqual ObjectClass.energy_gun_vs
|
||||
internals.guid mustEqual PlanetSideGUID(2615)
|
||||
internals.parentSlot mustEqual 1
|
||||
internals.obj.isInstanceOf[WeaponData] mustEqual true
|
||||
val wep = internals.obj.asInstanceOf[WeaponData]
|
||||
val internals = omft.internals.get.contents
|
||||
internals.head.objectClass mustEqual ObjectClass.energy_gun_vs
|
||||
internals.head.guid mustEqual PlanetSideGUID(2615)
|
||||
internals.head.parentSlot mustEqual 1
|
||||
internals.head.obj.isInstanceOf[WeaponData] mustEqual true
|
||||
val wep = internals.head.obj.asInstanceOf[WeaponData]
|
||||
wep.unk1 mustEqual 0x6
|
||||
wep.unk2 mustEqual 0x8
|
||||
wep.fire_mode mustEqual 0
|
||||
|
|
@ -55,20 +51,16 @@ class OneMannedFieldTurretDataTest extends Specification {
|
|||
|
||||
"encode (orion)" in {
|
||||
val obj = OneMannedFieldTurretData(
|
||||
CommonFieldData(
|
||||
PlacementData(3567.1406f, 2988.0078f, 71.84375f, 0f, 0f, 185.625f),
|
||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(2502)
|
||||
SmallDeployableData(
|
||||
PlacementData(Vector3(3567.1406f, 2988.0078f, 71.84375f), Vector3(0, 0, 185.625f)),
|
||||
PlanetSideEmpire.VS, false, false, 2, false, false, PlanetSideGUID(2502)
|
||||
),
|
||||
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 pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
val pkt_bitv = pkt.toBitVector
|
||||
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
|
||||
pkt mustEqual string_orion
|
||||
}
|
||||
|
||||
"avenger" in {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
|||
import net.psforever.packet.PacketCoding
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -22,14 +22,14 @@ class SmallDeployableDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[SmallDeployableData] mustEqual true
|
||||
val boomer = data.get.asInstanceOf[SmallDeployableData]
|
||||
boomer.deploy.pos.coord.x mustEqual 4704.172f
|
||||
boomer.deploy.pos.coord.y mustEqual 5546.4375f
|
||||
boomer.deploy.pos.coord.z mustEqual 82.234375f
|
||||
boomer.deploy.pos.orient.x mustEqual 0f
|
||||
boomer.deploy.pos.orient.y mustEqual 0f
|
||||
boomer.deploy.pos.orient.z mustEqual 272.8125f
|
||||
boomer.deploy.unk mustEqual 0
|
||||
boomer.deploy.player_guid mustEqual PlanetSideGUID(4145)
|
||||
boomer.pos.coord.x mustEqual 4704.172f
|
||||
boomer.pos.coord.y mustEqual 5546.4375f
|
||||
boomer.pos.coord.z mustEqual 82.234375f
|
||||
boomer.pos.orient.x mustEqual 0f
|
||||
boomer.pos.orient.y mustEqual 0f
|
||||
boomer.pos.orient.z mustEqual 272.8125f
|
||||
boomer.unk1 mustEqual 0
|
||||
boomer.owner_guid mustEqual PlanetSideGUID(8290)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -37,10 +37,8 @@ class SmallDeployableDataTest extends Specification {
|
|||
|
||||
"encode (boomer)" in {
|
||||
val obj = SmallDeployableData(
|
||||
CommonFieldData(
|
||||
PlacementData(4704.172f, 5546.4375f, 82.234375f, 0f, 0f, 272.8125f),
|
||||
PlanetSideEmpire.TR, 0, PlanetSideGUID(4145)
|
||||
)
|
||||
PlacementData(Vector3(4704.172f, 5546.4375f, 82.234375f), Vector3.z(272.8125f)),
|
||||
PlanetSideEmpire.TR, false, false, 0, false, false, PlanetSideGUID(8290)
|
||||
)
|
||||
val msg = ObjectCreateMessage(ObjectClass.boomer, PlanetSideGUID(3840), obj)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package game.objectcreate
|
|||
|
||||
import net.psforever.packet.PacketCoding
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.packet.game.objectcreate.{SmallDeployableData, _}
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -23,16 +23,12 @@ class SmallTurretDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
||||
val turret = data.get.asInstanceOf[SmallTurretData]
|
||||
turret.deploy.pos.coord.x mustEqual 4577.7812f
|
||||
turret.deploy.pos.coord.y mustEqual 5624.828f
|
||||
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.pos.coord mustEqual Vector3(4577.7812f, 5624.828f, 72.046875f)
|
||||
turret.deploy.pos.orient mustEqual Vector3(0, 2.8125f, 264.375f)
|
||||
turret.deploy.faction mustEqual PlanetSideEmpire.NC
|
||||
turret.deploy.destroyed mustEqual true
|
||||
turret.deploy.unk mustEqual 2
|
||||
turret.deploy.player_guid mustEqual PlanetSideGUID(3871)
|
||||
turret.deploy.unk1 mustEqual 2
|
||||
turret.deploy.owner_guid mustEqual PlanetSideGUID(7742)
|
||||
turret.health mustEqual 0
|
||||
turret.internals.isDefined mustEqual false
|
||||
case _ =>
|
||||
|
|
@ -50,23 +46,19 @@ class SmallTurretDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[SmallTurretData] mustEqual true
|
||||
val turret = data.get.asInstanceOf[SmallTurretData]
|
||||
turret.deploy.pos.coord.x mustEqual 4527.633f
|
||||
turret.deploy.pos.coord.y mustEqual 6271.3594f
|
||||
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.pos.coord mustEqual Vector3(4527.633f, 6271.3594f, 70.265625f)
|
||||
turret.deploy.pos.orient mustEqual Vector3(0, 0, 154.6875f)
|
||||
turret.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||
turret.deploy.unk mustEqual 2
|
||||
turret.deploy.player_guid mustEqual PlanetSideGUID(4232)
|
||||
turret.deploy.unk1 mustEqual 2
|
||||
turret.deploy.owner_guid mustEqual PlanetSideGUID(8208)
|
||||
turret.health mustEqual 255
|
||||
turret.internals.isDefined mustEqual true
|
||||
val internals = turret.internals.get
|
||||
internals.objectClass mustEqual ObjectClass.spitfire_weapon
|
||||
internals.guid mustEqual PlanetSideGUID(3064)
|
||||
internals.parentSlot mustEqual 0
|
||||
internals.obj.isInstanceOf[WeaponData] mustEqual true
|
||||
val wep = internals.obj.asInstanceOf[WeaponData]
|
||||
val internals = turret.internals.get.contents
|
||||
internals.head.objectClass mustEqual ObjectClass.spitfire_weapon
|
||||
internals.head.guid mustEqual PlanetSideGUID(3064)
|
||||
internals.head.parentSlot mustEqual 0
|
||||
internals.head.obj.isInstanceOf[WeaponData] mustEqual true
|
||||
val wep = internals.head.obj.asInstanceOf[WeaponData]
|
||||
wep.unk1 mustEqual 0x6
|
||||
wep.unk2 mustEqual 0x8
|
||||
wep.fire_mode mustEqual 0
|
||||
|
|
@ -83,37 +75,29 @@ class SmallTurretDataTest extends Specification {
|
|||
|
||||
"encode (spitfire, short)" in {
|
||||
val obj = SmallTurretData(
|
||||
CommonFieldData(
|
||||
PlacementData(4577.7812f, 5624.828f, 72.046875f, 0f, 2.8125f, 264.375f),
|
||||
PlanetSideEmpire.NC, true, 2, PlanetSideGUID(3871)
|
||||
SmallDeployableData(
|
||||
PlacementData(Vector3(4577.7812f, 5624.828f, 72.046875f), Vector3(0, 2.8125f, 264.375f)),
|
||||
PlanetSideEmpire.NC, false, true, 2, false, false, PlanetSideGUID(7742)
|
||||
),
|
||||
255 //sets to 0
|
||||
)
|
||||
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4208), obj)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
val pkt_bitv = pkt.toBitVector
|
||||
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
|
||||
pkt mustEqual string_spitfire_short
|
||||
}
|
||||
|
||||
"encode (spitfire)" in {
|
||||
val obj = SmallTurretData(
|
||||
CommonFieldData(
|
||||
PlacementData(4527.633f, 6271.3594f, 70.265625f, 0f, 0f, 154.6875f),
|
||||
PlanetSideEmpire.VS, 2, PlanetSideGUID(4232)
|
||||
SmallDeployableData(
|
||||
PlacementData(Vector3(4527.633f, 6271.3594f, 70.265625f), Vector3(0, 0, 154.6875f)),
|
||||
PlanetSideEmpire.VS, false, false, 2, false, true, PlanetSideGUID(8208)
|
||||
),
|
||||
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 pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
val pkt_bitv = pkt.toBitVector
|
||||
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
|
||||
pkt mustEqual string_spitfire
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package game.objectcreate
|
|||
import net.psforever.packet.PacketCoding
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -22,16 +22,11 @@ class TRAPDataTest extends Specification {
|
|||
data.isDefined mustEqual true
|
||||
data.get.isInstanceOf[TRAPData] mustEqual true
|
||||
val trap = data.get.asInstanceOf[TRAPData]
|
||||
trap.deploy.pos.coord.x mustEqual 3572.4453f
|
||||
trap.deploy.pos.coord.y mustEqual 3277.9766f
|
||||
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.pos.coord mustEqual Vector3(3572.4453f, 3277.9766f, 114.0f)
|
||||
trap.deploy.pos.orient mustEqual Vector3(0, 0, 90)
|
||||
trap.deploy.faction mustEqual PlanetSideEmpire.VS
|
||||
trap.deploy.unk mustEqual 2
|
||||
trap.deploy.owner_guid mustEqual PlanetSideGUID(4748)
|
||||
trap.health mustEqual 255
|
||||
trap.deploy.player_guid mustEqual PlanetSideGUID(2502)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -39,9 +34,9 @@ class TRAPDataTest extends Specification {
|
|||
|
||||
"encode" in {
|
||||
val obj = TRAPData(
|
||||
CommonFieldData(
|
||||
SmallDeployableData(
|
||||
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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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.definition._
|
||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.vehicles.DestroyedVehicle
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||
|
|
@ -109,13 +107,7 @@ class ConverterTest extends Specification {
|
|||
|
||||
"ConstructionItem" should {
|
||||
"convert to packet" in {
|
||||
val cdef = ConstructionItemDefinition(Unit.advanced_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)
|
||||
val obj = ConstructionItem(GlobalDefinitions.ace)
|
||||
obj.GUID = PlanetSideGUID(90)
|
||||
obj.Definition.Packet.DetailedConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
|
|
@ -123,6 +115,7 @@ class ConverterTest extends Specification {
|
|||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
||||
obj.Definition.Packet.ConstructorData(obj) match {
|
||||
case Success(pkt) =>
|
||||
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 {
|
||||
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
|
||||
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
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
import net.psforever.objects.ce.DeployedItem
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.CertificationType
|
||||
import org.specs2.mutable._
|
||||
|
||||
class EquipmentTest extends Specification {
|
||||
|
|
@ -329,62 +331,57 @@ class EquipmentTest extends Specification {
|
|||
}
|
||||
|
||||
"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 {
|
||||
val obj : ConstructionItem = ConstructionItem(advanced_ace_tr)
|
||||
obj.Definition.ObjectId mustEqual advanced_ace_tr.ObjectId
|
||||
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||
obj.Definition.ObjectId mustEqual GlobalDefinitions.ace.ObjectId
|
||||
}
|
||||
|
||||
"fire mode" in {
|
||||
//explanation: router_telepad has one fire mode and that fire mode is our only option
|
||||
val router_telepad : ConstructionItemDefinition = ConstructionItemDefinition(Unit.router_telepad)
|
||||
router_telepad.Modes += DeployedItem.router_telepad_deployable
|
||||
val obj : ConstructionItem = ConstructionItem(router_telepad)
|
||||
//fmode = 0
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.FireMode mustEqual DeployedItem.router_telepad_deployable
|
||||
//fmode -> 1 (0)
|
||||
obj.FireModeIndex = 1
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.FireMode mustEqual DeployedItem.router_telepad_deployable
|
||||
"fire modes" in {
|
||||
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||
obj.AmmoType mustEqual DeployedItem.boomer
|
||||
obj.NextFireMode
|
||||
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||
obj.NextFireMode
|
||||
obj.AmmoType mustEqual DeployedItem.spitfire_turret
|
||||
obj.NextFireMode
|
||||
obj.AmmoType mustEqual DeployedItem.motionalarmsensor
|
||||
obj.NextFireMode
|
||||
obj.AmmoType mustEqual DeployedItem.boomer
|
||||
}
|
||||
|
||||
"multiple fire modes" in {
|
||||
//explanation: advanced_ace_tr has three fire modes; adjusting the FireMode changes between them
|
||||
val obj : ConstructionItem = ConstructionItem(advanced_ace_tr)
|
||||
//fmode = 0
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.FireMode mustEqual DeployedItem.tank_traps
|
||||
//fmode -> 1
|
||||
"ammo types" in {
|
||||
val obj : ConstructionItem = ConstructionItem(GlobalDefinitions.ace)
|
||||
obj.NextFireMode
|
||||
obj.FireModeIndex mustEqual 1
|
||||
obj.FireMode mustEqual DeployedItem.portable_manned_turret_tr
|
||||
//fmode -> 2
|
||||
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||
obj.NextAmmoType
|
||||
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.FireModeIndex mustEqual 2
|
||||
obj.FireMode mustEqual DeployedItem.deployable_shield_generator
|
||||
//fmode -> 0
|
||||
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||
obj.NextAmmoType
|
||||
obj.AmmoType mustEqual DeployedItem.jammer_mine
|
||||
obj.NextFireMode //spitfire_turret
|
||||
obj.NextFireMode //motionalarmsensor
|
||||
obj.NextFireMode //boomer
|
||||
obj.NextFireMode
|
||||
obj.FireModeIndex mustEqual 0
|
||||
obj.FireMode mustEqual DeployedItem.tank_traps
|
||||
obj.AmmoType mustEqual DeployedItem.he_mine
|
||||
}
|
||||
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
"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
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.equipment.CItem.{DeployedItem, Unit}
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.types.ExoSuitType
|
||||
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.serverobject.mount.Mountable
|
||||
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.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||
|
|
@ -16,10 +16,10 @@ import org.specs2.mutable.Specification
|
|||
import scala.collection.mutable
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class MannedTurretTest extends Specification {
|
||||
"MannedTurretTest" should {
|
||||
class FacilityTurretTest extends Specification {
|
||||
"FacilityTurretTest" should {
|
||||
"define" in {
|
||||
val obj = new MannedTurretDefinition(480)
|
||||
val obj = new TurretDefinition(480)
|
||||
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
|
||||
obj.ReserveAmmunition mustEqual false
|
||||
obj.FactionLocked mustEqual true
|
||||
|
|
@ -28,7 +28,7 @@ class MannedTurretTest extends Specification {
|
|||
}
|
||||
|
||||
"construct" in {
|
||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
||||
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||
obj.Weapons.size mustEqual 1
|
||||
obj.Weapons(1).Equipment match {
|
||||
case Some(tool : Tool) =>
|
||||
|
|
@ -51,7 +51,7 @@ class MannedTurretTest extends Specification {
|
|||
}
|
||||
|
||||
"upgrade to a different weapon" in {
|
||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
||||
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||
obj.Upgrade = TurretUpgrade.None
|
||||
obj.Weapons(1).Equipment match {
|
||||
case Some(tool : Tool) =>
|
||||
|
|
@ -87,26 +87,26 @@ class MannedTurretTest extends Specification {
|
|||
}
|
||||
}
|
||||
|
||||
class MannedTurretControl1Test extends ActorTest {
|
||||
"MannedTurretControl" should {
|
||||
class FacilityTurretControl1Test extends ActorTest {
|
||||
"FacilityTurretControl" should {
|
||||
"construct" in {
|
||||
val obj = MannedTurret(GlobalDefinitions.manned_turret)
|
||||
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
|
||||
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
|
||||
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 obj = MannedTurret(GlobalDefinitions.manned_turret)
|
||||
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||
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)
|
||||
bldg.Amenities = obj
|
||||
bldg.Faction = PlanetSideEmpire.TR
|
||||
|
||||
"MannedTurretControl" should {
|
||||
"FacilityTurretControl" should {
|
||||
"seat on faction affiliation when FactionLock is true" in {
|
||||
assert(player.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 obj = MannedTurret(GlobalDefinitions.manned_turret)
|
||||
val obj = FacilityTurret(GlobalDefinitions.manned_turret)
|
||||
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)
|
||||
bldg.Amenities = obj
|
||||
|
||||
"MannedTurretControl" should {
|
||||
"FacilityTurretControl" should {
|
||||
"block seating on mismatched faction affiliation when FactionLock is true" in {
|
||||
assert(player.Faction == PlanetSideEmpire.TR)
|
||||
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 objDef = new MannedTurretDefinition(480)
|
||||
val objDef = new TurretDefinition(480)
|
||||
objDef.FactionLocked = false
|
||||
val obj = MannedTurret(objDef)
|
||||
val obj = FacilityTurret(objDef)
|
||||
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)
|
||||
bldg.Amenities = obj
|
||||
|
||||
"MannedTurretControl" should {
|
||||
"FacilityTurretControl" should {
|
||||
"seating even with mismatched faction affiliation when FactionLock is false" in {
|
||||
assert(player.Faction == PlanetSideEmpire.TR)
|
||||
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
|
||||
|
|
@ -191,7 +191,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 0)
|
||||
|
||||
val reply4 = probe1.receiveOne(500 milliseconds)
|
||||
assert(obj.LowNtuWarningOn == false)
|
||||
assert(!obj.LowNtuWarningOn)
|
||||
assert(reply4.isInstanceOf[AvatarServiceMessage])
|
||||
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
|
||||
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.serverobject.turret.MannedTurret
|
||||
"MannedTurretObjectBuilder" should {
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
"FacilityTurretObjectBuilder" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
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 ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(1000, "ms"))
|
||||
assert(reply.isInstanceOf[MannedTurret])
|
||||
assert(reply.asInstanceOf[MannedTurret].HasGUID)
|
||||
assert(reply.asInstanceOf[MannedTurret].GUID == PlanetSideGUID(1))
|
||||
assert(reply.isInstanceOf[FacilityTurret])
|
||||
assert(reply.asInstanceOf[FacilityTurret].HasGUID)
|
||||
assert(reply.asInstanceOf[FacilityTurret].GUID == PlanetSideGUID(1))
|
||||
assert(reply == hub(1).get)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import base.ActorTest
|
|||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
|
||||
class GUIDTaskRegister2Test extends ActorTest {
|
||||
class GUIDTaskRegisterAmmoTest extends ActorTest {
|
||||
"RegisterEquipment -> RegisterObjectTask" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = AmmoBox(GlobalDefinitions.energy_cell)
|
||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects._
|
|||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||
|
||||
class GUIDTaskRegister5Test extends ActorTest {
|
||||
class GUIDTaskRegisterAvatarTest extends ActorTest {
|
||||
"RegisterAvatar" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
|
|
@ -4,7 +4,7 @@ package objects.guidtask
|
|||
import base.ActorTest
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
|
||||
class GUIDTaskRegister1Test extends ActorTest {
|
||||
class GUIDTaskRegisterObjectTest extends ActorTest {
|
||||
"RegisterObjectTask" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = new GUIDTaskTest.TestObject
|
||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects._
|
|||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
|
||||
|
||||
class GUIDTaskRegister6Test extends ActorTest {
|
||||
class GUIDTaskRegisterPlayerTest extends ActorTest {
|
||||
"RegisterPlayer" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
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.guid.{GUIDTask, TaskResolver}
|
||||
|
||||
class GUIDTaskRegister3Test extends ActorTest {
|
||||
class GUIDTaskRegisterToolTest extends ActorTest {
|
||||
"RegisterEquipment -> RegisterTool" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
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.guid.{GUIDTask, TaskResolver}
|
||||
|
||||
class GUIDTaskRegister4Test extends ActorTest {
|
||||
class GUIDTaskRegisterVehicleTest extends ActorTest {
|
||||
"RegisterVehicle" in {
|
||||
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
|
||||
val obj = Vehicle(GlobalDefinitions.fury)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue