importing controlled implementation changes from original exp-for-kda branch; assist kill experience rewarded

This commit is contained in:
Fate-JH 2023-03-25 23:29:03 -04:00
parent 2f787a467f
commit 6774edf5b6
11 changed files with 403 additions and 49 deletions

View file

@ -6,7 +6,7 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
import java.util.concurrent.atomic.AtomicInteger
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.scoring.{Death, EquipmentStat, KDAStat, Kill}
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill}
import org.joda.time.{LocalDateTime, Seconds}
import scala.collection.mutable
import scala.concurrent.{ExecutionContextExecutor, Future, Promise}
@ -2953,6 +2953,8 @@ class AvatarActor(
zone.actor ! ZoneActor.RewardOurSupporters(playerSource, obj.History, kill, exp)
}
zone.actor ! ZoneActor.RewardOurSupporters(playerSource, player.History, kill, exp)
case _: Assist =>
()
case _: Death =>
zone.AvatarEvents ! AvatarServiceMessage(
player.Name,

View file

@ -398,6 +398,9 @@ class SessionAvatarHandlers(
case AvatarResponse.UpdateKillsDeathsAssists(_, kda) =>
avatarActor ! AvatarActor.UpdateKillsDeathsAssists(kda)
case AvatarResponse.AwardSupportBep(_, bep) =>
avatarActor ! AvatarActor.AwardBep(bep, ExperienceType.Support)
case AvatarResponse.SendResponse(msg) =>
sendResponse(msg)

View file

@ -154,7 +154,7 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
experience ! ExperienceCalculator.RewardThisDeath(entity)
case RewardOurSupporters(target, history, kill, bep) =>
()
experience ! ExperienceCalculator.RewardOurSupporters(target, history, kill, bep)
case ZoneMapUpdate() =>
zone.Buildings
@ -162,7 +162,6 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
.values
.foreach(_.Actor ! BuildingActor.MapUpdate())
}
this
}
}

View file

@ -10,6 +10,8 @@ import net.psforever.objects.equipment.EquipmentSlot
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.loadouts.InfantryLoadout
import net.psforever.objects.sourcing.PlayerSource
import net.psforever.objects.vital.RevivingActivity
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.types.{ChatMessageType, ExoSuitType, Vector3}
@ -64,7 +66,7 @@ object Players {
val name = target.Name
val medicName = medic.Name
log.info(s"$medicName had revived $name")
//target.History(PlayerRespawn(PlayerSource(target), target.Zone, target.Position, Some(PlayerSource(medic))))
target.LogActivity(RevivingActivity(PlayerSource(target), PlayerSource(medic), target.MaxHealth, item.Definition))
val magazine = item.Discharge(Some(25))
target.Zone.AvatarEvents ! AvatarServiceMessage(
medicName,

View file

@ -28,13 +28,25 @@ trait InGameActivity {
*/
trait GeneralActivity extends InGameActivity
final case class SpawningActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry]) extends GeneralActivity
trait SupportActivityCausedByAnother {
def user: PlayerSource
def amount: Int
}
final case class ReconstructionActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry]) extends GeneralActivity
final case class SpawningActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry])
extends GeneralActivity
final case class ShieldCharge(amount: Int, cause: Option[SourceEntry]) extends GeneralActivity
final case class ReconstructionActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry])
extends GeneralActivity
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value) extends GeneralActivity
final case class RevivingActivity(target: SourceEntry, user: PlayerSource, amount: Int, equipment: EquipmentDefinition)
extends GeneralActivity with SupportActivityCausedByAnother
final case class ShieldCharge(amount: Int, cause: Option[SourceEntry])
extends GeneralActivity
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value)
extends GeneralActivity
/* vitals history */
@ -76,7 +88,7 @@ final case class HealFromKit(kit_def: KitDefinition, amount: Int)
extends HealingActivity
final case class HealFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
extends HealingActivity
extends HealingActivity with SupportActivityCausedByAnother
final case class HealFromTerm(term: AmenitySource, amount: Int)
extends HealingActivity
@ -91,7 +103,7 @@ final case class RepairFromKit(kit_def: KitDefinition, amount: Int)
extends RepairingActivity()
final case class RepairFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
extends RepairingActivity
extends RepairingActivity with SupportActivityCausedByAnother
final case class RepairFromTerm(term: AmenitySource, amount: Int) extends RepairingActivity

View file

