adjusted self-reporting zone interaction logic; allowed for status of mounting into turret entities; made routine mounting behavior callbacks

This commit is contained in:
Fate-JH 2026-01-12 19:54:29 -05:00
parent 52dbe6a649
commit 26b70dbcd9
14 changed files with 289 additions and 241 deletions

View file

@ -9,12 +9,14 @@ import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.turret.{MountableTurret, WeaponTurrets} import net.psforever.objects.serverobject.turret.{MountableTurret, WeaponTurrets}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, TurretSource}
import net.psforever.objects.vital.{InGameActivity, ShieldCharge} import net.psforever.objects.vital.{DismountingActivity, InGameActivity, MountingActivity, ShieldCharge}
import net.psforever.packet.game.HackState1 import net.psforever.packet.game.HackState1
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.PlanetSideGUID import net.psforever.types.PlanetSideGUID
import scala.annotation.unused
/** definition */ /** definition */
class FieldTurretDeployableDefinition(private val objectId: Int) class FieldTurretDeployableDefinition(private val objectId: Int)
@ -70,6 +72,21 @@ class FieldTurretControl(turret: TurretDeployable)
player: Player player: Player
): Boolean = MountableTurret.MountTest(TurretObject, player) ): Boolean = MountableTurret.MountTest(TurretObject, player)
override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
super.mountActionResponse(user, mountPoint, seatNumber)
if (turret.PassengerInSeat(user).contains(0)) {
val vsrc = TurretSource(turret)
user.LogActivity(MountingActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber = 0), turret.Zone.Number))
}
}
override def dismountActionResponse(user: Player, seatBeingDismounted: Int): Unit = {
super.dismountActionResponse(user, seatBeingDismounted)
if (!turret.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated
user.LogActivity(DismountingActivity(TurretSource(turret), PlayerSource(user), turret.Zone.Number))
}
}
//make certain vehicles don't charge shields too quickly //make certain vehicles don't charge shields too quickly
private def canChargeShields: Boolean = { private def canChargeShields: Boolean = {
val func: InGameActivity => Boolean = WithShields.LastShieldChargeOrDamage(System.currentTimeMillis(), turret.Definition) val func: InGameActivity => Boolean = WithShields.LastShieldChargeOrDamage(System.currentTimeMillis(), turret.Definition)

View file

@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.types.BailType import net.psforever.types.BailType
import scala.annotation.unused
import scala.collection.mutable import scala.collection.mutable
trait MountableBehavior { trait MountableBehavior {
@ -45,13 +46,17 @@ trait MountableBehavior {
case Some(seatNum) if mountTest(obj, seatNum, user) && tryMount(obj, seatNum, user) => case Some(seatNum) if mountTest(obj, seatNum, user) && tryMount(obj, seatNum, user) =>
user.VehicleSeated = obj.GUID user.VehicleSeated = obj.GUID
usedMountPoint.put(user.Name, mount_point) usedMountPoint.put(user.Name, mount_point)
obj.Zone.actor ! ZoneActor.RemoveFromBlockMap(user) mountActionResponse(user, mount_point, seatNum)
sender() ! Mountable.MountMessages(user, Mountable.CanMount(obj, seatNum, mount_point)) sender() ! Mountable.MountMessages(user, Mountable.CanMount(obj, seatNum, mount_point))
case _ => case _ =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, mount_point)) sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(obj, mount_point))
} }
} }
def mountActionResponse(user: Player, @unused mountPoint: Int, @unused seatIndex: Int): Unit = {
MountableObject.Zone.actor ! ZoneActor.RemoveFromBlockMap(user)
}
protected def mountTest( protected def mountTest(
obj: PlanetSideServerObject with Mountable, obj: PlanetSideServerObject with Mountable,
seatNumber: Int, seatNumber: Int,
@ -87,7 +92,7 @@ trait MountableBehavior {
val obj = MountableObject val obj = MountableObject
if (dismountTest(obj, seat_number, user) && tryDismount(obj, seat_number, user, bail_type)) { if (dismountTest(obj, seat_number, user) && tryDismount(obj, seat_number, user, bail_type)) {
user.VehicleSeated = None user.VehicleSeated = None
obj.Zone.actor ! ZoneActor.AddToBlockMap(user, obj.Position) dismountActionResponse(user, seat_number)
sender() ! Mountable.MountMessages( sender() ! Mountable.MountMessages(
user, user,
Mountable.CanDismount(obj, seat_number, getUsedMountPoint(user.Name, seat_number)) Mountable.CanDismount(obj, seat_number, getUsedMountPoint(user.Name, seat_number))
@ -98,6 +103,10 @@ trait MountableBehavior {
} }
} }
def dismountActionResponse(user: Player, @unused seatIndex: Int): Unit = {
MountableObject.Zone.actor ! ZoneActor.AddToBlockMap(user, MountableObject.Position)
}
protected def dismountTest( protected def dismountTest(
obj: Mountable with WorldEntity, obj: Mountable with WorldEntity,
seatNumber: Int, seatNumber: Int,

View file

@ -5,7 +5,6 @@ import net.psforever.objects.{GlobalDefinitions, Player, Tool}
import net.psforever.objects.equipment.Ammo import net.psforever.objects.equipment.Ammo
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.damage.Damageable import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.GenericHackables
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.AmenityAutoRepair import net.psforever.objects.serverobject.repair.AmenityAutoRepair
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl} import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}

View file

@ -4,6 +4,10 @@ package net.psforever.objects.serverobject.turret
import net.psforever.objects.Player import net.psforever.objects.Player
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.sourcing.{PlayerSource, TurretSource}
import net.psforever.objects.vital.{DismountingActivity, MountingActivity}
import scala.annotation.unused
trait MountableTurretControl trait MountableTurretControl
extends TurretControl extends TurretControl
@ -11,9 +15,22 @@ trait MountableTurretControl
override def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable override def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable
/** commonBehavior does not implement mountingBehavior; please do so when implementing */ /** commonBehavior does not implement mountingBehavior; please do so when implementing */
override def commonBehavior: Receive = override def commonBehavior: Receive = super.commonBehavior.orElse(dismountBehavior)
super.commonBehavior
.orElse(dismountBehavior) override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
super.mountActionResponse(user, mountPoint, seatNumber)
if (TurretObject.PassengerInSeat(user).contains(0)) {
val vsrc = TurretSource(TurretObject)
user.LogActivity(MountingActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber = 0), TurretObject.Zone.Number))
}
}
override def dismountActionResponse(user: Player, seatBeingDismounted: Int): Unit = {
super.dismountActionResponse(user, seatBeingDismounted)
if (!TurretObject.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated
user.LogActivity(DismountingActivity(TurretSource(TurretObject), PlayerSource(user), TurretObject.Zone.Number))
}
}
override protected def mountTest( override protected def mountTest(
obj: PlanetSideServerObject with Mountable, obj: PlanetSideServerObject with Mountable,

View file

@ -0,0 +1,6 @@
// Copyright (c) 2026 PSForever
package net.psforever.objects.sourcing
trait MountableEntry {
def occupants: List[SourceEntry]
}

View file

@ -18,7 +18,7 @@ final case class TurretSource(
Orientation: Vector3, Orientation: Vector3,
occupants: List[SourceEntry], occupants: List[SourceEntry],
unique: SourceUniqueness unique: SourceUniqueness
) extends SourceWithHealthEntry with SourceWithShieldsEntry { ) extends SourceWithHealthEntry with SourceWithShieldsEntry with MountableEntry {
def Name: String = SourceEntry.NameFormat(Definition.Descriptor) def Name: String = SourceEntry.NameFormat(Definition.Descriptor)
def Health: Int = health def Health: Int = health
def Shields: Int = shields def Shields: Int = shields

View file

@ -33,7 +33,7 @@ final case class VehicleSource(
occupants: List[SourceEntry], occupants: List[SourceEntry],
Modifiers: ResistanceProfile, Modifiers: ResistanceProfile,
unique: UniqueVehicle unique: UniqueVehicle
) extends SourceWithHealthEntry with SourceWithShieldsEntry { ) extends SourceWithHealthEntry with SourceWithShieldsEntry with MountableEntry {
def Name: String = SourceEntry.NameFormat(Definition.Name) def Name: String = SourceEntry.NameFormat(Definition.Name)
def Health: Int = health def Health: Int = health
def Shields: Int = shields def Shields: Int = shields

View file

@ -20,6 +20,7 @@ import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types._ import net.psforever.types._
import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -170,8 +171,21 @@ class BfrControl(vehicle: Vehicle)
specialArmWeaponEquipManagement(item, slot, handiness) specialArmWeaponEquipManagement(item, slot, handiness)
} }
override def dismountCleanup(seatBeingDismounted: Int, player: Player): Unit = { override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
super.dismountCleanup(seatBeingDismounted, player) super.mountActionResponse(user, mountPoint, seatNumber)
if (vehicle.Seats.values.exists(_.isOccupied)) {
vehicle.Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match {
case Some(subsys)
if !subsys.Enabled && vehicle.Shields > 0 && subsys.Enabled_=(state = true) =>
//if the shield is damaged, it does not turn on until the damaged is cleared
vehicleSubsystemMessages(subsys.changedMessages(vehicle))
case _ => ()
}
}
}
override def dismountActionResponse(user: Player, seatBeingDismounted: Int): Unit = {
super.dismountActionResponse(user, seatBeingDismounted)
if (!vehicle.Seats.values.exists(_.isOccupied)) { if (!vehicle.Seats.values.exists(_.isOccupied)) {
vehicle vehicle
.Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match { .Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match {
@ -196,19 +210,6 @@ class BfrControl(vehicle: Vehicle)
} }
} }
override def mountCleanup(mount_point: Int, user: Player): Unit = {
super.mountCleanup(mount_point, user)
if (vehicle.Seats.values.exists(_.isOccupied)) {
vehicle.Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match {
case Some(subsys)
if !subsys.Enabled && vehicle.Shields > 0 && subsys.Enabled_=(state = true) =>
//if the shield is damaged, it does not turn on until the damaged is cleared
vehicleSubsystemMessages(subsys.changedMessages(vehicle))
case _ => ()
}
}
}
override def permitTerminalMessage(player: Player, msg: ItemTransactionMessage): Boolean = { override def permitTerminalMessage(player: Player, msg: ItemTransactionMessage): Boolean = {
if (msg.transaction_type == TransactionType.Loadout) { if (msg.transaction_type == TransactionType.Loadout) {
!vehicle.Jammed !vehicle.Jammed
@ -455,7 +456,7 @@ class BfrControl(vehicle: Vehicle)
} }
} }
def specialArmWeaponEquipManagement(item: Equipment, slot: Int, handiness: equipment.Hand): Unit = { def specialArmWeaponEquipManagement(item: Equipment, slot: Int, @unused handiness: equipment.Hand): Unit = {
if (item.Size == EquipmentSize.BFRArmWeapon && vehicle.VisibleSlots.contains(slot)) { if (item.Size == EquipmentSize.BFRArmWeapon && vehicle.VisibleSlots.contains(slot)) {
val weapons = vehicle.Weapons val weapons = vehicle.Weapons
//budget logic: the arm weapons are "next to each other" index-wise //budget logic: the arm weapons are "next to each other" index-wise

View file

@ -5,7 +5,6 @@ import akka.actor.ActorRef
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior} import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.types._ import net.psforever.types._
/** /**
@ -36,13 +35,10 @@ class DeployingVehicleControl(vehicle: Vehicle)
*/ */
override def commonDisabledBehavior : Receive = override def commonDisabledBehavior : Receive =
super.commonDisabledBehavior super.commonDisabledBehavior
.orElse(dismountBehavior)
.orElse { .orElse {
case msg : Deployment.TryUndeploy => case msg: Deployment.TryUndeploy =>
deployBehavior.apply(msg) deployBehavior.apply(msg)
case msg @ Mountable.TryDismount(player, seat_num, _) =>
dismountBehavior.apply(msg)
dismountCleanup(seat_num, player)
} }
/** /**

View file

@ -29,7 +29,7 @@ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.interaction.WithWater import net.psforever.objects.vehicles.interaction.WithWater
import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity} import net.psforever.objects.vital.{DamagingActivity, DismountingActivity, InGameActivity, MountingActivity, ShieldCharge, SpawningActivity}
import net.psforever.objects.zones._ import net.psforever.objects.zones._
import net.psforever.objects.zones.interaction.IndependentZoneInteraction import net.psforever.objects.zones.interaction.IndependentZoneInteraction
import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.PlanetSideGamePacket
@ -40,6 +40,7 @@ import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.util.Random import scala.util.Random
@ -104,7 +105,80 @@ class VehicleControl(vehicle: Vehicle)
endAllCargoOperations() endAllCargoOperations()
} }
private val mountingFailureReasons: Receive = {
case Mountable.TryMount(user, mountPoint)
if vehicle.DeploymentState == DriveState.AutoPilot =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
case Mountable.TryMount(user, mountPoint)
if vehicle.Zone.blockMap.sector(vehicle).buildingList.exists {
case wg: WarpGate =>
Vector3.DistanceSquared(vehicle.Position, wg.Position) < math.pow(wg.Definition.SOIRadius, 2)
case _ => false
} && user.Carrying.contains(SpecialCarry.CaptureFlag) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
}
private val disountingFailureReasons: Receive = {
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.DeploymentState == DriveState.AutoPilot =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
// Issue 1133. Todo: There may be a better way to address the issue?
case Mountable.TryDismount(user, seat_num, bailType) if GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3000L => true
case _ => false
}) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType) if !GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 8500L => true
case _ => false
}) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .1).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 4000L => true
case _ if Random.nextInt(10) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .2).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3500L => true
case _ if Random.nextInt(5) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .35).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3000L => true
case _ if Random.nextInt(4) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.isMoving(test = 1f) && bailType == BailType.Normal =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
}
def commonEnabledBehavior: Receive = checkBehavior def commonEnabledBehavior: Receive = checkBehavior
.orElse(mountingFailureReasons)
.orElse(mountBehavior)
.orElse(disountingFailureReasons)
.orElse(dismountBehavior)
.orElse(attributeBehavior) .orElse(attributeBehavior)
.orElse(jammableBehavior) .orElse(jammableBehavior)
.orElse(takesDamage) .orElse(takesDamage)
@ -122,79 +196,6 @@ class VehicleControl(vehicle: Vehicle)
case Vehicle.Ownership(Some(player)) => case Vehicle.Ownership(Some(player)) =>
GainOwnership(player) GainOwnership(player)
case Mountable.TryMount(user, mountPoint)
if vehicle.DeploymentState == DriveState.AutoPilot =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
case Mountable.TryMount(user, mountPoint)
if vehicle.Zone.blockMap.sector(vehicle).buildingList.exists {
case wg: WarpGate =>
Vector3.DistanceSquared(vehicle.Position, wg.Position) < math.pow(wg.Definition.SOIRadius, 2)
case _ => false
} && user.Carrying.contains(SpecialCarry.CaptureFlag) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
case msg @ Mountable.TryMount(player, mount_point) =>
mountBehavior.apply(msg)
mountCleanup(mount_point, player)
// Issue 1133. Todo: There may be a better way to address the issue?
case Mountable.TryDismount(user, seat_num, bailType) if GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3000L => true
case _ => false
}) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType) if !GlobalDefinitions.isFlightVehicle(vehicle.Definition) &&
(vehicle.History.find { entry => entry.isInstanceOf[SpawningActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 8500L => true
case _ => false
}) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .1).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 4000L => true
case _ if Random.nextInt(10) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .2).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3500L => true
case _ if Random.nextInt(5) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.Health <= (vehicle.Definition.MaxHealth * .35).round && bailType == BailType.Bailed
&& GlobalDefinitions.isFlightVehicle(vehicle.Definition)
&& (seat_num == 0 || vehicle.SeatPermissionGroup(seat_num).getOrElse(0) == AccessPermissionGroup.Gunner)
&& (vehicle.History.findLast { entry => entry.isInstanceOf[DamagingActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time < 3000L => true
case _ if Random.nextInt(4) == 1 => false
case _ => true }) =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.DeploymentState == DriveState.AutoPilot =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case Mountable.TryDismount(user, seat_num, bailType)
if vehicle.isMoving(test = 1f) && bailType == BailType.Normal =>
sender() ! Mountable.MountMessages(user, Mountable.CanNotDismount(vehicle, seat_num, bailType))
case msg @ Mountable.TryDismount(player, seat_num, _) =>
dismountBehavior.apply(msg)
dismountCleanup(seat_num, player)
case CommonMessages.ChargeShields(amount, motivator) => case CommonMessages.ChargeShields(amount, motivator) =>
chargeShields(amount, motivator.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }) chargeShields(amount, motivator.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) })
@ -215,7 +216,6 @@ class VehicleControl(vehicle: Vehicle)
events ! VehicleServiceMessage(toChannel, VehicleAction.SendResponse(guid0, pkt)) events ! VehicleServiceMessage(toChannel, VehicleAction.SendResponse(guid0, pkt))
} }
case FactionAffinity.ConvertFactionAffinity(faction) => case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = vehicle.Faction val originalAffinity = vehicle.Faction
if (originalAffinity != (vehicle.Faction = faction)) { if (originalAffinity != (vehicle.Faction = faction)) {
@ -253,7 +253,7 @@ class VehicleControl(vehicle: Vehicle)
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result = true) AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result = true)
) )
case _ => ; case _ => ()
} }
} else { } else {
zone.AvatarEvents ! AvatarServiceMessage( zone.AvatarEvents ! AvatarServiceMessage(
@ -294,11 +294,8 @@ class VehicleControl(vehicle: Vehicle)
} }
def commonDisabledBehavior: Receive = checkBehavior def commonDisabledBehavior: Receive = checkBehavior
.orElse(dismountBehavior)
.orElse { .orElse {
case msg @ Mountable.TryDismount(user, seat_num, _) =>
dismountBehavior.apply(msg)
dismountCleanup(seat_num, user)
case Vehicle.Deconstruct(time) => case Vehicle.Deconstruct(time) =>
time match { time match {
case Some(delay) if vehicle.Definition.undergoesDecay => case Some(delay) if vehicle.Definition.undergoesDecay =>
@ -317,7 +314,7 @@ class VehicleControl(vehicle: Vehicle)
final def Disabled: Receive = commonDisabledBehavior final def Disabled: Receive = commonDisabledBehavior
.orElse { .orElse {
case _ => ; case _ => ()
} }
def commonDeleteBehavior: Receive = checkBehavior def commonDeleteBehavior: Receive = checkBehavior
@ -333,7 +330,7 @@ class VehicleControl(vehicle: Vehicle)
final def ReadyToDelete: Receive = commonDeleteBehavior final def ReadyToDelete: Receive = commonDeleteBehavior
.orElse { .orElse {
case _ => ; case _ => ()
} }
override protected def mountTest( override protected def mountTest(
@ -351,37 +348,32 @@ class VehicleControl(vehicle: Vehicle)
super.mountTest(obj, seatNumber, user) super.mountTest(obj, seatNumber, user)
} }
def mountCleanup(mount_point: Int, user: Player): Unit = { override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
vehicle.PassengerInSeat(user) match { super.mountActionResponse(user, mountPoint, seatNumber)
case Some(0) => //driver seat val vsrc = VehicleSource(vehicle)
val vsrc = VehicleSource(vehicle) user.LogActivity(MountingActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber = 0), vehicle.Zone.Number)) if (seatNumber == 0) {
//if the driver mount, change ownership if that is permissible for this vehicle //if the driver mount, change ownership if that is permissible for this vehicle
if (!vehicle.OwnerName.contains(user.Name) && vehicle.Definition.CanBeOwned.nonEmpty) { if (!vehicle.OwnerName.contains(user.Name) && vehicle.Definition.CanBeOwned.nonEmpty) {
//whatever vehicle was previously owned //whatever vehicle was previously owned
vehicle.Zone.GUID(user.avatar.vehicle) match { vehicle.Zone.GUID(user.avatar.vehicle) match {
case Some(v: Vehicle) => case Some(v: Vehicle) =>
v.Actor ! Vehicle.Ownership(None) v.Actor ! Vehicle.Ownership(None)
case _ => case _ =>
user.avatar.vehicle = None user.avatar.vehicle = None
}
GainOwnership(user) //gain new ownership
} else {
decaying = false
decayTimer.cancel()
} }
TryStopInteractionSelfReporting() GainOwnership(user) //gain new ownership
updateZoneInteractionProgressUI(user) } else {
case Some(seatNumber) => //literally any other seat
val vsrc = VehicleSource(vehicle)
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
decaying = false decaying = false
decayTimer.cancel() decayTimer.cancel()
StopInteractionSelfReporting() }
updateZoneInteractionProgressUI(user) TryStopInteractionSelfReporting()
updateZoneInteractionProgressUI(user)
case None => () } else {
decaying = false
decayTimer.cancel()
StopInteractionSelfReporting()
updateZoneInteractionProgressUI(user)
} }
} }
@ -393,51 +385,37 @@ class VehicleControl(vehicle: Vehicle)
vehicle.DeploymentState == DriveState.Deployed || super.dismountTest(obj, seatNumber, user) vehicle.DeploymentState == DriveState.Deployed || super.dismountTest(obj, seatNumber, user)
} }
def dismountCleanup(seatBeingDismounted: Int, user: Player): Unit = { override def dismountActionResponse(user: Player, @unused seatBeingDismounted: Int): Unit = {
super.dismountActionResponse(user, seatBeingDismounted)
user.LogActivity(DismountingActivity(VehicleSource(vehicle), PlayerSource(user), vehicle.Zone.Number))
val obj = MountableObject val obj = MountableObject
// Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount if (seatBeingDismounted == 0) {
if (!obj.Seats(0).isOccupied) {
obj.Velocity = Some(Vector3.Zero) obj.Velocity = Some(Vector3.Zero)
} }
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied) if (TestToStartSelfReporting()) {
val otherTests = TestToStartSelfReporting()
if (allSeatsUnoccupied && otherTests) {
StartInteractionSelfReporting() StartInteractionSelfReporting()
} }
if (!obj.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated //we were only owning the vehicle while we sat in its driver seat
user.LogActivity(VehicleDismountActivity(VehicleSource(vehicle), PlayerSource(user), vehicle.Zone.Number)) val canBeOwned = obj.Definition.CanBeOwned
//we were only owning the vehicle while we sat in its driver seat if (canBeOwned.contains(false) && seatBeingDismounted == 0) {
val canBeOwned = obj.Definition.CanBeOwned LoseOwnership()
if (canBeOwned.contains(false) && seatBeingDismounted == 0) { }
LoseOwnership() //are we already decaying? are we unowned? is no one seated anywhere?
} if (!decaying &&
//are we already decaying? are we unowned? is no one seated anywhere? obj.Definition.undergoesDecay &&
if (!decaying && obj.OwnerGuid.isEmpty &&
obj.Definition.undergoesDecay && !vehicle.Seats.values.exists(_.isOccupied)) {
obj.OwnerGuid.isEmpty && decaying = true
allSeatsUnoccupied) { decayTimer = context.system.scheduler.scheduleOnce(
decaying = true MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes),
decayTimer = context.system.scheduler.scheduleOnce( self,
MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes), VehicleControl.PrepareForDeletion()
self, )
VehicleControl.PrepareForDeletion()
)
}
} }
} }
def TestToStartSelfReporting(): Boolean = { def TestToStartSelfReporting(): Boolean = {
vehicle.MountedIn.isEmpty vehicle.MountedIn.isEmpty && !vehicle.Seats.values.exists(_.isOccupied)
}
def PerformSelfReportRunCheck(): Unit = {
val noOccupancy = !vehicle.Seats.values.exists(_.isOccupied)
val otherTests = TestToStartSelfReporting()
if (noOccupancy && otherTests) {
StartInteractionSelfReporting()
} else {
StopInteractionSelfReporting()
}
} }
def PrepareForDisabled(kickPassengers: Boolean) : Unit = { def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
@ -566,7 +544,7 @@ class VehicleControl(vehicle: Vehicle)
VehicleAction.InventoryState2(Service.defaultPlayerGUID, box.GUID, iguid, box.Capacity) VehicleAction.InventoryState2(Service.defaultPlayerGUID, box.GUID, iguid, box.Capacity)
) )
} }
case _ => ; case _ => ()
} }
} }
@ -710,7 +688,7 @@ class VehicleControl(vehicle: Vehicle)
VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, vguid) VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, vguid)
) )
} }
case _ => ; // No player seated case _ => () // No player seated
} }
} }
vehicle.CargoHolds.foreach { vehicle.CargoHolds.foreach {
@ -722,11 +700,11 @@ class VehicleControl(vehicle: Vehicle)
// Instruct client to start bail dismount procedure // Instruct client to start bail dismount procedure
self ! DismountVehicleCargoMsg(dguid, cargo.GUID, bailed = true, requestedByPassenger = false, kicked = false) self ! DismountVehicleCargoMsg(dguid, cargo.GUID, bailed = true, requestedByPassenger = false, kicked = false)
} }
case None => ; // No vehicle in cargo case None => () // No vehicle in cargo
} }
} }
} }
case None => ; case None => ()
} }
} else { } else {
log.warn( log.warn(
@ -780,9 +758,7 @@ class VehicleControl(vehicle: Vehicle)
override def endCargoDismounting(carrierGuid: PlanetSideGUID): Unit = { override def endCargoDismounting(carrierGuid: PlanetSideGUID): Unit = {
super.endCargoDismounting(carrierGuid) super.endCargoDismounting(carrierGuid)
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied) if (TestToStartSelfReporting()) {
val otherTests = TestToStartSelfReporting()
if (allSeatsUnoccupied && otherTests) {
StartInteractionSelfReporting() StartInteractionSelfReporting()
} }
vehicle.Zone.GUID(carrierGuid) match { vehicle.Zone.GUID(carrierGuid) match {

View file

@ -4,7 +4,7 @@ package net.psforever.objects.vital
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.{EquipmentDefinition, KitDefinition, ToolDefinition} import net.psforever.objects.definition.{EquipmentDefinition, KitDefinition, ToolDefinition}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.sourcing.{AmenitySource, DeployableSource, PlayerSource, SourceEntry, SourceUniqueness, SourceWithHealthEntry, VehicleSource} import net.psforever.objects.sourcing.{AmenitySource, DeployableSource, MountableEntry, PlayerSource, SourceEntry, SourceUniqueness, SourceWithHealthEntry, VehicleSource}
import net.psforever.objects.vital.environment.EnvironmentReason import net.psforever.objects.vital.environment.EnvironmentReason
import net.psforever.objects.vital.etc.{ExplodingEntityReason, PainboxReason, SuicideReason} import net.psforever.objects.vital.etc.{ExplodingEntityReason, PainboxReason, SuicideReason}
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
@ -79,44 +79,48 @@ final case class RevivingActivity(target: SourceEntry, user: PlayerSource, amoun
final case class ShieldCharge(amount: Int, cause: Option[SourceEntry]) final case class ShieldCharge(amount: Int, cause: Option[SourceEntry])
extends GeneralActivity extends GeneralActivity
trait TerminalUse {
def terminal: AmenitySource
}
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value) final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value)
extends GeneralActivity extends GeneralActivity with TerminalUse
final case class TelepadUseActivity(router: VehicleSource, telepad: DeployableSource, player: PlayerSource) final case class TelepadUseActivity(router: VehicleSource, telepad: DeployableSource, player: PlayerSource)
extends GeneralActivity extends GeneralActivity
sealed trait VehicleMountChange extends GeneralActivity { sealed trait MountChange extends GeneralActivity {
def vehicle: VehicleSource def mount: SourceEntry with MountableEntry
def zoneNumber: Int def zoneNumber: Int
} }
sealed trait VehiclePassengerMountChange extends VehicleMountChange { sealed trait PassengerMountChange extends MountChange {
def player: PlayerSource def player: PlayerSource
} }
sealed trait VehicleCargoMountChange extends VehicleMountChange { sealed trait CargoMountChange extends MountChange {
def cargo: VehicleSource def cargo: VehicleSource
} }
final case class VehicleMountActivity(vehicle: VehicleSource, player: PlayerSource, zoneNumber: Int) final case class MountingActivity(mount: SourceEntry with MountableEntry, player: PlayerSource, zoneNumber: Int)
extends VehiclePassengerMountChange extends PassengerMountChange
final case class VehicleDismountActivity( final case class DismountingActivity(
vehicle: VehicleSource, mount: SourceEntry with MountableEntry,
player: PlayerSource, player: PlayerSource,
zoneNumber: Int, zoneNumber: Int,
pairedEvent: Option[VehicleMountActivity] = None pairedEvent: Option[MountingActivity] = None
) extends VehiclePassengerMountChange ) extends PassengerMountChange
final case class VehicleCargoMountActivity(vehicle: VehicleSource, cargo: VehicleSource, zoneNumber: Int) final case class VehicleCargoMountActivity(mount: VehicleSource, cargo: VehicleSource, zoneNumber: Int)
extends VehicleCargoMountChange extends CargoMountChange
final case class VehicleCargoDismountActivity( final case class VehicleCargoDismountActivity(
vehicle: VehicleSource, mount: VehicleSource,
cargo: VehicleSource, cargo: VehicleSource,
zoneNumber: Int, zoneNumber: Int,
pairedEvent: Option[VehicleCargoMountActivity] = None pairedEvent: Option[VehicleCargoMountActivity] = None
) extends VehicleCargoMountChange ) extends CargoMountChange
final case class Contribution(src: SourceUniqueness, entries: List[InGameActivity]) final case class Contribution(src: SourceUniqueness, entries: List[InGameActivity])
extends GeneralActivity { extends GeneralActivity {
@ -165,8 +169,8 @@ final case class HealFromKit(kit_def: KitDefinition, amount: Int)
final case class HealFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int) final case class HealFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
extends HealingActivity with SupportActivityCausedByAnother extends HealingActivity with SupportActivityCausedByAnother
final case class HealFromTerminal(term: AmenitySource, amount: Int) final case class HealFromTerminal(terminal: AmenitySource, amount: Int)
extends HealingActivity extends HealingActivity with TerminalUse
final case class HealFromImplant(implant: ImplantType, amount: Int) final case class HealFromImplant(implant: ImplantType, amount: Int)
extends HealingActivity extends HealingActivity
@ -180,7 +184,8 @@ final case class RepairFromKit(kit_def: KitDefinition, amount: Int)
final case class RepairFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int) final case class RepairFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
extends RepairingActivity with SupportActivityCausedByAnother extends RepairingActivity with SupportActivityCausedByAnother
final case class RepairFromTerminal(term: AmenitySource, amount: Int) extends RepairingActivity final case class RepairFromTerminal(terminal: AmenitySource, amount: Int)
extends RepairingActivity with TerminalUse
final case class RepairFromArmorSiphon(siphon_def: ToolDefinition, vehicle: VehicleSource, amount: Int) final case class RepairFromArmorSiphon(siphon_def: ToolDefinition, vehicle: VehicleSource, amount: Int)
extends RepairingActivity extends RepairingActivity
@ -251,24 +256,24 @@ trait InGameHistory {
*/ */
def LogActivity(action: Option[InGameActivity]): List[InGameActivity] = { def LogActivity(action: Option[InGameActivity]): List[InGameActivity] = {
action match { action match {
case Some(act: VehicleDismountActivity) if act.pairedEvent.isEmpty => case Some(act: DismountingActivity) if act.pairedEvent.isEmpty =>
history history
.findLast(_.isInstanceOf[VehicleMountActivity]) .findLast(_.isInstanceOf[MountingActivity])
.collect { .collect {
case event: VehicleMountActivity if event.vehicle.unique == act.vehicle.unique => case event: MountingActivity if event.mount.unique == act.mount.unique =>
history = history :+ InGameActivity.ShareTime(act.copy(pairedEvent = Some(event)), act) history = history :+ InGameActivity.ShareTime(act.copy(pairedEvent = Some(event)), act)
} }
.orElse { .orElse {
history = history :+ act history = history :+ act
None None
} }
case Some(act: VehicleDismountActivity) => case Some(act: DismountingActivity) =>
history = history :+ act history = history :+ act
case Some(act: VehicleCargoDismountActivity) => case Some(act: VehicleCargoDismountActivity) =>
history history
.findLast(_.isInstanceOf[VehicleCargoMountActivity]) .findLast(_.isInstanceOf[VehicleCargoMountActivity])
.collect { .collect {
case event: VehicleCargoMountActivity if event.vehicle.unique == act.vehicle.unique => case event: VehicleCargoMountActivity if event.mount.unique == act.mount.unique =>
history = history :+ InGameActivity.ShareTime(act.copy(pairedEvent = Some(event)), act) history = history :+ InGameActivity.ShareTime(act.copy(pairedEvent = Some(event)), act)
} }
.orElse { .orElse {

View file

@ -4,8 +4,8 @@ package net.psforever.objects.zones.exp
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.GlobalDefinitions import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.avatar.scoring.{Kill, SupportActivity} import net.psforever.objects.avatar.scoring.{Kill, SupportActivity}
import net.psforever.objects.sourcing.{BuildingSource, PlayerSource, SourceEntry, SourceUniqueness, TurretSource, VehicleSource} import net.psforever.objects.sourcing.{BuildingSource, MountableEntry, PlayerSource, SourceEntry, SourceUniqueness, TurretSource, UniquePlayer, VehicleSource}
import net.psforever.objects.vital.{Contribution, HealFromTerminal, InGameActivity, RepairFromTerminal, RevivingActivity, TelepadUseActivity, TerminalUsedActivity, VehicleCargoDismountActivity, VehicleCargoMountActivity, VehicleDismountActivity, VehicleMountActivity} import net.psforever.objects.vital.{Contribution, InGameActivity, RevivingActivity, TelepadUseActivity, TerminalUsedActivity, VehicleCargoDismountActivity, VehicleCargoMountActivity, DismountingActivity, MountingActivity}
import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.exp.rec.{CombinedHealthAndArmorContributionProcess, MachineRecoveryExperienceContributionProcess} import net.psforever.objects.zones.exp.rec.{CombinedHealthAndArmorContributionProcess, MachineRecoveryExperienceContributionProcess}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -388,13 +388,15 @@ object KillContributions {
the player should not get credit from being the vehicle owner in matters of transportation the player should not get credit from being the vehicle owner in matters of transportation
there are considerations of time and distance traveled before the kill as well there are considerations of time and distance traveled before the kill as well
*/ */
case out: VehicleDismountActivity case out: DismountingActivity
if !out.vehicle.owner.contains(out.player.unique) && out.pairedEvent.nonEmpty => (out.pairedEvent.get, out) if out.mount.isInstanceOf[VehicleSource] &&
!ownershipFromMount(out.mount).contains(out.player.unique) &&
out.pairedEvent.nonEmpty => (out.pairedEvent.get, out)
} }
.collect { .collect {
case (in: VehicleMountActivity, out: VehicleDismountActivity) case (in: MountingActivity, out: DismountingActivity)
if in.vehicle.unique == out.vehicle.unique && if in.mount.unique == out.mount.unique &&
out.vehicle.Faction == out.player.Faction && out.mount.Faction == out.player.Faction &&
/* /*
considerations of time and distance transported before the kill considerations of time and distance transported before the kill
*/ */
@ -407,7 +409,7 @@ object KillContributions {
} }
} || { } || {
val sameZone = in.zoneNumber == out.zoneNumber val sameZone = in.zoneNumber == out.zoneNumber
val distanceTransported = Vector3.DistanceSquared(in.vehicle.Position.xy, out.vehicle.Position.xy) val distanceTransported = Vector3.DistanceSquared(in.mount.Position.xy, out.mount.Position.xy)
val distanceMoved = { val distanceMoved = {
val killLocation = killerOpt.map(_.Position.xy).getOrElse(Vector3.Zero) val killLocation = killerOpt.map(_.Position.xy).getOrElse(Vector3.Zero)
Vector3.DistanceSquared(killLocation, out.player.Position.xy) Vector3.DistanceSquared(killLocation, out.player.Position.xy)
@ -423,9 +425,9 @@ object KillContributions {
} }
//apply //apply
dismountActivity dismountActivity
.groupBy { _.vehicle } .groupBy { _.mount }
.collect { case (mount, dismountsFromVehicle) if mount.owner.nonEmpty => .collect { case (mount, dismountsFromVehicle) if ownershipFromMount(mount).nonEmpty =>
val promotedOwner = PlayerSource(mount.owner.get, mount.Position) val promotedOwner = PlayerSource(ownershipFromMount(mount).get, mount.Position)
val size = dismountsFromVehicle.size val size = dismountsFromVehicle.size
val time = dismountsFromVehicle.maxBy(_.time).time val time = dismountsFromVehicle.maxBy(_.time).time
List((HotDropKillAssist(mount.Definition.ObjectId, 0), "hotdrop", promotedOwner)) List((HotDropKillAssist(mount.Definition.ObjectId, 0), "hotdrop", promotedOwner))
@ -457,6 +459,24 @@ object KillContributions {
} }
} }
/**
* Determine the owner of the entity based on information about the entity.
* @param mount mountable entity which can be owned
* @return the optional unique referential signature for the owner
*/
private def ownershipFromMount(mount: SourceEntry with MountableEntry): Option[UniquePlayer] = {
mount match {
case v: VehicleSource =>
v.owner
case t: TurretSource => t.occupants.headOption.flatMap {
case p: PlayerSource => Some(p.unique)
case _ => None
}
case _ =>
None
}
}
/** /**
* Gather and reward specific in-game equipment use activity.<br> * Gather and reward specific in-game equipment use activity.<br>
* na * na
@ -486,14 +506,14 @@ object KillContributions {
val dismountActivity = history val dismountActivity = history
.collect { .collect {
case out: VehicleCargoDismountActivity case out: VehicleCargoDismountActivity
if out.vehicle.owner.nonEmpty && out.pairedEvent.nonEmpty => (out.pairedEvent.get, out) if ownershipFromMount(out.mount).nonEmpty && out.pairedEvent.nonEmpty => (out.pairedEvent.get, out)
} }
.collect { .collect {
case (in: VehicleCargoMountActivity, out: VehicleCargoDismountActivity) case (in: VehicleCargoMountActivity, out: VehicleCargoDismountActivity)
if in.vehicle.unique == out.vehicle.unique && if in.mount.unique == out.mount.unique &&
out.vehicle.Faction == out.cargo.Faction && out.mount.Faction == out.cargo.Faction &&
(in.vehicle.Definition == GlobalDefinitions.router || { (in.mount.Definition == GlobalDefinitions.router || {
val distanceTransported = Vector3.DistanceSquared(in.vehicle.Position.xy, out.vehicle.Position.xy) val distanceTransported = Vector3.DistanceSquared(in.mount.Position.xy, out.mount.Position.xy)
val distanceMoved = { val distanceMoved = {
val killLocation = kill.info.adversarial val killLocation = kill.info.adversarial
.collect { adversarial => adversarial.attacker.Position.xy } .collect { adversarial => adversarial.attacker.Position.xy }
@ -513,7 +533,7 @@ object KillContributions {
val promotedOwner = PlayerSource(mount.owner.get, mount.Position) val promotedOwner = PlayerSource(mount.owner.get, mount.Position)
val mountId = mount.Definition.ObjectId val mountId = mount.Definition.ObjectId
dismountsFromVehicle dismountsFromVehicle
.groupBy(_.vehicle) .groupBy(_.mount)
.map { case (vehicle, events) => .map { case (vehicle, events) =>
val size = events.size val size = events.size
val time = events.maxBy(_.time).time val time = events.maxBy(_.time).time
@ -673,8 +693,6 @@ object KillContributions {
): Unit = { ): Unit = {
history history
.collect { .collect {
case h: HealFromTerminal => (h.term, h)
case r: RepairFromTerminal => (r.term, r)
case t: TerminalUsedActivity => (t.terminal, t) case t: TerminalUsedActivity => (t.terminal, t)
} }
.groupBy(_._1.unique) .groupBy(_._1.unique)

View file

@ -2,7 +2,7 @@
package net.psforever.objects.zones.exp package net.psforever.objects.zones.exp
import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.sourcing.PlayerSource
import net.psforever.objects.vital.{ExoSuitChange, InGameActivity, RevivingActivity, TerminalUsedActivity, VehicleDismountActivity, VehicleMountActivity, VehicleMountChange, VitalityDefinition} import net.psforever.objects.vital.{ExoSuitChange, InGameActivity, RevivingActivity, TerminalUsedActivity, DismountingActivity, MountingActivity, MountChange, VitalityDefinition}
import net.psforever.types.{ExoSuitType, PlanetSideEmpire} import net.psforever.types.{ExoSuitType, PlanetSideEmpire}
import net.psforever.util.{Config, DefinitionUtil, ThreatAssessment, ThreatLevel} import net.psforever.util.{Config, DefinitionUtil, ThreatAssessment, ThreatLevel}
@ -82,7 +82,7 @@ object Support {
val wornTime: mutable.HashMap[Int, Long] = mutable.HashMap[Int, Long]() val wornTime: mutable.HashMap[Int, Long] = mutable.HashMap[Int, Long]()
var currentSuit: Int = initialExosuit.id var currentSuit: Int = initialExosuit.id
var lastActTime: Long = history.head.time var lastActTime: Long = history.head.time
var lastMountAct: Option[VehicleMountChange] = None var lastMountAct: Option[MountChange] = None
//collect history events that encompass changes to exo-suits and to mounting conditions //collect history events that encompass changes to exo-suits and to mounting conditions
history.collect { history.collect {
case suitChange: ExoSuitChange => case suitChange: ExoSuitChange =>
@ -93,7 +93,7 @@ object Support {
) )
currentSuit = suitChange.exosuit.id currentSuit = suitChange.exosuit.id
lastActTime = suitChange.time lastActTime = suitChange.time
case mount: VehicleMountActivity => case mount: MountingActivity =>
updateEquippedEntry( updateEquippedEntry(
currentSuit, currentSuit,
mount.time - lastActTime, mount.time - lastActTime,
@ -101,18 +101,18 @@ object Support {
) )
lastActTime = mount.time lastActTime = mount.time
lastMountAct = Some(mount) lastMountAct = Some(mount)
case dismount: VehicleDismountActivity case dismount: DismountingActivity
if dismount.pairedEvent.isEmpty => if dismount.pairedEvent.isEmpty =>
updateEquippedEntry( updateEquippedEntry(
dismount.vehicle.Definition.ObjectId, dismount.mount.Definition.ObjectId,
dismount.time - lastActTime, dismount.time - lastActTime,
wornTime wornTime
) )
lastActTime = dismount.time lastActTime = dismount.time
lastMountAct = None lastMountAct = None
case dismount: VehicleDismountActivity => case dismount: DismountingActivity =>
updateEquippedEntry( updateEquippedEntry(
dismount.vehicle.Definition.ObjectId, dismount.mount.Definition.ObjectId,
dismount.time - dismount.pairedEvent.get.time, dismount.time - dismount.pairedEvent.get.time,
wornTime wornTime
) )
@ -125,7 +125,7 @@ object Support {
.collect { mount => .collect { mount =>
//dying in a vehicle is a reason to care about the last mount activity //dying in a vehicle is a reason to care about the last mount activity
updateEquippedEntry( updateEquippedEntry(
mount.vehicle.Definition.ObjectId, mount.mount.Definition.ObjectId,
lastTime - mount.time, lastTime - mount.time,
wornTime wornTime
) )

View file

@ -39,10 +39,15 @@ trait IndependentZoneInteraction {
} }
} }
def PerformSelfReportRunCheck(): Unit def PerformSelfReportRunCheck(): Unit = {
if (TestToStartSelfReporting()) {
StartInteractionSelfReporting()
} else {
StopInteractionSelfReporting()
}
}
final def StartInteractionSelfReporting(): Unit = { final def StartInteractionSelfReporting(): Unit = {
org.log4s.getLogger("ZIT").info("starting timer")
zoneInteractionTimer.cancel() zoneInteractionTimer.cancel()
zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay( zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay(
0.seconds, 0.seconds,
@ -53,7 +58,6 @@ trait IndependentZoneInteraction {
} }
final def StartInteractionSelfReporting(initialDelay: FiniteDuration): Unit = { final def StartInteractionSelfReporting(initialDelay: FiniteDuration): Unit = {
org.log4s.getLogger("ZIT").info("starting timer")
zoneInteractionTimer.cancel() zoneInteractionTimer.cancel()
zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay( zoneInteractionTimer = context.system.scheduler.scheduleWithFixedDelay(
initialDelay, initialDelay,