zone interaction for turret discovery, players only so far; minor field value change for small turret data; automated turret target recognition; grammatical and linter fixes

This commit is contained in:
Fate-JH 2023-12-04 11:48:34 -05:00
parent e9daae5802
commit 9952803883
8 changed files with 274 additions and 26 deletions

View file

@ -3,7 +3,7 @@ package net.psforever.objects
import net.psforever.objects.avatar.{Avatar, LoadoutManager, SpecialCarry}
import net.psforever.objects.ballistics.InteractWithRadiationClouds
import net.psforever.objects.ce.{Deployable, InteractWithMines}
import net.psforever.objects.ce.{Deployable, InteractWithMines, InteractWithTurrets}
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
@ -38,6 +38,7 @@ class Player(var avatar: Avatar)
with MountableEntity {
interaction(new InteractWithEnvironment())
interaction(new InteractWithMinesUnlessSpectating(obj = this, range = 10))
interaction(new InteractWithTurrets(range = 25f))
interaction(new InteractWithRadiationClouds(range = 10f, Some(this)))
private var backpack: Boolean = false

View file

@ -14,7 +14,7 @@ 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.{TurretDefinition, WeaponTurret}
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior, TurretDefinition, WeaponTurret}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
@ -26,10 +26,11 @@ class TurretDeployable(tdef: TurretDeployableDefinition)
extends Deployable(tdef)
with WeaponTurret
with JammableUnit
with Hackable {
with Hackable
with AutomatedTurret {
WeaponTurret.LoadDefinition(this)
override def Definition = tdef
override def Definition: TurretDeployableDefinition = tdef
}
class TurretDeployableDefinition(private val objectId: Int)
@ -46,7 +47,7 @@ class TurretDeployableDefinition(private val objectId: Int)
//override to clarify inheritance conflict
override def MaxHealth_=(max: Int): Int = super[DeployableDefinition].MaxHealth_=(max)
override def Initialize(obj: Deployable, context: ActorContext) = {
override def Initialize(obj: Deployable, context: ActorContext): Unit = {
obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
}
@ -66,13 +67,15 @@ class TurretControl(turret: TurretDeployable)
with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events
with MountableBehavior
with DamageableWeaponTurret
with RepairableWeaponTurret {
def DeployableObject = turret
def MountableObject = turret
def JammableObject = turret
def FactionObject = turret
def DamageableObject = turret
def RepairableObject = turret
with RepairableWeaponTurret
with AutomatedTurretBehavior {
def DeployableObject: TurretDeployable = turret
def MountableObject: TurretDeployable = turret
def JammableObject: TurretDeployable = turret
def FactionObject: TurretDeployable = turret
def DamageableObject: TurretDeployable = turret
def RepairableObject: TurretDeployable = turret
def AutomatedTurretObject: TurretDeployable = turret
override def postStop(): Unit = {
super.postStop()
@ -88,8 +91,9 @@ class TurretControl(turret: TurretDeployable)
.orElse(dismountBehavior)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(automatedTurretBehavior)
.orElse {
case _ => ;
case _ => ()
}
override protected def mountTest(
@ -122,7 +126,7 @@ class TurretControl(turret: TurretDeployable)
zone.id,
VehicleAction.KickPassenger(tplayer.GUID, 4, wasKickedByDriver, turret.GUID)
)
case None => ;
case None => ()
}
}
Some(time.getOrElse(Deployable.cleanup) + Deployable.cleanup)

View file

@ -0,0 +1,62 @@
// Copyright (c) 2021 PSForever
package net.psforever.objects.ce
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.turret.{AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.zones.blockmap.SectorPopulation
import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.types.Vector3
case object TurretInteraction extends ZoneInteractionType
/**
* ...
*/
class InteractWithTurrets(val range: Float)
extends ZoneInteraction {
def Type: TurretInteraction.type = TurretInteraction
/**
* ...
*/
def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
target match {
case clarifiedTarget: AutomatedTurret.Target =>
val posxy = clarifiedTarget.Position.xy
val unique = SourceEntry(clarifiedTarget).unique
val targets = getTurretTargets(sector, posxy).filter { turret => turret.Detected(unique).isEmpty }
targets.foreach { t => t.Actor ! AutomatedTurretBehavior.Alert(clarifiedTarget) }
case _ => ()
}
}
private def getTurretTargets(
sector: SectorPopulation,
position: Vector3
): List[PlanetSideServerObject with AutomatedTurret] = {
(sector
.deployableList
.collect {
case turret: AutomatedTurret => turret
} ++ sector
.amenityList
.collect {
case turret: AutomatedTurret => turret
})
.filter { turret => Vector3.DistanceSquared(turret.Position.xy, position) < 625 }
}
/**
* ...
* @param target na
*/
def resetInteraction(target: InteractsWithZone): Unit = {
getTurretTargets(
target.getInteractionSector(),
target.Position.xy
).foreach { turret =>
turret.Actor ! AutomatedTurretBehavior.ResetAlerts
}
}
}

View file

@ -9,7 +9,7 @@ import scala.util.{Success, Try}
class AmmoBoxConverter extends ObjectCreateConverter[AmmoBox] {
override def ConstructorData(obj: AmmoBox): Try[CommonFieldData] = {
Success(CommonFieldData()(false))
Success(CommonFieldData()(flag = false))
}
override def DetailedConstructorData(obj: AmmoBox): Try[DetailedAmmoBoxData] = {
@ -19,9 +19,9 @@ class AmmoBoxConverter extends ObjectCreateConverter[AmmoBox] {
PlanetSideEmpire.NEUTRAL,
bops = false,
alternate = false,
true,
v1 = true,
None,
false,
jammered = false,
None,
None,
PlanetSideGUID(0)

View file

@ -21,9 +21,9 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
obj.Faction,
bops = false,
alternate = false,
false,
v1 = true,
None,
jammered = obj.Jammed,
obj.Jammed,
Some(true),
None,
obj.OwnerGuid match {
@ -45,9 +45,9 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
obj.Faction,
bops = false,
alternate = true,
false,
v1 = false,
None,
false,
jammered = false,
Some(false),
None,
PlanetSideGUID(0)

View file

@ -21,7 +21,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() {
obj.Faction,
bops = false,
alternate = false,
true,
v1 = true,
None,
obj.Jammed,
None,
@ -47,7 +47,7 @@ class ToolConverter extends ObjectCreateConverter[Tool]() {
obj.Faction,
bops = false,
alternate = false,
true,
v1 = true,
None,
obj.Jammed,
None,

View file

@ -0,0 +1,181 @@
// Copyright (c) 2023 PSForever
package net.psforever.objects.serverobject.turret
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.{Default, Player}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
import net.psforever.objects.vital.Vitality
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ObjectDetectedMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.collection.mutable
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
trait AutomatedTurret
extends PlanetSideServerObject
with WeaponTurret {
import AutomatedTurret.Target
private var targets: List[Target] = List[Target]()
def Targets: List[Target] = targets
def Detected(target: Target): Option[Target] = {
val unique = SourceEntry(target).unique
targets.find(SourceEntry(_).unique == unique)
}
def Detected(target: SourceUniqueness): Option[Target] = {
targets.find(SourceEntry(_).unique == target)
}
def AddTarget(target: Target): Unit = {
targets = targets :+ target
}
def RemoveTarget(target: Target): Unit = {
val unique = SourceEntry(target).unique
targets = targets.filterNot(SourceEntry(_).unique == unique)
}
def Clear(): List[Target] = {
val oldTargets = targets
targets = Nil
oldTargets
}
def Definition: ObjectDefinition with TurretDefinition
}
object AutomatedTurret {
type Target = PlanetSideServerObject with Vitality
}
trait AutomatedTurretBehavior {
_: Actor =>
import AutomatedTurret.Target
import AutomatedTurretBehavior.TurretTargetEntry
private val targets: mutable.HashMap[SourceUniqueness, TurretTargetEntry] =
mutable.HashMap[SourceUniqueness, TurretTargetEntry]()
private var targetDistanceCheck: Cancellable = Default.Cancellable
def AutomatedTurretObject: AutomatedTurret
val automatedTurretBehavior: Actor.Receive = {
case AutomatedTurretBehavior.Alert(target) =>
val targets = AutomatedTurretObject.Targets
val size = targets.size
AutomatedTurretObject.Detected(target)
.orElse {
AutomatedTurretObject.AddTarget(target)
retimeDistanceCheck(size)
Some(target)
}
.foreach { newDetectedTarget }
case AutomatedTurretBehavior.Unalert(target) =>
val targets = AutomatedTurretObject.Targets
val size = targets.size
AutomatedTurretObject.Detected(target)
.collect { out =>
AutomatedTurretObject.RemoveTarget(target)
testDistanceCheckQualifications(size)
out
}
.foreach { noLongerDetectedTarget }
case AutomatedTurretBehavior.ResetAlerts =>
AutomatedTurretObject.Clear().foreach { noLongerDetectedTarget }
testDistanceCheckQualifications(beforeSize = 1)
case AutomatedTurretBehavior.PeriodicDistanceCheck =>
performPeriodicDistanceCheck()
}
private def newDetectedTarget(target: Target): Unit = {
target match {
case target: Player =>
target.Zone.AvatarEvents ! AvatarServiceMessage(
target.Name,
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(target.GUID)))
)
target.Zone.AvatarEvents ! AvatarServiceMessage(
target.Name,
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Start(AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID))
)
case _ => ()
}
}
private def noLongerDetectedTarget(target: Target): Unit = {
target match {
case target: Player =>
target.Zone.AvatarEvents ! AvatarServiceMessage(
target.Name,
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(PlanetSideGUID(0))))
)
case _ => ()
}
}
private def testDistanceCheckQualifications(beforeSize: Int): Unit = {
if (beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty) {
targetDistanceCheck.cancel()
}
}
private def retimeDistanceCheck(beforeSize: Int): Unit = {
if (beforeSize == 0 && AutomatedTurretObject.Targets.nonEmpty) {
targetDistanceCheck = context.system.scheduler.scheduleAtFixedRate(
1.second,
1.second,
self,
AutomatedTurretBehavior.PeriodicDistanceCheck
)
}
}
private def performPeriodicDistanceCheck(): List[Target] = {
val pos = AutomatedTurretObject.Position
val earlyTargets = AutomatedTurretObject.Targets
val earlySize = earlyTargets.size
val removedTargets = earlyTargets
.collect {
case t if Vector3.DistanceSquared(t.Position, pos) > 625 =>
AutomatedTurretObject.RemoveTarget(t)
t
}
removedTargets.foreach { noLongerDetectedTarget }
testDistanceCheckQualifications(earlySize)
removedTargets
}
}
object AutomatedTurretBehavior {
import AutomatedTurret.Target
final case class Alert(target: Target)
final case class Unalert(target: Target)
final case object ResetAlerts
private case object PeriodicDistanceCheck
final case class TurretTargetEntry(target: Target, regard: RegardTargetAs.TurretOpinion)
object RegardTargetAs {
trait TurretOpinion
final case object Friendly extends TurretOpinion
final case object Testing extends TurretOpinion
final case object Unreachable extends TurretOpinion
final case object Blocked extends TurretOpinion
final case object Invalid extends TurretOpinion
final case object Attack extends TurretOpinion
}
}

View file

@ -81,13 +81,13 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
CommonFieldData(faction, false, false, false, None, false, None, None, PlanetSideGUID(0))
def apply(faction: PlanetSideEmpire.Value, unk: Int): CommonFieldData =
CommonFieldData(faction, false, false, unk > 1, None, unk % 1 == 1, None, None, PlanetSideGUID(0))
CommonFieldData(faction, false, false, unk > 1, None, unk > 0, None, None, PlanetSideGUID(0))
def apply(faction: PlanetSideEmpire.Value, unk: Int, player_guid: PlanetSideGUID): CommonFieldData =
CommonFieldData(faction, false, false, unk > 1, None, unk % 1 == 1, None, None, player_guid)
CommonFieldData(faction, false, false, unk > 1, None, unk > 0, None, None, player_guid)
def apply(faction: PlanetSideEmpire.Value, destroyed: Boolean, unk: Int): CommonFieldData =
CommonFieldData(faction, false, destroyed, unk > 1, None, unk % 1 == 1, None, None, PlanetSideGUID(0))
CommonFieldData(faction, false, destroyed, unk > 1, None, unk > 0, None, None, PlanetSideGUID(0))
def apply(
faction: PlanetSideEmpire.Value,