@ -7,7 +7,7 @@ import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.avatar.scoring.{Assist, Death, Kill}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.{DamagingActivity, HealingActivity, InGameActivity, InGameHistory, ReconstructionActivity, RepairFromExoSuitChange, RepairingActivity, SpawningActivity}
import net.psforever.objects.vital.{DamagingActivity, HealingActivity, InGameActivity, InGameHistory, ReconstructionActivity, RepairFromExoSuitChange, RepairingActivity, RevivingActivity, SpawningActivity}
import net.psforever.objects.vital.interaction.{Adversarial, DamageResult}
import net.psforever.objects.zones.Zone
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -33,6 +33,36 @@ object ExperienceCalculator {
}
}
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 limitHistoryToThisLife(history: List[InGameActivity]): List[InGameActivity] = {
val spawnIndex = history.lastIndexWhere {
case _: SpawningActivity => true
case _: RevivingActivity => true
case _ => false
}
val endIndex = history.lastIndexWhere {
case damage: DamagingActivity => damage.data.targetAfter.asInstanceOf[PlayerSource].Health == 0
case _ => false
}
if (spawnIndex == -1 || endIndex == -1) {
Nil //throw VitalsHistoryException(history.head, "vitals history does not contain expected conditions")
// } 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 {
history.slice(spawnIndex, endIndex)
}
}
def calculateExperience(
victim: PlayerSource,
history: Iterable[InGameActivity]
@ -64,6 +94,38 @@ object ExperienceCalculator {
base
}
}
def onlyOriginalAssistEntries(
first: mutable.LongMap[ContributionStatsOutput],
second: mutable.LongMap[ContributionStatsOutput]
): Iterable[ContributionStatsOutput] = {
onlyOriginalAssistEntriesIterable(first.values, second.values)
}
def onlyOriginalAssistEntriesIterable(
first: Iterable[ContributionStatsOutput],
second: Iterable[ContributionStatsOutput]
): Iterable[ContributionStatsOutput] = {
if (second.isEmpty) {
first
} else if (first.isEmpty) {
second
} else {
//overlap discriminated by percentage
val shared: mutable.LongMap[ContributionStatsOutput] = mutable.LongMap[ContributionStatsOutput]()
for {
h @ ContributionStatsOutput(hid, hwep, hkda) <- first
a @ ContributionStatsOutput(aid, awep, akda) <- second
(id, out) = if (hkda < akda)
(aid.CharId, a.copy(implements = (a.implements ++ hwep).distinct))
else
(hid.CharId, h.copy(implements = (h.implements ++ awep).distinct))
if hid == aid && shared.put(id, out).isEmpty
} yield ()
val sharedKeys = shared.keys
(first ++ second).filterNot { case ContributionStatsOutput(id, _, _) => sharedKeys.exists(_ == id.CharId) } ++ shared.values
}
}
}
class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command], zone: Zone)
@ -74,13 +136,10 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
def onMessage(msg: Command): Behavior[Command] = {
msg match {
case RewardThisDeath(victim: PlayerSource, lastDamage, history) =>
rewardThisPlayerDeath(
victim,
lastDamage,
limitHistoryToThisLife(history.toList)
)
case _ =>
()
rewardThisPlayerDeath(victim, lastDamage, history)
case RewardOurSupporters(target, history, kill, bep) =>
rewardTheseSupporters(target, history.toList, kill, bep)
case _ => ()
}
Behaviors.same
}
@ -88,24 +147,25 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
def rewardThisPlayerDeath(
victim: PlayerSource,
lastDamage: Option[DamageResult],
history: List[InGameActivity]
history: Iterable[InGameActivity]
): Unit = {
val everyone = determineKiller(lastDamage, history) match {
val shortHistory = ExperienceCalculator.limitHistoryToThisLife(history.toList)
val everyone = determineKiller(lastDamage, shortHistory) match {
case Some((result, killer: PlayerSource)) =>
val assists = collectAssistsForPlayer(victim, history, Some(killer))
val fullBep = KillDeathAssists.calculateExperience(killer, victim, history)
val assists = collectAssistsForPlayer(victim, shortHistory, Some(killer))
val fullBep = KillDeathAssists.calculateExperience(killer, victim, shortHistory)
val hitSquad = (killer, Kill(victim, result, fullBep)) +: assists.map {
case ContributionStatsOutput(p, w, r) => (p, Assist(victim, w, r, (fullBep * r).toLong))
}.toSeq
(victim, Death(hitSquad.map { _._1 }, history.last.time - history.head.time, fullBep)) +: hitSquad
(victim, Death(hitSquad.map { _._1 }, shortHistory.last.time - shortHistory.head.time, fullBep)) +: hitSquad
case _ =>
val assists = collectAssistsForPlayer(victim, history, None)
val fullBep = ExperienceCalculator.calculateExperience(victim, history)
val assists = collectAssistsForPlayer(victim, shortHistory, None)
val fullBep = ExperienceCalculator.calculateExperience(victim, shortHistory)
val hitSquad = assists.map {
case ContributionStatsOutput(p, w, r) => (p, Assist(victim, w, r, (fullBep * r).toLong))
}.toSeq
(victim, Death(hitSquad.map { _._1 }, history.last.time - history.head.time, fullBep)) +: hitSquad
(victim, Death(hitSquad.map { _._1 }, shortHistory.last.time - shortHistory.head.time, fullBep)) +: hitSquad
}
val events = zone.AvatarEvents
everyone.foreach { case (p, kda) =>
@ -113,27 +173,6 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
}
}
def limitHistoryToThisLife(history: List[InGameActivity]): List[InGameActivity] = {
val spawnIndex = history.indexWhere {
case SpawningActivity(_, _, _) => true
case _ => false
}
val endIndex = history.lastIndexWhere {
case damage: DamagingActivity => damage.data.targetAfter.asInstanceOf[PlayerSource].Health == 0
case _ => false
}
if (spawnIndex == -1 || endIndex == -1) {
Nil //throw VitalsHistoryException(history.head, "vitals history does not contain expected conditions")
// } 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 {
history.slice(spawnIndex, endIndex)
}
}
def determineKiller(lastDamageActivity: Option[DamageResult], history: List[InGameActivity]): Option[(DamageResult, SourceEntry)] = {
val now = System.currentTimeMillis()
val compareTimeMillis = 10.seconds.toMillis
@ -254,7 +293,7 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
mod.copy(
amount = mod.amount + amount,
weapons = weapons,
totalDamage = mod.totalDamage + amount,
total = mod.total + amount,
shots = mod.shots + 1,
time = activity.time
)
@ -333,4 +372,30 @@ class ExperienceCalculator(context: ActorContext[ExperienceCalculator.Command],
}
newOrder ++ order.drop(count)
}
private def rewardTheseSupporters(
target: SourceEntry,
history: List[InGameActivity],
kill: Kill,
bep: Long
): Unit = {
val time = kill.time
val normalAssists = ExperienceCalculator.onlyOriginalAssistEntries(
Support.collectHealingSupportAssists(target, time, history),
Support.collectRepairingSupportAssists(target, time, history)
)
// retainedSupportAssists.get(target.unique) match {
// case Some(support) =>
// ExperienceCalculator.onlyOriginalAssistEntriesIterable(normalAssists, support.assists)
// case None =>
// normalAssists
// }
val events = zone.AvatarEvents
normalAssists.foreach { case ContributionStatsOutput(p, _, ratio) =>
events ! AvatarServiceMessage(p.Name, AvatarAction.AwardSupportBep(p.CharId, (ratio * bep).toLong))
}
Support.collectTerminalSupportAssists(target, history).foreach { case ContributionStatsOutput(p, _, reward) =>
events ! AvatarServiceMessage(p.Name, AvatarAction.AwardSupportBep(p.CharId, reward.toLong))
}
}
}

