mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
events for mounting and dismounting of both passenger and cargo; id'ing the installation of an amenity (vehicle or facility); separation of kill/assist experience and support experience calculations; retention of kill record which allows for the calculation of menace
This commit is contained in:
parent
da6ea21147
commit
4b87526e4d
16 changed files with 367 additions and 140 deletions
|
|
@ -2473,7 +2473,11 @@ class SessionData(
|
||||||
continent.id,
|
continent.id,
|
||||||
LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid)
|
LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid)
|
||||||
)
|
)
|
||||||
player.LogActivity(VehicleDismountActivity(VehicleSource(router), PlayerSource(player)))
|
val vSource = VehicleSource(router)
|
||||||
|
val zoneNumber = continent.Number
|
||||||
|
player.LogActivity(VehicleMountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||||
|
player.Position = dest.Position
|
||||||
|
player.LogActivity(VehicleDismountActivity(vSource, PlayerSource(player), zoneNumber))
|
||||||
} else {
|
} else {
|
||||||
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
log.warn(s"UseRouterTelepadSystem: ${player.Name} can not teleport")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import net.psforever.objects.avatar.scoring.Kill
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
|
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
|
||||||
import net.psforever.objects.zones.exp.ExperienceCalculator
|
import net.psforever.objects.zones.exp.{ExperienceCalculator, SupportExperienceCalculator}
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
import net.psforever.persistence
|
import net.psforever.persistence
|
||||||
|
|
||||||
|
|
@ -87,6 +87,7 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
private[this] val log = org.log4s.getLogger
|
private[this] val log = org.log4s.getLogger
|
||||||
private val players: mutable.ListBuffer[Player] = mutable.ListBuffer()
|
private val players: mutable.ListBuffer[Player] = mutable.ListBuffer()
|
||||||
private val experience: ActorRef[ExperienceCalculator.Command] = context.spawnAnonymous(ExperienceCalculator(zone))
|
private val experience: ActorRef[ExperienceCalculator.Command] = context.spawnAnonymous(ExperienceCalculator(zone))
|
||||||
|
private val supportExperience: ActorRef[SupportExperienceCalculator.Command] = context.spawnAnonymous(SupportExperienceCalculator(zone))
|
||||||
|
|
||||||
zone.actor = context.self
|
zone.actor = context.self
|
||||||
zone.init(context.toClassic)
|
zone.init(context.toClassic)
|
||||||
|
|
@ -154,7 +155,7 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
experience ! ExperienceCalculator.RewardThisDeath(entity)
|
experience ! ExperienceCalculator.RewardThisDeath(entity)
|
||||||
|
|
||||||
case RewardOurSupporters(target, history, kill, bep) =>
|
case RewardOurSupporters(target, history, kill, bep) =>
|
||||||
experience ! ExperienceCalculator.RewardOurSupporters(target, history, kill, bep)
|
supportExperience ! SupportExperienceCalculator.RewardOurSupporters(target, history, kill, bep)
|
||||||
|
|
||||||
case ZoneMapUpdate() =>
|
case ZoneMapUpdate() =>
|
||||||
zone.Buildings
|
zone.Buildings
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@ object MineDeployableControl {
|
||||||
jumping = false,
|
jumping = false,
|
||||||
ExoSuitDefinition.Select(exosuit, faction),
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
bep = 0,
|
bep = 0,
|
||||||
kills = Nil,
|
progress = PlayerSource.Nobody.progress,
|
||||||
UniquePlayer(charId, name, CharacterSex.Male, mine.Faction)
|
UniquePlayer(charId, name, CharacterSex.Male, mine.Faction)
|
||||||
)
|
)
|
||||||
case None =>
|
case None =>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ final case class AmenitySource(
|
||||||
health: Int,
|
health: Int,
|
||||||
Orientation: Vector3,
|
Orientation: Vector3,
|
||||||
occupants: List[SourceEntry],
|
occupants: List[SourceEntry],
|
||||||
|
installation: SourceEntry,
|
||||||
hacked: Option[HackInfo],
|
hacked: Option[HackInfo],
|
||||||
unique: UniqueAmenity
|
unique: UniqueAmenity
|
||||||
) extends SourceWithHealthEntry {
|
) extends SourceWithHealthEntry {
|
||||||
|
|
@ -54,6 +55,7 @@ object AmenitySource {
|
||||||
health,
|
health,
|
||||||
obj.Orientation,
|
obj.Orientation,
|
||||||
Nil,
|
Nil,
|
||||||
|
SourceEntry(obj.Owner),
|
||||||
hackData,
|
hackData,
|
||||||
sourcing.UniqueAmenity(obj.Zone.Number, obj.GUID, obj.Position)
|
sourcing.UniqueAmenity(obj.Zone.Number, obj.GUID, obj.Position)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.sourcing
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.scoring.Life
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition}
|
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition}
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
|
@ -28,7 +29,7 @@ final case class PlayerSource(
|
||||||
jumping: Boolean,
|
jumping: Boolean,
|
||||||
Modifiers: ResistanceProfile,
|
Modifiers: ResistanceProfile,
|
||||||
bep: Long,
|
bep: Long,
|
||||||
kills: Seq[Any],
|
progress: Life,
|
||||||
unique: UniquePlayer
|
unique: UniquePlayer
|
||||||
) extends SourceWithHealthEntry {
|
) extends SourceWithHealthEntry {
|
||||||
override def Name: String = unique.name
|
override def Name: String = unique.name
|
||||||
|
|
@ -46,6 +47,7 @@ object PlayerSource {
|
||||||
val exosuit = p.ExoSuit
|
val exosuit = p.ExoSuit
|
||||||
val faction = p.Faction
|
val faction = p.Faction
|
||||||
val seatedEntity = mountableAndSeat(p)
|
val seatedEntity = mountableAndSeat(p)
|
||||||
|
val avatar = p.avatar
|
||||||
PlayerSource(
|
PlayerSource(
|
||||||
p.Definition,
|
p.Definition,
|
||||||
exosuit,
|
exosuit,
|
||||||
|
|
@ -58,8 +60,8 @@ object PlayerSource {
|
||||||
p.Crouching,
|
p.Crouching,
|
||||||
p.Jumping,
|
p.Jumping,
|
||||||
ExoSuitDefinition.Select(exosuit, faction),
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
p.avatar.bep,
|
avatar.bep,
|
||||||
kills = Nil,
|
progress = avatar.scorecard.CurrentLife,
|
||||||
UniquePlayer(p.CharId, p.Name, p.Sex, faction)
|
UniquePlayer(p.CharId, p.Name, p.Sex, faction)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +80,7 @@ object PlayerSource {
|
||||||
jumping = false,
|
jumping = false,
|
||||||
GlobalDefinitions.Standard,
|
GlobalDefinitions.Standard,
|
||||||
bep = 0L,
|
bep = 0L,
|
||||||
kills = Nil,
|
progress = tokenLife,
|
||||||
UniquePlayer(0L, name, CharacterSex.Male, faction)
|
UniquePlayer(0L, name, CharacterSex.Male, faction)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +118,7 @@ object PlayerSource {
|
||||||
def inSeat(player: Player, source: SourceEntry, seatNumber: Int): PlayerSource = {
|
def inSeat(player: Player, source: SourceEntry, seatNumber: Int): PlayerSource = {
|
||||||
val exosuit = player.ExoSuit
|
val exosuit = player.ExoSuit
|
||||||
val faction = player.Faction
|
val faction = player.Faction
|
||||||
|
val avatar = player.avatar
|
||||||
PlayerSource(
|
PlayerSource(
|
||||||
player.Definition,
|
player.Definition,
|
||||||
exosuit,
|
exosuit,
|
||||||
|
|
@ -128,8 +131,8 @@ object PlayerSource {
|
||||||
player.Crouching,
|
player.Crouching,
|
||||||
player.Jumping,
|
player.Jumping,
|
||||||
ExoSuitDefinition.Select(exosuit, faction),
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
player.avatar.bep,
|
avatar.bep,
|
||||||
kills = Nil,
|
progress = tokenLife,
|
||||||
UniquePlayer(player.CharId, player.Name, player.Sex, faction)
|
UniquePlayer(player.CharId, player.Name, player.Sex, faction)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -142,4 +145,6 @@ object PlayerSource {
|
||||||
* the others first: this will be my guest-gift to you.”
|
* the others first: this will be my guest-gift to you.”
|
||||||
*/
|
*/
|
||||||
final val Nobody = PlayerSource("Nobody", PlanetSideEmpire.NEUTRAL, Vector3.Zero)
|
final val Nobody = PlayerSource("Nobody", PlanetSideEmpire.NEUTRAL, Vector3.Zero)
|
||||||
|
|
||||||
|
private val tokenLife: Life = Life()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,9 @@ import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.packet.game.{
|
import net.psforever.objects.sourcing.VehicleSource
|
||||||
CargoMountPointStatusMessage,
|
import net.psforever.objects.vital.VehicleCargoMountActivity
|
||||||
ObjectAttachMessage,
|
import net.psforever.packet.game.{CargoMountPointStatusMessage, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage}
|
||||||
ObjectDetachMessage,
|
|
||||||
PlanetsideAttributeMessage
|
|
||||||
}
|
|
||||||
import net.psforever.types.{BailType, CargoStatus, PlanetSideGUID, Vector3}
|
import net.psforever.types.{BailType, CargoStatus, PlanetSideGUID, Vector3}
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
@ -117,7 +114,7 @@ trait CarrierBehavior {
|
||||||
case _ =>
|
case _ =>
|
||||||
obj.CargoHold(mountPoint) match {
|
obj.CargoHold(mountPoint) match {
|
||||||
case Some(hold) if hold.isOccupied && hold.occupant.get.GUID == cargo_guid =>
|
case Some(hold) if hold.isOccupied && hold.occupant.get.GUID == cargo_guid =>
|
||||||
hold.unmount(hold.occupant.get)
|
CarrierBehavior.CargoDismountAction(obj, hold.occupant.get, hold, BailType.Normal)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
@ -213,10 +210,8 @@ object CarrierBehavior {
|
||||||
if (distance <= 64) {
|
if (distance <= 64) {
|
||||||
//cargo vehicle is close enough to assume to be physically within the carrier's hold; mount it
|
//cargo vehicle is close enough to assume to be physically within the carrier's hold; mount it
|
||||||
log.debug(s"HandleCheckCargoMounting: mounting cargo vehicle in carrier at distance of $distance")
|
log.debug(s"HandleCheckCargoMounting: mounting cargo vehicle in carrier at distance of $distance")
|
||||||
hold.mount(cargo)
|
CargoMountAction(carrier, cargo, hold, carrierGUID)
|
||||||
cargo.MountedIn = carrierGUID
|
|
||||||
cargo.Velocity = None
|
cargo.Velocity = None
|
||||||
cargo.Actor ! CargoBehavior.EndCargoMounting(carrierGUID)
|
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
s"${cargo.Actor}",
|
s"${cargo.Actor}",
|
||||||
VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health))
|
VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health))
|
||||||
|
|
@ -358,9 +353,7 @@ object CarrierBehavior {
|
||||||
//obviously, don't do this
|
//obviously, don't do this
|
||||||
} else if (iteration > 40) {
|
} else if (iteration > 40) {
|
||||||
//cargo vehicle has spent too long not getting far enough away; restore the cargo's mount in the carrier hold
|
//cargo vehicle has spent too long not getting far enough away; restore the cargo's mount in the carrier hold
|
||||||
hold.mount(cargo)
|
CargoMountAction(carrier, cargo, hold, carrierGUID)
|
||||||
cargo.MountedIn = carrierGUID
|
|
||||||
cargo.Actor ! CargoBehavior.EndCargoMounting(carrierGUID)
|
|
||||||
CargoMountBehaviorForAll(carrier, cargo, mountPoint)
|
CargoMountBehaviorForAll(carrier, cargo, mountPoint)
|
||||||
zone.actor ! ZoneActor.RemoveFromBlockMap(cargo)
|
zone.actor ! ZoneActor.RemoveFromBlockMap(cargo)
|
||||||
false
|
false
|
||||||
|
|
@ -441,9 +434,10 @@ object CarrierBehavior {
|
||||||
val zone = carrier.Zone
|
val zone = carrier.Zone
|
||||||
carrier.CargoHolds.find({ case (_, hold) => hold.occupant.contains(cargo) }) match {
|
carrier.CargoHolds.find({ case (_, hold) => hold.occupant.contains(cargo) }) match {
|
||||||
case Some((mountPoint, hold)) =>
|
case Some((mountPoint, hold)) =>
|
||||||
cargo.MountedIn = None
|
CarrierBehavior.CargoDismountAction(
|
||||||
hold.unmount(
|
carrier,
|
||||||
cargo,
|
cargo,
|
||||||
|
hold,
|
||||||
if (bailed) BailType.Bailed else if (kicked) BailType.Kicked else BailType.Normal
|
if (bailed) BailType.Bailed else if (kicked) BailType.Kicked else BailType.Normal
|
||||||
)
|
)
|
||||||
val driverOpt = cargo.Seats(0).occupant
|
val driverOpt = cargo.Seats(0).occupant
|
||||||
|
|
@ -653,4 +647,40 @@ object CarrierBehavior {
|
||||||
)
|
)
|
||||||
msgs
|
msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param carrier the ferrying vehicle
|
||||||
|
* @param cargo the ferried vehicle
|
||||||
|
* @param hold na
|
||||||
|
* @param carrierGuid the ferrying vehicle's unique identifier
|
||||||
|
*/
|
||||||
|
private def CargoMountAction(
|
||||||
|
carrier: Vehicle,
|
||||||
|
cargo: Vehicle,
|
||||||
|
hold: Cargo,
|
||||||
|
carrierGuid: PlanetSideGUID): Unit = {
|
||||||
|
hold.mount(cargo)
|
||||||
|
cargo.MountedIn = carrierGuid
|
||||||
|
cargo.LogActivity(VehicleCargoMountActivity(VehicleSource(carrier), VehicleSource(cargo), carrier.Zone.Number))
|
||||||
|
cargo.Actor ! CargoBehavior.EndCargoMounting(carrierGuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param carrier the ferrying vehicle
|
||||||
|
* @param cargo the ferried vehicle
|
||||||
|
* @param hold na
|
||||||
|
* @param bailType na
|
||||||
|
*/
|
||||||
|
private def CargoDismountAction(
|
||||||
|
carrier: Vehicle,
|
||||||
|
cargo: Vehicle,
|
||||||
|
hold: Cargo,
|
||||||
|
bailType: BailType.Value
|
||||||
|
): Unit = {
|
||||||
|
cargo.MountedIn = None
|
||||||
|
hold.unmount(cargo, bailType)
|
||||||
|
cargo.LogActivity(VehicleCargoMountActivity(VehicleSource(carrier), VehicleSource(cargo), carrier.Zone.Number))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,8 +170,8 @@ class BfrControl(vehicle: Vehicle)
|
||||||
specialArmWeaponEquipManagement(item, slot, handiness)
|
specialArmWeaponEquipManagement(item, slot, handiness)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def dismountCleanup(seatBeingDismounted: Int): Unit = {
|
override def dismountCleanup(seatBeingDismounted: Int, player: Player): Unit = {
|
||||||
super.dismountCleanup(seatBeingDismounted)
|
super.dismountCleanup(seatBeingDismounted, player)
|
||||||
if (!vehicle.Seats.values.exists(_.isOccupied)) {
|
if (!vehicle.Seats.values.exists(_.isOccupied)) {
|
||||||
vehicle.Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match {
|
vehicle.Subsystems(VehicleSubsystemEntry.BattleframeShieldGenerator) match {
|
||||||
case Some(subsys) =>
|
case Some(subsys) =>
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
||||||
case msg : Deployment.TryUndeploy =>
|
case msg : Deployment.TryUndeploy =>
|
||||||
deployBehavior.apply(msg)
|
deployBehavior.apply(msg)
|
||||||
|
|
||||||
case msg @ Mountable.TryDismount(_, seat_num, _) =>
|
case msg @ Mountable.TryDismount(player, seat_num, _) =>
|
||||||
dismountBehavior.apply(msg)
|
dismountBehavior.apply(msg)
|
||||||
dismountCleanup(seat_num)
|
dismountCleanup(seat_num, player)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ 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.repair.RepairableVehicle
|
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.sourcing.{SourceEntry, VehicleSource}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge}
|
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge, VehicleDismountActivity, VehicleMountActivity}
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||||
import net.psforever.objects.vital.etc.SuicideReason
|
import net.psforever.objects.vital.etc.SuicideReason
|
||||||
import net.psforever.objects.zones._
|
import net.psforever.objects.zones._
|
||||||
|
|
@ -128,9 +128,9 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
mountBehavior.apply(msg)
|
mountBehavior.apply(msg)
|
||||||
mountCleanup(mount_point, player)
|
mountCleanup(mount_point, player)
|
||||||
|
|
||||||
case msg @ Mountable.TryDismount(_, seat_num, _) =>
|
case msg @ Mountable.TryDismount(player, seat_num, _) =>
|
||||||
dismountBehavior.apply(msg)
|
dismountBehavior.apply(msg)
|
||||||
dismountCleanup(seat_num)
|
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) })
|
||||||
|
|
@ -237,9 +237,9 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
|
|
||||||
def commonDisabledBehavior: Receive = checkBehavior
|
def commonDisabledBehavior: Receive = checkBehavior
|
||||||
.orElse {
|
.orElse {
|
||||||
case msg @ Mountable.TryDismount(_, seat_num, _) =>
|
case msg @ Mountable.TryDismount(user, seat_num, _) =>
|
||||||
dismountBehavior.apply(msg)
|
dismountBehavior.apply(msg)
|
||||||
dismountCleanup(seat_num)
|
dismountCleanup(seat_num, user)
|
||||||
|
|
||||||
case Vehicle.Deconstruct(time) =>
|
case Vehicle.Deconstruct(time) =>
|
||||||
time match {
|
time match {
|
||||||
|
|
@ -297,6 +297,8 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
val obj = MountableObject
|
val obj = MountableObject
|
||||||
obj.PassengerInSeat(user) match {
|
obj.PassengerInSeat(user) match {
|
||||||
case Some(seatNumber) =>
|
case Some(seatNumber) =>
|
||||||
|
val vsrc = VehicleSource(vehicle)
|
||||||
|
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vehicle, vsrc), vehicle.Zone.Number))
|
||||||
//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 (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) {
|
if (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) {
|
||||||
//whatever vehicle was previously owned
|
//whatever vehicle was previously owned
|
||||||
|
|
@ -325,7 +327,7 @@ 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): Unit = {
|
def dismountCleanup(seatBeingDismounted: Int, user: Player): Unit = {
|
||||||
val obj = MountableObject
|
val obj = MountableObject
|
||||||
// Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount
|
// Reset velocity to zero when driver dismounts, to allow jacking/repair if vehicle was moving slightly before dismount
|
||||||
if (!obj.Seats(0).isOccupied) {
|
if (!obj.Seats(0).isOccupied) {
|
||||||
|
|
@ -340,6 +342,7 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!obj.Seats(seatBeingDismounted).isOccupied) { //seat was vacated
|
if (!obj.Seats(seatBeingDismounted).isOccupied) { //seat 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
|
//we were only owning the vehicle while we sat in its driver seat
|
||||||
val canBeOwned = obj.Definition.CanBeOwned
|
val canBeOwned = obj.Definition.CanBeOwned
|
||||||
if (canBeOwned.contains(false) && seatBeingDismounted == 0) {
|
if (canBeOwned.contains(false) && seatBeingDismounted == 0) {
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,30 @@ final case class ShieldCharge(amount: Int, cause: Option[SourceEntry])
|
||||||
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value)
|
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value)
|
||||||
extends GeneralActivity
|
extends GeneralActivity
|
||||||
|
|
||||||
final case class VehicleDismountActivity(vehicle: VehicleSource, player: PlayerSource)
|
sealed trait VehicleMountChange extends GeneralActivity {
|
||||||
extends GeneralActivity
|
def vehicle: VehicleSource
|
||||||
|
def zoneNumber: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait VehiclePassengerMountChange extends VehicleMountChange {
|
||||||
|
def player: PlayerSource
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait VehicleCargoMountChange extends VehicleMountChange {
|
||||||
|
def cargo: VehicleSource
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class VehicleMountActivity(vehicle: VehicleSource, player: PlayerSource, zoneNumber: Int)
|
||||||
|
extends VehiclePassengerMountChange
|
||||||
|
|
||||||
|
final case class VehicleDismountActivity(vehicle: VehicleSource, player: PlayerSource, zoneNumber: Int)
|
||||||
|
extends VehiclePassengerMountChange
|
||||||
|
|
||||||
|
final case class VehicleCargoMountActivity(vehicle: VehicleSource, cargo: VehicleSource, zoneNumber: Int)
|
||||||
|
extends VehicleCargoMountChange
|
||||||
|
|
||||||
|
final case class VehicleCargoDismountActivity(vehicle: VehicleSource, cargo: VehicleSource, zoneNumber: Int)
|
||||||
|
extends VehicleCargoMountChange
|
||||||
|
|
||||||
final case class Contribution(src: SourceUniqueness, entries: List[InGameActivity])
|
final case class Contribution(src: SourceUniqueness, entries: List[InGameActivity])
|
||||||
extends GeneralActivity {
|
extends GeneralActivity {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package net.psforever.objects.zones.exp
|
||||||
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
||||||
import akka.actor.typed.{Behavior, SupervisorStrategy}
|
import akka.actor.typed.{Behavior, SupervisorStrategy}
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.avatar.scoring.Kill
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
|
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
|
||||||
|
|
@ -27,45 +26,6 @@ object ExperienceCalculator {
|
||||||
RewardThisDeath(SourceEntry(obj), obj.LastDamage, obj.History)
|
RewardThisDeath(SourceEntry(obj), obj.LastDamage, obj.History)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command
|
|
||||||
|
|
||||||
object RewardOurSupporters {
|
|
||||||
def apply(obj: PlanetSideGameObject with FactionAffinity with InGameHistory, kill: Kill): RewardOurSupporters = {
|
|
||||||
RewardOurSupporters(SourceEntry(obj), obj.History, kill, kill.experienceEarned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def calculateExperience(
|
|
||||||
victim: PlayerSource,
|
|
||||||
history: Iterable[InGameActivity]
|
|
||||||
): Long = {
|
|
||||||
val lifespan = (history.headOption, history.lastOption) match {
|
|
||||||
case (Some(spawn), Some(death)) => death.time - spawn.time
|
|
||||||
case _ => 0L
|
|
||||||
}
|
|
||||||
val wasEverAMax = Support.wasEverAMax(victim, history)
|
|
||||||
val base = if (wasEverAMax) { //shamed
|
|
||||||
250L
|
|
||||||
} else if (victim.Seated || victim.kills.nonEmpty) {
|
|
||||||
100L
|
|
||||||
} else if (lifespan > 15000L) {
|
|
||||||
50L
|
|
||||||
} else {
|
|
||||||
1L
|
|
||||||
}
|
|
||||||
if (base > 1) {
|
|
||||||
//black ops modifier
|
|
||||||
//TODO x10
|
|
||||||
base
|
|
||||||
} else {
|
|
||||||
base
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed case class InheritedAssistEntry(assists: Iterable[ContributionStatsOutput]) {
|
|
||||||
val time: Long = System.currentTimeMillis()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command], zone: Zone)
|
class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command], zone: Zone)
|
||||||
|
|
@ -78,9 +38,6 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
|
||||||
case RewardThisDeath(victim: PlayerSource, lastDamage, history) =>
|
case RewardThisDeath(victim: PlayerSource, lastDamage, history) =>
|
||||||
KillAssists.rewardThisPlayerDeath(victim, lastDamage, history, zone.AvatarEvents)
|
KillAssists.rewardThisPlayerDeath(victim, lastDamage, history, zone.AvatarEvents)
|
||||||
|
|
||||||
case RewardOurSupporters(target: PlayerSource, history, kill, bep) =>
|
|
||||||
KillContributions.rewardTheseSupporters(target, history, kill, bep, zone.AvatarEvents)
|
|
||||||
|
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,7 @@ object KillAssists {
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
if (spawnIndex == -1 || endIndex == -1) {
|
if (spawnIndex == -1 || endIndex == -1) {
|
||||||
Nil //throw VitalsHistoryException(history.head, "vitals history does not contain expected conditions")
|
Nil
|
||||||
// } else
|
|
||||||
// if (spawnIndex == -1) {
|
|
||||||
// Nil //throw VitalsHistoryException(history.head, "vitals history does not contain initial spawn conditions")
|
|
||||||
// } else if (endIndex == -1) {
|
|
||||||
// Nil //throw VitalsHistoryException(history.last, "vitals history does not contain end of life conditions")
|
|
||||||
} else {
|
} else {
|
||||||
history.slice(spawnIndex, endIndex)
|
history.slice(spawnIndex, endIndex)
|
||||||
}
|
}
|
||||||
|
|
@ -54,13 +49,57 @@ object KillAssists {
|
||||||
.collect { case (dam, Some(adv)) => (dam, adv.attacker) }
|
.collect { case (dam, Some(adv)) => (dam, adv.attacker) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private[exp] def calculateExperience(
|
/**
|
||||||
killer: PlayerSource,
|
* "Menace" is a crude measurement of how much consistent destructive power a player has been demonstrating.
|
||||||
victim: PlayerSource,
|
* Within the last ten kills, the rate of the player's killing speed is measured.
|
||||||
history: Iterable[InGameActivity]
|
* The measurement - a "streak" in modern lingo - is transformed into the form of an `Integer` for simplicity.
|
||||||
): Long = {
|
* @param player the player
|
||||||
|
* @param mercy a time value that can be used to continue a missed streak
|
||||||
|
* @return an `Integer` between 0 and 7
|
||||||
|
*/
|
||||||
|
private[exp] def calculateMenace(player: PlayerSource, mercy: Long = 2500L): Int = {
|
||||||
|
val allKills = player.progress.kills.reverse
|
||||||
|
val restBetweenKills = allKills.take(10) match {
|
||||||
|
case firstKill :: kills if kills.size == 9 =>
|
||||||
|
var xTime = firstKill.time.toDate.getTime
|
||||||
|
kills.map { kill =>
|
||||||
|
val time = kill.time.toDate.getTime
|
||||||
|
val timeOut = time - xTime
|
||||||
|
xTime = time
|
||||||
|
timeOut
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
//TODO the math here is not very meaningful
|
||||||
|
math.floor(math.sqrt(
|
||||||
|
math.max(0, takeWhileDelay(restBetweenKills, testValue = 20000L, mercy).size - 1) +
|
||||||
|
math.max(0, takeWhileDelay(restBetweenKills, testValue = 10000L, mercy).size - 5) * 3 +
|
||||||
|
math.max(0, takeWhileDelay(restBetweenKills, testValue = 5000L, mercy = 1000L).size - 4) * 5
|
||||||
|
)).toInt
|
||||||
|
}
|
||||||
|
|
||||||
|
private def takeWhileDelay(list: Iterable[Long], testValue: Long, mercy: Long = 2500L): Iterable[Long] = {
|
||||||
|
var onGoingMercy: Long = mercy
|
||||||
|
list.takeWhile { time =>
|
||||||
|
if (time < testValue) {
|
||||||
|
true
|
||||||
|
} else if (time - onGoingMercy - 1 < testValue) {
|
||||||
|
onGoingMercy = math.ceil(onGoingMercy * 0.65).toLong
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def calculateExperience(
|
||||||
|
killer: PlayerSource,
|
||||||
|
victim: PlayerSource,
|
||||||
|
history: Iterable[InGameActivity]
|
||||||
|
): Long = {
|
||||||
//base value (the kill experience before modifiers)
|
//base value (the kill experience before modifiers)
|
||||||
val base = ExperienceCalculator.calculateExperience(victim, history)
|
val base = Support.baseExperience(victim, history)
|
||||||
if (base > 1) {
|
if (base > 1) {
|
||||||
//battle rank disparity modifiers
|
//battle rank disparity modifiers
|
||||||
val battleRankDisparity = {
|
val battleRankDisparity = {
|
||||||
|
|
@ -81,7 +120,9 @@ object KillAssists {
|
||||||
math.floor(-0.15f * base - killerLevel + victimLevel).toLong
|
math.floor(-0.15f * base - killerLevel + victimLevel).toLong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
math.max(1, base + battleRankDisparity)
|
//menace modifiers
|
||||||
|
val menace = 1f + calculateMenace(victim) * 0.14f
|
||||||
|
math.max(1, (base + battleRankDisparity) * menace).toLong
|
||||||
} else {
|
} else {
|
||||||
base
|
base
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +146,7 @@ object KillAssists {
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
val assists = collectKillAssistsForPlayer(victim, shortHistory, None)
|
val assists = collectKillAssistsForPlayer(victim, shortHistory, None)
|
||||||
val fullBep = ExperienceCalculator.calculateExperience(victim, shortHistory)
|
val fullBep = Support.baseExperience(victim, shortHistory)
|
||||||
val hitSquad = assists.map {
|
val hitSquad = assists.map {
|
||||||
case ContributionStatsOutput(p, w, r) => (p, Assist(victim, w, r, (fullBep * r).toLong))
|
case ContributionStatsOutput(p, w, r) => (p, Assist(victim, w, r, (fullBep * r).toLong))
|
||||||
}.toSeq
|
}.toSeq
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
package net.psforever.objects.zones.exp
|
package net.psforever.objects.zones.exp
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
|
import net.psforever.objects.GlobalDefinitions
|
||||||
import net.psforever.objects.avatar.scoring.{Assist, Kill}
|
import net.psforever.objects.avatar.scoring.{Assist, Kill}
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
||||||
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource, SourceEntry, SourceUniqueness, VehicleSource}
|
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource, SourceEntry, SourceUniqueness, VehicleSource}
|
||||||
import net.psforever.objects.vital.{Contribution, InGameActivity, RevivingActivity, TerminalUsedActivity, VehicleDismountActivity}
|
import net.psforever.objects.vital.{Contribution, InGameActivity, RevivingActivity, TerminalUsedActivity, VehicleCargoDismountActivity, VehicleCargoMountActivity, VehicleCargoMountChange, VehicleDismountActivity, VehicleMountActivity, VehiclePassengerMountChange}
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
|
@ -22,7 +23,10 @@ object KillContributions {
|
||||||
medical_terminal,
|
medical_terminal,
|
||||||
adv_med_terminal,
|
adv_med_terminal,
|
||||||
crystals_health_a,
|
crystals_health_a,
|
||||||
crystals_health_b
|
crystals_health_b,
|
||||||
|
bfr_rearm_terminal,
|
||||||
|
multivehicle_rearm_terminal,
|
||||||
|
lodestar_repair_terminal
|
||||||
).collect { _.ObjectId }
|
).collect { _.ObjectId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,25 +53,34 @@ object KillContributions {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
//sort by applicable time periods, as long as the longer period is represented by activity
|
//sort by applicable time periods, as long as the longer period is represented by activity
|
||||||
val otherContributionCalculations = contributionScoringAndCulling(faction, kill, Seq(target.CharId), contributions, bep)(_, _)
|
val otherContributionCalculations = contributionScoringAndCulling(faction, kill, contributions, bep)(_, _)
|
||||||
val finalContributions = if (longHistory.isEmpty) {
|
val finalContributions = if (longHistory.nonEmpty && KillAssists.calculateMenace(target) > 2) {
|
||||||
val contributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Nil)
|
|
||||||
contributionProcess.submit(shortHistory)
|
|
||||||
val contributionEntries = otherContributionCalculations(shortHistory, contributionProcess.output())
|
|
||||||
contributionEntries
|
|
||||||
.keys
|
|
||||||
.map { composeContributionOutput(_, contributionEntries, contributionEntries, bep) }
|
|
||||||
} else {
|
|
||||||
val longContributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Nil)
|
val longContributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Nil)
|
||||||
val shortContributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Seq(longContributionProcess))
|
val shortContributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Seq(longContributionProcess))
|
||||||
longContributionProcess.submit(longHistory)
|
longContributionProcess.submit(longHistory)
|
||||||
shortContributionProcess.submit(shortHistory)
|
shortContributionProcess.submit(shortHistory)
|
||||||
val longContributionEntries = otherContributionCalculations(longHistory, longContributionProcess.output())
|
val longContributionEntries = otherContributionCalculations(longHistory, longContributionProcess.output())
|
||||||
val shortContributionEntries = otherContributionCalculations(shortHistory, shortContributionProcess.output())
|
val shortContributionEntries = otherContributionCalculations(shortHistory, shortContributionProcess.output())
|
||||||
(longContributionEntries.keys ++ shortContributionEntries.keys)
|
longContributionEntries.remove(target.CharId)
|
||||||
|
longContributionEntries.remove(kill.victim.CharId)
|
||||||
|
shortContributionEntries.remove(target.CharId)
|
||||||
|
shortContributionEntries.remove(kill.victim.CharId)
|
||||||
|
(longContributionEntries ++ shortContributionEntries)
|
||||||
.toSeq
|
.toSeq
|
||||||
.distinct
|
.distinctBy(_._2.player.unique)
|
||||||
.map { composeContributionOutput(_, shortContributionEntries, longContributionEntries, bep) }
|
.map { case (_, stats) =>
|
||||||
|
composeContributionOutput(stats.player, shortContributionEntries, longContributionEntries, bep)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val contributionProcess = new CombinedHealthAndArmorContributionProcess(faction, contributions, Nil)
|
||||||
|
contributionProcess.submit(shortHistory)
|
||||||
|
val contributionEntries = otherContributionCalculations(shortHistory, contributionProcess.output())
|
||||||
|
contributionEntries.remove(target.CharId)
|
||||||
|
contributionEntries.remove(kill.victim.CharId)
|
||||||
|
contributionEntries
|
||||||
|
.map { case (_, stats) =>
|
||||||
|
composeContributionOutput(stats.player, contributionEntries, contributionEntries, bep)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//take the output and transform that into contribution distribution data
|
//take the output and transform that into contribution distribution data
|
||||||
val victim = kill.victim
|
val victim = kill.victim
|
||||||
|
|
@ -98,7 +111,6 @@ object KillContributions {
|
||||||
private def contributionScoringAndCulling(
|
private def contributionScoringAndCulling(
|
||||||
faction: PlanetSideEmpire.Value,
|
faction: PlanetSideEmpire.Value,
|
||||||
kill: Kill,
|
kill: Kill,
|
||||||
excludeTargets: Seq[Long],
|
|
||||||
contributions: Map[SourceUniqueness, List[InGameActivity]],
|
contributions: Map[SourceUniqueness, List[InGameActivity]],
|
||||||
bep: Long
|
bep: Long
|
||||||
)
|
)
|
||||||
|
|
@ -113,11 +125,11 @@ object KillContributions {
|
||||||
contributionEntries.put(id, stat.copy(weapons = newWeaponStats))
|
contributionEntries.put(id, stat.copy(weapons = newWeaponStats))
|
||||||
}
|
}
|
||||||
contributeWithKillWhileMountedActivity(faction, kill, history, contributionEntries)
|
contributeWithKillWhileMountedActivity(faction, kill, history, contributionEntries)
|
||||||
contributeWithVehicleTransportActivity(history, contributionEntries)
|
|
||||||
contributeWithRevivalActivity(history, contributionEntries)
|
contributeWithRevivalActivity(history, contributionEntries)
|
||||||
|
contributeWithVehicleTransportActivity(history, contributionEntries)
|
||||||
|
contributeWithVehicleCargoTransportActivity(history, contributionEntries)
|
||||||
contributeWithTerminalActivity(faction, history, contributions, contributionEntries)
|
contributeWithTerminalActivity(faction, history, contributions, contributionEntries)
|
||||||
contributionEntries.remove(0)
|
contributionEntries.remove(0)
|
||||||
excludeTargets.foreach { contributionEntries.remove }
|
|
||||||
contributionEntries
|
contributionEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,23 +197,99 @@ object KillContributions {
|
||||||
history: List[InGameActivity],
|
history: List[InGameActivity],
|
||||||
participants: mutable.LongMap[ContributionStats]
|
participants: mutable.LongMap[ContributionStats]
|
||||||
): List[InGameActivity] = {
|
): List[InGameActivity] = {
|
||||||
val shortHistory = history.collect { case out: VehicleDismountActivity => out }
|
/*
|
||||||
shortHistory
|
collect the dismount activity of all vehicles from which this player is not the owner
|
||||||
|
make certain all dismount activity can be paired with a mounting activity
|
||||||
|
certain other qualifications of the prior mounting must be met before the support bonus applies
|
||||||
|
*/
|
||||||
|
val dismountActivity = history
|
||||||
|
.collect {
|
||||||
|
case inAndOut: VehiclePassengerMountChange
|
||||||
|
if !inAndOut.vehicle.owner.contains(inAndOut.player) => inAndOut
|
||||||
|
}
|
||||||
|
.grouped(2)
|
||||||
|
.collect {
|
||||||
|
case List(in: VehicleMountActivity, out: VehicleDismountActivity)
|
||||||
|
if in.vehicle.unique == out.vehicle.unique &&
|
||||||
|
out.vehicle.Faction == out.player.Faction &&
|
||||||
|
(in.vehicle.Definition == GlobalDefinitions.router || {
|
||||||
|
val inTime = in.time
|
||||||
|
val outTime = out.time
|
||||||
|
out.player.progress.kills.exists { death =>
|
||||||
|
val deathTime = death.info.interaction.hitTime
|
||||||
|
inTime < deathTime && deathTime <= outTime
|
||||||
|
}
|
||||||
|
} || {
|
||||||
|
val sameZone = in.zoneNumber == out.zoneNumber
|
||||||
|
val distanceMoved = Vector3.DistanceSquared(in.vehicle.Position.xy, out.vehicle.Position.xy)
|
||||||
|
val timeSpent = out.time - in.time
|
||||||
|
timeSpent >= 210000 /* 3:30 */ ||
|
||||||
|
(sameZone && (distanceMoved > 160000f || distanceMoved > 10000f && timeSpent >= 60000)) |
|
||||||
|
(!sameZone && (distanceMoved > 10000f || timeSpent >= 120000))
|
||||||
|
}) =>
|
||||||
|
out
|
||||||
|
}.toList
|
||||||
|
//apply
|
||||||
|
dismountActivity
|
||||||
.groupBy { a => a.vehicle.owner }
|
.groupBy { a => a.vehicle.owner }
|
||||||
.foreach { case (Some(owner), dismountsFromVehicle) =>
|
.collect { case (Some(owner), dismountsFromVehicle) =>
|
||||||
val numberOfDismounts = dismountsFromVehicle.size
|
val numberOfDismounts = dismountsFromVehicle.size
|
||||||
contributeWithCombinedActivity(
|
contributeWithCombinedActivity(
|
||||||
owner.CharId,
|
owner.CharId,
|
||||||
dismountsFromVehicle.map { act => WeaponStats(act.vehicle.Definition.ObjectId, 1, 1, act.time, 15f) },
|
dismountsFromVehicle.map { act => WeaponStats(act.vehicle.Definition.ObjectId, 0, numberOfDismounts, act.time, 15f) },
|
||||||
dismountsFromVehicle.head.vehicle.owner.get,
|
dismountsFromVehicle.head.vehicle.owner.get,
|
||||||
numberOfDismounts,
|
amount = 0,
|
||||||
numberOfDismounts,
|
total = 0,
|
||||||
numberOfDismounts,
|
numberOfDismounts,
|
||||||
dismountsFromVehicle.maxBy(_.time).time,
|
dismountsFromVehicle.maxBy(_.time).time,
|
||||||
participants
|
participants
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
shortHistory
|
dismountActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
private def contributeWithVehicleCargoTransportActivity(
|
||||||
|
history: List[InGameActivity],
|
||||||
|
participants: mutable.LongMap[ContributionStats]
|
||||||
|
): List[InGameActivity] = {
|
||||||
|
/*
|
||||||
|
collect the dismount activity of all vehicles from which this player is not the owner
|
||||||
|
make certain all dismount activity can be paired with a mounting activity
|
||||||
|
certain other qualifications of the prior mounting must be met before the support bonus applies
|
||||||
|
*/
|
||||||
|
val dismountActivity = history
|
||||||
|
.collect {
|
||||||
|
case inAndOut: VehicleCargoMountChange if inAndOut.vehicle.owner.nonEmpty => inAndOut
|
||||||
|
}
|
||||||
|
.grouped(2)
|
||||||
|
.collect {
|
||||||
|
case List(in: VehicleCargoMountActivity, out: VehicleCargoDismountActivity)
|
||||||
|
if in.vehicle.unique == out.vehicle.unique &&
|
||||||
|
out.vehicle.Faction == out.cargo.Faction &&
|
||||||
|
(in.vehicle.Definition == GlobalDefinitions.router || {
|
||||||
|
val distanceMoved = Vector3.DistanceSquared(in.vehicle.Position.xy, out.vehicle.Position.xy)
|
||||||
|
val timeSpent = out.time - in.time
|
||||||
|
timeSpent >= 210000 /* 3:30 */ || distanceMoved > 640000f
|
||||||
|
}) =>
|
||||||
|
out
|
||||||
|
}.toList
|
||||||
|
//apply
|
||||||
|
dismountActivity
|
||||||
|
.groupBy { a => a.vehicle.owner }
|
||||||
|
.collect { case (Some(owner), dismountsFromVehicle) =>
|
||||||
|
val numberOfDismounts = dismountsFromVehicle.size
|
||||||
|
contributeWithCombinedActivity(
|
||||||
|
owner.CharId,
|
||||||
|
dismountsFromVehicle.map { act => WeaponStats(act.vehicle.Definition.ObjectId, 0, numberOfDismounts, act.time, 15f) },
|
||||||
|
dismountsFromVehicle.head.vehicle.owner.get,
|
||||||
|
amount = 0,
|
||||||
|
total = 0,
|
||||||
|
numberOfDismounts,
|
||||||
|
dismountsFromVehicle.maxBy(_.time).time,
|
||||||
|
participants
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dismountActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private def contributeWithRevivalActivity(
|
private def contributeWithRevivalActivity(
|
||||||
|
|
@ -251,11 +339,22 @@ object KillContributions {
|
||||||
case (t, terminal, Some(info)) if terminal.Faction != faction =>
|
case (t, terminal, Some(info)) if terminal.Faction != faction =>
|
||||||
participants.getOrElseUpdate(
|
participants.getOrElseUpdate(
|
||||||
info.player.CharId,
|
info.player.CharId,
|
||||||
ContributionStats(info.player, Seq(WeaponStats(0, 1, 1, t.time, 10f)), 1, 1, 1, t.time)
|
ContributionStats(info.player, Seq(WeaponStats(terminal.Definition.ObjectId, 0, 1, t.time, 10f)), 0, 0, 1, t.time)
|
||||||
)
|
)
|
||||||
t
|
t
|
||||||
case (t, terminal, _) =>
|
case (t, terminal, _) =>
|
||||||
extractContributionsForEntityByUser(faction, terminal, contributions, participants)
|
terminal.installation match {
|
||||||
|
case v: VehicleSource =>
|
||||||
|
v.owner.collect {
|
||||||
|
owner =>
|
||||||
|
participants.getOrElseUpdate(
|
||||||
|
owner.CharId,
|
||||||
|
ContributionStats(owner, Seq(WeaponStats(terminal.Definition.ObjectId, 0, 1, t.time, 10f)), 0, 0, 1, t.time)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
extractContributionsForEntityByUser(faction, terminal, contributions, participants)
|
||||||
|
}
|
||||||
t
|
t
|
||||||
}.toList
|
}.toList
|
||||||
}
|
}
|
||||||
|
|
@ -317,32 +416,33 @@ object KillContributions {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def composeContributionOutput(
|
private def composeContributionOutput(
|
||||||
charId: Long,
|
player: PlayerSource,
|
||||||
shortPeriod: mutable.LongMap[ContributionStats],
|
shortPeriod: mutable.LongMap[ContributionStats],
|
||||||
longPeriod: mutable.LongMap[ContributionStats],
|
longPeriod: mutable.LongMap[ContributionStats],
|
||||||
bep: Long
|
bep: Long
|
||||||
): (Long, ContributionStatsOutput) = {
|
): (Long, ContributionStatsOutput) = {
|
||||||
shortPeriod
|
val charId = player.CharId
|
||||||
|
longPeriod
|
||||||
.get(charId)
|
.get(charId)
|
||||||
.collect {
|
.collect {
|
||||||
case entry =>
|
case entry =>
|
||||||
val weapons = entry.weapons
|
val weapons = entry.weapons
|
||||||
(
|
(
|
||||||
entry.player,
|
entry.player,
|
||||||
weapons.map { _.equipment_id },
|
weapons.filter { _.amount == 0 }.map { _.equipment_id },
|
||||||
math.min(weapons.foldLeft(0f)(_ + _.contributions).toLong, bep)
|
(0.9f * math.min(weapons.foldLeft(0f)(_ + _.contributions), bep.toFloat)).toLong
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
longPeriod
|
shortPeriod
|
||||||
.get(charId)
|
.get(charId)
|
||||||
.collect {
|
.collect {
|
||||||
case entry =>
|
case entry =>
|
||||||
val weapons = entry.weapons
|
val weapons = entry.weapons
|
||||||
(
|
(
|
||||||
entry.player,
|
entry.player,
|
||||||
weapons.filter { _.amount == 0 }.map { _.equipment_id },
|
weapons.map { _.equipment_id },
|
||||||
(0.9f * math.min(weapons.foldLeft(0f)(_ + _.contributions), bep.toFloat)).toLong
|
math.min(weapons.foldLeft(0f)(_ + _.contributions).toLong, bep)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -350,7 +450,7 @@ object KillContributions {
|
||||||
Some((PlayerSource.Nobody, Seq(0), 0L))
|
Some((PlayerSource.Nobody, Seq(0), 0L))
|
||||||
}
|
}
|
||||||
.collect {
|
.collect {
|
||||||
case (player, weaponIds, experience) =>
|
case (player, weaponIds, experience) if experience > 0 =>
|
||||||
(charId, ContributionStatsOutput(player, weaponIds, experience.toFloat))
|
(charId, ContributionStatsOutput(player, weaponIds, experience.toFloat))
|
||||||
}
|
}
|
||||||
.get
|
.get
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource, SourceEntry}
|
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, ReconstructionActivity, RepairFromEquipment, RepairFromExoSuitChange, RevivingActivity, SpawningActivity, SupportActivityCausedByAnother, TerminalUsedActivity}
|
import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, ReconstructionActivity, RepairFromEquipment, RepairFromExoSuitChange, RevivingActivity, SpawningActivity, SupportActivityCausedByAnother, TerminalUsedActivity}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
|
import net.psforever.objects.zones.exp.KillAssists.calculateMenace
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, TransactionType}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, TransactionType}
|
||||||
import net.psforever.zones.Zones
|
import net.psforever.zones.Zones
|
||||||
|
|
||||||
|
|
@ -18,6 +19,33 @@ import scala.collection.mutable
|
||||||
object Support {
|
object Support {
|
||||||
private type SupportActivity = InGameActivity with SupportActivityCausedByAnother
|
private type SupportActivity = InGameActivity with SupportActivityCausedByAnother
|
||||||
|
|
||||||
|
private[exp] def baseExperience(
|
||||||
|
victim: PlayerSource,
|
||||||
|
history: Iterable[InGameActivity]
|
||||||
|
): Long = {
|
||||||
|
val lifespan = (history.headOption, history.lastOption) match {
|
||||||
|
case (Some(spawn), Some(death)) => death.time - spawn.time
|
||||||
|
case _ => 0L
|
||||||
|
}
|
||||||
|
val wasEverAMax = Support.wasEverAMax(victim, history)
|
||||||
|
val base = if (wasEverAMax) { //shamed
|
||||||
|
250L
|
||||||
|
} else if (victim.Seated || victim.progress.kills.nonEmpty) {
|
||||||
|
100L
|
||||||
|
} else if (lifespan > 15000L) {
|
||||||
|
50L
|
||||||
|
} else {
|
||||||
|
1L
|
||||||
|
}
|
||||||
|
if (base > 1) {
|
||||||
|
//black ops modifier
|
||||||
|
//TODO x10
|
||||||
|
base
|
||||||
|
} else {
|
||||||
|
base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private[exp] def onlyOriginalAssistEntries(
|
private[exp] def onlyOriginalAssistEntries(
|
||||||
first: mutable.LongMap[ContributionStatsOutput],
|
first: mutable.LongMap[ContributionStatsOutput],
|
||||||
second: mutable.LongMap[ContributionStatsOutput],
|
second: mutable.LongMap[ContributionStatsOutput],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.zones.exp
|
||||||
|
|
||||||
|
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
||||||
|
import akka.actor.typed.{Behavior, SupervisorStrategy}
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.avatar.scoring.Kill
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
|
import net.psforever.objects.vital.{InGameActivity, InGameHistory}
|
||||||
|
import net.psforever.objects.zones.Zone
|
||||||
|
|
||||||
|
object SupportExperienceCalculator {
|
||||||
|
def apply(zone: Zone): Behavior[Command] =
|
||||||
|
Behaviors.supervise[Command] {
|
||||||
|
Behaviors.setup(context => new SupportExperienceCalculator(context, zone))
|
||||||
|
}.onFailure[Exception](SupervisorStrategy.restart)
|
||||||
|
|
||||||
|
sealed trait Command
|
||||||
|
|
||||||
|
final case class RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command
|
||||||
|
|
||||||
|
object RewardOurSupporters {
|
||||||
|
def apply(obj: PlanetSideGameObject with FactionAffinity with InGameHistory, kill: Kill): RewardOurSupporters = {
|
||||||
|
RewardOurSupporters(SourceEntry(obj), obj.History, kill, kill.experienceEarned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SupportExperienceCalculator(context: ActorContext[SupportExperienceCalculator.Command], zone: Zone)
|
||||||
|
extends AbstractBehavior[SupportExperienceCalculator.Command](context) {
|
||||||
|
|
||||||
|
import SupportExperienceCalculator._
|
||||||
|
|
||||||
|
def onMessage(msg: Command): Behavior[Command] = {
|
||||||
|
msg match {
|
||||||
|
case RewardOurSupporters(target: PlayerSource, history, kill, bep) =>
|
||||||
|
KillContributions.rewardTheseSupporters(target, history, kill, bep, zone.AvatarEvents)
|
||||||
|
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
// Copyright (c) 2023 PSForever
|
|
||||||
package net.psforever.objects.zones.exp
|
|
||||||
|
|
||||||
import net.psforever.objects.vital.InGameActivity
|
|
||||||
|
|
||||||
final case class VitalsHistoryException(
|
|
||||||
head: InGameActivity, //InGameActivity might be more suitable?
|
|
||||||
private val message: String = "",
|
|
||||||
private val cause: Throwable = None.orNull
|
|
||||||
) extends Exception(message, cause)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue