diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index 951fbb467..9a011532a 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -398,21 +398,80 @@ class SessionAvatarHandlers( case AvatarResponse.UpdateKillsDeathsAssists(_, kda) => avatarActor ! AvatarActor.UpdateKillsDeathsAssists(kda) - case AvatarResponse.AwardSupportBep(_, bep) => - avatarActor ! AvatarActor.AwardBep(bep, ExperienceType.Support) + case AvatarResponse.AwardBep(_, bep, expType) => + avatarActor ! AvatarActor.AwardBep(bep, expType) case AvatarResponse.AwardCep(0, cep) => - //must lead a squad to be awarded CEP + //must be in a squad to earn experience + val id = player.CharId val squadUI = sessionData.squad.squadUI + val participation = continent + .Buildings + .values + .collect { case building if { + val soi = building.Definition.SOIRadius * building.Definition.SOIRadius + val pos = player.Position.xy + Vector3.DistanceSquared(building.Position.xy, pos) < soi + } => + building.Participation.PlayerContribution() + } squadUI - .find { _._1 == avatar.id } + .find { _._1 == id } .collect { case (_, elem) if elem.index == 0 => - val thisZone = continent.Number - val squadSize = squadUI.count { case (_, e) => e.zone == thisZone } -1 - val maxCepList = Config.app.game.maximumCepPerSquadSize - val maxRate = maxCepList.lift(squadSize).getOrElse(squadSize * maxCepList.head).toLong - avatarActor ! AvatarActor.AwardCep(math.min(cep, maxRate)) + //squad leader earns CEP, modified by squad effort + val maxRate: Long = { + val maxCepList = Config.app.game.maximumCepPerSquadSize + val squadSize: Int = { + val squadSizeList: Iterable[Int] = participation + .map { facilityMap => + squadUI.count { case (id, _) => facilityMap.contains(id) } + } + if (squadSizeList.nonEmpty) { + squadSizeList.max + } else { + 0 + } + } + maxCepList.lift(squadSize - 1).getOrElse(squadSize * maxCepList.head).toLong + } + val groupContribution: Float = { + val eachSquadMemberParticipation: Iterable[Float] = participation.map { facilityMap => + val foundSquadMemberParticipation: Iterable[Float] = squadUI + .keys + .flatMap { facilityMap.get } + if (foundSquadMemberParticipation.nonEmpty) { + foundSquadMemberParticipation.sum / 10f + } else { + 0f + } + } + if (eachSquadMemberParticipation.nonEmpty) { + eachSquadMemberParticipation.max + } else { + 0 + } + } + val modifiedExp: Long = math.min((cep.toFloat * groupContribution).toLong, maxRate) + avatarActor ! AvatarActor.AwardCep(modifiedExp) + Some(modifiedExp) + + case _ => + //squad member earns BEP based on CEP, modified by personal effort + val individualContribution = { + val contributionList = for { + facilityMap <- participation + if facilityMap.contains(id) + } yield facilityMap(id) + if (contributionList.nonEmpty) { + contributionList.max + } else { + 0f + } + } + val modifiedExp = (cep * individualContribution).toLong + avatarActor ! AvatarActor.AwardBep(modifiedExp, ExperienceType.Normal) + Some(modifiedExp) } case AvatarResponse.AwardCep(charId, cep) => diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala index ea03fb043..b643c36ab 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala @@ -17,6 +17,15 @@ trait FacilityHackParticipation extends ParticipationLogic { protected var playerContribution: mutable.LongMap[(Player, Int, Long)] = mutable.LongMap[(Player, Int, Long)]() protected var playerPopulationOverTime: Seq[Map[PlanetSideEmpire.Value, Int]] = Seq[ Map[PlanetSideEmpire.Value, Int]]() + def PlayerContribution(timeDelay: Long): Map[Long, Float] = { + playerContribution + .collect { + case (id, (_, d, _)) if d < timeDelay => (id, d.toFloat / timeDelay.toFloat) + case (id, (_, _, _)) => (id, 1.0f) + } + .toMap[Long, Float] + } + protected def updatePlayers(list: List[Player]): Unit = { val hackTime = building.CaptureTerminal.get.Definition.FacilityHackTime.toMillis val curr = System.currentTimeMillis() diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/NoParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/NoParticipation.scala index e1a565e95..2d758368d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/NoParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/NoParticipation.scala @@ -16,4 +16,5 @@ case object NoParticipation extends ParticipationLogic { completionTime: Long, isResecured: Boolean ): Unit = { /* nothing here */ } + override def PlayerContribution(timeDelay: Long): Map[Long, Float] = Map.empty[Long, Float] } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/ParticipationLogic.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/ParticipationLogic.scala index 0c9b57d32..8e91e3441 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/ParticipationLogic.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/ParticipationLogic.scala @@ -30,4 +30,6 @@ trait ParticipationLogic { completionTime: Long, isResecured: Boolean ): Unit + + def PlayerContribution(timeDelay: Long = 600): Map[Long, Float] } diff --git a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala index d1655265c..9b0cb0f0f 100644 --- a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala @@ -1,7 +1,7 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.vehicles -import akka.actor.ActorRef +import akka.actor.{ActorRef, Cancellable} import net.psforever.actors.commands.NtuCommand import net.psforever.actors.zone.BuildingActor import net.psforever.objects.serverobject.deploy.Deployment @@ -9,21 +9,23 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.serverobject.transfer.{TransferBehavior, TransferContainer} import net.psforever.objects.{NtuContainer, _} -import net.psforever.types.DriveState +import net.psforever.types.{DriveState, ExperienceType} import net.psforever.services.Service import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import akka.actor.typed.scaladsl.adapter._ +import net.psforever.objects.serverobject.transfer.TransferContainer.TransferMaterial +import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { var panelAnimationFunc: () => Unit = NoCharge - var ntuChargingTick = Default.Cancellable + var ntuChargingTick: Cancellable = Default.Cancellable findChargeTargetFunc = Vehicles.FindANTChargingSource findDischargeTargetFunc = Vehicles.FindANTDischargingTarget - def TransferMaterial = Ntu.Nanites + def TransferMaterial: TransferMaterial = Ntu.Nanites def ChargeTransferObject: Vehicle with NtuContainer @@ -138,6 +140,10 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { chargeable.NtuCapacitor -= chargeToDeposit UpdateNtuUI(chargeable) Ntu.Grant(chargeable, chargeToDeposit) + vehicle.Zone.AvatarEvents ! AvatarServiceMessage( + vehicle.OwnerName.getOrElse(""), + AvatarAction.AwardBep(0, 100L, ExperienceType.Normal) + ) } /** Stopping */ diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala index 68ae6f12c..53c50321d 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala @@ -438,12 +438,12 @@ class AvatarService(zone: Zone) extends Actor { ) ) - case AvatarAction.AwardSupportBep(charId, bep) => + case AvatarAction.AwardBep(charId, bep, expType) => AvatarEvents.publish( AvatarServiceResponse( s"/$forChannel/Avatar", Service.defaultPlayerGUID, - AvatarResponse.AwardSupportBep(charId, bep) + AvatarResponse.AwardBep(charId, bep, expType) ) ) diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala index 6775b8005..d66925062 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala @@ -12,7 +12,7 @@ import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.zones.Zone import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent} -import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} +import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} import scala.concurrent.duration.FiniteDuration @@ -156,7 +156,7 @@ object AvatarAction { final case class KitNotUsed(kit_guid: PlanetSideGUID, msg: String) extends Action final case class UpdateKillsDeathsAssists(charId: Long, kda: KDAStat) extends Action - final case class AwardSupportBep(charId: Long, bep: Long) extends Action + final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends Action final case class AwardCep(charId: Long, bep: Long) extends Action final case class TeardownConnection() extends Action diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala index 9c98c3622..af730abcd 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala @@ -11,7 +11,7 @@ import net.psforever.objects.sourcing.SourceEntry import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.packet.game.ObjectCreateMessage -import net.psforever.types.{ExoSuitType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} +import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} import net.psforever.services.GenericEventBusMsg final case class AvatarServiceResponse( @@ -127,6 +127,6 @@ object AvatarResponse { final case class KitNotUsed(kit_guid: PlanetSideGUID, msg: String) extends Response final case class UpdateKillsDeathsAssists(charId: Long, kda: KDAStat) extends Response - final case class AwardSupportBep(charId: Long, bep: Long) extends Response + final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends Response final case class AwardCep(charId: Long, bep: Long) extends Response }