View file

@ -14,7 +14,7 @@ private case class ContributionStats(
player: PlayerSource,
weapons: Seq[WeaponStats],
amount: Int,
totalDamage: Int,
total: Int,
shots: Int,
time: Long
)

View file

@ -0,0 +1,260 @@
// Copyright (c) 2023 PSForever
package net.psforever.objects.zones.exp
import net.psforever.objects.GlobalDefinitions
import java.util.Date
import org.joda.time.{Instant, LocalDateTime => JodaLocalDateTime}
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.sourcing.{AmenitySource, PlayerSource, SourceEntry}
import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, RepairFromEquipment, RevivingActivity, SpawningActivity, SupportActivityCausedByAnother, TerminalUsedActivity}
import net.psforever.objects.zones.Zone
import net.psforever.types.TransactionType
import net.psforever.zones.Zones
import scala.collection.mutable
object Support {
private type SupportActivity = InGameActivity with SupportActivityCausedByAnother
private[exp] def collectHealingSupportAssists(
target: SourceEntry,
time: JodaLocalDateTime,
history: Iterable[InGameActivity]
): mutable.LongMap[ContributionStatsOutput] = {
//normal heals
val healInfo = collectSupportContributions(
target, time, ExperienceCalculator.limitHistoryToThisLife(history.toList).collect { case heals: HealFromEquipment
if heals.amount > 0 && heals.user.unique != target.unique => (heals, heals.equipment_def.ObjectId)
}, mapContributionPointsByPercentage
)
//revivals (count all revivals performed by the player of the last one, if any)
val reviveInfo = collectSupportContributions(
target, time, history.collect { case revive: RevivingActivity
if revive.user.unique != target.unique => (revive, revive.equipment.ObjectId)
}.lastOption.toSeq, mapContributionPointsByCount
)
//combine and output
reviveInfo.foreach { case (reviverId, contribution) =>
val result = healInfo.get(reviverId)
val percentage = result.map { a => a.percentage }.getOrElse { contribution.percentage }
val implements = result.map { a => (a.implements ++ contribution.implements).distinct }.getOrElse { contribution.implements }
healInfo.put(reviverId, ContributionStatsOutput(contribution.player, implements, percentage))
}
healInfo
}
private[exp] def collectRepairingSupportAssists(
target: SourceEntry,
time: JodaLocalDateTime,
history: Iterable[InGameActivity]
): mutable.LongMap[ContributionStatsOutput] = {
collectSupportContributions(
target, time, ExperienceCalculator.limitHistoryToThisLife(history.toList).collect { case repairs: RepairFromEquipment
if repairs.amount > 0 && repairs.user.unique != target.unique => (repairs, repairs.equipment_def.ObjectId)
}, mapContributionPointsByPercentage
)
}
private[exp] def collectTerminalSupportAssists(
target: SourceEntry,
history: List[InGameActivity]
): Iterable[ContributionStatsOutput] = {
val delay: Long = 300000L
val curr = System.currentTimeMillis()
var zone: Zone = Zone.Nowhere
val termsUsed: mutable.LongMap[Option[AmenitySource]] = mutable.LongMap[Option[AmenitySource]]()
val credit: mutable.LongMap[ContributionStatsOutput] = mutable.LongMap[ContributionStatsOutput]()
val faction = target.Faction
val terminalUseHistory = history.filter {
case _: SpawningActivity => true
case entry: TerminalUsedActivity => curr - entry.time < delay
case _ => false
}
terminalUseHistory.collect {
case entry: SpawningActivity =>
zone = Zones.zones(entry.zoneNumber)
case entry: TerminalUsedActivity
if !termsUsed.contains(entry.terminal.unique.guid.guid.toLong) =>
if (
entry.transaction == TransactionType.Loadout ||
entry.transaction == TransactionType.Buy ||
entry.transaction == TransactionType.Learn
) {
val terminal = entry.terminal
val guid = terminal.unique.guid.guid
terminal.hacked.collect {
case Hackable.HackInfo(player, _, _, _)
if target.CharId != player.CharId && faction == player.Faction =>
//accessed a hacked terminal
termsUsed.put(guid.toLong, Some(terminal))
addTerminalContributionEntry(credit, player, Seq(GlobalDefinitions.remote_electronics_kit.ObjectId), percentage = 1f)
case _
if terminal.Faction == faction =>
//accessed a faction-friendly terminal; check log for repair history
termsUsed.put(guid.toLong, Some(terminal))
zone.GUID(guid)
.asInstanceOf[Terminal]
.History
.collect { case a: RepairFromEquipment =>
val user = a.user
addTerminalContributionEntry(credit, user, Seq(a.equipment_def.ObjectId), percentage = 0.5f)
}
case _ =>
//what is this?
termsUsed.put(guid.toLong, None)
}
}
}
credit.remove(target.CharId)
credit.values
}
private def addTerminalContributionEntry(
contributions: mutable.LongMap[ContributionStatsOutput],
player: PlayerSource,
implements: Seq[Int],
percentage: Float
): Unit = {
val charId = player.CharId
contributions.get(charId) match {
case Some(entry) if percentage > entry.percentage =>
contributions.put(charId, entry.copy(percentage = percentage))
case Some(entry) =>
contributions.put(charId, entry.copy(
implements = (entry.implements ++ implements).distinct,
percentage = entry.percentage + 0.05f
))
case None =>
contributions.put(charId, ContributionStatsOutput(player, implements, percentage))
}
}
private def findTimeApplicableActivities(
activity: Seq[SupportActivity],
killLastTime: JodaLocalDateTime,
timeOffset: Int //s
): Seq[SupportActivity] = {
val dateTimeConverter: Date=>JodaLocalDateTime = JodaLocalDateTime.fromDateFields
val milliToInstant: Long=>org.joda.time.Instant = Instant.ofEpochMilli
val targetTime = killLastTime.minusSeconds(timeOffset)
//find all activities that occurred after the kill time after the offset is considered
activity
.filter { a =>
val activityTime = dateTimeConverter(milliToInstant(a.time).toDate)
targetTime.isBefore(activityTime) || targetTime.equals(activityTime)
}
.sortBy(_.time)(Ordering.Long.reverse)
}
private def allocateSupportAssists(
longTerm: Seq[SupportActivity],
shortTerm: Seq[SupportActivity],
defaultTool: Int
): mutable.LongMap[ContributionStats] = {
val contributions: mutable.LongMap[ContributionStats] = mutable.LongMap[ContributionStats]()
(longTerm ++ shortTerm).foreach { h =>
val amount = h.amount
val user = h.user
val charId = user.CharId
contributions.get(charId) match {
case Some(entry) =>
contributions.update(charId, entry.copy(
amount = entry.amount + amount,
total = entry.total + amount,
shots = entry.shots + 1,
time = math.max(entry.time, h.time)
))
case None =>
contributions.put(charId, ContributionStats(user, Seq(WeaponStats(defaultTool, amount, 1, h.time)), amount, amount, 1, h.time))
}
}
contributions
}
private def collectSupportContributions(
target: SourceEntry,
time: JodaLocalDateTime,
pairs: Seq[(InGameActivity, Int)],
contributionPointsMap: (Float,Iterable[SupportActivity])=>(Long,ContributionStats)=>(Long,ContributionStatsOutput)
): mutable.LongMap[ContributionStatsOutput] = {
val (activity, tools) = pairs.unzip
//TODO this is not correct, but it will do for now
if (tools.isEmpty) {
mutable.LongMap[ContributionStatsOutput]()
} else if (tools.distinct.size == 1) {
collectSupportAssists(target, time, activity, tools.head, contributionPointsMap)
} else {
var output = Iterable[ContributionStatsOutput]()
tools.groupBy(identity).keys.foreach { implement =>
output = ExperienceCalculator.onlyOriginalAssistEntriesIterable(
output,
collectSupportAssists(target, time, activity, implement, contributionPointsMap).values
)
}
mutable.LongMap[ContributionStatsOutput]().addAll(output.map { entry => (entry.player.CharId, entry) })
}
}
private def collectSupportAssists(
target: SourceEntry,
killLastTime: JodaLocalDateTime,
activity: Seq[InGameActivity],
defaultImplement: Int,
contributionPointsMap: (Float,Iterable[SupportActivity])=>(Long,ContributionStats)=>(Long,ContributionStatsOutput)
): mutable.LongMap[ContributionStatsOutput] = {
val activityByAnother = activity.collect {
case activity: SupportActivity => activity
}
val longTermActivity = findTimeApplicableActivities(activityByAnother, killLastTime, timeOffset = 600)
val shortTermActivity = findTimeApplicableActivities(activityByAnother, killLastTime, timeOffset = 300)
val contributions = allocateSupportAssists(longTermActivity, shortTermActivity, defaultImplement)
contributions.remove(target.CharId)
val mapFunc = contributionPointsMap(contributions.values.foldLeft(0)(_ + _.total).toFloat, shortTermActivity)
contributions.map { case (a, b) => mapFunc(a, b) }
}
private def mapContributionPointsByPercentage(
total: Float,
compareList: Iterable[SupportActivity]
)
(
charId: Long,
contribution: ContributionStats,
): (Long, ContributionStatsOutput) = {
val user = contribution.player
val unique = user.unique
val points = contribution.amount
val value = if (points < 75) {
//a small contribution means the lower time limit
if (compareList.exists { a => a.user.unique == unique }) {
math.max(0.2f, points / total)
} else {
0f
}
} else {
//large contribution is always okay
if (points > 299) {
1.0f
} else if (points > 100) {
0.75f
} else {
0.5f
}
}
(charId, ContributionStatsOutput(user, contribution.weapons.map { _.weapon_id }, value))
}
//noinspection ScalaUnusedSymbol
private def mapContributionPointsByCount(
total: Float,
compareList: Iterable[SupportActivity]
)
(
charId: Long,
contribution: ContributionStats,
): (Long, ContributionStatsOutput) = {
(charId, ContributionStatsOutput(contribution.player, contribution.weapons.map { _.weapon_id }, compareList.size.toFloat))
}
}

View file

@ -438,6 +438,15 @@ class AvatarService(zone: Zone) extends Actor {
)
)
case AvatarAction.AwardSupportBep(charId, bep) =>
AvatarEvents.publish(
AvatarServiceResponse(
s"/$forChannel/Avatar",
Service.defaultPlayerGUID,
AvatarResponse.AwardSupportBep(charId, bep)
)
)
case _ => ;
}

View file

@ -156,6 +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 TeardownConnection() extends Action
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action

View file

@ -127,4 +127,5 @@ 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
}