Graverobbing (#490)

* added corpse control to manage (only) looting; swapping controls in ZonePopulationActor

* making test timing more forgiving; fixing case conditions for corpse addition
This commit is contained in:
Fate-JH 2020-06-10 09:23:52 -04:00 committed by GitHub
parent cf64a0ba7f
commit 3ea51d404e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 69 deletions

View file

@ -0,0 +1,76 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.avatar
import akka.actor.Actor
import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage}
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.types.{PlanetSideEmpire, Vector3}
import services.Service
import services.avatar.{AvatarAction, AvatarServiceMessage}
class CorpseControl(player : Player) extends Actor
with ContainableBehavior {
def ContainerObject = player
//private [this] val log = org.log4s.getLogger(player.Name)
def receive : Receive = containerBehavior.orElse { case _ => ; }
def MessageDeferredCallback(msg : Any) : Unit = {
msg match {
case Containable.MoveItem(_, item, _) =>
//momentarily put item back where it was originally
val obj = ContainerObject
obj.Find(item) match {
case Some(slot) =>
obj.Zone.AvatarEvents ! AvatarServiceMessage(
player.Zone.Id,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
)
case None => ;
}
case _ => ;
}
}
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
val obj = ContainerObject
val zone = obj.Zone
val events = zone.AvatarEvents
item.Faction = PlanetSideEmpire.NEUTRAL
events ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID))
}
def PutItemInSlotCallback(item : Equipment, slot : Int) : Unit = {
val obj = ContainerObject
val zone = obj.Zone
val events = zone.AvatarEvents
val definition = item.Definition
events ! AvatarServiceMessage(
zone.Id,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(obj.GUID, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
)
}
def SwapItemCallback(item : Equipment) : Unit = {
val obj = ContainerObject
val zone = obj.Zone
zone.AvatarEvents ! AvatarServiceMessage(zone.Id,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)
)
)
}
}

View file

@ -595,9 +595,8 @@ class PlayerControl(player : Player) extends Actor
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
val obj = ContainerObject
val zone = obj.Zone
val name = player.Name
val toChannel = if(obj.VisibleSlots.contains(slot) || obj.isBackpack) zone.Id else name
val events = zone.AvatarEvents
val toChannel = if(obj.VisibleSlots.contains(slot)) zone.Id else player.Name
item.Faction = PlanetSideEmpire.NEUTRAL
if(slot == obj.DrawnSlot) {
obj.DrawnSlot = Player.HandsDownSlot
@ -612,55 +611,51 @@ class PlayerControl(player : Player) extends Actor
val events = zone.AvatarEvents
val name = player.Name
val definition = item.Definition
val msg = AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(guid, slot),
definition.Packet.DetailedConstructorData(item).get
val faction = obj.Faction
item.Faction = faction
events ! AvatarServiceMessage(
name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(guid, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
)
if(obj.isBackpack) {
item.Faction = PlanetSideEmpire.NEUTRAL
events ! AvatarServiceMessage(zone.Id, msg)
if(obj.VisibleSlots.contains(slot)) {
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
}
else {
val faction = obj.Faction
item.Faction = faction
events ! AvatarServiceMessage(name, msg)
if(obj.VisibleSlots.contains(slot)) {
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
}
//handle specific types of items
item match {
case trigger : BoomerTrigger =>
//pick up the trigger, own the boomer; make certain whole faction is aware of that
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name }) match {
case (Some(boomer : BoomerDeployable), Some(avatar))
if !boomer.OwnerName.contains(name) || boomer.Faction != faction =>
val bguid = boomer.GUID
val faction = player.Faction
val factionChannel = faction.toString
if(avatar.Deployables.Add(boomer)) {
boomer.Faction = faction
boomer.AssignOwnership(player)
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach { case (currElem, curr, maxElem, max) =>
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max))
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr))
}
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone))
events ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction))
zone.LocalEvents ! LocalServiceMessage(factionChannel,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build,
DeployableInfo(bguid, DeployableIcon.Boomer, boomer.Position, boomer.Owner.getOrElse(PlanetSideGUID(0)))
)
)
//handle specific types of items
item match {
case trigger : BoomerTrigger =>
//pick up the trigger, own the boomer; make certain whole faction is aware of that
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name }) match {
case (Some(boomer : BoomerDeployable), Some(avatar))
if !boomer.OwnerName.contains(name) || boomer.Faction != faction =>
val bguid = boomer.GUID
val faction = player.Faction
val factionChannel = faction.toString
if(avatar.Deployables.Add(boomer)) {
boomer.Faction = faction
boomer.AssignOwnership(player)
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach { case (currElem, curr, maxElem, max) =>
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max))
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr))
}
case _ => ; //pointless trigger?
}
case _ => ;
}
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone))
events ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction))
zone.LocalEvents ! LocalServiceMessage(factionChannel,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build,
DeployableInfo(bguid, DeployableIcon.Boomer, boomer.Position, boomer.Owner.getOrElse(PlanetSideGUID(0)))
)
)
}
case _ => ; //pointless trigger?
}
case _ => ;
}
}

