mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-09 07:20:30 +00:00
radiator is turned off due to potential for server crashes; cerebus -> cerberus; turret kills name owner only when they are in the same zone; fewer chances for turrets to fire when they should not
This commit is contained in:
parent
a699c6c223
commit
e5cde75e72
14 changed files with 110 additions and 81 deletions
|
|
@ -76,6 +76,7 @@ add_property pulsar equiptime 600
|
|||
add_property pulsar holstertime 600
|
||||
add_property punisher equiptime 600
|
||||
add_property punisher holstertime 600
|
||||
add_property radiator allowed false
|
||||
add_property r_shotgun equiptime 750
|
||||
add_property r_shotgun holstertime 750
|
||||
add_property remote_electronics_kit equiptime 500
|
||||
|
|
|
|||
|
|
@ -545,9 +545,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||
Some(target)
|
||||
|
||||
case turret: AutomatedTurret =>
|
||||
case turret: AutomatedTurret with OwnableByPlayer =>
|
||||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||
HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, turret.TurretOwner, projectileTypeId))
|
||||
HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, projectileTypeId))
|
||||
Some(target)
|
||||
}
|
||||
}
|
||||
|
|
@ -558,9 +558,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
case target: PlanetSideServerObject with FactionAffinity with Vitality =>
|
||||
sessionLogic.validObject(attackerGuid, decorator = "AIDamage/Attacker")
|
||||
.collect {
|
||||
case turret: AutomatedTurret if turret.Target.nonEmpty =>
|
||||
case turret: AutomatedTurret with OwnableByPlayer if turret.Target.nonEmpty =>
|
||||
//the turret must be shooting at something (else) first
|
||||
HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, turret.TurretOwner, projectileTypeId))
|
||||
HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, projectileTypeId))
|
||||
}
|
||||
Some(target)
|
||||
}
|
||||
|
|
@ -1268,14 +1268,17 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
|||
}
|
||||
|
||||
private def CompileAutomatedTurretDamageData(
|
||||
turret: AutomatedTurret,
|
||||
owner: SourceEntry,
|
||||
turret: AutomatedTurret with OwnableByPlayer,
|
||||
projectileTypeId: Long
|
||||
): Option[(AutomatedTurret, Tool, SourceEntry, ProjectileDefinition)] = {
|
||||
turret.Weapons
|
||||
.values
|
||||
.flatMap { _.Equipment }
|
||||
.collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) }
|
||||
.collect {
|
||||
case weapon: Tool =>
|
||||
val source = Deployables.AssignBlameTo(continent, turret.OwnerName, SourceEntry(turret))
|
||||
(turret, weapon, source, weapon.Projectile)
|
||||
}
|
||||
.find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import net.psforever.objects.avatar.{Avatar, Certification}
|
|||
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
|
|
@ -261,4 +262,48 @@ object Deployables {
|
|||
}
|
||||
(sample intersect testDiff equals testDiff) && (sampleIntersect intersect testIntersect equals testIntersect)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a player with a given name in this zone.
|
||||
* The assumption is the player is the owner of a given deployable entity.
|
||||
* If the player can not be found, the deployable entity can stand in as it's own owner.
|
||||
* @param zone continent in which the player should be found;
|
||||
* should be the same zone as the deployable, but not required
|
||||
* @param nameOpt optional player's name
|
||||
* @param deployableSource deployable entity
|
||||
* @return discovered player as a reference
|
||||
*/
|
||||
def AssignBlameTo(zone: Zone, nameOpt: Option[String], deployableSource: SourceEntry): SourceEntry = {
|
||||
zone
|
||||
.Players
|
||||
.find(a => nameOpt.contains(a.name))
|
||||
.collect { a =>
|
||||
val name = a.name
|
||||
Deployables.AssignBlameToFrom(name, zone.LivePlayers)
|
||||
.orElse(Deployables.AssignBlameToFrom(name, zone.Corpses))
|
||||
.getOrElse {
|
||||
val player = PlayerSource(name, deployableSource.Faction, deployableSource.Position) //might report minor inconsistencies, e.g., exo-suit type
|
||||
player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife)
|
||||
}
|
||||
}
|
||||
.getOrElse(deployableSource)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a player with a given name from this list of possible players.
|
||||
* If the player is seated, attach a shallow copy of the mounting information.
|
||||
* @param name player name
|
||||
* @param blameList possible players in which to find the player name
|
||||
* @return discovered player as a reference, or `None` if not found
|
||||
*/
|
||||
private def AssignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = {
|
||||
blameList
|
||||
.find(_.Name.equals(name))
|
||||
.map { player =>
|
||||
PlayerSource
|
||||
.mountableAndSeat(player)
|
||||
.map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) }
|
||||
.getOrElse { PlayerSource(player) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import akka.actor.{ActorContext, ActorRef, Props}
|
|||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, SourceEntry}
|
||||
import net.psforever.objects.sourcing.{DeployableSource, SourceEntry}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.etc.TrippedMineReason
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
|
|
@ -98,40 +98,8 @@ object MineDeployableControl {
|
|||
private case class Triggered()
|
||||
|
||||
def trippedMineReason(mine: ExplosiveDeployable): TrippedMineReason = {
|
||||
lazy val deployableSource = DeployableSource(mine)
|
||||
val zone = mine.Zone
|
||||
val ownerName = mine.OwnerName
|
||||
val blame = zone
|
||||
.Players
|
||||
.find(a => ownerName.contains(a.name))
|
||||
.collect { a =>
|
||||
val name = a.name
|
||||
assignBlameToFrom(name, zone.LivePlayers)
|
||||
.orElse(assignBlameToFrom(name, zone.Corpses))
|
||||
.getOrElse {
|
||||
val player = PlayerSource(name, mine.Faction, mine.Position) //might report minor inconsistencies, e.g., exo-suit type
|
||||
player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife)
|
||||
}
|
||||
}
|
||||
.getOrElse(deployableSource)
|
||||
val deployableSource = DeployableSource(mine)
|
||||
val blame = Deployables.AssignBlameTo(mine.Zone, mine.OwnerName, deployableSource)
|
||||
TrippedMineReason(deployableSource, blame)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a player with a given name from this list of possible players.
|
||||
* If the player is seated, attach a shallow copy of the mounting information.
|
||||
* @param name player name
|
||||
* @param blameList possible players in which to find the player name
|
||||
* @return discovered player as a reference, or `None` if not found
|
||||
*/
|
||||
private def assignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = {
|
||||
blameList
|
||||
.find(_.Name.equals(name))
|
||||
.map { player =>
|
||||
PlayerSource
|
||||
.mountableAndSeat(player)
|
||||
.map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) }
|
||||
.getOrElse { PlayerSource(player) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import scala.collection.mutable
|
|||
* As deployables are added and removed, and tracked certifications are added and removed,
|
||||
* these structures are updated to reflect proper count.
|
||||
* For example, the greatest number of spitfire turrets that can be placed is 15 (individual count)
|
||||
* and the greatest number of shadow turrets and cerebus turrets that can be placed is 5 each (individual counts)
|
||||
* and the greatest number of shadow turrets and cerberus turrets that can be placed is 5 each (individual counts)
|
||||
* but the maximum number of small turrets that can be placed overall is only 15 (categorical count).
|
||||
* Spitfire turrets, shadow turrets, and cerebus turrets are all included in the category of small turrets.
|
||||
* Spitfire turrets, shadow turrets, and cerberus turrets are all included in the category of small turrets.
|
||||
*/
|
||||
class DeployableToolbox {
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ object Deployable {
|
|||
DeployedItem.jammer_mine.id -> DeployableIcon.DisruptorMine,
|
||||
DeployedItem.spitfire_turret.id -> DeployableIcon.SpitfireTurret,
|
||||
DeployedItem.spitfire_cloaked.id -> DeployableIcon.ShadowTurret,
|
||||
DeployedItem.spitfire_aa.id -> DeployableIcon.CerebusTurret,
|
||||
DeployedItem.spitfire_aa.id -> DeployableIcon.cerberusTurret,
|
||||
DeployedItem.motionalarmsensor.id -> DeployableIcon.MotionAlarmSensor,
|
||||
DeployedItem.sensor_shield.id -> DeployableIcon.SensorDisruptor,
|
||||
DeployedItem.tank_traps.id -> DeployableIcon.TRAP,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ object DeployedItem extends Enumeration {
|
|||
final val jammer_mine = Value(420) //disruptor mine
|
||||
final val motionalarmsensor = Value(575)
|
||||
final val sensor_shield = Value(752) //sensor disruptor
|
||||
final val spitfire_aa = Value(819) //cerebus turret
|
||||
final val spitfire_aa = Value(819) //cerberus turret
|
||||
final val spitfire_cloaked = Value(825) //shadow turret
|
||||
final val spitfire_turret = Value(826)
|
||||
final val tank_traps = Value(849) //trap
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class UniqueNumberOps(
|
|||
private val poolActors: Map[String, ActorRef]
|
||||
) {
|
||||
/** The timeout used by all number pool `ask` messaging */
|
||||
private implicit val timeout = UniqueNumberOps.timeout
|
||||
private implicit val timeout: Timeout = UniqueNumberOps.timeout
|
||||
|
||||
/**
|
||||
* The entry point for the entity GUID registration process.
|
||||
|
|
@ -149,25 +149,26 @@ class UniqueNumberOps(
|
|||
val localPool = pool
|
||||
|
||||
val result = ask(pool, NumberPoolActor.GetAnyNumber())(timeout)
|
||||
result.onComplete {
|
||||
case Success(NumberPoolActor.GiveNumber(number)) =>
|
||||
UniqueNumberOps.processRegisterResult(
|
||||
localPromise,
|
||||
localTarget,
|
||||
localUns,
|
||||
localPoolName,
|
||||
localPool,
|
||||
number
|
||||
)
|
||||
case Success(NumberPoolActor.NoNumber(ex)) =>
|
||||
registrationProcessRetry(localPromise, ex, localTarget, localUns, localPools, localPoolName)
|
||||
case msg =>
|
||||
UniqueNumberOps.log.warn(s"unexpected message during $localTarget's registration process - $msg")
|
||||
}
|
||||
result.recover {
|
||||
case ex: AskTimeoutException =>
|
||||
localPromise.failure(new RegisteringException(msg = s"did not register entity $localTarget in time", ex))
|
||||
}
|
||||
result
|
||||
.recover {
|
||||
case ex: AskTimeoutException =>
|
||||
localPromise.failure(new RegisteringException(msg = s"did not register entity $localTarget in time", ex))
|
||||
}
|
||||
.onComplete {
|
||||
case Success(NumberPoolActor.GiveNumber(number)) =>
|
||||
UniqueNumberOps.processRegisterResult(
|
||||
localPromise,
|
||||
localTarget,
|
||||
localUns,
|
||||
localPoolName,
|
||||
localPool,
|
||||
number
|
||||
)
|
||||
case Success(NumberPoolActor.NoNumber(ex)) =>
|
||||
registrationProcessRetry(localPromise, ex, localTarget, localUns, localPools, localPoolName)
|
||||
case msg =>
|
||||
UniqueNumberOps.log.warn(s"unexpected message during $localTarget's registration process - $msg")
|
||||
}
|
||||
|
||||
case None =>
|
||||
//do not log
|
||||
|
|
@ -197,7 +198,7 @@ class UniqueNumberOps(
|
|||
if (poolName.equals("generic")) {
|
||||
promise.failure(new RegisteringException(msg = s"did not register entity $obj", exception))
|
||||
} else {
|
||||
org.log4s.getLogger("UniqueNumberOps").warn(s"${exception.getLocalizedMessage()} - $poolName")
|
||||
UniqueNumberOps.log.warn(s"${exception.getLocalizedMessage()} - $poolName")
|
||||
promise.completeWith(registrationProcess(obj, guid, pools, poolName = "generic"))
|
||||
}
|
||||
}
|
||||
|
|
@ -302,7 +303,7 @@ class UniqueNumberOps(
|
|||
|
||||
object UniqueNumberOps {
|
||||
private val log = org.log4s.getLogger
|
||||
private implicit val timeout = Timeout(2.seconds)
|
||||
private implicit val timeout: Timeout = Timeout(4.seconds)
|
||||
|
||||
/**
|
||||
* Final step of the object registration process.
|
||||
|
|
@ -431,7 +432,7 @@ class UniqueNumberSetup(
|
|||
) extends Actor {
|
||||
init()
|
||||
|
||||
final def receive: Receive = { case _ => ; }
|
||||
final def receive: Receive = { case _ => () }
|
||||
|
||||
def init(): UniqueNumberOps = {
|
||||
new UniqueNumberOps(hub, poolActorConversionFunc(context, hub))
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
override protected def tryMount(obj: PlanetSideServerObject with Mountable, seatNumber: Int, player: Player): Boolean = {
|
||||
val originalAutoState = AutomaticOperation
|
||||
AutomaticOperation = false //turn off
|
||||
if (JammableObject.Jammed) {
|
||||
if (AutomaticOperationPossible && JammableObject.Jammed) {
|
||||
val zone = TurretObject.Zone
|
||||
AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID) //can not recover lost jamming aggro
|
||||
}
|
||||
|
|
@ -220,6 +220,15 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
!TurretObject.isUpgrading
|
||||
}
|
||||
|
||||
override def AutomaticOperationPossible: Boolean = {
|
||||
super.AutomaticOperationPossible &&
|
||||
(turret.Owner match {
|
||||
case b: Building if b.CaptureTerminal.isEmpty => false
|
||||
case b: Building => !b.CaptureTerminal.exists(_.Definition == GlobalDefinitions.secondary_capture)
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
private def primaryWeaponFireModeOnly(): Unit = {
|
||||
if (testToResetToDefaultFireMode) {
|
||||
val zone = TurretObject.Zone
|
||||
|
|
@ -295,7 +304,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
|
||||
override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = {
|
||||
super.TryJammerEffectActivate(target, cause)
|
||||
if (JammableObject.Jammed) {
|
||||
if (AutomaticOperationPossible && AutomaticOperation && JammableObject.Jammed) {
|
||||
AutomaticOperation = false
|
||||
if (!MountableObject.Seats.values.exists(_.isOccupied) && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) {
|
||||
//look in direction of cause of jamming
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ trait AutomatedTurretBehavior {
|
|||
Actor.emptyBehavior
|
||||
}
|
||||
|
||||
def AutomaticOperationPossible: Boolean = autoStats.isDefined
|
||||
|
||||
def AutomaticOperation: Boolean = automaticOperation
|
||||
|
||||
/**
|
||||
|
|
@ -111,7 +113,7 @@ trait AutomatedTurretBehavior {
|
|||
* @return `true`, if it would be possible for automated behavior to become operational;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
protected def AutomaticOperationFunctionalityChecks: Boolean = { autoStats.isDefined }
|
||||
protected def AutomaticOperationFunctionalityChecks: Boolean = AutomaticOperationPossible
|
||||
|
||||
/**
|
||||
* The last time weapons fire from the turret was confirmed by this control agency.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ object DeploymentAction extends Enumeration {
|
|||
object DeployableIcon extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val Boomer, HEMine, MotionAlarmSensor, SpitfireTurret, RouterTelepad, DisruptorMine, ShadowTurret, CerebusTurret,
|
||||
val Boomer, HEMine, MotionAlarmSensor, SpitfireTurret, RouterTelepad, DisruptorMine, ShadowTurret, cerberusTurret,
|
||||
TRAP, AegisShieldGenerator, FieldTurret, SensorDisruptor = Value
|
||||
|
||||
implicit val codec: Codec[DeployableIcon.Value] = PacketHelpers.createEnumerationCodec(this, uint4L)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import scodec.codecs._
|
|||
* `86 - max spitfire turrets`<br>
|
||||
* `87 - max motion sensors`<br>
|
||||
* `88 - max shadow turrets`<br>
|
||||
* `89 - max cerebus turrets`<br>
|
||||
* `89 - max cerberus turrets`<br>
|
||||
* `90 - max Aegis shield generators`<br>
|
||||
* `91 - max TRAPs`<br>
|
||||
* `92 - max OMFTs`<br>
|
||||
|
|
@ -43,7 +43,7 @@ import scodec.codecs._
|
|||
* `97 - spitfire turrets`<br>
|
||||
* `98 - motion sensors`<br>
|
||||
* `99 - shadow turrets`<br>
|
||||
* `100 - cerebus turrets`<br>
|
||||
* `100 - cerberus turrets`<br>
|
||||
* `101 - Aegis shield generators`<br>
|
||||
* `102 - TRAPSs`<br>
|
||||
* `103 - OMFTs`<br>
|
||||
|
|
|
|||
|
|
@ -138,14 +138,14 @@ class TurretDeployableTest extends Specification {
|
|||
DeployedItem.portable_manned_turret_nc.id,
|
||||
DeployedItem.portable_manned_turret_vs.id
|
||||
).foreach(id => {
|
||||
try { new TurretDeployableDefinition(id) }
|
||||
try { new TurretDeployableDefinition(id) { } }
|
||||
catch { case _: Exception => ko }
|
||||
})
|
||||
ok
|
||||
}
|
||||
|
||||
"define (invalid object)" in {
|
||||
new TurretDeployableDefinition(5) must throwA[NoSuchElementException] //wrong object id altogether
|
||||
new TurretDeployableDefinition(objectId = 5) { } must throwA[NoSuchElementException] //wrong object id altogether
|
||||
}
|
||||
|
||||
"construct" in {
|
||||
|
|
@ -212,7 +212,7 @@ class DeployableMake extends Specification {
|
|||
}
|
||||
}
|
||||
|
||||
"construct a cerebus turret" in {
|
||||
"construct a cerberus turret" in {
|
||||
val func = Deployables.Make(DeployedItem.spitfire_aa)
|
||||
func() match {
|
||||
case obj: TurretDeployable if obj.Definition == GlobalDefinitions.spitfire_aa => ok
|
||||
|
|
|
|||
|
|
@ -819,13 +819,13 @@ class DeployableToolboxTest extends Specification {
|
|||
val obj = new DeployableToolbox
|
||||
obj.Initialize(Set(CombatEngineering))
|
||||
|
||||
val cerebus = new TurretDeployable(GlobalDefinitions.spitfire_aa) //cerebus turret
|
||||
obj.Valid(cerebus) mustEqual false
|
||||
val cerberus = new TurretDeployable(GlobalDefinitions.spitfire_aa) //cerberus turret
|
||||
obj.Valid(cerberus) mustEqual false
|
||||
obj.CountDeployable(DeployedItem.spitfire_aa).productIterator.toList mustEqual List(0, 0)
|
||||
|
||||
obj.UpdateMaxCounts(Set(CombatEngineering, AdvancedEngineering))
|
||||
|
||||
obj.Valid(cerebus) mustEqual true
|
||||
obj.Valid(cerberus) mustEqual true
|
||||
obj.CountDeployable(DeployedItem.spitfire_aa).productIterator.toList mustEqual List(0, 5)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue