diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index 1dd805e3e..b0899301e 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -3039,6 +3039,8 @@ class AvatarActor( if (exp > 0L) { setBep(avatar.bep + exp, msg) zone.actor ! ZoneActor.RewardOurSupporters(playerSource, historyTranscript, killStat, exp) + zone.AvatarEvents ! AvatarServiceMessage( + player.Name, AvatarAction.ShareKillExperienceWithSquad(player, exp)) } } diff --git a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala index 96ef7e81c..eb430a45b 100644 --- a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala @@ -473,6 +473,9 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A case AvatarResponse.FacilityCaptureRewards(buildingId, zoneNumber, cep) => ops.facilityCaptureRewards(buildingId, zoneNumber, cep) + case AvatarResponse.ShareKillExperienceWithSquad(killer, exp) => + ops.shareKillExperienceWithSquad(killer, exp) + case AvatarResponse.SendResponse(msg) => sendResponse(msg) 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 a85c2dcfe..37e2d03da 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -3,7 +3,7 @@ package net.psforever.actors.session.support import akka.actor.{ActorContext, typed} import net.psforever.objects.serverobject.mount.Mountable -import net.psforever.objects.{Default, PlanetSideGameObject} +import net.psforever.objects.{Default, PlanetSideGameObject, Player} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.objects.zones.exp @@ -123,6 +123,31 @@ class SessionAvatarHandlers( avatarActor ! AvatarActor.AwardFacilityCaptureBep(cep) } + /** + * + * @param killer the player who got the kill + * @param exp the amount of bep they received for the kill + * Squad members of a "killer" will receive a split of the experience if they are both alive + * and in the same zone as the killer. The amount received is + * based on the size of the squad. Each squad member that meets the criteria will receive a fractional split. + */ + def shareKillExperienceWithSquad(killer: Player, exp: Long): Unit = { + //TODO consider squad experience waypoint in exp calculation + val squadUI = sessionLogic.squad.squadUI + val squadSize = squadUI.size + if (squadSize > 1) { + val expSplit = exp / squadSize + val squadMembers = squadUI.filterNot(_._1 == killer.CharId).map { case (_, member) => member }.toList.map(_.name) + val playersInZone = killer.Zone.Players.map { avatar => (avatar.id, avatar.basic.name) } + val squadMembersHere = playersInZone.filter(member => squadMembers.contains(member._2)) + squadMembersHere.foreach { member => + killer.Zone.AvatarEvents ! AvatarServiceMessage( + member._2, + AvatarAction.AwardBep(member._1, expSplit, ExperienceType.Normal)) + } + } + } + /** * Properly format a `DestroyDisplayMessage` packet * given sufficient information about a target (victim) and an actor (killer). diff --git a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala index 63bf81700..c5747619e 100644 --- a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala +++ b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala @@ -182,7 +182,8 @@ class ZoneActor( case ZoneMapUpdate() => zone.Buildings - .filter(_._2.BuildingType == StructureType.Facility) + .filter(building => + building._2.BuildingType == StructureType.Facility || building._2.BuildingType == StructureType.Tower) .values .foreach(_.Actor ! BuildingActor.MapUpdate()) Behaviors.same diff --git a/src/main/scala/net/psforever/objects/zones/exp/Support.scala b/src/main/scala/net/psforever/objects/zones/exp/Support.scala index 0233426ca..1ad7ead6e 100644 --- a/src/main/scala/net/psforever/objects/zones/exp/Support.scala +++ b/src/main/scala/net/psforever/objects/zones/exp/Support.scala @@ -29,8 +29,13 @@ object Support { //setup val historyList = history.toList val withKills = victim.progress.kills.nonEmpty + //TODO Issue #1259 - Use another method to capture time of death than current time ("kill shots" aren't working) + /* val fullLifespan = (historyList.headOption, historyList.lastOption) match { case (Some(spawn), Some(death)) => death.time - spawn.time + */ + val fullLifespan = historyList.headOption match { + case Some(spawn) => System.currentTimeMillis() - spawn.time case _ => 0L } val recordOfWornTimes = countTimeWhileExoSuitOrMounted(historyList) diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala index d7e585f0a..f61e43949 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala @@ -458,6 +458,15 @@ class AvatarService(zone: Zone) extends Actor { ) ) + case AvatarAction.ShareKillExperienceWithSquad(killer, exp) => + AvatarEvents.publish( + AvatarServiceResponse( + s"/$forChannel/Avatar", + Service.defaultPlayerGUID, + AvatarResponse.ShareKillExperienceWithSquad(killer, exp) + ) + ) + case _ => () } diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala index ec2959b11..5cea9482d 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala @@ -160,6 +160,7 @@ object AvatarAction { 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 FacilityCaptureRewards(building_id: Int, zone_number: Int, exp: Long) extends Action + final case class ShareKillExperienceWithSquad(killer: Player, exp: Long) extends Action final case class TeardownConnection() extends Action // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) 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 897d105b7..5c26d9a02 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala @@ -132,4 +132,5 @@ object AvatarResponse { final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends Response final case class AwardCep(charId: Long, bep: Long) extends Response final case class FacilityCaptureRewards(building_id: Int, zone_number: Int, exp: Long) extends Response + final case class ShareKillExperienceWithSquad(killer: Player, exp: Long) extends Response }