mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-02-28 19:13:40 +00:00
separated overworld facility turrets from cavern facility turrets, called vanu sentry turrets; tightened functionality inheritance between turret deployables and facility turrets; corrected issue where recharging vehicle weapons didn't work
This commit is contained in:
parent
18c3162dfe
commit
fc2ce70aae
10 changed files with 389 additions and 180 deletions
|
|
@ -208,6 +208,7 @@ class SessionLocalHandlers(
|
|||
continent.GUID(vehicleGuid)
|
||||
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
|
||||
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
|
||||
.getOrElse(Set.empty)
|
||||
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
|
||||
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.zones.Zoning
|
||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior}
|
||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, VanuSentry}
|
||||
import net.psforever.objects.zones.exp.ToDatabase
|
||||
|
||||
import scala.collection.mutable
|
||||
|
|
@ -52,8 +52,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
private[support] var shotsWhileDead: Int = 0
|
||||
private val projectiles: Array[Option[Projectile]] =
|
||||
Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None)
|
||||
private var zoningOpt: Option[ZoningOperations] = None
|
||||
def zoning: ZoningOperations = zoningOpt.orNull
|
||||
|
||||
/* packets */
|
||||
|
||||
def handleWeaponFire(pkt: WeaponFireMessage): Unit = {
|
||||
|
|
@ -569,11 +568,6 @@ private[support] class WeaponAndProjectileOperations(
|
|||
)
|
||||
continent.Projectile ! ZoneProjectile.Add(player.GUID, qualityprojectile)
|
||||
}
|
||||
obj match {
|
||||
case turret: FacilityTurret if turret.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
turret.Actor ! FacilityTurret.WeaponDischarged()
|
||||
case _ => ()
|
||||
}
|
||||
} else {
|
||||
log.warn(
|
||||
s"WeaponFireMessage: ${player.Name}'s ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect"
|
||||
|
|
@ -1224,6 +1218,10 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
|
||||
private def fireStateStartMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionData.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStart
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Start(player.GUID, itemGuid)
|
||||
|
|
@ -1286,6 +1284,10 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
|
||||
private def fireStateStopMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionData.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStop
|
||||
}
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.ChangeFireState_Stop(player.GUID, itemGuid)
|
||||
|
|
@ -1416,6 +1418,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
addShotsToMap(shotsFired, weaponId, shots)
|
||||
}
|
||||
|
||||
//noinspection SameParameterValue
|
||||
private def addShotsLanded(weaponId: Int, shots: Int): Unit = {
|
||||
addShotsToMap(shotsLanded, weaponId, shots)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,17 @@ import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
|||
import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem}
|
||||
import net.psforever.objects.definition.DeployableDefinition
|
||||
import net.psforever.objects.definition.converter.SmallTurretConverter
|
||||
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.serverobject.damage.DamageableWeaponTurret
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
|
||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, TurretDefinition, WeaponTurret}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, MountableTurretControl, TurretDefinition, WeaponTurret}
|
||||
import net.psforever.objects.vital.damage.DamageCalculations
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance, Vitality}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
|
@ -65,11 +62,9 @@ class TurretControl(turret: TurretDeployable)
|
|||
extends Actor
|
||||
with DeployableBehavior
|
||||
with FactionAffinityBehavior.Check
|
||||
with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events
|
||||
with MountableBehavior
|
||||
with DamageableWeaponTurret
|
||||
with RepairableWeaponTurret
|
||||
with MountableTurretControl
|
||||
with AutomatedTurretBehavior {
|
||||
def TurretObject: TurretDeployable = turret
|
||||
def DeployableObject: TurretDeployable = turret
|
||||
def MountableObject: TurretDeployable = turret
|
||||
def JammableObject: TurretDeployable = turret
|
||||
|
|
@ -81,18 +76,14 @@ class TurretControl(turret: TurretDeployable)
|
|||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
deployableBehaviorPostStop()
|
||||
damageableWeaponTurretPostStop()
|
||||
automaticTurretPostStop()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
deployableBehavior
|
||||
commonBehavior
|
||||
.orElse(deployableBehavior)
|
||||
.orElse(checkBehavior)
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(mountBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(automatedTurretBehavior)
|
||||
.orElse {
|
||||
case _ => ()
|
||||
|
|
@ -112,7 +103,7 @@ class TurretControl(turret: TurretDeployable)
|
|||
AutomaticOperation = false
|
||||
//look in direction of cause of jamming
|
||||
val zone = JammableObject.Zone
|
||||
TurretControl.getAttackerFromCause(zone, cause).foreach {
|
||||
AutomatedTurretBehavior.getAttackerFromCause(zone, cause).foreach {
|
||||
attacker =>
|
||||
val channel = zone.id
|
||||
val guid = AutomatedTurretObject.GUID
|
||||
|
|
@ -131,7 +122,7 @@ class TurretControl(turret: TurretDeployable)
|
|||
override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any): Unit = {
|
||||
if (AutomaticOperation && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
||||
//turret retribution
|
||||
TurretControl.getAttackerFromCause(target.Zone, cause).collect {
|
||||
AutomatedTurretBehavior.getAttackerFromCause(target.Zone, cause).collect {
|
||||
case attacker if attacker.Faction != target.Faction =>
|
||||
engageNewDetectedTarget(attacker)
|
||||
}
|
||||
|
|
@ -141,7 +132,7 @@ class TurretControl(turret: TurretDeployable)
|
|||
|
||||
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
|
||||
AutomaticOperation = false
|
||||
super.DestructionAwareness(target, cause)
|
||||
//super.DestructionAwareness(target, cause)
|
||||
CancelJammeredSound(target)
|
||||
CancelJammeredStatus(target)
|
||||
Deployables.AnnounceDestroyDeployable(turret, None)
|
||||
|
|
@ -184,40 +175,3 @@ class TurretControl(turret: TurretDeployable)
|
|||
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret))
|
||||
}
|
||||
}
|
||||
|
||||
object TurretControl {
|
||||
private def getAttackerFromCause(zone: Zone, cause: DamageResult): Option[PlanetSideServerObject with Vitality] = {
|
||||
import net.psforever.objects.sourcing._
|
||||
cause
|
||||
.interaction
|
||||
.adversarial
|
||||
.collect { adversarial =>
|
||||
adversarial.attacker match {
|
||||
case p: PlayerSource =>
|
||||
p.seatedIn
|
||||
.map { _._1.unique }
|
||||
.collect {
|
||||
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
|
||||
case a: UniqueAmenity => zone.GUID(a.guid)
|
||||
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
|
||||
}
|
||||
.flatten
|
||||
.orElse {
|
||||
val name = p.Name
|
||||
zone.LivePlayers.find(_.Name.equals(name))
|
||||
}
|
||||
case o =>
|
||||
o.unique match {
|
||||
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
|
||||
case a: UniqueAmenity => zone.GUID(a.guid)
|
||||
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.collect {
|
||||
case out: PlanetSideServerObject with Vitality => out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
|
|||
import net.psforever.objects.serverobject.damage.DamageableEntity
|
||||
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.zones.{InteractsWithZone, Zone}
|
||||
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
|
@ -206,7 +207,7 @@ trait AutomatedTurretBehavior {
|
|||
None
|
||||
}
|
||||
|
||||
private def trySelectNewTarget(): Option[Target] = {
|
||||
protected def trySelectNewTarget(): Option[Target] = {
|
||||
currentTarget.orElse {
|
||||
val turretPosition = AutomatedTurretObject.Position
|
||||
val radiusSquared = autoStats.get.targetingRange * autoStats.get.targetingRange
|
||||
|
|
@ -358,4 +359,39 @@ object AutomatedTurretBehavior {
|
|||
LocalAction.SendResponse(ChangeFireStateMessage_Stop(weaponGuid))
|
||||
)
|
||||
}
|
||||
|
||||
def getAttackerFromCause(zone: Zone, cause: DamageResult): Option[PlanetSideServerObject with Vitality] = {
|
||||
import net.psforever.objects.sourcing._
|
||||
cause
|
||||
.interaction
|
||||
.adversarial
|
||||
.collect { adversarial =>
|
||||
adversarial.attacker match {
|
||||
case p: PlayerSource =>
|
||||
p.seatedIn
|
||||
.map { _._1.unique }
|
||||
.collect {
|
||||
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
|
||||
case a: UniqueAmenity => zone.GUID(a.guid)
|
||||
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
|
||||
}
|
||||
.flatten
|
||||
.orElse {
|
||||
val name = p.Name
|
||||
zone.LivePlayers.find(_.Name.equals(name))
|
||||
}
|
||||
case o =>
|
||||
o.unique match {
|
||||
case v: UniqueVehicle => zone.Vehicles.find(SourceEntry(_).unique == v)
|
||||
case a: UniqueAmenity => zone.GUID(a.guid)
|
||||
case d: UniqueDeployable => zone.DeployableList.find(SourceEntry(_).unique == d)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.collect {
|
||||
case out: PlanetSideServerObject with Vitality => out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,25 @@
|
|||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class FacilityTurret(tDef: FacilityTurretDefinition)
|
||||
extends Amenity
|
||||
with WeaponTurret
|
||||
with AutomatedTurret
|
||||
with JammableUnit
|
||||
with CaptureTerminalAware {
|
||||
WeaponTurret.LoadDefinition(this)
|
||||
|
||||
override def Owner: AmenityOwner = {
|
||||
if (Zone.map.cavern) {
|
||||
Building.NoBuilding
|
||||
} else {
|
||||
super.Owner
|
||||
}
|
||||
}
|
||||
|
||||
def Definition: FacilityTurretDefinition = tDef
|
||||
}
|
||||
|
||||
|
|
@ -27,9 +35,6 @@ object FacilityTurret {
|
|||
new FacilityTurret(tDef)
|
||||
}
|
||||
|
||||
final case class RechargeAmmo()
|
||||
final case class WeaponDischarged()
|
||||
|
||||
import akka.actor.ActorContext
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,26 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import akka.actor.Cancellable
|
||||
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
|
||||
import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons}
|
||||
import net.psforever.objects.entity.WorldEntity
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
|
||||
import net.psforever.objects.equipment.Ammo
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret}
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.hackable.GenericHackables.getTurretUpgradeTime
|
||||
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableWeaponTurret}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.repair.AmenityAutoRepair
|
||||
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.packet.game.ChangeFireModeMessage
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br>
|
||||
* <br>
|
||||
|
|
@ -31,133 +26,106 @@ import scala.concurrent.duration._
|
|||
* @param turret the `MannedTurret` object being governed
|
||||
*/
|
||||
class FacilityTurretControl(turret: FacilityTurret)
|
||||
extends PoweredAmenityControl
|
||||
with FactionAffinityBehavior.Check
|
||||
with MountableBehavior
|
||||
with DamageableWeaponTurret
|
||||
with RepairableWeaponTurret
|
||||
extends PoweredAmenityControl
|
||||
with AmenityAutoRepair
|
||||
with JammableMountedWeapons
|
||||
with MountableTurretControl
|
||||
with AutomatedTurretBehavior
|
||||
with CaptureTerminalAwareBehavior {
|
||||
def TurretObject: FacilityTurret = turret
|
||||
def FactionObject: FacilityTurret = turret
|
||||
def MountableObject: FacilityTurret = turret
|
||||
def JammableObject: FacilityTurret = turret
|
||||
def DamageableObject: FacilityTurret = turret
|
||||
def RepairableObject: FacilityTurret = turret
|
||||
def AutoRepairObject: FacilityTurret = turret
|
||||
def AutomatedTurretObject: FacilityTurret = turret
|
||||
def CaptureTerminalAwareObject: FacilityTurret = turret
|
||||
|
||||
// Used for timing ammo recharge for vanu turrets in caves
|
||||
var weaponAmmoRechargeTimer: Cancellable = Default.Cancellable
|
||||
private var testToResetToDefaultFireMode: Boolean = false
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
damageableWeaponTurretPostStop()
|
||||
automaticTurretPostStop()
|
||||
stopAutoRepair()
|
||||
}
|
||||
|
||||
def commonBehavior: Receive =
|
||||
checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
.orElse(captureTerminalAwareBehaviour)
|
||||
private val upgradeableTurret: Receive = {
|
||||
case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int)))
|
||||
if player.Faction == TurretObject.Faction &&
|
||||
item.Definition == GlobalDefinitions.nano_dispenser && item.AmmoType == Ammo.upgrade_canister &&
|
||||
item.Magazine > 0 && TurretObject.Seats.values.forall(!_.isOccupied) =>
|
||||
TurretUpgrade.values.find(_.id == upgradeValue).foreach {
|
||||
case upgrade
|
||||
if TurretObject.Upgrade != upgrade && TurretObject.Definition.WeaponPaths.values
|
||||
.flatMap(_.keySet)
|
||||
.exists(_ == upgrade) =>
|
||||
TurretObject.setMiddleOfUpgrade(true)
|
||||
sender() ! CommonMessages.Progress(
|
||||
1.25f,
|
||||
WeaponTurrets.FinishUpgradingMannedTurret(TurretObject, player, item, upgrade),
|
||||
GenericHackables.TurretUpgradingTickAction(progressType = 2, player, TurretObject, item.GUID)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def poweredStateLogic: Receive =
|
||||
override def commonBehavior: Receive = super.commonBehavior
|
||||
.orElse(automatedTurretBehavior)
|
||||
.orElse(captureTerminalAwareBehaviour)
|
||||
|
||||
override def poweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse(mountBehavior)
|
||||
.orElse(upgradeableTurret)
|
||||
.orElse {
|
||||
case CommonMessages.Use(player, Some((item: Tool, upgradeValue: Int)))
|
||||
if player.Faction == turret.Faction &&
|
||||
item.Definition == GlobalDefinitions.nano_dispenser && item.AmmoType == Ammo.upgrade_canister &&
|
||||
item.Magazine > 0 && turret.Seats.values.forall(!_.isOccupied) =>
|
||||
TurretUpgrade.values.find(_.id == upgradeValue) match {
|
||||
case Some(upgrade)
|
||||
if turret.Upgrade != upgrade && turret.Definition.WeaponPaths.values
|
||||
.flatMap(_.keySet)
|
||||
.exists(_ == upgrade) =>
|
||||
turret.setMiddleOfUpgrade(true)
|
||||
sender() ! CommonMessages.Progress(
|
||||
1.25f,
|
||||
WeaponTurrets.FinishUpgradingMannedTurret(turret, player, item, upgrade),
|
||||
GenericHackables.TurretUpgradingTickAction(progressType = 2, player, turret, item.GUID)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case FacilityTurret.WeaponDischarged() =>
|
||||
if (weaponAmmoRechargeTimer != Default.Cancellable) {
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
}
|
||||
|
||||
weaponAmmoRechargeTimer = context.system.scheduler.scheduleWithFixedDelay(
|
||||
3 seconds,
|
||||
200 milliseconds,
|
||||
self,
|
||||
FacilityTurret.RechargeAmmo()
|
||||
)
|
||||
|
||||
case FacilityTurret.RechargeAmmo() =>
|
||||
turret.ControlledWeapon(wepNumber = 1).foreach {
|
||||
case weapon: Tool =>
|
||||
// recharge when last shot fired 3s delay, +1, 200ms interval
|
||||
if (weapon.Magazine < weapon.MaxMagazine && System.currentTimeMillis() - weapon.LastDischarge > 3000L) {
|
||||
weapon.Magazine += 1
|
||||
val seat = turret.Seat(0).get
|
||||
seat.occupant match {
|
||||
case Some(player : Player) =>
|
||||
turret.Zone.LocalEvents ! LocalServiceMessage(
|
||||
turret.Zone.id,
|
||||
LocalAction.RechargeVehicleWeapon(player.GUID, turret.GUID, weapon.GUID)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
else if (weapon.Magazine == weapon.MaxMagazine && weaponAmmoRechargeTimer != Default.Cancellable) {
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
weaponAmmoRechargeTimer = Default.Cancellable
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
def unpoweredStateLogic: Receive =
|
||||
override def unpoweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
.orElse {
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
override protected def mountTest(
|
||||
obj: PlanetSideServerObject with Mountable,
|
||||
seatNumber: Int,
|
||||
player: Player): Boolean = {
|
||||
(!turret.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed && !turret.isUpgrading ||
|
||||
System.currentTimeMillis() - getTurretUpgradeTime >= 1500L
|
||||
super.mountTest(obj, seatNumber, player) &&
|
||||
(!TurretObject.isUpgrading || System.currentTimeMillis() - GenericHackables.getTurretUpgradeTime >= 1500L) &&
|
||||
AutomaticOperation_=(state = false)
|
||||
}
|
||||
|
||||
override protected def dismountTest(obj: Mountable with WorldEntity, seatNumber: Int, user: Player): Boolean = {
|
||||
super.dismountTest(obj, seatNumber, user) &&
|
||||
AutomaticOperation_=(autoTurretFunctionalityChecks)
|
||||
}
|
||||
|
||||
override protected def DamageAwareness(target: Damageable.Target, cause: DamageResult, amount: Any) : Unit = {
|
||||
tryAutoRepair()
|
||||
if (AutomaticOperation) {
|
||||
if (TurretObject.Health < TurretObject.Definition.DamageDisablesAt) {
|
||||
AutomaticOperation = false
|
||||
} else if (AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
||||
//turret retribution
|
||||
AutomatedTurretBehavior.getAttackerFromCause(target.Zone, cause).collect {
|
||||
case attacker if attacker.Faction != target.Faction =>
|
||||
engageNewDetectedTarget(attacker)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.DamageAwareness(target, cause, amount)
|
||||
}
|
||||
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
||||
tryAutoRepair()
|
||||
super.DestructionAwareness(target, cause)
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val tguid = target.GUID
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 1))
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 1))
|
||||
tryAutoRepair()
|
||||
AutomaticOperation = false
|
||||
}
|
||||
|
||||
override def PerformRepairs(target : Damageable.Target, amount : Int) : Int = {
|
||||
val newHealth = super.PerformRepairs(target, amount)
|
||||
if(newHealth == target.Definition.MaxHealth) {
|
||||
if (newHealth == target.Definition.MaxHealth) {
|
||||
stopAutoRepair()
|
||||
}
|
||||
newHealth
|
||||
|
|
@ -165,12 +133,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
|
||||
override def Restoration(obj: Damageable.Target): Unit = {
|
||||
super.Restoration(obj)
|
||||
val zone = turret.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val tguid = turret.GUID
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0))
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0))
|
||||
AutomaticOperation = autoTurretFunctionalityChecks
|
||||
}
|
||||
|
||||
override def tryAutoRepair() : Boolean = {
|
||||
|
|
@ -179,12 +142,13 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
|
||||
def powerTurnOffCallback(): Unit = {
|
||||
stopAutoRepair()
|
||||
AutomaticOperation = false
|
||||
//kick all occupants
|
||||
val guid = turret.GUID
|
||||
val zone = turret.Zone
|
||||
val guid = TurretObject.GUID
|
||||
val zone = TurretObject.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.VehicleEvents
|
||||
turret.Seats.values.foreach(seat =>
|
||||
TurretObject.Seats.values.foreach(seat =>
|
||||
seat.occupant match {
|
||||
case Some(player) =>
|
||||
seat.unmount(player)
|
||||
|
|
@ -192,12 +156,83 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
if (player.HasGUID) {
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid))
|
||||
}
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def powerTurnOnCallback(): Unit = {
|
||||
tryAutoRepair()
|
||||
AutomaticOperation = autoTurretFunctionalityChecks
|
||||
}
|
||||
|
||||
override def AutomaticOperation_=(state: Boolean): Boolean = {
|
||||
val result = super.AutomaticOperation_=(state)
|
||||
testToResetToDefaultFireMode = result && TurretObject.Definition.AutoFire.exists(_.revertToDefaultFireMode)
|
||||
result
|
||||
}
|
||||
|
||||
private def autoTurretFunctionalityChecks: Boolean = {
|
||||
isPowered &&
|
||||
!JammableObject.Jammed &&
|
||||
TurretObject.Health > TurretObject.Definition.DamageDisablesAt &&
|
||||
!TurretObject.Seats.values.exists(_.isOccupied)
|
||||
}
|
||||
|
||||
private def primaryWeaponFireModeOnly(): Unit = {
|
||||
if (testToResetToDefaultFireMode) {
|
||||
val zone = TurretObject.Zone
|
||||
val zoneid = zone.id
|
||||
val events = zone.VehicleEvents
|
||||
TurretObject.Weapons.values
|
||||
.flatMap(_.Equipment)
|
||||
.collect { case weapon: Tool if weapon.FireModeIndex > 0 =>
|
||||
weapon.FireModeIndex = 0
|
||||
events ! VehicleServiceMessage(
|
||||
zoneid,
|
||||
VehicleAction.SendResponse(Service.defaultPlayerGUID, ChangeFireModeMessage(weapon.GUID, 0))
|
||||
)
|
||||
}
|
||||
}
|
||||
testToResetToDefaultFireMode = false
|
||||
}
|
||||
|
||||
override protected def trySelectNewTarget(): Option[AutomatedTurret.Target] = {
|
||||
if (autoTurretFunctionalityChecks) {
|
||||
primaryWeaponFireModeOnly()
|
||||
super.trySelectNewTarget()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
override def engageNewDetectedTarget(target: AutomatedTurret.Target): Unit = {
|
||||
if (autoTurretFunctionalityChecks) {
|
||||
primaryWeaponFireModeOnly()
|
||||
super.engageNewDetectedTarget(target)
|
||||
}
|
||||
}
|
||||
|
||||
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
||||
val startsUnjammed = !JammableObject.Jammed
|
||||
super.TryJammerEffectActivate(target, cause)
|
||||
if (startsUnjammed && JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDuration > 0)) {
|
||||
AutomaticOperation = false
|
||||
//look in direction of cause of jamming
|
||||
val zone = JammableObject.Zone
|
||||
AutomatedTurretBehavior.getAttackerFromCause(zone, cause).foreach {
|
||||
attacker =>
|
||||
val channel = zone.id
|
||||
val guid = AutomatedTurretObject.GUID
|
||||
AutomatedTurretBehavior.startTrackingTargets(zone, channel, guid, List(attacker.GUID))
|
||||
AutomatedTurretBehavior.stopTrackingTargets(zone, channel, guid) //TODO delay by a few milliseconds?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def CancelJammeredStatus(target: Any): Unit = {
|
||||
val startsJammed = JammableObject.Jammed
|
||||
super.CancelJammeredStatus(target)
|
||||
startsJammed && AutomaticOperation_=(autoTurretFunctionalityChecks)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.JammableMountedWeapons
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
trait MountableTurretControl
|
||||
extends FactionAffinityBehavior.Check
|
||||
with MountableBehavior
|
||||
with DamageableWeaponTurret
|
||||
with RepairableWeaponTurret
|
||||
with JammableMountedWeapons
|
||||
with Actor { /* note: jammable status is reported as vehicle events, not local events */
|
||||
def TurretObject: PlanetSideServerObject with WeaponTurret
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
damageableWeaponTurretPostStop()
|
||||
}
|
||||
|
||||
/** commonBehavior does not implement mountingBehavior; please do so when implementing */
|
||||
def commonBehavior: Receive =
|
||||
checkBehavior
|
||||
.orElse(jammableBehavior)
|
||||
.orElse(dismountBehavior)
|
||||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
|
||||
override protected def mountTest(
|
||||
obj: PlanetSideServerObject with Mountable,
|
||||
seatNumber: Int,
|
||||
player: Player): Boolean = {
|
||||
(!TurretObject.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed
|
||||
}
|
||||
|
||||
/**
|
||||
* An override for `Restoration`, best for facility turrets.
|
||||
* @param obj the entity being restored
|
||||
*/
|
||||
override def Restoration(obj: Damageable.Target): Unit = {
|
||||
super.Restoration(obj)
|
||||
val zone = TurretObject.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val tguid = TurretObject.GUID
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0))
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0))
|
||||
}
|
||||
|
||||
/**
|
||||
* An override for `DamageAwareness`, best for facility turrets.
|
||||
* @param target the entity being destroyed
|
||||
* @param cause historical information about the damage
|
||||
*/
|
||||
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
||||
super.DestructionAwareness(target, cause)
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val tguid = target.GUID
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 1))
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 1))
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ final case class Automation(
|
|||
detectionSpeed: FiniteDuration = 1.seconds,
|
||||
targetSelectCooldown: Long = 1500L, //ms
|
||||
missedShotCooldown: Long = 3000L, //ms
|
||||
targetEliminationCooldown: Long = 0L //ms
|
||||
targetEliminationCooldown: Long = 0L, //ms
|
||||
revertToDefaultFireMode: Boolean = true
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package net.psforever.objects.serverobject.turret
|
||||
|
||||
import akka.actor.Cancellable
|
||||
import net.psforever.objects.serverobject.ServerObjectControl
|
||||
import net.psforever.objects.{Default, Player, Tool}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class VanuSentryControl(turret: FacilityTurret)
|
||||
extends ServerObjectControl
|
||||
with MountableTurretControl {
|
||||
def TurretObject: FacilityTurret = turret
|
||||
def FactionObject: FacilityTurret = turret
|
||||
def MountableObject: FacilityTurret = turret
|
||||
def JammableObject: FacilityTurret = turret
|
||||
def DamageableObject: FacilityTurret = turret
|
||||
def RepairableObject: FacilityTurret = turret
|
||||
|
||||
// Used for timing ammo recharge for vanu turrets in caves
|
||||
private var weaponAmmoRechargeTimer: Cancellable = Default.Cancellable
|
||||
|
||||
private val weaponAmmoRecharge: Receive = {
|
||||
case VanuSentry.ChangeFireStart =>
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
weaponAmmoRechargeTimer = Default.Cancellable
|
||||
|
||||
case VanuSentry.ChangeFireStop =>
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
weaponAmmoRechargeTimer = context.system.scheduler.scheduleWithFixedDelay(
|
||||
3 seconds,
|
||||
200 milliseconds,
|
||||
self,
|
||||
VanuSentry.RechargeAmmo
|
||||
)
|
||||
|
||||
case VanuSentry.RechargeAmmo =>
|
||||
TurretObject.ControlledWeapon(wepNumber = 1).collect {
|
||||
case weapon: Tool =>
|
||||
// recharge when last shot fired 3s delay, +1, 200ms interval
|
||||
if (weapon.Magazine < weapon.MaxMagazine && System.currentTimeMillis() - weapon.LastDischarge > 3000L) {
|
||||
weapon.Magazine += 1
|
||||
val seat = TurretObject.Seat(0).get
|
||||
seat.occupant.collect {
|
||||
case player: Player =>
|
||||
TurretObject.Zone.LocalEvents ! LocalServiceMessage(
|
||||
TurretObject.Zone.id,
|
||||
LocalAction.RechargeVehicleWeapon(player.GUID, TurretObject.GUID, weapon.GUID)
|
||||
)
|
||||
}
|
||||
}
|
||||
else if (weapon.Magazine == weapon.MaxMagazine && weaponAmmoRechargeTimer != Default.Cancellable) {
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
weaponAmmoRechargeTimer = Default.Cancellable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
weaponAmmoRechargeTimer.cancel()
|
||||
}
|
||||
|
||||
def receive: Receive =
|
||||
commonBehavior
|
||||
.orElse(mountBehavior)
|
||||
.orElse(weaponAmmoRecharge)
|
||||
.orElse {
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
override def parseAttribute(attribute: Int, value: Long, other: Option[Any]): Unit = { /*intentionally blank*/ }
|
||||
}
|
||||
|
||||
object VanuSentry {
|
||||
final case object RechargeAmmo
|
||||
final case object ChangeFireStart
|
||||
final case object ChangeFireStop
|
||||
|
||||
import akka.actor.ActorContext
|
||||
def Constructor(pos: Vector3, tdef: FacilityTurretDefinition)(id: Int, context: ActorContext): FacilityTurret = {
|
||||
import akka.actor.Props
|
||||
val obj = FacilityTurret(tdef)
|
||||
obj.Position = pos
|
||||
obj.Actor = context.actorOf(Props(classOf[VanuSentryControl], obj), s"${tdef.Name}_$id")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import net.psforever.objects.serverobject.structures.{Building, BuildingDefiniti
|
|||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalDefinition}
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition, VanuSentry}
|
||||
import net.psforever.objects.serverobject.zipline.ZipLinePath
|
||||
import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, TurretSource, VehicleSource}
|
||||
import net.psforever.objects.zones.{MapInfo, Zone, ZoneInfo, ZoneMap}
|
||||
|
|
@ -585,7 +585,7 @@ object Zones {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
case "manned_turret" | "vanu_sentry_turret" =>
|
||||
case "manned_turret" =>
|
||||
zoneMap.addLocalObject(
|
||||
obj.guid,
|
||||
FacilityTurret.Constructor(
|
||||
|
|
@ -596,6 +596,17 @@ object Zones {
|
|||
)
|
||||
zoneMap.linkTurretToWeapon(obj.guid, turretWeaponGuid.getAndIncrement())
|
||||
|
||||
case "vanu_sentry_turret" =>
|
||||
zoneMap.addLocalObject(
|
||||
obj.guid,
|
||||
VanuSentry.Constructor(
|
||||
obj.position,
|
||||
obj.objectDefinition.asInstanceOf[FacilityTurretDefinition]
|
||||
),
|
||||
owningBuildingGuid = ownerGuid
|
||||
)
|
||||
zoneMap.linkTurretToWeapon(obj.guid, turretWeaponGuid.getAndIncrement())
|
||||
|
||||
case "implant_terminal_mech" =>
|
||||
zoneMap.addLocalObject(
|
||||
obj.guid,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue