mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
commit
a5f1638fd6
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
|
||||
class BoomerDeployable(cdef : DeployableDefinition) extends ExplosiveDeployable(cdef) {
|
||||
class BoomerDeployable(cdef : ExplosiveDeployableDefinition) extends ExplosiveDeployable(cdef) {
|
||||
private var trigger : Option[BoomerTrigger] = None
|
||||
|
||||
def Trigger : Option[BoomerTrigger] = trigger
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.packet.game.{DeployableInfo, DeploymentAction, PlanetSideGUID}
|
||||
import services.RemoverActor
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
object Deployables {
|
||||
object Make {
|
||||
|
|
@ -26,4 +32,43 @@ object Deployables {
|
|||
DeployedItem.router_telepad_deployable -> { () => new TelepadDeployable(GlobalDefinitions.router_telepad_deployable) }
|
||||
).withDefaultValue( { ()=> new ExplosiveDeployable(GlobalDefinitions.boomer) } )
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute information that a deployable has been destroyed.
|
||||
* The deployable may not have yet been eliminated from the game world (client or server),
|
||||
* but its health is zero and it has entered the conditions where it is nearly irrelevant.<br>
|
||||
* <br>
|
||||
* The typical use case of this function involves destruction via weapon fire, attributed to a particular player.
|
||||
* Contrast this to simply destroying a deployable by being the deployable's owner and using the map icon controls.
|
||||
* This function eventually invokes the same routine
|
||||
* but mainly goes into effect when the deployable has been destroyed
|
||||
* and may still leave a physical component in the game world to be cleaned up later.
|
||||
* That is the task `EliminateDeployable` performs.
|
||||
* Additionally, since the player who destroyed the deployable isn't necessarily the owner,
|
||||
* and the real owner will still be aware of the existence of the deployable,
|
||||
* that player must be informed of the loss of the deployable directly.
|
||||
* @see `DeployableRemover`
|
||||
* @see `Vitality.DamageResolution`
|
||||
* @see `LocalResponse.EliminateDeployable`
|
||||
* @see `DeconstructDeployable`
|
||||
* @param target the deployable that is destroyed
|
||||
* @param time length of time that the deployable is allowed to exist in the game world;
|
||||
* `None` indicates the normal un-owned existence time (180 seconds)
|
||||
*/
|
||||
def AnnounceDestroyDeployable(target : PlanetSideServerObject with Deployable, time : Option[FiniteDuration]) : Unit = {
|
||||
val zone = target.Zone
|
||||
target.OwnerName match {
|
||||
case Some(owner) =>
|
||||
target.OwnerName = None
|
||||
zone.LocalEvents ! LocalServiceMessage(owner, LocalAction.AlertDestroyDeployable(PlanetSideGUID(0), target))
|
||||
case None => ;
|
||||
}
|
||||
zone.LocalEvents ! LocalServiceMessage(s"${target.Faction}", LocalAction.DeployableMapIcon(
|
||||
PlanetSideGUID(0),
|
||||
DeploymentAction.Dismiss,
|
||||
DeployableInfo(target.GUID, Deployable.Icon(target.Definition.Item), target.Position, PlanetSideGUID(0)))
|
||||
)
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(target), zone))
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(target, zone, time))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.SimpleDeployable
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import akka.actor.{Actor, ActorContext, Props}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.ce._
|
||||
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
|
||||
import net.psforever.objects.definition.converter.SmallDeployableConverter
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vital.{StandardResolutions, Vitality}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
class ExplosiveDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef) {
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class ExplosiveDeployable(cdef : ExplosiveDeployableDefinition) extends ComplexDeployable(cdef)
|
||||
with JammableUnit {
|
||||
private var exploded : Boolean = false
|
||||
|
||||
def Exploded : Boolean = exploded
|
||||
|
|
@ -13,4 +26,86 @@ class ExplosiveDeployable(cdef : DeployableDefinition) extends SimpleDeployable(
|
|||
exploded = fuse
|
||||
Exploded
|
||||
}
|
||||
|
||||
override def Definition : ExplosiveDeployableDefinition = cdef
|
||||
}
|
||||
|
||||
class ExplosiveDeployableDefinition(private val objectId : Int) extends ComplexDeployableDefinition(objectId) {
|
||||
Name = "explosive_deployable"
|
||||
DeployCategory = DeployableCategory.Mines
|
||||
Model = StandardResolutions.SimpleDeployables
|
||||
Packet = new SmallDeployableConverter
|
||||
|
||||
private var detonateOnJamming : Boolean = true
|
||||
|
||||
def DetonateOnJamming : Boolean = detonateOnJamming
|
||||
|
||||
def DetonateOnJamming_=(detonate : Boolean) : Boolean = {
|
||||
detonateOnJamming = detonate
|
||||
DetonateOnJamming
|
||||
}
|
||||
|
||||
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
obj.Actor = context.actorOf(Props(classOf[ExplosiveDeployableControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
|
||||
}
|
||||
|
||||
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
SimpleDeployableDefinition.SimpleUninitialize(obj, context)
|
||||
}
|
||||
}
|
||||
|
||||
object ExplosiveDeployableDefinition {
|
||||
def apply(dtype : DeployedItem.Value) : ExplosiveDeployableDefinition = {
|
||||
new ExplosiveDeployableDefinition(dtype.id)
|
||||
}
|
||||
}
|
||||
|
||||
class ExplosiveDeployableControl(mine : ExplosiveDeployable) extends Actor {
|
||||
def receive : Receive = {
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val originalHealth = mine.Health
|
||||
if(originalHealth > 0) {
|
||||
val cause = damage_func(mine)
|
||||
ExplosiveDeployableControl.HandleDamageResolution(mine, cause, originalHealth - mine.Health)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object ExplosiveDeployableControl {
|
||||
def HandleDamageResolution(target : ExplosiveDeployable, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val playerGUID = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => PlanetSideGUID(0)
|
||||
}
|
||||
if(target.Health == 0) {
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
else if(!target.Jammed && cause.projectile.profile.JammerProjectile) {
|
||||
if(target.Jammed = {
|
||||
val radius = cause.projectile.profile.DamageRadius
|
||||
Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius
|
||||
}) {
|
||||
if(target.Definition.DetonateOnJamming) {
|
||||
target.Zone.LocalEvents ! LocalServiceMessage(target.Zone.Id, LocalAction.Detonate(target.GUID, target))
|
||||
}
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : ExplosiveDeployable, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
val zone = target.Zone
|
||||
Deployables.AnnounceDestroyDeployable(target, Some(if(target.Jammed) 0 seconds else 500 milliseconds))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ 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.structures.SphereOfInfluence
|
||||
import net.psforever.objects.serverobject.turret.{TurretDefinition, TurretUpgrade}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, SeatArmorRestriction, UtilityType}
|
||||
import net.psforever.objects.vital.{DamageType, StandardMaxDamage, StandardResolutions}
|
||||
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire, Vector3}
|
||||
|
|
@ -870,11 +870,11 @@ object GlobalDefinitions {
|
|||
/*
|
||||
combat engineering deployables
|
||||
*/
|
||||
val boomer = DeployableDefinition(DeployedItem.boomer)
|
||||
val boomer = ExplosiveDeployableDefinition(DeployedItem.boomer)
|
||||
|
||||
val he_mine = DeployableDefinition(DeployedItem.he_mine)
|
||||
val he_mine = ExplosiveDeployableDefinition(DeployedItem.he_mine)
|
||||
|
||||
val jammer_mine = DeployableDefinition(DeployedItem.jammer_mine)
|
||||
val jammer_mine = ExplosiveDeployableDefinition(DeployedItem.jammer_mine)
|
||||
|
||||
val spitfire_turret = TurretDeployableDefinition(DeployedItem.spitfire_turret)
|
||||
|
||||
|
|
@ -882,11 +882,11 @@ object GlobalDefinitions {
|
|||
|
||||
val spitfire_aa = TurretDeployableDefinition(DeployedItem.spitfire_aa)
|
||||
|
||||
val motionalarmsensor = DeployableDefinition(DeployedItem.motionalarmsensor)
|
||||
val motionalarmsensor = SensorDeployableDefinition(DeployedItem.motionalarmsensor)
|
||||
|
||||
val sensor_shield = DeployableDefinition(DeployedItem.sensor_shield)
|
||||
val sensor_shield = SensorDeployableDefinition(DeployedItem.sensor_shield)
|
||||
|
||||
val tank_traps = DeployableDefinition(DeployedItem.tank_traps)
|
||||
val tank_traps = SimpleDeployableDefinition(DeployedItem.tank_traps)
|
||||
|
||||
val portable_manned_turret = TurretDeployableDefinition(DeployedItem.portable_manned_turret)
|
||||
|
||||
|
|
@ -898,10 +898,10 @@ object GlobalDefinitions {
|
|||
|
||||
val deployable_shield_generator = new ShieldGeneratorDefinition
|
||||
|
||||
val router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||
val router_telepad_deployable = SimpleDeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||
|
||||
//this is only treated like a deployable
|
||||
val internal_router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||
val internal_router_telepad_deployable = SimpleDeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||
init_deployables()
|
||||
|
||||
/*
|
||||
|
|
@ -989,7 +989,7 @@ object GlobalDefinitions {
|
|||
|
||||
val ground_rearm_terminal = new OrderTerminalDefinition(384)
|
||||
|
||||
val manned_turret = new TurretDefinition(480)
|
||||
val manned_turret = new FacilityTurretDefinition(480)
|
||||
|
||||
val painbox = new PainboxDefinition(622)
|
||||
val painbox_continuous = new PainboxDefinition(623)
|
||||
|
|
@ -2686,6 +2686,14 @@ object GlobalDefinitions {
|
|||
jammer_cartridge_projectile.ProjectileDamageType = DamageType.Splash
|
||||
jammer_cartridge_projectile.InitialVelocity = 30
|
||||
jammer_cartridge_projectile.Lifespan = 15f
|
||||
jammer_cartridge_projectile.AdditionalEffect = true
|
||||
jammer_cartridge_projectile.JammerProjectile = true
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000
|
||||
jammer_cartridge_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
|
||||
|
||||
jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b"
|
||||
|
|
@ -2697,6 +2705,14 @@ object GlobalDefinitions {
|
|||
jammer_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
|
||||
jammer_cartridge_projectile_b.InitialVelocity = 30
|
||||
jammer_cartridge_projectile_b.Lifespan = 2f
|
||||
jammer_cartridge_projectile_b.AdditionalEffect = true
|
||||
jammer_cartridge_projectile_b.JammerProjectile = true
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000
|
||||
jammer_cartridge_projectile_b.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
|
||||
|
||||
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
|
||||
|
|
@ -2707,6 +2723,14 @@ object GlobalDefinitions {
|
|||
jammer_grenade_projectile.ProjectileDamageType = DamageType.Splash
|
||||
jammer_grenade_projectile.InitialVelocity = 30
|
||||
jammer_grenade_projectile.Lifespan = 15f
|
||||
jammer_grenade_projectile.AdditionalEffect = true
|
||||
jammer_grenade_projectile.JammerProjectile = true
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000
|
||||
jammer_grenade_projectile.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
|
||||
|
||||
jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh"
|
||||
|
|
@ -2718,6 +2742,14 @@ object GlobalDefinitions {
|
|||
jammer_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
|
||||
jammer_grenade_projectile_enh.InitialVelocity = 30
|
||||
jammer_grenade_projectile_enh.Lifespan = 3f
|
||||
jammer_grenade_projectile_enh.AdditionalEffect = true
|
||||
jammer_grenade_projectile_enh.JammerProjectile = true
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player) -> 1000
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.AMS) -> 5000
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.MotionSensor) -> 30000
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Deployable, EffectTarget.Validation.Spitfire) -> 30000
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret) -> 30000
|
||||
jammer_grenade_projectile_enh.JammedEffectDuration += TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.VehicleNotAMS) -> 10000
|
||||
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
|
||||
|
||||
katana_projectile.Name = "katana_projectile"
|
||||
|
|
@ -5870,24 +5902,20 @@ object GlobalDefinitions {
|
|||
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
|
||||
jammer_mine.DetonateOnJamming = false
|
||||
|
||||
spitfire_turret.Name = "spitfire_turret"
|
||||
spitfire_turret.Descriptor= "Spitfires"
|
||||
spitfire_turret.Descriptor = "Spitfires"
|
||||
spitfire_turret.MaxHealth = 100
|
||||
spitfire_turret.Weapons += 1 -> new mutable.HashMap()
|
||||
spitfire_turret.Weapons(1) += TurretUpgrade.None -> spitfire_weapon
|
||||
|
|
@ -5897,7 +5925,7 @@ object GlobalDefinitions {
|
|||
spitfire_turret.Model = StandardResolutions.ComplexDeployables
|
||||
|
||||
spitfire_cloaked.Name = "spitfire_cloaked"
|
||||
spitfire_cloaked.Descriptor= "CloakingSpitfires"
|
||||
spitfire_cloaked.Descriptor = "CloakingSpitfires"
|
||||
spitfire_cloaked.MaxHealth = 100
|
||||
spitfire_cloaked.Weapons += 1 -> new mutable.HashMap()
|
||||
spitfire_cloaked.Weapons(1) += TurretUpgrade.None -> spitfire_weapon
|
||||
|
|
@ -5907,7 +5935,7 @@ object GlobalDefinitions {
|
|||
spitfire_cloaked.Model = StandardResolutions.ComplexDeployables
|
||||
|
||||
spitfire_aa.Name = "spitfire_aa"
|
||||
spitfire_aa.Descriptor= "FlakSpitfires"
|
||||
spitfire_aa.Descriptor = "FlakSpitfires"
|
||||
spitfire_aa.MaxHealth = 100
|
||||
spitfire_aa.Weapons += 1 -> new mutable.HashMap()
|
||||
spitfire_aa.Weapons(1) += TurretUpgrade.None -> spitfire_aa_weapon
|
||||
|
|
@ -5919,16 +5947,12 @@ object GlobalDefinitions {
|
|||
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"
|
||||
|
|
@ -6096,62 +6120,62 @@ object GlobalDefinitions {
|
|||
teleportpad_terminal.Name = "teleportpad_terminal"
|
||||
teleportpad_terminal.Tab += 0 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.routerTerminal)
|
||||
|
||||
adv_med_terminal.Name = "adv_med_terminal"
|
||||
adv_med_terminal.Interval = 500
|
||||
adv_med_terminal.HealAmount = 8
|
||||
adv_med_terminal.ArmorAmount = 15
|
||||
adv_med_terminal.UseRadius = 0.75f
|
||||
adv_med_terminal.TargetValidation += ProximityTarget.Player -> ProximityTerminalControl.Validation.Medical
|
||||
|
||||
crystals_health_a.Name = "crystals_health_a"
|
||||
crystals_health_a.Interval = 500
|
||||
crystals_health_a.HealAmount = 4
|
||||
crystals_health_a.UseRadius = 5
|
||||
crystals_health_a.TargetValidation += ProximityTarget.Player -> ProximityTerminalControl.Validation.HealthCrystal
|
||||
|
||||
crystals_health_b.Name = "crystals_health_b"
|
||||
crystals_health_b.Interval = 500
|
||||
crystals_health_b.HealAmount = 4
|
||||
crystals_health_b.UseRadius = 1.3f
|
||||
crystals_health_b.TargetValidation += ProximityTarget.Player -> ProximityTerminalControl.Validation.HealthCrystal
|
||||
|
||||
medical_terminal.Name = "medical_terminal"
|
||||
medical_terminal.Interval = 500
|
||||
medical_terminal.HealAmount = 5
|
||||
medical_terminal.ArmorAmount = 10
|
||||
medical_terminal.UseRadius = 0.75f
|
||||
medical_terminal.TargetValidation += ProximityTarget.Player -> ProximityTerminalControl.Validation.Medical
|
||||
medical_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
|
||||
|
||||
adv_med_terminal.Name = "adv_med_terminal"
|
||||
adv_med_terminal.Interval = 500
|
||||
adv_med_terminal.HealAmount = 8
|
||||
adv_med_terminal.ArmorAmount = 15
|
||||
adv_med_terminal.UseRadius = 0.75f
|
||||
adv_med_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
|
||||
|
||||
crystals_health_a.Name = "crystals_health_a"
|
||||
crystals_health_a.Interval = 500
|
||||
crystals_health_a.HealAmount = 4
|
||||
crystals_health_a.UseRadius = 5
|
||||
crystals_health_a.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.HealthCrystal
|
||||
|
||||
crystals_health_b.Name = "crystals_health_b"
|
||||
crystals_health_b.Interval = 500
|
||||
crystals_health_b.HealAmount = 4
|
||||
crystals_health_b.UseRadius = 1.3f
|
||||
crystals_health_b.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.HealthCrystal
|
||||
|
||||
portable_med_terminal.Name = "portable_med_terminal"
|
||||
portable_med_terminal.Interval = 500
|
||||
portable_med_terminal.HealAmount = 5
|
||||
portable_med_terminal.ArmorAmount = 10
|
||||
portable_med_terminal.UseRadius = 3
|
||||
portable_med_terminal.TargetValidation += ProximityTarget.Player -> ProximityTerminalControl.Validation.Medical
|
||||
portable_med_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
|
||||
|
||||
pad_landing_frame.Name = "pad_landing_frame"
|
||||
pad_landing_frame.Interval = 1000
|
||||
pad_landing_frame.HealAmount = 60
|
||||
pad_landing_frame.UseRadius = 20
|
||||
pad_landing_frame.TargetValidation += ProximityTarget.Aircraft -> ProximityTerminalControl.Validation.PadLanding
|
||||
pad_landing_frame.TargetValidation += EffectTarget.Category.Aircraft -> EffectTarget.Validation.PadLanding
|
||||
|
||||
pad_landing_tower_frame.Name = "pad_landing_tower_frame"
|
||||
pad_landing_tower_frame.Interval = 1000
|
||||
pad_landing_tower_frame.HealAmount = 60
|
||||
pad_landing_tower_frame.UseRadius = 20
|
||||
pad_landing_tower_frame.TargetValidation += ProximityTarget.Aircraft -> ProximityTerminalControl.Validation.PadLanding
|
||||
pad_landing_tower_frame.TargetValidation += EffectTarget.Category.Aircraft -> EffectTarget.Validation.PadLanding
|
||||
|
||||
repair_silo.Name = "repair_silo"
|
||||
repair_silo.Interval = 1000
|
||||
repair_silo.HealAmount = 60
|
||||
repair_silo.UseRadius = 20
|
||||
repair_silo.TargetValidation += ProximityTarget.Vehicle -> ProximityTerminalControl.Validation.RepairSilo
|
||||
repair_silo.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.RepairSilo
|
||||
|
||||
lodestar_repair_terminal.Name = "lodestar_repair_terminal"
|
||||
lodestar_repair_terminal.Interval = 1000
|
||||
lodestar_repair_terminal.HealAmount = 60
|
||||
lodestar_repair_terminal.UseRadius = 20
|
||||
lodestar_repair_terminal.TargetValidation += ProximityTarget.Vehicle -> ProximityTerminalControl.Validation.RepairSilo
|
||||
lodestar_repair_terminal.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.RepairSilo
|
||||
|
||||
multivehicle_rearm_terminal.Name = "multivehicle_rearm_terminal"
|
||||
multivehicle_rearm_terminal.Tab += 3 -> OrderTerminalDefinition.EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ package net.psforever.objects
|
|||
|
||||
import net.psforever.objects.avatar.LoadoutManager
|
||||
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||
|
|
@ -16,11 +17,12 @@ import net.psforever.types._
|
|||
import scala.annotation.tailrec
|
||||
import scala.util.{Success, Try}
|
||||
|
||||
class Player(private val core : Avatar) extends PlanetSideGameObject
|
||||
class Player(private val core : Avatar) extends PlanetSideServerObject
|
||||
with FactionAffinity
|
||||
with Vitality
|
||||
with ResistanceProfile
|
||||
with Container
|
||||
with JammableUnit
|
||||
with ZoneAware {
|
||||
private var alive : Boolean = false
|
||||
private var backpack : Boolean = false
|
||||
|
|
|
|||
|
|
@ -1,9 +1,133 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.SimpleDeployable
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import akka.actor.{Actor, ActorContext, Props}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.ce._
|
||||
import net.psforever.objects.definition.converter.SmallDeployableConverter
|
||||
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
|
||||
import net.psforever.objects.equipment.{JammableBehavior, JammableUnit}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.vital.{StandardResolutions, Vitality}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
class SensorDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef)
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class SensorDeployable(cdef : SensorDeployableDefinition) extends ComplexDeployable(cdef)
|
||||
with Hackable
|
||||
with JammableUnit
|
||||
|
||||
class SensorDeployableDefinition(private val objectId : Int) extends ComplexDeployableDefinition(objectId) {
|
||||
Name = "sensor_deployable"
|
||||
DeployCategory = DeployableCategory.Sensors
|
||||
Model = StandardResolutions.SimpleDeployables
|
||||
Packet = new SmallDeployableConverter
|
||||
|
||||
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
obj.Actor = context.actorOf(Props(classOf[SensorDeployableControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
|
||||
}
|
||||
|
||||
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
SimpleDeployableDefinition.SimpleUninitialize(obj, context)
|
||||
}
|
||||
}
|
||||
|
||||
object SensorDeployableDefinition {
|
||||
def apply(dtype : DeployedItem.Value) : SensorDeployableDefinition = {
|
||||
new SensorDeployableDefinition(dtype.id)
|
||||
}
|
||||
}
|
||||
|
||||
class SensorDeployableControl(sensor : SensorDeployable) extends Actor
|
||||
with JammableBehavior {
|
||||
|
||||
def JammableObject = sensor
|
||||
|
||||
def receive : Receive = jammableBehavior.orElse {
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val originalHealth = sensor.Health
|
||||
if(originalHealth > 0) {
|
||||
val cause = damage_func(sensor)
|
||||
SensorDeployableControl.HandleDamageResolution(sensor, cause, originalHealth - sensor.Health)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override def StartJammeredSound(target : Any, dur : Int) : Unit = target match {
|
||||
case obj : PlanetSideServerObject if !jammedSound =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1))
|
||||
super.StartJammeredSound(obj, dur)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
|
||||
case obj : PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
||||
sensor.Zone.LocalEvents ! LocalServiceMessage(sensor.Zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000))
|
||||
super.StartJammeredStatus(obj, dur)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override def CancelJammeredSound(target : Any) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject if jammedSound =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0))
|
||||
case _ => ;
|
||||
}
|
||||
super.CancelJammeredSound(target)
|
||||
}
|
||||
|
||||
override def CancelJammeredStatus(target : Any) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
||||
sensor.Zone.LocalEvents ! LocalServiceMessage(sensor.Zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000))
|
||||
case _ => ;
|
||||
}
|
||||
super.CancelJammeredStatus(target)
|
||||
}
|
||||
}
|
||||
|
||||
object SensorDeployableControl {
|
||||
def HandleDamageResolution(target : SensorDeployable, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => PlanetSideGUID(0)
|
||||
}
|
||||
if(target.Health > 0) {
|
||||
//activity on map
|
||||
if(damage > 0) {
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
}
|
||||
if(cause.projectile.profile.JammerProjectile) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
else {
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(targetGUID, 0, target.Health))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : SensorDeployable, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
target.Actor ! JammableUnit.ClearJammeredSound()
|
||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
val zone = target.Zone
|
||||
Deployables.AnnounceDestroyDeployable(target, Some(0 seconds))
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, false, 1000))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,125 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.{ComplexDeployable, DeployableCategory}
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import akka.actor.{Actor, ActorContext, Props}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployableCategory}
|
||||
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
|
||||
import net.psforever.objects.definition.converter.ShieldGeneratorConverter
|
||||
import net.psforever.objects.equipment.{JammableBehavior, JammableUnit}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.Service
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
class ShieldGeneratorDeployable(cdef : ShieldGeneratorDefinition) extends ComplexDeployable(cdef)
|
||||
with Hackable
|
||||
with JammableUnit
|
||||
|
||||
class ShieldGeneratorDefinition extends DeployableDefinition(240) {
|
||||
class ShieldGeneratorDefinition extends ComplexDeployableDefinition(240) {
|
||||
Packet = new ShieldGeneratorConverter
|
||||
DeployCategory = DeployableCategory.ShieldGenerators
|
||||
|
||||
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
obj.Actor = context.actorOf(Props(classOf[ShieldGeneratorControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
|
||||
}
|
||||
|
||||
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
|
||||
SimpleDeployableDefinition.SimpleUninitialize(obj, context)
|
||||
}
|
||||
}
|
||||
|
||||
class ShieldGeneratorControl(gen : ShieldGeneratorDeployable) extends Actor
|
||||
with JammableBehavior {
|
||||
|
||||
def JammableObject = gen
|
||||
|
||||
def receive : Receive = jammableBehavior
|
||||
.orElse {
|
||||
case Vitality.Damage(damage_func) => //note: damage status is reported as vehicle events, not local events
|
||||
if(gen.Health > 0) {
|
||||
val originalHealth = gen.Health
|
||||
val cause = damage_func(gen)
|
||||
val health = gen.Health
|
||||
val damageToHealth = originalHealth - health
|
||||
ShieldGeneratorControl.HandleDamageResolution(gen, cause, damageToHealth)
|
||||
if(damageToHealth > 0) {
|
||||
val name = gen.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint + 1, name.length - 1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
|
||||
}
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
/*
|
||||
while the shield generator is technically a supported jammable target, how that works is currently unknown
|
||||
electing to use a "status only, no sound" approach by overriding one with an empty function is not entirely arbitrary
|
||||
the superclass of "status" calls also sets the jammed object property
|
||||
*/
|
||||
override def StartJammeredSound(target : Any, dur : Int) : Unit = { }
|
||||
|
||||
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
|
||||
case obj : PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1))
|
||||
super.StartJammeredStatus(obj, dur)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override def CancelJammeredSound(target : Any) : Unit = { }
|
||||
|
||||
override def CancelJammeredStatus(target : Any) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0))
|
||||
case _ => ;
|
||||
}
|
||||
super.CancelJammeredStatus(target)
|
||||
}
|
||||
}
|
||||
|
||||
object ShieldGeneratorControl {
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
*/
|
||||
def HandleDamageResolution(target : ShieldGeneratorDeployable, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => PlanetSideGUID(0)
|
||||
}
|
||||
if(target.Health > 0) {
|
||||
//activity on map
|
||||
if(damage > 0) {
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
}
|
||||
if(cause.projectile.profile.JammerProjectile) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
else {
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(targetGUID, 0, target.Health))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : ShieldGeneratorDeployable, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
target.Actor ! JammableUnit.ClearJammeredSound()
|
||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
val zone = target.Zone
|
||||
Deployables.AnnounceDestroyDeployable(target, None)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.{SimpleDeployable, TelepadLike}
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.SimpleDeployableDefinition
|
||||
|
||||
class TelepadDeployable(ddef : DeployableDefinition) extends SimpleDeployable(ddef)
|
||||
class TelepadDeployable(ddef : SimpleDeployableDefinition) extends SimpleDeployable(ddef)
|
||||
with TelepadLike
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import scala.annotation.tailrec
|
|||
* @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] {
|
||||
with FireModeSwitch[FireModeDefinition]
|
||||
with JammableUnit {
|
||||
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
|
||||
private var fireModeIndex : Int = toolDef.DefaultFireModeIndex
|
||||
/** current ammunition slot being used by this fire mode */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.SimpleDeployable
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.SimpleDeployableDefinition
|
||||
|
||||
class TrapDeployable(cdef : DeployableDefinition) extends SimpleDeployable(cdef)
|
||||
class TrapDeployable(cdef : SimpleDeployableDefinition) extends SimpleDeployable(cdef)
|
||||
|
|
|
|||
|
|
@ -2,65 +2,58 @@
|
|||
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.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.ce.{ComplexDeployable, Deployable, DeployedItem}
|
||||
import net.psforever.objects.definition.{ComplexDeployableDefinition, SimpleDeployableDefinition}
|
||||
import net.psforever.objects.definition.converter.SmallTurretConverter
|
||||
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
|
||||
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}
|
||||
import net.psforever.objects.vital.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance, Vitality}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
class TurretDeployable(tdef : TurretDeployableDefinition) extends PlanetSideServerObject
|
||||
with Deployable
|
||||
class TurretDeployable(tdef : TurretDeployableDefinition) extends ComplexDeployable(tdef)
|
||||
with WeaponTurret
|
||||
with JammableUnit
|
||||
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 def Health : Int = super[ComplexDeployable].Health
|
||||
//override to clarify inheritance conflict
|
||||
override def Health_=(toHealth : Int) : Int = super[Deployable].Health_=(toHealth)
|
||||
override def Health_=(toHealth : Int) : Int = super[ComplexDeployable].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
|
||||
class TurretDeployableDefinition(private val objectId : Int) extends ComplexDeployableDefinition(objectId)
|
||||
with TurretDefinition {
|
||||
Name = "turret_deployable"
|
||||
Packet = new SmallTurretConverter
|
||||
|
||||
def Item : DeployedItem.Value = item
|
||||
Damage = StandardVehicleDamage
|
||||
Resistance = StandardVehicleResistance
|
||||
Model = StandardResolutions.FacilityTurrets
|
||||
|
||||
//override to clarify inheritance conflict
|
||||
override def MaxHealth : Int = super[BaseDeployableDefinition].MaxHealth
|
||||
override def MaxHealth : Int = super[ComplexDeployableDefinition].MaxHealth
|
||||
//override to clarify inheritance conflict
|
||||
override def MaxHealth_=(max : Int) : Int = super[BaseDeployableDefinition].MaxHealth_=(max)
|
||||
override def MaxHealth_=(max : Int) : Int = super[ComplexDeployableDefinition].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)
|
||||
SimpleDeployableDefinition.SimpleUninitialize(obj, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,16 +67,115 @@ object TurretDeployableDefinition {
|
|||
|
||||
class TurretControl(turret : TurretDeployable) extends Actor
|
||||
with FactionAffinityBehavior.Check
|
||||
with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events
|
||||
with MountableBehavior.Mount
|
||||
with MountableBehavior.Dismount {
|
||||
def MountableObject = turret //do not add type!
|
||||
|
||||
def JammableObject = turret
|
||||
|
||||
def FactionObject : FactionAffinity = turret
|
||||
|
||||
def receive : Receive = checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(turretMountBehavior)
|
||||
.orElse {
|
||||
case Vitality.Damage(damage_func) => //note: damage status is reported as vehicle events, not local events
|
||||
if(turret.Health > 0) {
|
||||
val originalHealth = turret.Health
|
||||
val cause = damage_func(turret)
|
||||
val health = turret.Health
|
||||
val damageToHealth = originalHealth - health
|
||||
TurretControl.HandleDamageResolution(turret, cause, damageToHealth)
|
||||
if(damageToHealth > 0) {
|
||||
val name = turret.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint + 1, name.length - 1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
|
||||
}
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object TurretControl {
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
*/
|
||||
def HandleDamageResolution(target : TurretDeployable, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => PlanetSideGUID(0)
|
||||
}
|
||||
if(target.Health > 0) {
|
||||
//activity on map
|
||||
if(damage > 0) {
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert occupants to damage source
|
||||
HandleDamageAwareness(target, playerGUID, cause)
|
||||
}
|
||||
if(cause.projectile.profile.JammerProjectile) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
else {
|
||||
//alert to turret death (hence, occupants' deaths)
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDamageAwareness(target : TurretDeployable, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
val zone = target.Zone
|
||||
//alert occupants to damage source
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(attribution, tplayer.GUID))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : TurretDeployable, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
target.Actor ! JammableUnit.ClearJammeredSound()
|
||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
val zone = target.Zone
|
||||
val continentId = zone.Id
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//vehicle wreckage has no weapons
|
||||
target.Weapons.values
|
||||
.filter {
|
||||
_.Equipment.nonEmpty
|
||||
}
|
||||
.foreach(slot => {
|
||||
val wep = slot.Equipment.get
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
|
||||
})
|
||||
Deployables.AnnounceDestroyDeployable(target, None)
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects
|
|||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryTile}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
|
@ -73,6 +73,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner
|
|||
with Vitality
|
||||
with OwnableByPlayer
|
||||
with StandardResistanceProfile
|
||||
with JammableUnit
|
||||
with Container {
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
|
||||
private var health : Int = 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.avatar
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.Player
|
||||
|
||||
/**
|
||||
* na;
|
||||
* stub for future development
|
||||
*/
|
||||
class PlayerControl(player : Player) extends Actor {
|
||||
def receive : Receive = {
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.ballistics
|
|||
|
||||
import net.psforever.objects.ce.{ComplexDeployable, SimpleDeployable}
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle}
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
|
||||
import net.psforever.objects.entity.WorldEntity
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||
|
|
@ -34,7 +34,6 @@ object SourceEntry {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.ce
|
||||
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.ComplexDeployableDefinition
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
|
||||
abstract class ComplexDeployable(cdef : DeployableDefinition) extends PlanetSideServerObject
|
||||
abstract class ComplexDeployable(cdef : ComplexDeployableDefinition) extends PlanetSideServerObject
|
||||
with Deployable {
|
||||
Health = Definition.MaxHealth
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ 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.objects.zones.ZoneAware
|
||||
import net.psforever.packet.game.DeployableIcon
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
trait Deployable extends FactionAffinity
|
||||
with Vitality
|
||||
with OwnableByPlayer {
|
||||
with OwnableByPlayer
|
||||
with ZoneAware {
|
||||
this : PlanetSideGameObject =>
|
||||
private var health : Int = 1
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
package net.psforever.objects.ce
|
||||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.SimpleDeployableDefinition
|
||||
|
||||
abstract class SimpleDeployable(cdef : DeployableDefinition) extends PlanetSideGameObject
|
||||
abstract class SimpleDeployable(cdef : SimpleDeployableDefinition) extends PlanetSideGameObject
|
||||
with Deployable {
|
||||
Health = Definition.MaxHealth
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.definition
|
||||
|
||||
import net.psforever.objects.ballistics.Projectiles
|
||||
import net.psforever.objects.equipment.JammingUnit
|
||||
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
|
||||
|
||||
/**
|
||||
|
|
@ -10,6 +11,7 @@ import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
|
|||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||
with JammingUnit
|
||||
with StandardDamageProfile {
|
||||
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
|
||||
private var acceleration : Int = 0
|
||||
|
|
@ -26,6 +28,8 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
private var existsOnRemoteClients : Boolean = false //`true` spawns a server-managed object
|
||||
private var remoteClientData : (Int, Int) = (0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
|
||||
private var autoLock : Boolean = false
|
||||
private var additionalEffect : Boolean = false
|
||||
private var jammerProjectile : Boolean = false
|
||||
//derived calculations
|
||||
private var distanceMax : Float = 0f
|
||||
private var distanceFromAcceleration : Float = 0f
|
||||
|
|
@ -133,6 +137,20 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
|||
AutoLock
|
||||
}
|
||||
|
||||
def AdditionalEffect : Boolean = additionalEffect
|
||||
|
||||
def AdditionalEffect_=(effect : Boolean) : Boolean = {
|
||||
additionalEffect = effect
|
||||
AdditionalEffect
|
||||
}
|
||||
|
||||
def JammerProjectile : Boolean = jammerProjectile
|
||||
|
||||
def JammerProjectile_=(effect : Boolean) : Boolean = {
|
||||
jammerProjectile = effect
|
||||
JammerProjectile
|
||||
}
|
||||
|
||||
def DistanceMax : Float = distanceMax //accessor only
|
||||
|
||||
def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ 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.resistance.ResistanceProfileMutators
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage, StandardResistanceProfile}
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, NoResistanceSelection, StandardDeployableDamage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ trait BaseDeployableDefinition extends DamageResistanceModel
|
|||
def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) : Unit = { }
|
||||
}
|
||||
|
||||
class DeployableDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||
class SimpleDeployableDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||
with BaseDeployableDefinition {
|
||||
private val item = DeployedItem(objectId) //let throw NoSuchElementException
|
||||
Packet = new SmallDeployableConverter
|
||||
|
|
@ -61,9 +61,16 @@ class DeployableDefinition(private val objectId : Int) extends ObjectDefinition(
|
|||
def Item : DeployedItem.Value = item
|
||||
}
|
||||
|
||||
object DeployableDefinition {
|
||||
def apply(item : DeployedItem.Value) : DeployableDefinition =
|
||||
new DeployableDefinition(item.id)
|
||||
abstract class ComplexDeployableDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||
with BaseDeployableDefinition {
|
||||
private val item = DeployedItem(objectId) //let throw NoSuchElementException
|
||||
|
||||
def Item : DeployedItem.Value = item
|
||||
}
|
||||
|
||||
object SimpleDeployableDefinition {
|
||||
def apply(item : DeployedItem.Value) : SimpleDeployableDefinition =
|
||||
new SimpleDeployableDefinition(item.id)
|
||||
|
||||
def SimpleUninitialize(obj : PlanetSideGameObject, context : ActorContext) : Unit = { }
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ object AvatarConverter {
|
|||
alt_model_flag,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
obj.Jammed,
|
||||
None,
|
||||
v5 = None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.{Player, Tool}
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
|
|
@ -136,17 +136,38 @@ class CharacterSelectConverter extends AvatarConverter {
|
|||
}
|
||||
else {
|
||||
val slot : EquipmentSlot = iter.next
|
||||
if(slot.Equipment.isDefined) {
|
||||
val equip : Equipment = slot.Equipment.get
|
||||
recursiveMakeHolsters(
|
||||
iter,
|
||||
list :+ AvatarConverter.BuildDetailedEquipment(index, equip),
|
||||
index + 1
|
||||
)
|
||||
}
|
||||
else {
|
||||
recursiveMakeHolsters(iter, list, index + 1)
|
||||
slot.Equipment match {
|
||||
case Some(equip : Tool) =>
|
||||
val jammed = equip.Jammed
|
||||
equip.Jammed = false
|
||||
val slot = AvatarConverter.BuildDetailedEquipment(index, equip)
|
||||
equip.Jammed = jammed
|
||||
recursiveMakeHolsters(
|
||||
iter,
|
||||
list :+ slot,
|
||||
index + 1
|
||||
)
|
||||
case Some(equip) =>
|
||||
recursiveMakeHolsters(
|
||||
iter,
|
||||
list :+ AvatarConverter.BuildDetailedEquipment(index, equip),
|
||||
index + 1
|
||||
)
|
||||
case _ =>
|
||||
recursiveMakeHolsters(iter, list, index + 1)
|
||||
}
|
||||
// if(slot.Equipment.isDefined) {
|
||||
//
|
||||
// val equip : Equipment = slot.Equipment.get
|
||||
// recursiveMakeHolsters(
|
||||
// iter,
|
||||
// list :+ AvatarConverter.BuildDetailedEquipment(index, equip),
|
||||
// index + 1
|
||||
// )
|
||||
// }
|
||||
// else {
|
||||
// recursiveMakeHolsters(iter, list, index + 1)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class FieldTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
|||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
jammered = obj.Jammed,
|
||||
Some(false),
|
||||
None,
|
||||
obj.Owner match {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl
|
|||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
jammered = obj.Jammed,
|
||||
None,
|
||||
None,
|
||||
obj.Owner match {
|
||||
|
|
@ -45,7 +45,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl
|
|||
alternate = true,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
jammered = obj.Jammed,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.definition.converter
|
|||
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
|
||||
|
|
@ -15,11 +16,14 @@ class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObjec
|
|||
PlacementData(obj.Position, obj.Orientation),
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
false,
|
||||
false,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
jammered = obj match {
|
||||
case o : JammableUnit => o.Jammed
|
||||
case _ => false
|
||||
},
|
||||
Some(false),
|
||||
None,
|
||||
obj.Owner match {
|
||||
|
|
@ -33,4 +37,4 @@ class SmallDeployableConverter extends ObjectCreateConverter[PlanetSideGameObjec
|
|||
|
||||
override def DetailedConstructorData(obj : PlanetSideGameObject with Deployable) : Try[CommonFieldDataWithPlacement] =
|
||||
Failure(new Exception("converter should not be used to generate detailed small deployable data"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
|
|||
alternate = false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
jammered = obj.Jammed,
|
||||
Some(true),
|
||||
None,
|
||||
obj.Owner match {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() {
|
|||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
obj.Jammed,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
@ -45,7 +45,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() {
|
|||
alternate = false,
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
obj.Jammed,
|
||||
None,
|
||||
None,
|
||||
PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
|||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
jammered = obj.Jammed,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
obj.Owner match {
|
||||
|
|
@ -56,7 +56,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
|||
alternate = true,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
v3 = false,
|
||||
jammered = obj.Jammed,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
guid = PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ce.DeployableCategory
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
|
||||
final case class TargetValidation(category : EffectTarget.Category.Value, test : EffectTarget.Validation.Value)
|
||||
|
||||
object EffectTarget {
|
||||
/**
|
||||
* A classification of the target of interactions.
|
||||
* Arbitrary, but useful.
|
||||
*/
|
||||
object Category extends Enumeration {
|
||||
val
|
||||
Aircraft,
|
||||
Deployable,
|
||||
Equipment,
|
||||
Player,
|
||||
Turret,
|
||||
Vehicle
|
||||
= Value
|
||||
}
|
||||
|
||||
object Validation {
|
||||
type Value = PlanetSideGameObject => Boolean
|
||||
|
||||
def Invalid(target : PlanetSideGameObject) : Boolean = false
|
||||
|
||||
def Medical(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case p : Player =>
|
||||
p.Health > 0 && (p.Health < p.MaxHealth || p.Armor < p.MaxArmor)
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def HealthCrystal(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case p : Player =>
|
||||
p.Health > 0 && p.Health < p.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def RepairSilo(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def PadLanding(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def Player(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case p : Player =>
|
||||
p.isAlive
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def MotionSensor(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case s : SensorDeployable =>
|
||||
s.Health > 0
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def Spitfire(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case t : TurretDeployable =>
|
||||
t.Definition.DeployCategory == DeployableCategory.SmallTurrets && t.Health > 0
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def Turret(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case t : TurretDeployable =>
|
||||
t.Definition.DeployCategory == DeployableCategory.FieldTurrets && t.Health > 0
|
||||
case t : FacilityTurret =>
|
||||
t.Health > 0
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def AMS(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
v.Health > 0 && v.Definition == GlobalDefinitions.ams
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def VehicleNotAMS(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
v.Health > 0 && v.Definition != GlobalDefinitions.ams
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.equipment
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject, Tool}
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.vehicles.MountedWeapons
|
||||
import net.psforever.objects.zones.ZoneAware
|
||||
import net.psforever.types.Vector3
|
||||
import services.Service
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* A property conferred to game objects that can be affected by an electromagnetic pulse.
|
||||
* Being "jammered" is a status that causes weakness due to temporary equipment disabling or the elimination of certain objects.
|
||||
*/
|
||||
trait JammableUnit {
|
||||
/** being jammed (jammered) is an on/off state */
|
||||
private var jammed : Boolean = false
|
||||
|
||||
def Jammed : Boolean = jammed
|
||||
|
||||
def Jammed_=(state : Boolean) : Boolean = {
|
||||
jammed = state
|
||||
Jammed
|
||||
}
|
||||
}
|
||||
|
||||
object JammableUnit {
|
||||
/**
|
||||
* A message for generic jammering.
|
||||
* Currently, unused.
|
||||
*/
|
||||
final case class Jammer()
|
||||
/**
|
||||
* A message for jammering due to a projectile.
|
||||
* @param cause information pertaining to the projectile
|
||||
*/
|
||||
final case class Jammered(cause : ResolvedProjectile)
|
||||
/**
|
||||
* Stop the auditory aspect of being jammered.
|
||||
*/
|
||||
final case class ClearJammeredSound()
|
||||
/**
|
||||
* Stop the status effects of being jammered.
|
||||
*/
|
||||
final case class ClearJammeredStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* A property conferred onto game objects that can induce the effects of an electromagnetic pulse.
|
||||
* @see `TargetValidation`
|
||||
* @see `EffectTarget`
|
||||
*/
|
||||
trait JammingUnit {
|
||||
/** a list of qualifying conditional tests for determining if an object is to be affected by the jammered status;
|
||||
* if qualifying, that object will be inflicted with a number of milliseconds of the jammered status */
|
||||
private val jammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = new mutable.ListBuffer()
|
||||
|
||||
def HasJammedEffectDuration : Boolean = jammedEffectDuration.isEmpty
|
||||
|
||||
def JammedEffectDuration : mutable.ListBuffer[(TargetValidation, Int)] = jammedEffectDuration
|
||||
}
|
||||
|
||||
object JammingUnit {
|
||||
/**
|
||||
* Determine whether an object that can be jammered is to be jammered by this source,
|
||||
* and for how long.
|
||||
* If the object succeeds for multiple qualification tests,
|
||||
* prioritize the lengthiest duration.
|
||||
* @param jammer the source of the "jammered" status
|
||||
* @param target the object to be determined if affected by the source's jammering
|
||||
* @return the duration to be jammered, if any, in milliseconds
|
||||
*/
|
||||
def FindJammerDuration(jammer : JammingUnit, target : PlanetSideGameObject) : Option[Int] = {
|
||||
jammer.JammedEffectDuration
|
||||
.collect { case (TargetValidation(_, test), duration) if test(target) => duration }
|
||||
.toList
|
||||
.sortWith(_ > _)
|
||||
.headOption
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a group of objects that can be jammered is to be jammered by this source,
|
||||
* and for how long.
|
||||
* If the object succeeds for multiple qualification tests,
|
||||
* prioritize the lengthiest duration.
|
||||
* @param jammer the source of the "jammered" status
|
||||
* @param targets the objects to be determined if affected by the source's jammering
|
||||
* @return the indexed durations to be jammered, if any, in milliseconds
|
||||
*/
|
||||
def FindJammerDuration(jammer : JammingUnit, targets : Seq[PlanetSideGameObject]) : Seq[Option[Int]] = {
|
||||
targets.map { target => FindJammerDuration(jammer, target) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Actor` control object mix-in that manages common responses to the "jammerable" status.
|
||||
* Two aspects to jammering are supported -
|
||||
* a telling buzzing sound that follows the affected target
|
||||
* and actual effects upon the target's actions -
|
||||
* and are controlled independently.
|
||||
* The primary purpose of this behavior is to control timers that toggle the states of these two aspects.
|
||||
*/
|
||||
trait JammableBehavior {
|
||||
this : Actor =>
|
||||
/** flag for jammed sound */
|
||||
protected var jammedSound : Boolean = false
|
||||
/** the sound timer */
|
||||
protected var jammeredSoundTimer : Cancellable = DefaultCancellable.obj
|
||||
/** the effect timer */
|
||||
protected var jammeredStatusTimer : Cancellable = DefaultCancellable.obj
|
||||
|
||||
/** `ZoneAware` is used for callback to the event systems */
|
||||
def JammableObject : PlanetSideServerObject with JammableUnit with ZoneAware
|
||||
|
||||
/**
|
||||
* If the target can be validated against, affect it with the jammered status.
|
||||
* @param target the objects to be determined if affected by the source's jammering
|
||||
* @param cause the source of the "jammered" status
|
||||
*/
|
||||
def TryJammerEffectActivate(target : Any, cause : ResolvedProjectile) : Unit = target match {
|
||||
case obj : PlanetSideServerObject =>
|
||||
val radius = cause.projectile.profile.DamageRadius
|
||||
JammingUnit.FindJammerDuration(cause.projectile.profile, obj) match {
|
||||
case Some(dur) if Vector3.DistanceSquared(cause.hit_pos, cause.target.Position) < radius * radius =>
|
||||
StartJammeredSound(obj, dur)
|
||||
StartJammeredStatus(obj, dur)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a distinctive buzzing sound effect.
|
||||
* Due to considerations of the object that is the target, this is left to be implemented by a subclass.
|
||||
* We merely start the timer.
|
||||
* @param target an object that can be affected by the jammered status
|
||||
* @param dur the duration of the timer, in milliseconds;
|
||||
* by default, 30000
|
||||
*/
|
||||
def StartJammeredSound(target : Any, dur : Int = 30000) : Unit = {
|
||||
if(!jammedSound) {
|
||||
jammedSound = true
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
jammeredSoundTimer.cancel
|
||||
jammeredSoundTimer = context.system.scheduler.scheduleOnce(dur seconds, self, JammableUnit.ClearJammeredSound())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the effects of the jammered status.
|
||||
* Due to considerations of the object that is the target, this is left to be implemented by a subclass.
|
||||
* We merely stop the timer.
|
||||
* @param target an object that can be affected by the jammered status
|
||||
* @param dur the duration of the timer, in milliseconds
|
||||
*/
|
||||
def StartJammeredStatus(target : Any, dur : Int) : Unit = {
|
||||
JammableObject.Jammed = true
|
||||
jammeredStatusTimer.cancel
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
jammeredStatusTimer = context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredStatus())
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a distinctive buzzing sound effect.
|
||||
* Due to considerations of the object that is the target, this is left to be implemented by a subclass.
|
||||
* We merely stop the timer.
|
||||
* @param target an object that can be affected by the jammered status
|
||||
*/
|
||||
def CancelJammeredSound(target : Any) : Unit = {
|
||||
jammedSound = false
|
||||
jammeredSoundTimer.cancel
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the effects of the jammered status.
|
||||
* Due to considerations of the object that is the target, this is left to be implemented by a subclass.
|
||||
* We merely stop the timer.
|
||||
* @param target an object that can be affected by the jammered status
|
||||
*/
|
||||
def CancelJammeredStatus(target : Any) : Unit = {
|
||||
JammableObject.Jammed = false
|
||||
jammeredStatusTimer.cancel
|
||||
}
|
||||
|
||||
val jammableBehavior : Receive = {
|
||||
case JammableUnit.Jammered(cause) =>
|
||||
TryJammerEffectActivate(JammableObject, cause)
|
||||
|
||||
case JammableUnit.ClearJammeredSound() =>
|
||||
CancelJammeredSound(JammableObject)
|
||||
|
||||
case JammableUnit.ClearJammeredStatus() =>
|
||||
CancelJammeredStatus(JammableObject)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A common mix-in variation to manage common responses to the "jammerable" status for game objects with mounted weapons.
|
||||
* @see `MountedWeapons`
|
||||
* @see `Service`
|
||||
* @see `VehicleAction`
|
||||
* @see `VehicleService`
|
||||
* @see `VehicleServiceMessage`
|
||||
* @see `Zone.VehicleEvents`
|
||||
*/
|
||||
trait JammableMountedWeapons extends JammableBehavior {
|
||||
_ : Actor =>
|
||||
|
||||
override def StartJammeredSound(target : Any, dur : Int) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if !jammedSound =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1))
|
||||
super.StartJammeredSound(target, dur)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
override def StartJammeredStatus(target : Any, dur : Int) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if !obj.Jammed =>
|
||||
JammableMountedWeapons.JammeredStatus(obj, 1)
|
||||
super.StartJammeredStatus(target, dur)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
override def CancelJammeredSound(target : Any) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject if jammedSound =>
|
||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(obj.Zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0))
|
||||
case _ => ;
|
||||
}
|
||||
super.CancelJammeredSound(target)
|
||||
}
|
||||
|
||||
override def CancelJammeredStatus(target : Any) : Unit = {
|
||||
target match {
|
||||
case obj : PlanetSideServerObject with MountedWeapons with JammableUnit if obj.Jammed =>
|
||||
JammableMountedWeapons.JammeredStatus(obj, 0)
|
||||
case _ => ;
|
||||
}
|
||||
super.CancelJammeredStatus(target)
|
||||
}
|
||||
}
|
||||
|
||||
object JammableMountedWeapons {
|
||||
/**
|
||||
* Retrieve all of the weapons on a `MountedWeapons` target object and apply a jammered status effect to each.
|
||||
* @param target an object that can be affected by the jammered status
|
||||
* @param statusCode the jammered status condition;
|
||||
* 0 for deactivation;
|
||||
* 1 for activation
|
||||
*/
|
||||
def JammeredStatus(target : PlanetSideServerObject with MountedWeapons, statusCode : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.Id
|
||||
target.Weapons.values
|
||||
.map { _.Equipment }
|
||||
.collect {
|
||||
case Some(item : Tool) =>
|
||||
item.Jammed = statusCode==1
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, item.GUID, 27, statusCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals
|
|||
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.equipment.EffectTarget
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ trait ProximityDefinition {
|
|||
this : ObjectDefinition =>
|
||||
|
||||
private var useRadius : Float = 0f //TODO belongs on a wider range of object definitions
|
||||
private val targetValidation : mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean] = new mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean]()
|
||||
private val targetValidation : mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject=>Boolean] = new mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject=>Boolean]()
|
||||
|
||||
def UseRadius : Float = useRadius
|
||||
|
||||
|
|
@ -26,18 +27,14 @@ trait ProximityDefinition {
|
|||
UseRadius
|
||||
}
|
||||
|
||||
def TargetValidation : mutable.HashMap[ProximityTarget.Value, PlanetSideGameObject=>Boolean] = targetValidation
|
||||
def TargetValidation : mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject=>Boolean] = targetValidation
|
||||
|
||||
def Validations : Seq[PlanetSideGameObject=>Boolean] = {
|
||||
targetValidation.headOption match {
|
||||
case Some(_) =>
|
||||
targetValidation.values.toSeq
|
||||
case None =>
|
||||
Seq(ProximityDefinition.Invalid)
|
||||
Seq(EffectTarget.Validation.Invalid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ProximityDefinition {
|
||||
protected val Invalid : PlanetSideGameObject=>Boolean = (_ : PlanetSideGameObject) => false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
/**
|
||||
* A classification of the target of this terminal's interactions.
|
||||
* Arbitrary, but useful.
|
||||
*/
|
||||
object ProximityTarget extends Enumeration {
|
||||
val
|
||||
Aircraft,
|
||||
Equipment,
|
||||
Player,
|
||||
Vehicle
|
||||
= Value
|
||||
}
|
||||
|
|
@ -126,35 +126,5 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
|
|||
}
|
||||
|
||||
object ProximityTerminalControl {
|
||||
object Validation {
|
||||
def Medical(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case p : Player =>
|
||||
p.Health > 0 && (p.Health < p.MaxHealth || p.Armor < p.MaxArmor)
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def HealthCrystal(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case p : Player =>
|
||||
p.Health > 0 && p.Health < p.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def RepairSilo(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
def PadLanding(target : PlanetSideGameObject) : Boolean = target match {
|
||||
case v : Vehicle =>
|
||||
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private case class TerminalAction()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, StandardResistanceProfile, Vitality}
|
||||
|
||||
class FacilityTurret(tDef : TurretDefinition) extends Amenity
|
||||
class FacilityTurret(tDef : FacilityTurretDefinition) extends Amenity
|
||||
with WeaponTurret
|
||||
with JammableUnit
|
||||
with Vitality
|
||||
with StandardResistanceProfile {
|
||||
/** some turrets can be updated; they all start without updates */
|
||||
|
|
@ -53,7 +55,7 @@ class FacilityTurret(tDef : TurretDefinition) extends Amenity
|
|||
|
||||
def DamageModel = Definition.asInstanceOf[DamageResistanceModel]
|
||||
|
||||
def Definition : TurretDefinition = tDef
|
||||
def Definition : FacilityTurretDefinition = tDef
|
||||
}
|
||||
|
||||
object FacilityTurret {
|
||||
|
|
@ -62,7 +64,7 @@ object FacilityTurret {
|
|||
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
* @return a `FacilityTurret` object
|
||||
*/
|
||||
def apply(tDef : TurretDefinition) : FacilityTurret = {
|
||||
def apply(tDef : FacilityTurretDefinition) : FacilityTurret = {
|
||||
new FacilityTurret(tDef)
|
||||
}
|
||||
|
||||
|
|
@ -73,14 +75,14 @@ object FacilityTurret {
|
|||
* @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 = {
|
||||
def Constructor(tdef : FacilityTurretDefinition)(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
|
||||
}
|
||||
|
||||
def Constructor(pos: Vector3, tdef : TurretDefinition)(id : Int, context : ActorContext) : FacilityTurret = {
|
||||
def Constructor(pos: Vector3, tdef : FacilityTurretDefinition)(id : Int, context : ActorContext) : FacilityTurret = {
|
||||
import akka.actor.Props
|
||||
val obj = FacilityTurret(tdef)
|
||||
obj.Position = pos
|
||||
|
|
|
|||
|
|
@ -2,9 +2,17 @@
|
|||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import services.vehicle.support.TurretUpgrader
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br>
|
||||
|
|
@ -16,12 +24,17 @@ import net.psforever.objects.vital.Vitality
|
|||
*/
|
||||
class FacilityTurretControl(turret : FacilityTurret) extends Actor
|
||||
with FactionAffinityBehavior.Check
|
||||
with MountableBehavior.Dismount {
|
||||
def MountableObject = turret //do not add type!
|
||||
with MountableBehavior.Dismount
|
||||
with JammableMountedWeapons {
|
||||
|
||||
def MountableObject = turret
|
||||
|
||||
def JammableObject = turret
|
||||
|
||||
def FactionObject : FactionAffinity = turret
|
||||
|
||||
def receive : Receive = checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse {
|
||||
case Mountable.TryMount(user, seat_num) =>
|
||||
|
|
@ -42,15 +55,100 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor
|
|||
case Vitality.Damage(damage_func) =>
|
||||
if(turret.Health > 0) {
|
||||
val originalHealth = turret.Health
|
||||
damage_func(turret)
|
||||
val cause = damage_func(turret)
|
||||
val health = turret.Health
|
||||
val damageToHealth = originalHealth - health
|
||||
val name = turret.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
|
||||
sender ! Vitality.DamageResolution(turret)
|
||||
FacilityTurretControl.HandleDamageResolution(turret, cause, damageToHealth)
|
||||
if(damageToHealth > 0) {
|
||||
val name = turret.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint + 1, name.length - 1)}: BEFORE=$originalHealth, AFTER=$health, CHANGE=$damageToHealth")
|
||||
}
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
object FacilityTurretControl {
|
||||
def HandleDamageResolution(target : FacilityTurret, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => targetGUID
|
||||
}
|
||||
val continentId = zone.Id
|
||||
if(target.Health > 1) {
|
||||
//alert occupants to damage source
|
||||
if(damage > 0) {
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert occupants to damage source
|
||||
HandleDamageAwareness(target, playerGUID, cause)
|
||||
}
|
||||
if(cause.projectile.profile.JammerProjectile) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
else {
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
zone.VehicleEvents ! VehicleServiceMessage(continentId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDamageAwareness(target : FacilityTurret, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.Id
|
||||
//alert occupants to damage source
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.HitHint(attribution, tplayer.GUID))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : FacilityTurret, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
target.Actor ! JammableUnit.ClearJammeredSound()
|
||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.Id
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//turret wreckage has no weapons
|
||||
// target.Weapons.values
|
||||
// .filter {
|
||||
// _.Equipment.nonEmpty
|
||||
// }
|
||||
// .foreach(slot => {
|
||||
// val wep = slot.Equipment.get
|
||||
// zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
|
||||
// })
|
||||
// zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(targetGUID, playerGUID, playerGUID, player.Position))
|
||||
target.Health = 1 //TODO turret "death" at 0, as is proper
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zoneId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 0, target.Health)) //TODO not necessary
|
||||
if(target.Upgrade != TurretUpgrade.None) {
|
||||
zone.VehicleEvents ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(target), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(target, zone, TurretUpgrade.None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.vital.{StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
|
||||
|
||||
/**
|
||||
* The definition for any `FacilityTurret`.
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class FacilityTurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||
with TurretDefinition {
|
||||
Damage = StandardVehicleDamage
|
||||
Resistance = StandardVehicleResistance
|
||||
Model = StandardResolutions.FacilityTurrets
|
||||
}
|
||||
|
||||
|
|
@ -3,19 +3,18 @@ package net.psforever.objects.serverobject.turret
|
|||
|
||||
import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition}
|
||||
import net.psforever.objects.vehicles.Turrets
|
||||
import net.psforever.objects.vital.{DamageResistanceModel, StandardResolutions, StandardVehicleDamage, StandardVehicleResistance}
|
||||
import net.psforever.objects.vital.DamageResistanceModel
|
||||
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* The definition for any `MannedTurret`.
|
||||
* @param objectId the object's identifier number
|
||||
*/
|
||||
class TurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId)
|
||||
with ResistanceProfileMutators
|
||||
trait TurretDefinition extends ResistanceProfileMutators
|
||||
with DamageResistanceModel {
|
||||
Turrets(objectId) //let throw NoSuchElementException
|
||||
odef : ObjectDefinition =>
|
||||
Turrets(odef.ObjectId) //let throw NoSuchElementException
|
||||
|
||||
private var maxHealth : Int = 100
|
||||
/* key - entry point index, value - seat index */
|
||||
|
|
@ -29,9 +28,6 @@ class TurretDefinition(private val objectId : Int) extends ObjectDefinition(obje
|
|||
/** creates internal ammunition reserves that can not become depleted
|
||||
* see `MannedTurret.TurretAmmoBox` for details */
|
||||
private var hasReserveAmmunition : Boolean = false
|
||||
Damage = StandardVehicleDamage
|
||||
Resistance = StandardVehicleResistance
|
||||
Model = StandardResolutions.FacilityTurrets
|
||||
|
||||
def MaxHealth : Int = maxHealth
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package net.psforever.objects.vehicles
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.SimpleDeployableDefinition
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
|
|
@ -193,7 +193,7 @@ object Utility {
|
|||
* and allows it to serve as one of the terminal points of a Router-telepad teleportation system.
|
||||
* @param ddef na
|
||||
*/
|
||||
class InternalTelepad(ddef : DeployableDefinition) extends Amenity
|
||||
class InternalTelepad(ddef : SimpleDeployableDefinition) extends Amenity
|
||||
with TelepadLike {
|
||||
/** a link to the telepad that serves as the other endpoint of this teleportation system */
|
||||
private var activeTelepad : Option[PlanetSideGUID] = None
|
||||
|
|
|
|||
|
|
@ -2,13 +2,20 @@
|
|||
package net.psforever.objects.vehicles
|
||||
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.ballistics.VehicleSource
|
||||
import net.psforever.objects.{GlobalDefinitions, Vehicle}
|
||||
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
|
||||
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
||||
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
|
||||
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
|
||||
import net.psforever.types.ExoSuitType
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{DriveState, ExoSuitType, Vector3}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage}
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||
|
|
@ -21,12 +28,16 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
with FactionAffinityBehavior.Check
|
||||
with DeploymentBehavior
|
||||
with MountableBehavior.Mount
|
||||
with MountableBehavior.Dismount {
|
||||
with MountableBehavior.Dismount
|
||||
with JammableMountedWeapons {
|
||||
|
||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||
vehicle.Utilities.foreach({case (_, util) => util.Setup })
|
||||
|
||||
def MountableObject = vehicle
|
||||
|
||||
def JammableObject = vehicle
|
||||
|
||||
def FactionObject = vehicle
|
||||
|
||||
def DeploymentObject = vehicle
|
||||
|
|
@ -44,6 +55,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
def Enabled : Receive = checkBehavior
|
||||
.orElse(deployBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(jammableBehavior)
|
||||
.orElse {
|
||||
case Mountable.TryMount(user, seat_num) =>
|
||||
val exosuit = user.ExoSuit
|
||||
|
|
@ -73,15 +85,17 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
if(vehicle.Health > 0) {
|
||||
val originalHealth = vehicle.Health
|
||||
val originalShields = vehicle.Shields
|
||||
damage_func(vehicle)
|
||||
val cause = damage_func(vehicle)
|
||||
val health = vehicle.Health
|
||||
val shields = vehicle.Shields
|
||||
val damageToHealth = originalHealth - health
|
||||
val damageToShields = originalShields - shields
|
||||
val name = vehicle.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint+1, name.length-1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields")
|
||||
sender ! Vitality.DamageResolution(vehicle)
|
||||
VehicleControl.HandleDamageResolution(vehicle, cause, damageToHealth + damageToShields)
|
||||
if(damageToHealth > 0 || damageToShields > 0) {
|
||||
val name = vehicle.Actor.toString
|
||||
val slashPoint = name.lastIndexOf("/")
|
||||
org.log4s.getLogger("DamageResolution").info(s"${name.substring(slashPoint + 1, name.length - 1)}: BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields")
|
||||
}
|
||||
}
|
||||
|
||||
case Vehicle.ChargeShields(amount) =>
|
||||
|
|
@ -91,7 +105,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
!vehicle.History.exists(VehicleControl.LastShieldChargeOrDamage(now))) {
|
||||
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
||||
vehicle.Shields = vehicle.Shields + amount
|
||||
sender ! Vehicle.UpdateShieldsCharge(vehicle)
|
||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Actor}", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields))
|
||||
}
|
||||
|
||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||
|
|
@ -101,7 +115,9 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
}
|
||||
sender ! FactionAffinity.AssertFactionAffinity(vehicle, faction)
|
||||
|
||||
case Vehicle.PrepareForDeletion =>
|
||||
case Vehicle.PrepareForDeletion() =>
|
||||
CancelJammeredSound(vehicle)
|
||||
CancelJammeredStatus(vehicle)
|
||||
context.become(Disabled)
|
||||
|
||||
case _ => ;
|
||||
|
|
@ -110,10 +126,10 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
def Disabled : Receive = checkBehavior
|
||||
.orElse(dismountBehavior)
|
||||
.orElse {
|
||||
case Vehicle.Reactivate =>
|
||||
case Vehicle.Reactivate() =>
|
||||
context.become(Enabled)
|
||||
|
||||
case _ => ;
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,4 +152,116 @@ object VehicleControl {
|
|||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
*/
|
||||
def HandleDamageResolution(target : Vehicle, cause : ResolvedProjectile, damage : Int) : Unit = {
|
||||
val zone = target.Zone
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||
case Some(player) => player.GUID
|
||||
case _ => PlanetSideGUID(0)
|
||||
}
|
||||
if(target.Health > 0) {
|
||||
//activity on map
|
||||
if(damage > 0) {
|
||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||
//alert occupants to damage source
|
||||
HandleDamageAwareness(target, playerGUID, cause)
|
||||
}
|
||||
if(cause.projectile.profile.JammerProjectile) {
|
||||
target.Actor ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
else {
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
HandleDestructionAwareness(target, playerGUID, cause)
|
||||
}
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
|
||||
zone.VehicleEvents ! VehicleServiceMessage(s"${target.Actor}", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, target.Shields))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDamageAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
val zone = target.Zone
|
||||
//alert occupants to damage source
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(attribution, tplayer.GUID))
|
||||
})
|
||||
//alert cargo occupants to damage source
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Health = 0
|
||||
cargo.Shields = 0
|
||||
cargo.History(lastShot)
|
||||
HandleDamageAwareness(cargo, attribution, lastShot)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleDestructionAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
target.Actor ! JammableUnit.ClearJammeredSound()
|
||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||
val zone = target.Zone
|
||||
val continentId = zone.Id
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
zone.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//vehicle wreckage has no weapons
|
||||
target.Weapons.values
|
||||
.filter {
|
||||
_.Equipment.nonEmpty
|
||||
}
|
||||
.foreach(slot => {
|
||||
val wep = slot.Equipment.get
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
|
||||
})
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Health = 0
|
||||
cargo.Shields = 0
|
||||
cargo.Position += Vector3.z(1)
|
||||
cargo.History(lastShot) //necessary to kill cargo vehicle occupants //TODO: collision damage
|
||||
HandleDestructionAwareness(cargo, attribution, lastShot) //might cause redundant packets
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
target.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
case GlobalDefinitions.router =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
VehicleService.BeforeUnloadVehicle(target, zone)
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None))
|
||||
case _ => ;
|
||||
}
|
||||
zone.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, zone, Some(1 minute)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import net.psforever.objects.PlanetSideGameObject
|
|||
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry, VehicleSource}
|
||||
import net.psforever.objects.definition.KitDefinition
|
||||
import net.psforever.objects.serverobject.terminals.TerminalDefinition
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
||||
|
||||
abstract class VitalsActivity(target : SourceEntry) {
|
||||
|
|
@ -102,13 +103,13 @@ object Vitality {
|
|||
* upon a given vital object.
|
||||
* @param func a function literal
|
||||
*/
|
||||
final case class Damage(func : (Any)=>Unit)
|
||||
final case class Damage(func : ResolutionCalculations.Output)
|
||||
|
||||
final case class DamageOn(obj : Vitality, func : (Any)=>Unit)
|
||||
final case class DamageOn(obj : Vitality, func : ResolutionCalculations.Output)
|
||||
|
||||
/**
|
||||
* Report that a vitals object must be updated due to damage.
|
||||
* @param obj the vital object
|
||||
*/
|
||||
final case class DamageResolution(obj : Vitality)
|
||||
final case class DamageResolution(obj : Vitality, cause : ResolvedProjectile)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import net.psforever.objects.vital.projectile.ProjectileCalculations
|
|||
* @tparam A an internal type that converts between `calcFunc`'s output and `applyFunc`'s input;
|
||||
* never has to be defined explicitly, but will be checked upon object definition
|
||||
*/
|
||||
abstract class DamageResistCalculations[A](calcFunc : (ResolvedProjectile)=>((Int, Int)=>A),
|
||||
abstract class DamageResistCalculations[A](calcFunc : ResolvedProjectile=>(Int, Int)=>A,
|
||||
applyFunc : (A, ResolvedProjectile)=>ResolutionCalculations.Output)
|
||||
extends ResolutionCalculations {
|
||||
def Calculate(damages : ProjectileCalculations.Form, resistances : ProjectileCalculations.Form, data : ResolvedProjectile) : ResolutionCalculations.Output = {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.vital.resolution
|
|||
|
||||
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.ce.{ComplexDeployable, Deployable}
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
import net.psforever.objects.vital.projectile.ProjectileCalculations
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ trait ResolutionCalculations {
|
|||
}
|
||||
|
||||
object ResolutionCalculations {
|
||||
type Output = (Any)=>Unit
|
||||
type Output = Any=>ResolvedProjectile
|
||||
type Form = (ProjectileCalculations.Form, ProjectileCalculations.Form, ResolvedProjectile)=>Output
|
||||
|
||||
def NoDamage(data : ResolvedProjectile)(a : Int, b : Int) : Int = 0
|
||||
|
|
@ -107,7 +107,7 @@ object ResolutionCalculations {
|
|||
}
|
||||
}
|
||||
|
||||
def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : Unit = { }
|
||||
def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = data
|
||||
|
||||
def SubtractWithRemainder(current : Int, damage : Int) : (Int, Int) = {
|
||||
val a = Math.max(0, current - damage)
|
||||
|
|
@ -123,41 +123,44 @@ object ResolutionCalculations {
|
|||
* @param data the historical `ResolvedProjectile` information
|
||||
* @param target the `Player` object to be affected by these damage values (at some point)
|
||||
*/
|
||||
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case player : Player =>
|
||||
var (a, b) = damageValues
|
||||
var result = (0, 0)
|
||||
//TODO Personal Shield implant test should go here and modify the values a and b
|
||||
if(player.isAlive && !(a == 0 && b == 0)) {
|
||||
player.History(data)
|
||||
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
||||
// Subtract armour damage from capacitor
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, b)
|
||||
player.Capacitor = result._1
|
||||
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case player : Player =>
|
||||
var (a, b) = damageValues
|
||||
var result = (0, 0)
|
||||
//TODO Personal Shield implant test should go here and modify the values a and b
|
||||
if(player.isAlive && !(a == 0 && b == 0)) {
|
||||
player.History(data)
|
||||
if(player.Capacitor.toInt > 0 && player.isShielded) {
|
||||
// Subtract armour damage from capacitor
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, b)
|
||||
player.Capacitor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then follow up with health damage if any capacitor is left
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, a)
|
||||
player.Capacitor = result._1
|
||||
a = result._2
|
||||
}
|
||||
|
||||
// Subtract any remaining armour damage from armour
|
||||
result = SubtractWithRemainder(player.Armor, b)
|
||||
player.Armor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then follow up with health damage if any capacitor is left
|
||||
result = SubtractWithRemainder(player.Capacitor.toInt, a)
|
||||
player.Capacitor = result._1
|
||||
// Then bleed through to health if armour ran out
|
||||
result = SubtractWithRemainder(player.Health, b)
|
||||
player.Health = result._1
|
||||
b = result._2
|
||||
|
||||
// Finally, apply health damage to health
|
||||
result = SubtractWithRemainder(player.Health, a)
|
||||
player.Health = result._1
|
||||
a = result._2
|
||||
}
|
||||
|
||||
// Subtract any remaining armour damage from armour
|
||||
result = SubtractWithRemainder(player.Armor, b)
|
||||
player.Armor = result._1
|
||||
b = result._2
|
||||
|
||||
// Then bleed through to health if armour ran out
|
||||
result = SubtractWithRemainder(player.Health, b)
|
||||
player.Health = result._1
|
||||
b = result._2
|
||||
|
||||
// Finally, apply health damage to health
|
||||
result = SubtractWithRemainder(player.Health, a)
|
||||
player.Health = result._1
|
||||
a = result._2
|
||||
}
|
||||
case _ =>
|
||||
case _ =>
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -167,9 +170,9 @@ object ResolutionCalculations {
|
|||
* @param data the historical `ResolvedProjectile` information
|
||||
* @param target the `Vehicle` object to be affected by these damage values (at some point)
|
||||
*/
|
||||
def VehicleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case vehicle : Vehicle =>
|
||||
if(vehicle.Health > 0) {
|
||||
def VehicleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case vehicle : Vehicle if vehicle.Health > 0 && damage > 0 =>
|
||||
vehicle.History(data)
|
||||
val shields = vehicle.Shields
|
||||
if(shields > damage) {
|
||||
|
|
@ -182,57 +185,58 @@ object ResolutionCalculations {
|
|||
else {
|
||||
vehicle.Health = vehicle.Health - damage
|
||||
}
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : Unit = target match {
|
||||
case ce : SimpleDeployable =>
|
||||
if(ce.Health > 0) {
|
||||
ce.Health -= damage
|
||||
ce.History(data)
|
||||
}
|
||||
case turret : FacilityTurret =>
|
||||
if(turret.Health > 0) {
|
||||
def SimpleApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case obj : Deployable if obj.Health > 0 =>
|
||||
obj.Health -= damage
|
||||
obj.History(data)
|
||||
case turret : FacilityTurret if turret.Health > 0 =>
|
||||
turret.Health -= damage
|
||||
turret.History(data)
|
||||
}
|
||||
case _ =>
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
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
|
||||
def ComplexDeployableApplication(damage : Int, data : ResolvedProjectile)(target : Any) : ResolvedProjectile = {
|
||||
target match {
|
||||
case ce : ComplexDeployable if ce.Health > 0 && damage > 0 =>
|
||||
ce.History(data)
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
}
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
ce.Health -= 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
|
||||
case ce : TurretDeployable if ce.Health > 0 && damage > 0 =>
|
||||
ce.History(data)
|
||||
if(ce.Shields > 0) {
|
||||
if(damage > ce.Shields) {
|
||||
ce.Health -= (damage - ce.Shields)
|
||||
ce.Shields = 0
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
}
|
||||
}
|
||||
else {
|
||||
ce.Shields -= damage
|
||||
ce.Health -= damage
|
||||
}
|
||||
ce.History(data)
|
||||
}
|
||||
else if(ce.Health > 0) {
|
||||
ce.Health -= damage
|
||||
ce.History(data)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case _ => ;
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class ZoneDeployableActor(zone : Zone, deployableList : ListBuffer[PlanetSideGam
|
|||
case _ =>
|
||||
obj.Definition.Initialize(obj, context)
|
||||
}
|
||||
obj.Zone = zone
|
||||
sender ! Zone.Deployable.DeployableIsBuilt(obj, tool)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.avatar.PlayerControl
|
||||
import net.psforever.objects.vehicles.VehicleControl
|
||||
import net.psforever.objects.{Avatar, Player}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -34,16 +36,22 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
|
|||
case Zone.Population.Spawn(avatar, player) =>
|
||||
PopulationSpawn(avatar, player, playerMap) match {
|
||||
case Some(tplayer) =>
|
||||
tplayer.Zone = zone
|
||||
if(tplayer ne player) {
|
||||
sender ! Zone.Population.PlayerAlreadySpawned(zone, player)
|
||||
}
|
||||
else {
|
||||
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}")
|
||||
}
|
||||
case None =>
|
||||
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
|
||||
}
|
||||
|
||||
case Zone.Population.Release(avatar) =>
|
||||
PopulationRelease(avatar, playerMap) match {
|
||||
case Some(_) => ;
|
||||
case Some(tplayer) =>
|
||||
tplayer.Actor ! akka.actor.PoisonPill
|
||||
tplayer.Actor = ActorRef.noSender
|
||||
case None =>
|
||||
sender ! Zone.Population.PlayerHasLeft(zone, None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import shapeless.{::, HNil}
|
|||
* 15 - Displays "This facility's generator is under attack!"
|
||||
* 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!"
|
||||
* 17 - Displays "This facility's generator is back on line"
|
||||
* 19 - Cause mines to explode
|
||||
* 20 - Hit flinch? (orig, 82->80)
|
||||
* 21 - Reset build cooldown from using an ACE
|
||||
* 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?)
|
||||
|
|
|
|||
|
|
@ -115,10 +115,10 @@ import scodec.codecs._
|
|||
* 45 : Advanced Engineering (= Fortification Engineering + Assault Engineering) Must have Combat Engineering<br>
|
||||
* `25 - Forget certifications (same order as 24)`<br>
|
||||
* `26 - Certification reset timer (in seconds)`
|
||||
* `27 - PA_JAMMED - plays jammed buzzing sound`<br>
|
||||
* `27 - PA_JAMMED - plays jammed buzzing sound in vicinity of target, jams weapon discharge`<br>
|
||||
* `28 - PA_IMPLANT_ACTIVE - Plays implant sounds. Valid values seem to be up to 20.`<br>
|
||||
* `29 - PA_VAPORIZED - Visible ?! That's not the cloaked effect, Maybe for spectator mode ?. Value is 0 to visible, 1 to invisible.`<br>
|
||||
* `31 - Looking for Squad info (marquee and ui):<br>
|
||||
* `31 - Looking for Squad info (marquee and ui):`<br>
|
||||
* ` - 0 is LFS`<br>
|
||||
* ` - 1 is LFSM (Looking for Squad Members)`<br>
|
||||
* ` - n is the supplemental squad identifier number; same as "LFS;" for the leader, sets "LFSM" after the first manual flagging`<br>
|
||||
|
|
@ -162,7 +162,7 @@ import scodec.codecs._
|
|||
* `13 - Trunk permissions (same)`<br>
|
||||
* `21 - Declare a player the vehicle's owner, by globally unique identifier`<br>
|
||||
* `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`<br>
|
||||
* `54 - Vehicle EMP? Plays sound as if vehicle had been hit by EMP`<br>
|
||||
* `54 - Plays jammed buzzing sound in vicinity of target`<br>
|
||||
* `68 - Vehicle shield health`<br>
|
||||
* `79 - ???`<br>
|
||||
* `80 - Damage vehicle (unknown value)`<br>
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
|||
}
|
||||
else if(data.faction == PlanetSideEmpire.NEUTRAL) {
|
||||
Attempt.successful(
|
||||
CommonFieldData(faction, data.bops, data.alternate, data.v1, data.v2, data.v3, None, data.v5, PlanetSideGUID(0)) ::
|
||||
CommonFieldData(faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, None, data.v5, PlanetSideGUID(0)) ::
|
||||
name :: suit :: u5 :: sex :: head :: v1 :: u6 :: u7 :: u8 :: u9 :: uA :: HNil
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class CommonFieldDataExtra(unk1 : Int, unk2 : Boolean) extends StreamBitSize {
|
||||
final case class CommonFieldDataExtra(unk1 : Option[Int], unk2 : Boolean) extends StreamBitSize {
|
||||
override def bitsize : Long = 17L
|
||||
}
|
||||
|
||||
object CommonFieldDataExtra {
|
||||
implicit val codec : Codec[CommonFieldDataExtra] = (
|
||||
("unk1" | uint16L) ::
|
||||
def codec(unk1 : Boolean) : Codec[CommonFieldDataExtra] = (
|
||||
("unk1" | conditional(unk1, uint16L)) :: //not sure what flags this field
|
||||
("unk2" | bool)
|
||||
).as[CommonFieldDataExtra]
|
||||
}
|
||||
|
|
@ -28,10 +28,9 @@ object CommonFieldDataExtra {
|
|||
* when set on a tool, that tool will be rendered nonfunctional instead (though it can still be equipped)
|
||||
* @param v1 na
|
||||
* @param v2 na;
|
||||
* optional data whose reading is triggered in unknown conditions;
|
||||
* flag a weapon as "jammered"
|
||||
* @param v3 na;
|
||||
* for weapons, works like `alternate`
|
||||
* optional data whose reading is triggered in unknown conditions
|
||||
* @param jammered flag as "jammered;"
|
||||
* set on most game objects, that object will produce the characteristic jammered buzz
|
||||
* @param v4 na;
|
||||
* a field used by a second encoding format for this data
|
||||
* @param v5 na;
|
||||
|
|
@ -43,7 +42,7 @@ final case class CommonFieldData(faction : PlanetSideEmpire.Value,
|
|||
alternate : Boolean,
|
||||
v1 : Boolean,
|
||||
v2 : Option[CommonFieldDataExtra],
|
||||
v3 : Boolean,
|
||||
jammered : Boolean,
|
||||
v4 : Option[Boolean],
|
||||
v5 : Option[Int],
|
||||
guid : PlanetSideGUID
|
||||
|
|
@ -64,7 +63,7 @@ final case class CommonFieldData(faction : PlanetSideEmpire.Value,
|
|||
23L + extraSize + v4Size + v5Size
|
||||
}
|
||||
|
||||
def apply(flag : Boolean) : CommonFieldData = CommonFieldData(faction, bops, alternate, v1, v2, v3, Some(flag), v5, guid)
|
||||
def apply(flag : Boolean) : CommonFieldData = CommonFieldData(faction, bops, alternate, v1, v2, jammered, Some(flag), v5, guid)
|
||||
}
|
||||
|
||||
object CommonFieldData extends Marshallable[CommonFieldData] {
|
||||
|
|
@ -100,8 +99,8 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
|
|||
("bops" | bool) ::
|
||||
("alternate" | bool) ::
|
||||
("v1" | bool) :: //the purpose of this bit changes depending on the previous bit
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec) ::
|
||||
("v3" | bool) ::
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec(unk1 = false)) ::
|
||||
("jammered" | bool) ::
|
||||
optional(bool, "v5" | uint16L) ::
|
||||
("guid" | PlanetSideGUID.codec)
|
||||
).xmap[CommonFieldData] (
|
||||
|
|
@ -122,8 +121,8 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
|
|||
("bops" | bool) ::
|
||||
("alternate" | bool) ::
|
||||
("v1" | bool) :: //though the code path differs depending on the previous bit, this one gets read one way or another
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec) ::
|
||||
("v3" | bool) ::
|
||||
conditional(extra, "v2" | CommonFieldDataExtra.codec(unk1 = false)) ::
|
||||
("jammered" | bool) ::
|
||||
optional(bool, "v5" | uint16L) ::
|
||||
("v4" | bool) ::
|
||||
("guid" | PlanetSideGUID.codec)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
|
|||
OneMannedFieldTurretData(
|
||||
CommonFieldDataWithPlacement(
|
||||
deploy.pos,
|
||||
CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.v3, data.v4, data.v5, player)
|
||||
CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, data.v4, data.v5, player)
|
||||
),
|
||||
newHealth,
|
||||
newInternals
|
||||
|
|
@ -92,7 +92,7 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
|
|||
Attempt.successful(
|
||||
CommonFieldDataWithPlacement(
|
||||
pos,
|
||||
CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.v3, data.v4, data.v5, PlanetSideGUID(0))
|
||||
CommonFieldData(data.faction, data.bops, data.alternate, data.v1, data.v2, data.jammered, data.v4, data.v5, PlanetSideGUID(0))
|
||||
) :: data.guid :: false :: newHealth :: 0 :: 0xF :: 0 :: newInternals :: HNil
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.Container
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -31,7 +32,7 @@ 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 EnvironmentalDamage(player_guid : PlanetSideGUID, amount: Int) extends Action
|
||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : Any=>Unit) extends Action
|
||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : ResolutionCalculations.Output) 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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package services.avatar
|
|||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
|
|
@ -25,7 +26,7 @@ object AvatarResponse {
|
|||
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
||||
final case class ConcealPlayer() extends Response
|
||||
final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) extends Response
|
||||
final case class DamageResolution(target : Player, resolution_function : Any=>Unit) extends Response
|
||||
final case class DamageResolution(target : Player, resolution_function : ResolutionCalculations.Output) extends Response
|
||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response
|
||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ class LocalService(zone : Zone) extends Actor {
|
|||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DeployableMapIcon(behavior, deployInfo))
|
||||
)
|
||||
case LocalAction.Detonate(guid, obj) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.Detonate(guid, obj))
|
||||
)
|
||||
case LocalAction.DoorOpens(player_guid, _, door) =>
|
||||
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
||||
LocalEvents.publish(
|
||||
|
|
@ -282,9 +286,9 @@ class LocalService(zone : Zone) extends Actor {
|
|||
}
|
||||
|
||||
//synchronized damage calculations
|
||||
case Vitality.DamageOn(target : Deployable, func) =>
|
||||
func(target)
|
||||
sender ! Vitality.DamageResolution(target)
|
||||
case Vitality.DamageOn(target : Deployable, damage_func) =>
|
||||
val cause = damage_func(target)
|
||||
sender ! Vitality.DamageResolution(target, cause)
|
||||
|
||||
case msg =>
|
||||
log.warn(s"Unhandled message $msg from $sender")
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ object LocalAction {
|
|||
|
||||
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 Detonate(guid : PlanetSideGUID, obj : PlanetSideGameObject) 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 +19,7 @@ object LocalResponse {
|
|||
|
||||
final case class AlertDestroyDeployable(obj : PlanetSideGameObject with Deployable) extends Response
|
||||
final case class DeployableMapIcon(action : DeploymentAction.Value, deployInfo : DeployableInfo) extends Response
|
||||
final case class Detonate(guid : PlanetSideGUID, obj : PlanetSideGameObject) 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
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@
|
|||
package services.vehicle
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.{GlobalDefinitions, TelepadDeployable, Vehicle}
|
||||
import net.psforever.objects.ballistics.VehicleSource
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit}
|
||||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.objects.vital.RepairFromTerm
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
|
||||
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
|
||||
import net.psforever.types.DriveState
|
||||
import services.local.LocalServiceMessage
|
||||
import services.{GenericEventBus, RemoverActor, Service}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -253,3 +255,36 @@ class VehicleService(zone : Zone) extends Actor {
|
|||
.map(util => util().asInstanceOf[SpawnTube])
|
||||
}
|
||||
}
|
||||
|
||||
object VehicleService {
|
||||
/**
|
||||
* Before a vehicle is removed from the game world, the following actions must be performed.
|
||||
* @param vehicle the vehicle
|
||||
*/
|
||||
def BeforeUnloadVehicle(vehicle : Vehicle, zone : Zone) : Unit = {
|
||||
vehicle.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
case GlobalDefinitions.router =>
|
||||
RemoveTelepads(vehicle, zone)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveTelepads(vehicle: Vehicle, zone : Zone) : Unit = {
|
||||
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||
case Some(util : Utility.InternalTelepad) =>
|
||||
val telepad = util.Telepad
|
||||
util.Telepad = None
|
||||
zone.GUID(telepad)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(telepad : TelepadDeployable) =>
|
||||
telepad.Active = false
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, zone, Some(0 seconds)))
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class VehicleRemover extends RemoverActor {
|
|||
val vehicle = entry.obj.asInstanceOf[Vehicle]
|
||||
val vehicleGUID = vehicle.GUID
|
||||
val zoneId = entry.zone.Id
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion()
|
||||
//escape being someone else's cargo
|
||||
(vehicle.MountedIn match {
|
||||
case Some(carrierGUID) =>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class AegisShieldGeneratorDataTest extends Specification {
|
|||
basic.data.alternate mustEqual false
|
||||
basic.data.v1 mustEqual true
|
||||
basic.data.v2.isDefined mustEqual false
|
||||
basic.data.v3 mustEqual false
|
||||
basic.data.jammered mustEqual false
|
||||
basic.data.v5.isDefined mustEqual false
|
||||
basic.data.guid mustEqual PlanetSideGUID(2366)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class CharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Reinforced
|
||||
|
|
@ -162,7 +162,7 @@ class CharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Reinforced
|
||||
|
|
@ -231,7 +231,7 @@ class CharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.MAX
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class OneMannedFieldTurretDataTest extends Specification {
|
|||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.contains(false) mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(2502)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class RemoteProjectileDataTest extends Specification {
|
|||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.isEmpty mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
@ -63,7 +63,7 @@ class RemoteProjectileDataTest extends Specification {
|
|||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.isEmpty mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class SmallTurretDataTest extends Specification {
|
|||
deploy.alternate mustEqual true
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.contains(false) mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(7742)
|
||||
|
|
@ -61,7 +61,7 @@ class SmallTurretDataTest extends Specification {
|
|||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.contains(true) mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(8208)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class TRAPDataTest extends Specification {
|
|||
deploy.alternate mustEqual false
|
||||
deploy.v1 mustEqual true
|
||||
deploy.v2.isEmpty mustEqual true
|
||||
deploy.v3 mustEqual false
|
||||
deploy.jammered mustEqual false
|
||||
deploy.v4.contains(true) mustEqual true
|
||||
deploy.v5.isEmpty mustEqual true
|
||||
deploy.guid mustEqual PlanetSideGUID(4748)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual true
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Standard
|
||||
|
|
@ -259,7 +259,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Standard
|
||||
|
|
@ -444,7 +444,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual true
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.MAX
|
||||
|
|
@ -652,7 +652,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual true
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Agile
|
||||
|
|
@ -1104,7 +1104,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
cdata.data.alternate mustEqual false
|
||||
cdata.data.v1 mustEqual true
|
||||
cdata.data.v2.isEmpty mustEqual true
|
||||
cdata.data.v3 mustEqual false
|
||||
cdata.data.jammered mustEqual false
|
||||
cdata.data.v4.isEmpty mustEqual true
|
||||
cdata.data.v5.isEmpty mustEqual true
|
||||
cdata.data.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
@ -1160,7 +1160,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.alternate mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Standard
|
||||
|
|
@ -1310,7 +1310,7 @@ class DetailedCharacterDataTest extends Specification {
|
|||
a.data.alternate mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Standard
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class DetailedConstructionToolDataTest extends Specification {
|
|||
cdata.alternate mustEqual false
|
||||
cdata.v1 mustEqual true
|
||||
cdata.v2.isEmpty mustEqual true
|
||||
cdata.v3 mustEqual false
|
||||
cdata.jammered mustEqual false
|
||||
cdata.v4.isEmpty mustEqual true
|
||||
cdata.v5.isEmpty mustEqual true
|
||||
cdata.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
@ -71,7 +71,7 @@ class DetailedConstructionToolDataTest extends Specification {
|
|||
cdata.alternate mustEqual false
|
||||
cdata.v1 mustEqual true
|
||||
cdata.v2.isEmpty mustEqual true
|
||||
cdata.v3 mustEqual false
|
||||
cdata.jammered mustEqual false
|
||||
cdata.v4.isEmpty mustEqual true
|
||||
cdata.v5.isEmpty mustEqual true
|
||||
cdata.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
@ -111,7 +111,7 @@ class DetailedConstructionToolDataTest extends Specification {
|
|||
cdata.alternate mustEqual false
|
||||
cdata.v1 mustEqual true
|
||||
cdata.v2.isEmpty mustEqual true
|
||||
cdata.v3 mustEqual false
|
||||
cdata.jammered mustEqual false
|
||||
cdata.v4.isEmpty mustEqual true
|
||||
cdata.v5.contains(564) mustEqual true
|
||||
cdata.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
@ -139,7 +139,7 @@ class DetailedConstructionToolDataTest extends Specification {
|
|||
cdata.alternate mustEqual false
|
||||
cdata.v1 mustEqual false
|
||||
cdata.v2.isEmpty mustEqual true
|
||||
cdata.v3 mustEqual false
|
||||
cdata.jammered mustEqual false
|
||||
cdata.v4.isEmpty mustEqual true
|
||||
cdata.v5.isEmpty mustEqual true
|
||||
cdata.guid mustEqual PlanetSideGUID(0)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class MountedVehiclesTest extends Specification {
|
|||
vdata.data.bops mustEqual false
|
||||
vdata.data.alternate mustEqual false
|
||||
vdata.data.v1 mustEqual false
|
||||
vdata.data.v3 mustEqual false
|
||||
vdata.data.jammered mustEqual false
|
||||
vdata.data.v5.isEmpty mustEqual true
|
||||
vdata.data.guid mustEqual PlanetSideGUID(3776)
|
||||
vdata.health mustEqual 255
|
||||
|
|
@ -57,7 +57,7 @@ class MountedVehiclesTest extends Specification {
|
|||
a.data.bops mustEqual false
|
||||
a.data.v1 mustEqual false
|
||||
a.data.v2.isEmpty mustEqual true
|
||||
a.data.v3 mustEqual false
|
||||
a.data.jammered mustEqual false
|
||||
a.data.v4.isEmpty mustEqual true
|
||||
a.data.v5.isEmpty mustEqual true
|
||||
a.exosuit mustEqual ExoSuitType.Agile
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class NonstandardVehiclesTest extends Specification {
|
|||
basic.data.alternate mustEqual false
|
||||
basic.data.v1 mustEqual true
|
||||
basic.data.v2.isDefined mustEqual false
|
||||
basic.data.v3 mustEqual false
|
||||
basic.data.jammered mustEqual false
|
||||
basic.data.v5.isDefined mustEqual false
|
||||
basic.data.guid mustEqual PlanetSideGUID(0)
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class NormalVehiclesTest extends Specification {
|
|||
vdata.faction mustEqual PlanetSideEmpire.NC
|
||||
vdata.alternate mustEqual false
|
||||
vdata.v1 mustEqual true
|
||||
vdata.v3 mustEqual false
|
||||
vdata.jammered mustEqual false
|
||||
vdata.v5.isEmpty mustEqual true
|
||||
vdata.guid mustEqual PlanetSideGUID(0)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class UtilityVehiclesTest extends Specification {
|
|||
ant.data.faction mustEqual PlanetSideEmpire.VS
|
||||
ant.data.alternate mustEqual false
|
||||
ant.data.v1 mustEqual true
|
||||
ant.data.v3 mustEqual false
|
||||
ant.data.jammered mustEqual false
|
||||
ant.data.v5.isEmpty mustEqual true
|
||||
ant.data.guid mustEqual PlanetSideGUID(0)
|
||||
ant.driveState mustEqual DriveState.Mobile
|
||||
|
|
@ -59,7 +59,7 @@ class UtilityVehiclesTest extends Specification {
|
|||
ams.data.faction mustEqual PlanetSideEmpire.VS
|
||||
ams.data.alternate mustEqual false
|
||||
ams.data.v1 mustEqual false
|
||||
ams.data.v3 mustEqual false
|
||||
ams.data.jammered mustEqual false
|
||||
ams.data.v5.isEmpty mustEqual true
|
||||
ams.data.guid mustEqual PlanetSideGUID(2885)
|
||||
ams.driveState mustEqual DriveState.Deployed
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 87
|
||||
|
|
@ -338,7 +338,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
|
||||
func(tplayer)
|
||||
tplayer.Health mustEqual 98
|
||||
|
|
@ -352,7 +352,7 @@ class DamageModelTests extends Specification {
|
|||
tplayer.Armor mustEqual 50
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(tplayer), tplayer.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
tplayer.Armor = 0
|
||||
|
||||
func(tplayer)
|
||||
|
|
@ -365,7 +365,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Health mustEqual 650
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 641
|
||||
|
|
@ -378,7 +378,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Shields mustEqual 10
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 650
|
||||
|
|
@ -392,7 +392,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Shields mustEqual 10
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 650
|
||||
|
|
@ -407,7 +407,7 @@ class DamageModelTests extends Specification {
|
|||
vehicle.Health mustEqual 650
|
||||
|
||||
val resprojectile = ResolvedProjectile(ProjectileResolution.Hit, projectile, SourceEntry(vehicle), vehicle.DamageModel, Vector3.Zero)
|
||||
val func : (Any)=>Unit = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
val func : Any=>ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
|
||||
|
||||
func(vehicle)
|
||||
vehicle.Health mustEqual 641
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import scala.concurrent.duration._
|
|||
class FacilityTurretTest extends Specification {
|
||||
"FacilityTurretTest" should {
|
||||
"define" in {
|
||||
val obj = new TurretDefinition(480)
|
||||
val obj = new FacilityTurretDefinition(480)
|
||||
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
|
||||
obj.ReserveAmmunition mustEqual false
|
||||
obj.FactionLocked mustEqual true
|
||||
|
|
@ -152,7 +152,7 @@ class FacilityTurretControl3Test extends ActorTest {
|
|||
|
||||
class FacilityTurretControl4Test extends ActorTest {
|
||||
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
|
||||
val objDef = new TurretDefinition(480)
|
||||
val objDef = new FacilityTurretDefinition(480)
|
||||
objDef.FactionLocked = false
|
||||
val obj = FacilityTurret(objDef)
|
||||
obj.GUID = PlanetSideGUID(1)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package objects
|
||||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics.{PlayerSource, Projectile, ProjectileResolution, ResolvedProjectile}
|
||||
|
|
@ -9,9 +10,11 @@ import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types._
|
||||
import org.specs2.mutable._
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -324,66 +327,82 @@ class VehicleControlStopMountingTest extends ActorTest {
|
|||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref //necessary
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
val probe = new TestProbe(system)
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
val reply = probe.receiveOne(Duration.create(200, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 1)
|
||||
expectNoMsg(Duration.create(200, "ms"))
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.expectNoMsg(Duration.create(200, "ms")) //assertion failed: received unexpected message MountMessages(CanMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlRestartMountingTest extends ActorTest {
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
val probe = new TestProbe(system)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"reactivate and resume handling mount messages" in {
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(200, "ms")) //discard
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.expectNoMsg(Duration.create(200, "ms"))
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 1)
|
||||
expectNoMsg(Duration.create(200, "ms"))
|
||||
|
||||
vehicle.Actor ! Vehicle.Reactivate
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 1)
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
vehicle.Actor.tell(Vehicle.Reactivate(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
val reply = probe.receiveOne(Duration.create(200, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlAlwaysDismountTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"always allow dismount messages" in {
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 1)
|
||||
receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
|
||||
vehicle.Actor ! Mountable.TryDismount(player2, 1) //player2 requests dismount
|
||||
val reply1 = receiveOne(Duration.create(100, "ms"))
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player2, 1), probe.ref) //player2 requests dismount
|
||||
val reply1 = probe.receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply1.isInstanceOf[Mountable.MountMessages])
|
||||
assert(reply1.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player2 dismounts
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||
|
||||
vehicle.Actor ! Mountable.TryDismount(player1, 0) //player1 requests dismount
|
||||
val reply2 = receiveOne(Duration.create(100, "ms"))
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref) //player1 requests dismount
|
||||
val reply2 = probe.receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply2.isInstanceOf[Mountable.MountMessages])
|
||||
assert(reply2.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player1 dismounts
|
||||
}
|
||||
|
|
@ -391,8 +410,9 @@ class VehicleControlAlwaysDismountTest extends ActorTest {
|
|||
}
|
||||
|
||||
class VehicleControlMountingBlockedExosuitTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
def checkCanNotMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||
|
|
@ -402,7 +422,7 @@ class VehicleControlMountingBlockedExosuitTest extends ActorTest {
|
|||
}
|
||||
|
||||
def checkCanMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||
|
|
@ -410,49 +430,49 @@ class VehicleControlMountingBlockedExosuitTest extends ActorTest {
|
|||
assert(false)
|
||||
}
|
||||
}
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.ExoSuit = ExoSuitType.Reinforced
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.ExoSuit = ExoSuitType.MAX
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val player3 = Player(VehicleTest.avatar1)
|
||||
player3.ExoSuit = ExoSuitType.Agile
|
||||
player3.GUID = PlanetSideGUID(3)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"block players from sitting if their exo-suit is not allowed by the seat" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.ExoSuit = ExoSuitType.Reinforced
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.ExoSuit = ExoSuitType.MAX
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
val player3 = Player(VehicleTest.avatar1)
|
||||
player3.ExoSuit = ExoSuitType.Agile
|
||||
player3.GUID = PlanetSideGUID(3)
|
||||
|
||||
//disallow
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0) //Reinforced in non-MAX seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref) //Reinforced in non-MAX seat
|
||||
checkCanNotMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 0) //MAX in non-Reinforced seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref) //MAX in non-Reinforced seat
|
||||
checkCanNotMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 1) //MAX in non-MAX seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref) //MAX in non-MAX seat
|
||||
checkCanNotMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 9) //Reinforced in MAX-only seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 9), probe.ref) //Reinforced in MAX-only seat
|
||||
checkCanNotMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player3, 9) //Agile in MAX-only seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player3, 9), probe.ref) //Agile in MAX-only seat
|
||||
checkCanNotMount()
|
||||
|
||||
//allow
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 1)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 1), probe.ref)
|
||||
checkCanMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 9)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 9), probe.ref)
|
||||
checkCanMount()
|
||||
vehicle.Actor ! Mountable.TryMount(player3, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player3, 0), probe.ref)
|
||||
checkCanMount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
def checkCanNotMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||
|
|
@ -462,7 +482,7 @@ class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
|
|||
}
|
||||
|
||||
def checkCanMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||
|
|
@ -470,32 +490,33 @@ class VehicleControlMountingBlockedSeatPermissionTest extends ActorTest {
|
|||
assert(false)
|
||||
}
|
||||
}
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
"Vehicle Control" should {
|
||||
//11 June 2018: Group is not supported yet so do not bother testing it
|
||||
"block players from sitting if the seat does not allow it" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
vehicle.PermissionGroup(2,3) //passenger group -> empire
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 3) //passenger seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 3), probe.ref) //passenger seat
|
||||
checkCanMount()
|
||||
vehicle.PermissionGroup(2,0) //passenger group -> locked
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 4) //passenger seat
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 4), probe.ref) //passenger seat
|
||||
checkCanNotMount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlMountingDriverSeatTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
def checkCanMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||
|
|
@ -503,19 +524,18 @@ class VehicleControlMountingDriverSeatTest extends ActorTest {
|
|||
assert(false)
|
||||
}
|
||||
}
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"allow players to sit in the driver seat, even if it is locked, if the vehicle is unowned" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
|
||||
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
assert(vehicle.Owner.isEmpty)
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
checkCanMount()
|
||||
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||
}
|
||||
|
|
@ -523,8 +543,9 @@ class VehicleControlMountingDriverSeatTest extends ActorTest {
|
|||
}
|
||||
|
||||
class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
def checkCanNotMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
|
||||
|
|
@ -534,7 +555,7 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
|||
}
|
||||
|
||||
def checkCanMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||
|
|
@ -542,30 +563,28 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
|||
assert(false)
|
||||
}
|
||||
}
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"block players that are not the current owner from sitting in the driver seat (locked)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Locked)) //driver group -> locked
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
vehicle.Owner = player1.GUID
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
checkCanMount()
|
||||
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||
vehicle.Actor ! Mountable.TryDismount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref)
|
||||
checkCanNotMount()
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
}
|
||||
|
|
@ -573,8 +592,9 @@ class VehicleControlMountingOwnedLockedDriverSeatTest extends ActorTest {
|
|||
}
|
||||
|
||||
class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
def checkCanMount() : Unit = {
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
val reply = probe.receiveOne(Duration.create(100, "ms"))
|
||||
reply match {
|
||||
case msg : Mountable.MountMessages =>
|
||||
assert(msg.response.isInstanceOf[Mountable.CanMount])
|
||||
|
|
@ -582,31 +602,29 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
|
|||
assert(false)
|
||||
}
|
||||
}
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"allow players that are not the current owner to sit in the driver seat (empire)" in {
|
||||
val vehicle = Vehicle(GlobalDefinitions.apc_tr)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar1)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
|
||||
vehicle.PermissionGroup(0,3) //passenger group -> empire
|
||||
assert(vehicle.PermissionGroup(0).contains(VehicleLockState.Empire)) //driver group -> empire
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
vehicle.Owner = player1.GUID //owner set
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player1, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
checkCanMount()
|
||||
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||
vehicle.Actor ! Mountable.TryDismount(player1, 0)
|
||||
receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
assert(vehicle.Seats(0).Occupant.isEmpty)
|
||||
|
||||
vehicle.Actor ! Mountable.TryMount(player2, 0)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 0), probe.ref)
|
||||
checkCanMount()
|
||||
assert(vehicle.Seats(0).Occupant.nonEmpty)
|
||||
}
|
||||
|
|
@ -614,26 +632,37 @@ class VehicleControlMountingOwnedUnlockedDriverSeatTest extends ActorTest {
|
|||
}
|
||||
|
||||
class VehicleControlShieldsChargingTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = probe.ref
|
||||
}
|
||||
|
||||
"charge vehicle shields" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
|
||||
val msg = receiveOne(500 milliseconds)
|
||||
assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge])
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
val msg = probe.receiveOne(500 milliseconds)
|
||||
assert(msg match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(10), 68, 15)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(vehicle.Shields == 15)
|
||||
assert(vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlShieldsNotChargingVehicleDeadTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = probe.ref
|
||||
}
|
||||
|
||||
"not charge vehicle shields if the vehicle is destroyed" in {
|
||||
assert(vehicle.Health > 0)
|
||||
|
|
@ -641,18 +670,22 @@ class VehicleControlShieldsNotChargingVehicleDeadTest extends ActorTest {
|
|||
assert(vehicle.Health == 0)
|
||||
assert(vehicle.Shields == 0)
|
||||
assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
vehicle.Actor.tell(Vehicle.ChargeShields(15), probe.ref)
|
||||
|
||||
expectNoMsg(1 seconds)
|
||||
probe.expectNoMsg(1 seconds)
|
||||
assert(vehicle.Shields == 0)
|
||||
assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlShieldsNotChargingVehicleShieldsFullTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = probe.ref
|
||||
}
|
||||
|
||||
"not charge vehicle shields if the vehicle is destroyed" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
|
|
@ -661,54 +694,67 @@ class VehicleControlShieldsNotChargingVehicleShieldsFullTest extends ActorTest {
|
|||
assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
|
||||
expectNoMsg(1 seconds)
|
||||
probe.expectNoMsg(1 seconds)
|
||||
assert(!vehicle.History.exists({p => p.isInstanceOf[VehicleShieldCharge]}))
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = probe.ref
|
||||
}
|
||||
|
||||
"charge vehicle shields" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
|
||||
val msg = receiveOne(200 milliseconds)
|
||||
assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge])
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
val msg = probe.receiveOne(200 milliseconds)
|
||||
//assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge])
|
||||
assert(msg match {
|
||||
case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(10), 68, 15)) => true
|
||||
case _ => false
|
||||
})
|
||||
assert(vehicle.Shields == 15)
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
|
||||
expectNoMsg(200 milliseconds)
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
probe.expectNoMsg(200 milliseconds)
|
||||
assert(vehicle.Shields == 15)
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
vehicle.GUID = PlanetSideGUID(10)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
//
|
||||
val beamer_wep = Tool(GlobalDefinitions.beamer)
|
||||
val p_source = PlayerSource( Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
|
||||
val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
|
||||
val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
|
||||
val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime)
|
||||
|
||||
"not charge vehicle shields if recently damaged" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj)})
|
||||
|
||||
val msg = receiveOne(200 milliseconds)
|
||||
assert(msg.isInstanceOf[Vitality.DamageResolution])
|
||||
assert(vehicle.Shields == 0)
|
||||
vehicle.Actor ! Vehicle.ChargeShields(15)
|
||||
|
||||
expectNoMsg(200 milliseconds)
|
||||
assert(vehicle.Shields == 0)
|
||||
}
|
||||
}
|
||||
//TODO implement message protocol for zone startup completion
|
||||
//class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
|
||||
// val probe = new TestProbe(system)
|
||||
// val vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
// vehicle.GUID = PlanetSideGUID(10)
|
||||
// vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
// vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
// VehicleEvents = probe.ref
|
||||
// }
|
||||
// //
|
||||
// val beamer_wep = Tool(GlobalDefinitions.beamer)
|
||||
// val p_source = PlayerSource( Player(Avatar("TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
|
||||
// val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
|
||||
// val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
|
||||
// val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime)
|
||||
//
|
||||
// "not charge vehicle shields if recently damaged" in {
|
||||
// assert(vehicle.Shields == 0)
|
||||
// vehicle.Actor.tell(Vitality.Damage({case v : Vehicle => v.History(obj); obj }), probe.ref)
|
||||
//
|
||||
// val msg = probe.receiveOne(200 milliseconds)
|
||||
// assert(msg.isInstanceOf[Vitality.DamageResolution])
|
||||
// assert(vehicle.Shields == 0)
|
||||
// vehicle.Actor.tell(Vehicle.ChargeShields(15), probe.ref)
|
||||
//
|
||||
// probe.expectNoMsg(200 milliseconds)
|
||||
// assert(vehicle.Shields == 0)
|
||||
// }
|
||||
//}
|
||||
|
||||
object VehicleTest {
|
||||
import net.psforever.objects.Avatar
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@ import services.support.SupportActor
|
|||
|
||||
import scala.collection.mutable
|
||||
|
||||
class WorldSessionActor extends Actor with MDCContextAware {
|
||||
class WorldSessionActor extends Actor
|
||||
with MDCContextAware
|
||||
with JammableBehavior {
|
||||
import WorldSessionActor._
|
||||
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
|
@ -151,6 +153,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
var timeDL : Long = 0
|
||||
var timeSurge : Long = 0
|
||||
var skipStaminaRegenForTurns : Int = 0
|
||||
lazy val unsignedIntMaxValue : Long = Int.MaxValue.toLong * 2L + 1L
|
||||
var serverTime : Long = 0
|
||||
|
||||
|
|
@ -174,12 +177,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
import scala.language.implicitConversions
|
||||
implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0
|
||||
|
||||
def JammableObject = player
|
||||
|
||||
override def postStop() : Unit = {
|
||||
//TODO normally, player avatar persists a minute or so after disconnect; we are subject to the SessionReaper
|
||||
clientKeepAlive.cancel
|
||||
reviveTimer.cancel
|
||||
respawnTimer.cancel
|
||||
PlayerActionsToCancel()
|
||||
chatService ! Service.Leave()
|
||||
continent.AvatarEvents ! Service.Leave()
|
||||
continent.LocalEvents ! Service.Leave()
|
||||
|
|
@ -187,6 +191,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
galaxyService ! Service.Leave()
|
||||
LivePlayerList.Remove(sessionId)
|
||||
if(player != null && player.HasGUID) {
|
||||
PlayerActionsToCancel()
|
||||
squadService ! Service.Leave(Some(player.CharId.toString))
|
||||
val player_guid = player.GUID
|
||||
//handle orphaned deployables
|
||||
|
|
@ -316,7 +321,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
context.stop(self)
|
||||
}
|
||||
|
||||
def Started : Receive = {
|
||||
def Started : Receive = jammableBehavior.orElse {
|
||||
case ServiceManager.LookupResult("chat", endpoint) =>
|
||||
chatService = endpoint
|
||||
log.info("ID: " + sessionId + " Got chat service " + endpoint)
|
||||
|
|
@ -909,18 +914,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.Deployables ! Zone.Deployable.Dismiss(obj)
|
||||
}
|
||||
|
||||
case WorldSessionActor.FinalizeDeployable(obj : TurretDeployable, tool, index) =>
|
||||
//spitfires and deployable field turrets
|
||||
StartBundlingPackets()
|
||||
DeployableBuildActivity(obj)
|
||||
CommonDestroyConstructionItem(tool, index)
|
||||
FindReplacementConstructionItem(tool, index)
|
||||
StopBundlingPackets()
|
||||
|
||||
case WorldSessionActor.FinalizeDeployable(obj : ComplexDeployable, tool, index) =>
|
||||
//deployable_shield_generator
|
||||
case WorldSessionActor.FinalizeDeployable(obj : SensorDeployable, tool, index) =>
|
||||
//motion alarm sensor and sensor disruptor
|
||||
StartBundlingPackets()
|
||||
DeployableBuildActivity(obj)
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.TriggerEffectInfo(player.GUID, "on", obj.GUID, true, 1000))
|
||||
CommonDestroyConstructionItem(tool, index)
|
||||
FindReplacementConstructionItem(tool, index)
|
||||
StopBundlingPackets()
|
||||
|
|
@ -955,11 +953,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
FindReplacementConstructionItem(tool, index)
|
||||
StopBundlingPackets()
|
||||
|
||||
case WorldSessionActor.FinalizeDeployable(obj : SensorDeployable, tool, index) =>
|
||||
//motion alarm sensor and sensor disruptor
|
||||
case WorldSessionActor.FinalizeDeployable(obj : ComplexDeployable, tool, index) =>
|
||||
//spitfires and deployable field turrets and the deployable_shield_generator
|
||||
StartBundlingPackets()
|
||||
DeployableBuildActivity(obj)
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.TriggerEffectInfo(player.GUID, "on", obj.GUID, true, 1000))
|
||||
CommonDestroyConstructionItem(tool, index)
|
||||
FindReplacementConstructionItem(tool, index)
|
||||
StopBundlingPackets()
|
||||
|
|
@ -1011,7 +1008,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
StartBundlingPackets()
|
||||
sendResponse(GenericObjectActionMessage(guid, 21)) //reset build cooldown
|
||||
sendResponse(ObjectDeployedMessage.Failure(definition.Name))
|
||||
log.warn(s"FinalizeDeployable: deployable ${definition.asInstanceOf[DeployableDefinition].Item}@$guid not handled by specific case")
|
||||
log.warn(s"FinalizeDeployable: deployable ${definition.asInstanceOf[SimpleDeployableDefinition].Item}@$guid not handled by specific case")
|
||||
log.warn(s"FinalizeDeployable: deployable will be cleaned up, but may not get unregistered properly")
|
||||
TryDropConstructionTool(tool, index, obj.Position)
|
||||
obj.Position = Vector3.Zero
|
||||
|
|
@ -1100,10 +1097,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) =>
|
||||
HandleHackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction)
|
||||
|
||||
case Vitality.DamageResolution(target : Vehicle) =>
|
||||
HandleVehicleDamageResolution(target)
|
||||
|
||||
case Vitality.DamageResolution(target : TrapDeployable) =>
|
||||
case Vitality.DamageResolution(target : TrapDeployable, _) =>
|
||||
//tank_traps
|
||||
val guid = target.GUID
|
||||
val health = target.Health
|
||||
|
|
@ -1112,17 +1106,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
AnnounceDestroyDeployable(target, None)
|
||||
}
|
||||
|
||||
case Vitality.DamageResolution(target : SensorDeployable) =>
|
||||
//sensors
|
||||
val guid = target.GUID
|
||||
val health = target.Health
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(guid, 0, health))
|
||||
if(health <= 0) {
|
||||
AnnounceDestroyDeployable(target, Some(0 seconds))
|
||||
}
|
||||
|
||||
case Vitality.DamageResolution(target : SimpleDeployable) =>
|
||||
//boomers, mines
|
||||
case Vitality.DamageResolution(target : TelepadDeployable, _) =>
|
||||
//telepads
|
||||
if(target.Health <= 0) {
|
||||
//update if destroyed
|
||||
val guid = target.GUID
|
||||
|
|
@ -1130,27 +1115,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
AnnounceDestroyDeployable(target, Some(0 seconds))
|
||||
}
|
||||
|
||||
case Vitality.DamageResolution(target : TurretDeployable) =>
|
||||
HandleTurretDeployableDamageResolution(target)
|
||||
|
||||
case Vitality.DamageResolution(target : ComplexDeployable) =>
|
||||
//shield_generators
|
||||
val health = target.Health
|
||||
val guid = target.GUID
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(guid, 0, health))
|
||||
if(health <= 0) {
|
||||
AnnounceDestroyDeployable(target, None)
|
||||
}
|
||||
|
||||
case Vitality.DamageResolution(target : FacilityTurret) =>
|
||||
HandleFacilityTurretDamageResolution(target)
|
||||
|
||||
case Vitality.DamageResolution(target : PlanetSideGameObject) =>
|
||||
case Vitality.DamageResolution(target : PlanetSideGameObject, _) =>
|
||||
log.warn(s"Vital target ${target.Definition.Name} damage resolution not supported using this method")
|
||||
|
||||
case Vehicle.UpdateShieldsCharge(vehicle) =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Actor}", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, 68, vehicle.Shields))
|
||||
|
||||
case ResponseToSelf(pkt) =>
|
||||
log.info(s"Received a direct message: $pkt")
|
||||
sendResponse(pkt)
|
||||
|
|
@ -1246,22 +1213,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(GenericObjectActionMessage(guid, 9))
|
||||
|
||||
case AvatarResponse.EnvironmentalDamage(target, amount) =>
|
||||
if(player.isAlive) {
|
||||
if(player.isAlive && amount != 0) {
|
||||
val armor = player.Armor
|
||||
val capacitor = player.Capacitor
|
||||
val originalHealth = player.Health
|
||||
player.Health = player.Health - amount
|
||||
player.Health = originalHealth - amount
|
||||
sendResponse(PlanetsideAttributeMessage(target, 0, player.Health))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(target, 0, player.Health))
|
||||
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth, AFTER=${player.Health}, CHANGE=$amount")
|
||||
if(amount != 0) {
|
||||
val playerGUID = player.GUID
|
||||
sendResponse(PlanetsideAttributeMessage(playerGUID, 0, player.Health))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, player.Health))
|
||||
if(player.Health == 0 && player.isAlive) {
|
||||
KillPlayer(player)
|
||||
}
|
||||
else {
|
||||
//todo: History?
|
||||
}
|
||||
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$armor/$capacitor, AFTER=${player.Health}/$armor/$capacitor, CHANGE=$amount/0/0")
|
||||
if(player.Health == 0 && player.isAlive) {
|
||||
KillPlayer(player)
|
||||
}
|
||||
else {
|
||||
//todo: History?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1270,26 +1234,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val originalHealth = player.Health
|
||||
val originalArmor = player.Armor
|
||||
val originalCapacitor = player.Capacitor.toInt
|
||||
resolution_function(target)
|
||||
val cause = resolution_function(target)
|
||||
val health = player.Health
|
||||
val armor = player.Armor
|
||||
val capacitor = player.Capacitor.toInt
|
||||
val damageToHealth = originalHealth - health
|
||||
val damageToArmor = originalArmor - armor
|
||||
val damageToCapacitor = originalCapacitor - capacitor
|
||||
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalCapacitor, AFTER=$health/$armor/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToCapacitor")
|
||||
if(damageToHealth != 0 || damageToArmor != 0 || damageToCapacitor != 0) {
|
||||
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalCapacitor, AFTER=$health/$armor/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToCapacitor")
|
||||
val playerGUID = player.GUID
|
||||
if(damageToHealth > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
|
||||
}
|
||||
|
||||
if(damageToArmor > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 4, armor))
|
||||
}
|
||||
|
||||
if(damageToCapacitor > 0) {
|
||||
sendResponse(PlanetsideAttributeMessage(playerGUID, 7, capacitor))
|
||||
}
|
||||
|
|
@ -1301,10 +1263,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
KillPlayer(player)
|
||||
}
|
||||
else {
|
||||
//first damage entry -> most recent damage source -> killing blow
|
||||
target.History.find(p => p.isInstanceOf[DamagingActivity]) match {
|
||||
case Some(data : DamageFromProjectile) =>
|
||||
val owner = data.data.projectile.owner
|
||||
//damage cause -> recent damage source -> killing blow?
|
||||
cause match {
|
||||
case data : ResolvedProjectile =>
|
||||
val owner = data.projectile.owner
|
||||
owner match {
|
||||
case pSource : PlayerSource =>
|
||||
continent.LivePlayers.find(_.Name == pSource.Name) match {
|
||||
|
|
@ -1316,11 +1278,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(DamageWithPositionMessage(damageToHealth + damageToArmor, vSource.Position))
|
||||
case _ => ;
|
||||
}
|
||||
continent.Activity ! Zone.HotSpot.Activity(owner, data.Target, target.Position)
|
||||
continent.Activity ! Zone.HotSpot.Activity(owner, data.target, target.Position)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(target.isAlive && cause.projectile.profile.JammerProjectile) {
|
||||
self ! JammableUnit.Jammered(cause)
|
||||
}
|
||||
}
|
||||
|
||||
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
||||
|
|
@ -1459,13 +1424,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid))
|
||||
// continent.GUID(projectile_guid) match {
|
||||
// case Some(obj) =>
|
||||
// val definition = obj.Definition
|
||||
// sendResponse(ObjectCreateMessage(definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(obj).get))
|
||||
// case _ => ;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
case AvatarResponse.PutDownFDU(target) =>
|
||||
if(tplayer_guid != guid) {
|
||||
|
|
@ -1556,6 +1515,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
|
||||
}
|
||||
|
||||
case LocalResponse.Detonate(guid, obj : BoomerDeployable) =>
|
||||
sendResponse(TriggerEffectMessage(guid, "detonate_boomer"))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1))
|
||||
sendResponse(ObjectDeleteMessage(guid, 0))
|
||||
|
||||
case LocalResponse.Detonate(guid, obj : ExplosiveDeployable) =>
|
||||
sendResponse(GenericObjectActionMessage(guid, 19))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1))
|
||||
sendResponse(ObjectDeleteMessage(guid, 0))
|
||||
|
||||
case LocalResponse.Detonate(guid, obj) =>
|
||||
log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly")
|
||||
|
||||
case LocalResponse.DoorOpens(door_guid) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(GenericObjectStateMsg(door_guid, 16))
|
||||
|
|
@ -1572,6 +1544,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
DeconstructDeployable(obj, guid, pos, obj.Orientation, if(obj.MountPoints.isEmpty) 2 else 1)
|
||||
}
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj : ExplosiveDeployable, guid, pos) =>
|
||||
if(obj.Exploded || obj.Jammed || obj.Health == 0) {
|
||||
DeconstructDeployable(obj, guid, pos)
|
||||
}
|
||||
else {
|
||||
DeconstructDeployable(obj, guid, pos, obj.Orientation, 2)
|
||||
}
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj : ComplexDeployable, guid, pos) =>
|
||||
if(obj.Health == 0) {
|
||||
DeconstructDeployable(obj, guid, pos)
|
||||
|
|
@ -1580,14 +1560,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
DeconstructDeployable(obj, guid, pos, obj.Orientation, 1)
|
||||
}
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj : ExplosiveDeployable, guid, pos) =>
|
||||
if(obj.Exploded || obj.Health == 0) {
|
||||
DeconstructDeployable(obj, guid, pos)
|
||||
}
|
||||
else {
|
||||
DeconstructDeployable(obj, guid, pos, obj.Orientation, 2)
|
||||
}
|
||||
|
||||
case LocalResponse.EliminateDeployable(obj : TelepadDeployable, guid, pos) =>
|
||||
//if active, deactivate
|
||||
if(obj.Active) {
|
||||
|
|
@ -2458,7 +2430,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case VehicleResponse.ConcealPlayer(player_guid) =>
|
||||
sendResponse(GenericObjectActionMessage(player_guid, 9))
|
||||
//sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
|
||||
|
||||
case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) =>
|
||||
if(tplayer_guid != guid) {
|
||||
|
|
@ -2934,189 +2905,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
msgs
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
*/
|
||||
def HandleVehicleDamageResolution(target : Vehicle) : Unit = {
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = player.GUID
|
||||
val players = target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
})
|
||||
target.LastShot match { //TODO: collision damage from/in history
|
||||
case Some(shot) =>
|
||||
if(target.Health > 0) {
|
||||
//activity on map
|
||||
continent.Activity ! Zone.HotSpot.Activity(shot.target, shot.projectile.owner, shot.hit_pos)
|
||||
//alert occupants to damage source
|
||||
HandleVehicleDamageAwareness(target, playerGUID, shot)
|
||||
}
|
||||
else {
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
HandleVehicleDestructionAwareness(target, shot)
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
|
||||
continent.VehicleEvents ! VehicleServiceMessage(s"${target.Actor}", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, target.Shields))
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleVehicleDamageAwareness(target : Vehicle, attribution : PlanetSideGUID, lastShot : ResolvedProjectile) : Unit = {
|
||||
//alert occupants to damage source
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(attribution, tplayer.GUID))
|
||||
})
|
||||
//alert cargo occupants to damage source
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Health = 0
|
||||
cargo.Shields = 0
|
||||
cargo.History(lastShot)
|
||||
HandleVehicleDamageAwareness(cargo, attribution, lastShot)
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
* @param attribution na
|
||||
* @param lastShot na
|
||||
*/
|
||||
def HandleVehicleDestructionAwareness(target : Vehicle, lastShot : ResolvedProjectile) : Unit = {
|
||||
val playerGUID = player.GUID
|
||||
val continentId = continent.Id
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
}).foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//vehicle wreckage has no weapons
|
||||
target.Weapons.values
|
||||
.filter {
|
||||
_.Equipment.nonEmpty
|
||||
}
|
||||
.foreach(slot => {
|
||||
val wep = slot.Equipment.get
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
|
||||
})
|
||||
target.CargoHolds.values.foreach(hold => {
|
||||
hold.Occupant match {
|
||||
case Some(cargo) =>
|
||||
cargo.Health = 0
|
||||
cargo.Shields = 0
|
||||
cargo.Position += Vector3.z(1)
|
||||
cargo.History(lastShot) //necessary to kill cargo vehicle occupants //TODO: collision damage
|
||||
HandleVehicleDestructionAwareness(cargo, lastShot) //might cause redundant packets
|
||||
case None => ;
|
||||
}
|
||||
})
|
||||
target.Definition match {
|
||||
case GlobalDefinitions.ams =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
case GlobalDefinitions.router =>
|
||||
target.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
|
||||
BeforeUnloadVehicle(target)
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.ToggleTeleportSystem(PlanetSideGUID(0), target, None))
|
||||
case _ => ;
|
||||
}
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(target.GUID, playerGUID, playerGUID, target.Position))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, continent, Some(1 minute)))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target na
|
||||
*/
|
||||
def HandleTurretDeployableDamageResolution(target : TurretDeployable) : Unit = {
|
||||
//spitfires and field turrets
|
||||
val health = target.Health
|
||||
val guid = target.GUID
|
||||
val continentId = continent.Id
|
||||
if(health <= 0) {
|
||||
//if occupants, kill them
|
||||
target.Seats.values
|
||||
.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
})
|
||||
.foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//destroy weapons
|
||||
target.Weapons.values
|
||||
.map(slot => slot.Equipment)
|
||||
.collect { case Some(weapon) =>
|
||||
val wguid = weapon.GUID
|
||||
sendResponse(ObjectDeleteMessage(wguid, 0))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(player.GUID, wguid))
|
||||
}
|
||||
AnnounceDestroyDeployable(target, None)
|
||||
}
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.PlanetsideAttribute(guid, 0, health))
|
||||
}
|
||||
|
||||
def HandleFacilityTurretDamageResolution(target : FacilityTurret) : Unit = {
|
||||
val targetGUID = target.GUID
|
||||
val playerGUID = player.GUID
|
||||
val continentId = continent.Id
|
||||
val players = target.Seats.values.filter(seat => {
|
||||
seat.isOccupied && seat.Occupant.get.isAlive
|
||||
})
|
||||
if(target.Health > 1) { //TODO turret "death" at 0, as is proper
|
||||
//alert occupants to damage source
|
||||
players.foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.HitHint(playerGUID, tplayer.GUID))
|
||||
})
|
||||
}
|
||||
else {
|
||||
//alert to vehicle death (hence, occupants' deaths)
|
||||
players.foreach(seat => {
|
||||
val tplayer = seat.Occupant.get
|
||||
val tplayerGUID = tplayer.GUID
|
||||
continent.AvatarEvents ! AvatarServiceMessage(tplayer.Name, AvatarAction.KilledWhileInVehicle(tplayerGUID))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(tplayerGUID, tplayerGUID)) //dead player still sees self
|
||||
})
|
||||
//turret wreckage has no weapons
|
||||
// target.Weapons.values
|
||||
// .filter {
|
||||
// _.Equipment.nonEmpty
|
||||
// }
|
||||
// .foreach(slot => {
|
||||
// val wep = slot.Equipment.get
|
||||
// continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID))
|
||||
// })
|
||||
// continent.AvatarEvents ! AvatarServiceMessage(continentId, AvatarAction.Destroy(targetGUID, playerGUID, playerGUID, player.Position))
|
||||
target.Health = 1
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continentId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.MaxHealth)) //TODO not necessary
|
||||
if(target.Upgrade != TurretUpgrade.None) {
|
||||
continent.VehicleEvents ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(target), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(target, continent, TurretUpgrade.None))
|
||||
}
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continentId, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, target.Health))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param tplayer na
|
||||
|
|
@ -3548,6 +3336,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
|
||||
player.Locker.Inventory += 0 -> SimpleItem(remote_electronics_kit)
|
||||
player.Inventory.Items.foreach { _.obj.Faction = faction }
|
||||
player.Actor = self
|
||||
//TODO end temp player character auto-loading
|
||||
self ! ListAccountCharacters
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -3888,6 +3677,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) =>
|
||||
val isMoving = WorldEntity.isMoving(vel)
|
||||
val isMovingPlus = isMoving || is_jumping || jump_thrust
|
||||
//implants and stamina management start
|
||||
val implantsAreActive = avatar.Implants(0).Active || avatar.Implants(1).Active
|
||||
val staminaBefore = player.Stamina
|
||||
|
|
@ -3919,14 +3709,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
}
|
||||
CapacitorTick(jump_thrust)
|
||||
//if the player lost all stamina this turn (had stamina at the start), do not renew 1 stamina
|
||||
if(!isMoving && (if(player.Stamina > 0) player.Stamina < player.MaxStamina else !hadStaminaBefore)) {
|
||||
player.Stamina = player.Stamina + 1
|
||||
true
|
||||
if(skipStaminaRegenForTurns > 0) {
|
||||
//do not renew stamina for a while
|
||||
skipStaminaRegenForTurns -= 1
|
||||
player.Stamina > 0
|
||||
}
|
||||
else if(player.Stamina == 0 && hadStaminaBefore) {
|
||||
//if the player lost all stamina this turn (had stamina at the start), do not renew stamina for a while
|
||||
skipStaminaRegenForTurns = 4
|
||||
player.Stamina > 0
|
||||
}
|
||||
else if(isMovingPlus || player.Stamina == player.MaxStamina) {
|
||||
//ineligible for stamina regen
|
||||
player.Stamina > 0
|
||||
}
|
||||
else {
|
||||
player.Stamina > 0
|
||||
player.Stamina = player.Stamina + 1
|
||||
true
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -3938,18 +3737,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
|
||||
}
|
||||
if(implantsAreActive && !hasStaminaAfter) { //implants deactivated at 0 stamina
|
||||
if(avatar.Implants(0).Active) {
|
||||
avatar.Implants(0).Active = false
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
|
||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 0, 0))
|
||||
timeDL = 0
|
||||
}
|
||||
if(avatar.Implants(1).Active) {
|
||||
avatar.Implants(1).Active = false
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
|
||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 1, 0))
|
||||
timeSurge = 0
|
||||
}
|
||||
DeactivateImplants()
|
||||
}
|
||||
//implants and stamina management finish
|
||||
player.Position = pos
|
||||
|
|
@ -3959,8 +3747,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player.Crouching = is_crouching
|
||||
player.Jumping = is_jumping
|
||||
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && is_cloaking
|
||||
CapacitorTick(jump_thrust)
|
||||
|
||||
if(isMoving && usingMedicalTerminal.isDefined) {
|
||||
if(isMovingPlus && usingMedicalTerminal.isDefined) {
|
||||
continent.GUID(usingMedicalTerminal) match {
|
||||
case Some(term : Terminal with ProximityUnit) =>
|
||||
StopUsingProximityUnit(term)
|
||||
|
|
@ -3978,7 +3767,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
accessedContainer = None
|
||||
}
|
||||
case Some(container) => //just in case
|
||||
if(isMoving) {
|
||||
if(isMovingPlus) {
|
||||
val guid = player.GUID
|
||||
// If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first.
|
||||
if(container.HasGUID) {
|
||||
|
|
@ -4404,15 +4193,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(playerGUID, item_guid))
|
||||
continent.GUID(trigger.Companion) match {
|
||||
case Some(boomer : BoomerDeployable) =>
|
||||
val boomerGUID = boomer.GUID
|
||||
boomer.Exploded = true
|
||||
sendResponse(TriggerEffectMessage(boomerGUID, "detonate_boomer"))
|
||||
sendResponse(PlanetsideAttributeMessage(boomerGUID, 29, 1))
|
||||
sendResponse(ObjectDeleteMessage(boomerGUID, 0))
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.TriggerEffect(playerGUID, "detonate_boomer", boomerGUID))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(boomerGUID, 29, 1))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(playerGUID, boomerGUID))
|
||||
continent.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.AddTask(boomer, continent, Some(0 seconds)))
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.Detonate(boomer.GUID, boomer))
|
||||
Deployables.AnnounceDestroyDeployable(boomer, Some(500 milliseconds))
|
||||
case Some(_) | None => ;
|
||||
}
|
||||
FindEquipmentToDelete(item_guid, trigger)
|
||||
|
|
@ -4545,6 +4328,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case msg @ AvatarJumpMessage(state) =>
|
||||
//log.info("AvatarJump: " + msg)
|
||||
player.Stamina = player.Stamina - 10
|
||||
skipStaminaRegenForTurns = 5
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
|
||||
|
||||
case msg @ ZipLineMessage(player_guid,origin_side,action,id,pos) =>
|
||||
|
|
@ -4770,7 +4554,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
case msg @ UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) =>
|
||||
log.info("UseItem: " + msg)
|
||||
//log.info("UseItem: " + msg)
|
||||
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
|
||||
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
|
||||
continent.GUID(object_guid) match {
|
||||
|
|
@ -5495,6 +5279,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
case msg @ WeaponJammedMessage(weapon_guid) =>
|
||||
FindWeapon match {
|
||||
case Some(tool : Tool) =>
|
||||
log.info(s"WeaponJammed: ${tool.Definition.Name}@${weapon_guid.guid}")
|
||||
//TODO
|
||||
case _ =>
|
||||
log.info(s"WeaponJammed: ${weapon_guid.guid}")
|
||||
}
|
||||
|
||||
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
|
||||
log.info(s"WeaponFire: $msg")
|
||||
if(player.isShielded) {
|
||||
|
|
@ -5609,6 +5402,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case Some(projectile) =>
|
||||
projectile.Position = explosion_pos
|
||||
projectile.Velocity = projectile_vel
|
||||
//direct_victim_uid
|
||||
continent.GUID(direct_victim_uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
|
||||
|
|
@ -5618,6 +5412,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
case _ => ;
|
||||
}
|
||||
//other victims
|
||||
targets.foreach(elem => {
|
||||
continent.GUID(elem.uid) match {
|
||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
|
|
@ -7245,7 +7040,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
|
||||
continent.Ground ! Zone.Ground.DropItem(item2, pos, orient)
|
||||
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, sourceOrientZ)) //ground
|
||||
val objDef = item2.Definition
|
||||
val objDef = item2.Definition
|
||||
destination match {
|
||||
case obj : Vehicle =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid))
|
||||
|
|
@ -7912,9 +7707,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
* This is not a complete list but, for the purpose of enforcement, some pointers will be documented here.
|
||||
*/
|
||||
def PlayerActionsToCancel() : Unit = {
|
||||
CancelJammeredSound(player)
|
||||
CancelJammeredStatus(player)
|
||||
progressBarUpdate.cancel
|
||||
progressBarValue = None
|
||||
lastTerminalOrderFulfillment = true
|
||||
skipStaminaRegenForTurns = 0
|
||||
accessedContainer match {
|
||||
case Some(obj : Vehicle) =>
|
||||
if(obj.AccessingTrunk.contains(player.GUID)) {
|
||||
|
|
@ -8284,8 +8082,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
*/
|
||||
def FindProximityUnitTargetsInScope(terminal : Terminal with ProximityUnit) : Seq[PlanetSideGameObject] = {
|
||||
terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet collect {
|
||||
case ProximityTarget.Player => Some(player)
|
||||
case ProximityTarget.Vehicle | ProximityTarget.Aircraft => continent.GUID(player.VehicleSeated)
|
||||
case EffectTarget.Category.Player => Some(player)
|
||||
case EffectTarget.Category.Vehicle | EffectTarget.Category.Aircraft => continent.GUID(player.VehicleSeated)
|
||||
} collect {
|
||||
case Some(a) => a
|
||||
} toSeq
|
||||
|
|
@ -8548,8 +8346,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
||||
FindProjectileEntry(projectile_guid) match {
|
||||
case Some(projectile) =>
|
||||
val index = projectile_guid.guid - Projectile.BaseUID
|
||||
ResolveProjectileEntry(projectile, index, resolution, target, pos)
|
||||
ResolveProjectileEntry(projectile, resolution, target, pos)
|
||||
case None =>
|
||||
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
|
||||
None
|
||||
|
|
@ -8574,10 +8371,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
/**
|
||||
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
||||
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
|
||||
* The internal copy of the projectile is retained as merely `Resolved`
|
||||
* while the observed projectile is promoted to the suggested resolution status.
|
||||
* na
|
||||
* @param projectile the projectile object
|
||||
* @param resolution the resolution status to promote the projectile
|
||||
* @return a copy of the projectile
|
||||
|
|
@ -8637,15 +8431,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case obj : Player =>
|
||||
//damage is synchronized on the target player's `WSA` (results distributed from there)
|
||||
continent.AvatarEvents ! AvatarServiceMessage(obj.Name, AvatarAction.Damage(player.GUID, obj, func))
|
||||
case obj : Vehicle =>
|
||||
//damage is synchronized on the vehicle actor (results returned to and distributed from this `WSA`)
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
case obj : Deployable =>
|
||||
|
||||
case obj : Vehicle => obj.Actor ! Vitality.Damage(func)
|
||||
case obj : FacilityTurret => obj.Actor ! Vitality.Damage(func)
|
||||
case obj : ComplexDeployable => obj.Actor ! Vitality.Damage(func)
|
||||
|
||||
case obj : SimpleDeployable =>
|
||||
//damage is synchronized on `LSA` (results returned to and distributed from this `WSA`)
|
||||
continent.LocalEvents ! Vitality.DamageOn(obj, func)
|
||||
case obj : FacilityTurret =>
|
||||
//damage is synchronized on the turret actor (results returned to and distributed from this `WSA`)
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -9803,7 +9596,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case GlobalDefinitions.ams if vehicle.Faction == player.Faction =>
|
||||
log.info("BeforeUnload: cleaning up after a mobile spawn vehicle ...")
|
||||
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
|
||||
None
|
||||
case GlobalDefinitions.router =>
|
||||
//this may repeat for multiple players on the same continent but that's okay(?)
|
||||
log.info("BeforeUnload: cleaning up after a router ...")
|
||||
|
|
@ -10270,11 +10062,94 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
taskResolver ! UnregisterProjectile(projectile)
|
||||
projectiles(local_index) match {
|
||||
case Some(obj) if !obj.isResolved => obj.Miss
|
||||
case None => ;
|
||||
case _ => ;
|
||||
}
|
||||
projectilesToCleanUp(local_index) = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate all active implants.
|
||||
* This method is intended to support only the current Live server implants that are functional,
|
||||
* the darklight vision implant and the surge implant.
|
||||
*/
|
||||
def DeactivateImplants() : Unit = {
|
||||
DeactivateImplantDarkLight()
|
||||
DeactivateImplantSurge()
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the darklight vision implant.
|
||||
* This method is intended to support only the current Live server implants.
|
||||
*/
|
||||
def DeactivateImplantDarkLight() : Unit = {
|
||||
if(avatar.Implants(0).Active) {
|
||||
avatar.Implants(0).Active = false
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
|
||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 0, 0))
|
||||
timeDL = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the surge implant.
|
||||
* This method is intended to support only the current Live server implants.
|
||||
*/
|
||||
def DeactivateImplantSurge() : Unit = {
|
||||
if(avatar.Implants(1).Active) {
|
||||
avatar.Implants(1).Active = false
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
|
||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 1, 0))
|
||||
timeSurge = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the jammered buzzing.
|
||||
* Although, as a rule, the jammering sound effect should last as long as the jammering status,
|
||||
* Infantry seem to hear the sound for a bit longer than the effect.
|
||||
* @see `JammableHevaior.StartJammeredSound`
|
||||
* @param target an object that can be affected by the jammered status
|
||||
* @param dur the duration of the timer, in milliseconds;
|
||||
* by default, 30000
|
||||
*/
|
||||
override def StartJammeredSound(target : Any, dur : Int) : Unit = target match {
|
||||
case obj : Player if !jammedSound =>
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 1))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 1))
|
||||
super.StartJammeredSound(obj, 3000)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a variety of tasks to indicate being jammered.
|
||||
* Deactivate implants (should also uninitialize them),
|
||||
* delay stamina regeneration for a certain number of turns,
|
||||
* and set the jammered status on specific holstered equipment.
|
||||
* @see `JammableHevaior.StartJammeredStatus`
|
||||
* @param target an object that can be affected by the jammered status
|
||||
* @param dur the duration of the timer, in milliseconds
|
||||
*/
|
||||
override def StartJammeredStatus(target : Any, dur : Int) : Unit = target match {
|
||||
case obj : Player =>
|
||||
DeactivateImplants()
|
||||
skipStaminaRegenForTurns = 10
|
||||
super.StartJammeredStatus(target, dur)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the jammered buzzing.
|
||||
* @see `JammableHevaior.CancelJammeredSound`
|
||||
* @param target an object that can be affected by the jammered status
|
||||
*/
|
||||
override def CancelJammeredSound(target : Any) : Unit = target match {
|
||||
case obj : Player if jammedSound =>
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, 27, 0))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(obj.GUID, 27, 0))
|
||||
super.CancelJammeredSound(obj)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def failWithError(error : String) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
@ -10433,8 +10308,8 @@ object WorldSessionActor {
|
|||
|
||||
protected final case class SquadUIElement(name : String, index : Int, zone : Int, health : Int, armor : Int, position : Vector3)
|
||||
|
||||
private final case class NtuCharging(tplayer: Player,
|
||||
vehicle: Vehicle)
|
||||
private final case class NtuCharging(tplayer: Player, vehicle: Vehicle)
|
||||
|
||||
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
|
||||
|
||||
private final case class FinalizeDeployable(obj : PlanetSideGameObject with Deployable, tool : ConstructionItem, index : Int)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import akka.actor.Props
|
|||
import akka.routing.RandomPool
|
||||
import actor.base.ActorTest
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics.ResolvedProjectile
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
|
||||
|
|
@ -370,8 +371,8 @@ class ChangeFireStateStopTest extends ActorTest {
|
|||
}
|
||||
|
||||
class DamageTest extends ActorTest {
|
||||
val test_func_ref : (Any)=>Unit = {
|
||||
def test_func(o : Any) : Unit = { }
|
||||
val test_func_ref : Any=>ResolvedProjectile = {
|
||||
def test_func(o : Any) : ResolvedProjectile = { null }
|
||||
test_func
|
||||
}
|
||||
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
|
|
|
|||
Loading…
Reference in a new issue