View file

@ -2,7 +2,7 @@
package net.psforever.objects.zones
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.avatar.PlayerControl
import net.psforever.objects.avatar.{CorpseControl, PlayerControl}
import net.psforever.objects.{Avatar, Default, Player}
import scala.annotation.tailrec
@ -65,22 +65,18 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
case Zone.Corpse.Add(player) =>
//player can be a corpse if they are in the current zone or are not in any zone
//player is "found" if their avatar can be matched by name within this zone and it has a character
val (canBeCorpse, playerNotFound) = if(player.Zone == zone) {
playerMap.find { case (a, _) => a.name == player.Name } match {
case Some((a, Some(p))) if p eq player =>
PopulationRelease(a, playerMap)
(true, false)
case Some((a, None)) =>
(true, true)
case _ =>
(false, false)
}
val (canBeCorpse, control) = playerMap.find { case (a, _) => a.name == player.Name } match {
case Some((a, Some(p))) if p eq player =>
PopulationRelease(a, playerMap)
context.stop(player.Actor)
(true, Some(player.Actor))
case Some((_, None)) =>
(true, None)
case _ =>
(player.Zone == Zone.Nowhere || player.Zone == zone, None)
}
else {
(player.Zone == Zone.Nowhere, true)
}
if(canBeCorpse && CorpseAdd(player, corpseList) && playerNotFound) {
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), name = s"corpse_of_${GetPlayerControlName(player, None)}")
if(canBeCorpse && CorpseAdd(player, corpseList)) {
player.Actor = context.actorOf(Props(classOf[CorpseControl], player), name = s"corpse_of_${GetPlayerControlName(player, control)}")
player.Zone = zone
}

View file

@ -453,7 +453,7 @@ class ZonePopulationTest extends ActorTest {
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(500, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
}
@ -466,12 +466,12 @@ class ZonePopulationTest extends ActorTest {
zone.Actor ! Zone.Init()
expectNoMessage(200 milliseconds)
zone.Population ! Zone.Corpse.Add(player)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(500, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
zone.Population ! Zone.Corpse.Remove(player)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(200, "ms"))
assert(zone.Corpses.isEmpty)
}
@ -489,14 +489,14 @@ class ZonePopulationTest extends ActorTest {
zone.Population ! Zone.Corpse.Add(player1)
zone.Population ! Zone.Corpse.Add(player2)
zone.Population ! Zone.Corpse.Add(player3)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(500, "ms"))
assert(zone.Corpses.size == 3)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player2)
assert(zone.Corpses(2) == player3)
zone.Population ! Zone.Corpse.Remove(player2)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(200, "ms"))
assert(zone.Corpses.size == 2)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player3)
@ -512,7 +512,7 @@ class ZonePopulationTest extends ActorTest {
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMessage(Duration.create(100, "ms"))
expectNoMessage(Duration.create(200, "ms"))
assert(zone.Corpses.isEmpty)
}
}