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.turret.{MountableTurret, WeaponTurrets}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.{InGameActivity, ShieldCharge}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, TurretSource}
import net.psforever.objects.vital.{DismountingActivity, InGameActivity, MountingActivity, ShieldCharge}
import net.psforever.packet.game.HackState1
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.PlanetSideGUID
import scala.annotation.unused
/** definition */
class FieldTurretDeployableDefinition(private val objectId: Int)
@ -70,6 +72,21 @@ class FieldTurretControl(turret: TurretDeployable)
player: 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
private def canChargeShields: Boolean = {
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.types.BailType
import scala.annotation.unused
import scala.collection.mutable
trait MountableBehavior {
@ -45,13 +46,17 @@ trait MountableBehavior {
case Some(seatNum) if mountTest(obj, seatNum, user) && tryMount(obj, seatNum, user) =>
user.VehicleSeated = obj.GUID
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))
case _ =>
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(
obj: PlanetSideServerObject with Mountable,
seatNumber: Int,
@ -87,7 +92,7 @@ trait MountableBehavior {
val obj = MountableObject
if (dismountTest(obj, seat_number, user) && tryDismount(obj, seat_number, user, bail_type)) {
user.VehicleSeated = None
obj.Zone.actor ! ZoneActor.AddToBlockMap(user, obj.Position)
dismountActionResponse(user, seat_number)
sender() ! Mountable.MountMessages(
user,
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(
obj: Mountable with WorldEntity,
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.serverobject.{CommonMessages, PlanetSideServerObject}
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.repair.AmenityAutoRepair
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.serverobject.PlanetSideServerObject
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
extends TurretControl
@ -11,9 +15,22 @@ trait MountableTurretControl
override def TurretObject: PlanetSideServerObject with WeaponTurret with Mountable
/** commonBehavior does not implement mountingBehavior; please do so when implementing */
override def commonBehavior: Receive =
super.commonBehavior
.orElse(dismountBehavior)
override def commonBehavior: Receive = 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(
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,
occupants: List[SourceEntry],
unique: SourceUniqueness
) extends SourceWithHealthEntry with SourceWithShieldsEntry {
) extends SourceWithHealthEntry with SourceWithShieldsEntry with MountableEntry {
def Name: String = SourceEntry.NameFormat(Definition.Descriptor)
def Health: Int = health
def Shields: Int = shields

View file

@ -33,7 +33,7 @@ final case class VehicleSource(
occupants: List[SourceEntry],
Modifiers: ResistanceProfile,
unique: UniqueVehicle
) extends SourceWithHealthEntry with SourceWithShieldsEntry {
) extends SourceWithHealthEntry with SourceWithShieldsEntry with MountableEntry {
def Name: String = SourceEntry.NameFormat(Definition.Name)
def Health: Int = health
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.types._
import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@ -170,8 +171,21 @@ class BfrControl(vehicle: Vehicle)
specialArmWeaponEquipManagement(item, slot, handiness)
}
override def dismountCleanup(seatBeingDismounted: Int, player: Player): Unit = {
super.dismountCleanup(seatBeingDismounted, player)
override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
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)) {
vehicle
.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 = {
if (msg.transaction_type == TransactionType.Loadout) {
!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)) {
val weapons = vehicle.Weapons
//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.serverobject.deploy.Deployment.DeploymentObject
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.types._
/**
@ -36,13 +35,10 @@ class DeployingVehicleControl(vehicle: Vehicle)
*/
override def commonDisabledBehavior : Receive =
super.commonDisabledBehavior
.orElse(dismountBehavior)
.orElse {
case msg : Deployment.TryUndeploy =>
case msg: Deployment.TryUndeploy =>
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.interaction.WithWater
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.interaction.IndependentZoneInteraction
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.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.Random
@ -104,7 +105,80 @@ class VehicleControl(vehicle: Vehicle)
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
.orElse(mountingFailureReasons)
.orElse(mountBehavior)
.orElse(disountingFailureReasons)
.orElse(dismountBehavior)
.orElse(attributeBehavior)
.orElse(jammableBehavior)
.orElse(takesDamage)
@ -122,79 +196,6 @@ class VehicleControl(vehicle: Vehicle)
case Vehicle.Ownership(Some(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) =>
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))
}
case FactionAffinity.ConvertFactionAffinity(faction) =>
val originalAffinity = vehicle.Faction
if (originalAffinity != (vehicle.Faction = faction)) {
@ -253,7 +253,7 @@ class VehicleControl(vehicle: Vehicle)
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result = true)
)
case _ => ;
case _ => ()
}
} else {
zone.AvatarEvents ! AvatarServiceMessage(
@ -294,11 +294,8 @@ class VehicleControl(vehicle: Vehicle)
}
def commonDisabledBehavior: Receive = checkBehavior
.orElse(dismountBehavior)
.orElse {
case msg @ Mountable.TryDismount(user, seat_num, _) =>
dismountBehavior.apply(msg)
dismountCleanup(seat_num, user)
case Vehicle.Deconstruct(time) =>
time match {
case Some(delay) if vehicle.Definition.undergoesDecay =>
@ -317,7 +314,7 @@ class VehicleControl(vehicle: Vehicle)
final def Disabled: Receive = commonDisabledBehavior
.orElse {
case _ => ;
case _ => ()
}
def commonDeleteBehavior: Receive = checkBehavior
@ -333,7 +330,7 @@ class VehicleControl(vehicle: Vehicle)
final def ReadyToDelete: Receive = commonDeleteBehavior
.orElse {
case _ => ;
case _ => ()
}
override protected def mountTest(
@ -351,37 +348,32 @@ class VehicleControl(vehicle: Vehicle)
super.mountTest(obj, seatNumber, user)
}
def mountCleanup(mount_point: Int, user: Player): Unit = {
vehicle.PassengerInSeat(user) match {
case Some(0) => //driver seat
val vsrc = VehicleSource(vehicle)
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber = 0), vehicle.Zone.Number))
//if the driver mount, change ownership if that is permissible for this vehicle
if (!vehicle.OwnerName.contains(user.Name) && vehicle.Definition.CanBeOwned.nonEmpty) {
//whatever vehicle was previously owned
vehicle.Zone.GUID(user.avatar.vehicle) match {
case Some(v: Vehicle) =>
v.Actor ! Vehicle.Ownership(None)
case _ =>
user.avatar.vehicle = None
}
GainOwnership(user) //gain new ownership
} else {
decaying = false
decayTimer.cancel()
override def mountActionResponse(user: Player, @unused mountPoint: Int, seatNumber: Int): Unit = {
super.mountActionResponse(user, mountPoint, seatNumber)
val vsrc = VehicleSource(vehicle)
user.LogActivity(MountingActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
if (seatNumber == 0) {
//if the driver mount, change ownership if that is permissible for this vehicle
if (!vehicle.OwnerName.contains(user.Name) && vehicle.Definition.CanBeOwned.nonEmpty) {
//whatever vehicle was previously owned
vehicle.Zone.GUID(user.avatar.vehicle) match {
case Some(v: Vehicle) =>
v.Actor ! Vehicle.Ownership(None)
case _ =>
user.avatar.vehicle = None
}
TryStopInteractionSelfReporting()
updateZoneInteractionProgressUI(user)
case Some(seatNumber) => //literally any other seat
val vsrc = VehicleSource(vehicle)
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
GainOwnership(user) //gain new ownership
} else {
decaying = false
decayTimer.cancel()
StopInteractionSelfReporting()
updateZoneInteractionProgressUI(user)
case None => ()
}
TryStopInteractionSelfReporting()
updateZoneInteractionProgressUI(user)
} 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)
}
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
// Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount
if (!obj.Seats(0).isOccupied) {
if (seatBeingDismounted == 0) {
obj.Velocity = Some(Vector3.Zero)
}
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied)
val otherTests = TestToStartSelfReporting()
if (allSeatsUnoccupied && otherTests) {
if (TestToStartSelfReporting()) {
StartInteractionSelfReporting()
}
if (!obj.Seats(seatBeingDismounted).isOccupied) { //this seat really was vacated
user.LogActivity(VehicleDismountActivity(VehicleSource(vehicle), PlayerSource(user), vehicle.Zone.Number))
//we were only owning the vehicle while we sat in its driver seat
val canBeOwned = obj.Definition.CanBeOwned
if (canBeOwned.contains(false) && seatBeingDismounted == 0) {
LoseOwnership()
}
//are we already decaying? are we unowned? is no one seated anywhere?
if (!decaying &&
obj.Definition.undergoesDecay &&
obj.OwnerGuid.isEmpty &&
allSeatsUnoccupied) {
decaying = true
decayTimer = context.system.scheduler.scheduleOnce(
MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes),
self,
VehicleControl.PrepareForDeletion()
)
}
//we were only owning the vehicle while we sat in its driver seat
val canBeOwned = obj.Definition.CanBeOwned
if (canBeOwned.contains(false) && seatBeingDismounted == 0) {
LoseOwnership()
}
//are we already decaying? are we unowned? is no one seated anywhere?
if (!decaying &&
obj.Definition.undergoesDecay &&
obj.OwnerGuid.isEmpty &&
!vehicle.Seats.values.exists(_.isOccupied)) {
decaying = true
decayTimer = context.system.scheduler.scheduleOnce(
MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes),
self,
VehicleControl.PrepareForDeletion()
)
}
}
def TestToStartSelfReporting(): Boolean = {
vehicle.MountedIn.isEmpty
}
def PerformSelfReportRunCheck(): Unit = {
val noOccupancy = !vehicle.Seats.values.exists(_.isOccupied)
val otherTests = TestToStartSelfReporting()
if (noOccupancy && otherTests) {
StartInteractionSelfReporting()
} else {
StopInteractionSelfReporting()
}
vehicle.MountedIn.isEmpty && !vehicle.Seats.values.exists(_.isOccupied)
}
def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
@ -566,7 +544,7 @@ class VehicleControl(vehicle: Vehicle)
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)
)
}
case _ => ; // No player seated
case _ => () // No player seated
}
}
vehicle.CargoHolds.foreach {
@ -722,11 +700,11 @@ class VehicleControl(vehicle: Vehicle)
// Instruct client to start bail dismount procedure
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 {
log.warn(
@ -780,9 +758,7 @@ class VehicleControl(vehicle: Vehicle)
override def endCargoDismounting(carrierGuid: PlanetSideGUID): Unit = {
super.endCargoDismounting(carrierGuid)
val allSeatsUnoccupied = !vehicle.Seats.values.exists(_.isOccupied)
val otherTests = TestToStartSelfReporting()
if (allSeatsUnoccupied && otherTests) {
if (TestToStartSelfReporting()) {
StartInteractionSelfReporting()
}
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.definition.{EquipmentDefinition, KitDefinition, ToolDefinition}
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.etc.{ExplodingEntityReason, PainboxReason, SuicideReason}
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])
extends GeneralActivity
trait TerminalUse {
def terminal: AmenitySource
}
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)
extends GeneralActivity
sealed trait VehicleMountChange extends GeneralActivity {
def vehicle: VehicleSource
sealed trait MountChange extends GeneralActivity {
def mount: SourceEntry with MountableEntry
def zoneNumber: Int
}
sealed trait VehiclePassengerMountChange extends VehicleMountChange {
sealed trait PassengerMountChange extends MountChange {
def player: PlayerSource
}
sealed trait VehicleCargoMountChange extends VehicleMountChange {
sealed trait CargoMountChange extends MountChange {
def cargo: VehicleSource
}
final case class VehicleMountActivity(vehicle: VehicleSource, player: PlayerSource, zoneNumber: Int)
extends VehiclePassengerMountChange
final case class MountingActivity(mount: SourceEntry with MountableEntry, player: PlayerSource, zoneNumber: Int)
extends PassengerMountChange
final case class VehicleDismountActivity(
vehicle: VehicleSource,
player: PlayerSource,
zoneNumber: Int,
pairedEvent: Option[VehicleMountActivity] = None
) extends VehiclePassengerMountChange
final case class DismountingActivity(
mount: SourceEntry with MountableEntry,
player: PlayerSource,
zoneNumber: Int,
pairedEvent: Option[MountingActivity] = None
) extends PassengerMountChange
final case class VehicleCargoMountActivity(vehicle: VehicleSource, cargo: VehicleSource, zoneNumber: Int)
extends VehicleCargoMountChange
final case class VehicleCargoMountActivity(mount: VehicleSource, cargo: VehicleSource, zoneNumber: Int)
extends CargoMountChange
final case class VehicleCargoDismountActivity(
vehicle: VehicleSource,
mount: VehicleSource,
cargo: VehicleSource,
zoneNumber: Int,
pairedEvent: Option[VehicleCargoMountActivity] = None
) extends VehicleCargoMountChange
) extends CargoMountChange
final case class Contribution(src: SourceUniqueness, entries: List[InGameActivity])
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)
extends HealingActivity with SupportActivityCausedByAnother
final case class HealFromTerminal(term: AmenitySource, amount: Int)
extends HealingActivity
final case class HealFromTerminal(terminal: AmenitySource, amount: Int)
extends HealingActivity with TerminalUse
final case class HealFromImplant(implant: ImplantType, amount: Int)
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)
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)
extends RepairingActivity
@ -251,24 +256,24 @@ trait InGameHistory {
*/
def LogActivity(action: Option[InGameActivity]): List[InGameActivity] = {
action match {
case Some(act: VehicleDismountActivity) if act.pairedEvent.isEmpty =>
case Some(act: DismountingActivity) if act.pairedEvent.isEmpty =>
history
.findLast(_.isInstanceOf[VehicleMountActivity])
.findLast(_.isInstanceOf[MountingActivity])
.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)
}
.orElse {
history = history :+ act
None
}
case Some(act: VehicleDismountActivity) =>
case Some(act: DismountingActivity) =>
history = history :+ act
case Some(act: VehicleCargoDismountActivity) =>
history
.findLast(_.isInstanceOf[VehicleCargoMountActivity])
.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)
}
.orElse {

View file

@ -4,8 +4,8 @@ package net.psforever.objects.zones.exp
import akka.actor.ActorRef
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.avatar.scoring.{Kill, SupportActivity}
import net.psforever.objects.sourcing.{BuildingSource, PlayerSource, SourceEntry, SourceUniqueness, TurretSource, VehicleSource}
import net.psforever.objects.vital.{Contribution, HealFromTerminal, InGameActivity, RepairFromTerminal, RevivingActivity, TelepadUseActivity, TerminalUsedActivity, VehicleCargoDismountActivity, VehicleCargoMountActivity, VehicleDismountActivity, VehicleMountActivity}
import net.psforever.objects.sourcing.{BuildingSource, MountableEntry, PlayerSource, SourceEntry, SourceUniqueness, TurretSource, UniquePlayer, VehicleSource}
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.zones.exp.rec.{CombinedHealthAndArmorContributionProcess, MachineRecoveryExperienceContributionProcess}
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
there are considerations of time and distance traveled before the kill as well
*/
case out: VehicleDismountActivity
if !out.vehicle.owner.contains(out.player.unique) && out.pairedEvent.nonEmpty => (out.pairedEvent.get, out)
case out: DismountingActivity
if out.mount.isInstanceOf[VehicleSource] &&
!ownershipFromMount(out.mount).contains(out.player.unique) &&
out.pairedEvent.nonEmpty => (out.pairedEvent.get, out)
}
.collect {
case (in: VehicleMountActivity, out: VehicleDismountActivity)
if in.vehicle.unique == out.vehicle.unique &&
out.vehicle.Faction == out.player.Faction &&
case (in: MountingActivity, out: DismountingActivity)
if in.mount.unique == out.mount.unique &&
out.mount.Faction == out.player.Faction &&
/*
considerations of time and distance transported before the kill
*/
@ -407,7 +409,7 @@ object KillContributions {
}
} || {
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 killLocation = killerOpt.map(_.Position.xy).getOrElse(Vector3.Zero)
Vector3.DistanceSquared(killLocation, out.player.Position.xy)
@ -423,9 +425,9 @@ object KillContributions {
}
//apply
dismountActivity
.groupBy { _.vehicle }
.collect { case (mount, dismountsFromVehicle) if mount.owner.nonEmpty =>
val promotedOwner = PlayerSource(mount.owner.get, mount.Position)
.groupBy { _.mount }
.collect { case (mount, dismountsFromVehicle) if ownershipFromMount(mount).nonEmpty =>
val promotedOwner = PlayerSource(ownershipFromMount(mount).get, mount.Position)
val size = dismountsFromVehicle.size
val time = dismountsFromVehicle.maxBy(_.time).time
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>
* na
@ -486,14 +506,14 @@ object KillContributions {
val dismountActivity = history
.collect {
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 {
case (in: VehicleCargoMountActivity, out: VehicleCargoDismountActivity)
if in.vehicle.unique == out.vehicle.unique &&
out.vehicle.Faction == out.cargo.Faction &&
(in.vehicle.Definition == GlobalDefinitions.router || {
val distanceTransported = Vector3.DistanceSquared(in.vehicle.Position.xy, out.vehicle.Position.xy)
if in.mount.unique == out.mount.unique &&
out.mount.Faction == out.cargo.Faction &&
(in.mount.Definition == GlobalDefinitions.router || {
val distanceTransported = Vector3.DistanceSquared(in.mount.Position.xy, out.mount.Position.xy)
val distanceMoved = {
val killLocation = kill.info.adversarial
.collect { adversarial => adversarial.attacker.Position.xy }
@ -513,7 +533,7 @@ object KillContributions {
val promotedOwner = PlayerSource(mount.owner.get, mount.Position)
val mountId = mount.Definition.ObjectId
dismountsFromVehicle
.groupBy(_.vehicle)
.groupBy(_.mount)
.map { case (vehicle, events) =>
val size = events.size
val time = events.maxBy(_.time).time
@ -673,8 +693,6 @@ object KillContributions {
): Unit = {
history
.collect {
case h: HealFromTerminal => (h.term, h)
case r: RepairFromTerminal => (r.term, r)
case t: TerminalUsedActivity => (t.terminal, t)
}
.groupBy(_._1.unique)

View file

@ -2,7 +2,7 @@
package net.psforever.objects.zones.exp
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.util.{Config, DefinitionUtil, ThreatAssessment, ThreatLevel}
@ -82,7 +82,7 @@ object Support {
val wornTime: mutable.HashMap[Int, Long] = mutable.HashMap[Int, Long]()
var currentSuit: Int = initialExosuit.id
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
history.collect {
case suitChange: ExoSuitChange =>
@ -93,7 +93,7 @@ object Support {
)
currentSuit = suitChange.exosuit.id
lastActTime = suitChange.time
case mount: VehicleMountActivity =>
case mount: MountingActivity =>
updateEquippedEntry(
currentSuit,
mount.time - lastActTime,
@ -101,18 +101,18 @@ object Support {
)
lastActTime = mount.time
lastMountAct = Some(mount)
case dismount: VehicleDismountActivity
case dismount: DismountingActivity
if dismount.pairedEvent.isEmpty =>
updateEquippedEntry(
dismount.vehicle.Definition.ObjectId,
dismount.mount.Definition.ObjectId,
dismount.time - lastActTime,
wornTime
)
lastActTime = dismount.time
lastMountAct = None
case dismount: VehicleDismountActivity =>
case dismount: DismountingActivity =>
updateEquippedEntry(
dismount.vehicle.Definition.ObjectId,
dismount.mount.Definition.ObjectId,
dismount.time - dismount.pairedEvent.get.time,
wornTime
)
@ -125,7 +125,7 @@ object Support {
.collect { mount =>
//dying in a vehicle is a reason to care about the last mount activity
updateEquippedEntry(
mount.vehicle.Definition.ObjectId,
mount.mount.Definition.ObjectId,
lastTime - mount.time,
wornTime
)

View file

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