mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-04-29 16:25:30 +00:00
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:
parent
cf64a0ba7f
commit
3ea51d404e
4 changed files with 136 additions and 69 deletions
|
|
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -595,9 +595,8 @@ class PlayerControl(player : Player) extends Actor
|
||||||
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
|
def RemoveItemFromSlotCallback(item : Equipment, slot : Int) : Unit = {
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
val zone = obj.Zone
|
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 events = zone.AvatarEvents
|
||||||
|
val toChannel = if(obj.VisibleSlots.contains(slot)) zone.Id else player.Name
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
item.Faction = PlanetSideEmpire.NEUTRAL
|
||||||
if(slot == obj.DrawnSlot) {
|
if(slot == obj.DrawnSlot) {
|
||||||
obj.DrawnSlot = Player.HandsDownSlot
|
obj.DrawnSlot = Player.HandsDownSlot
|
||||||
|
|
@ -612,55 +611,51 @@ class PlayerControl(player : Player) extends Actor
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val name = player.Name
|
val name = player.Name
|
||||||
val definition = item.Definition
|
val definition = item.Definition
|
||||||
val msg = AvatarAction.SendResponse(
|
val faction = obj.Faction
|
||||||
Service.defaultPlayerGUID,
|
item.Faction = faction
|
||||||
ObjectCreateDetailedMessage(
|
events ! AvatarServiceMessage(
|
||||||
definition.ObjectId,
|
name,
|
||||||
item.GUID,
|
AvatarAction.SendResponse(
|
||||||
ObjectCreateMessageParent(guid, slot),
|
Service.defaultPlayerGUID,
|
||||||
definition.Packet.DetailedConstructorData(item).get
|
ObjectCreateDetailedMessage(
|
||||||
|
definition.ObjectId,
|
||||||
|
item.GUID,
|
||||||
|
ObjectCreateMessageParent(guid, slot),
|
||||||
|
definition.Packet.DetailedConstructorData(item).get
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if(obj.isBackpack) {
|
if(obj.VisibleSlots.contains(slot)) {
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
||||||
events ! AvatarServiceMessage(zone.Id, msg)
|
|
||||||
}
|
}
|
||||||
else {
|
//handle specific types of items
|
||||||
val faction = obj.Faction
|
item match {
|
||||||
item.Faction = faction
|
case trigger : BoomerTrigger =>
|
||||||
events ! AvatarServiceMessage(name, msg)
|
//pick up the trigger, own the boomer; make certain whole faction is aware of that
|
||||||
if(obj.VisibleSlots.contains(slot)) {
|
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name }) match {
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
case (Some(boomer : BoomerDeployable), Some(avatar))
|
||||||
}
|
if !boomer.OwnerName.contains(name) || boomer.Faction != faction =>
|
||||||
//handle specific types of items
|
val bguid = boomer.GUID
|
||||||
item match {
|
val faction = player.Faction
|
||||||
case trigger : BoomerTrigger =>
|
val factionChannel = faction.toString
|
||||||
//pick up the trigger, own the boomer; make certain whole faction is aware of that
|
if(avatar.Deployables.Add(boomer)) {
|
||||||
(zone.GUID(trigger.Companion), zone.Players.find { _.name == name }) match {
|
boomer.Faction = faction
|
||||||
case (Some(boomer : BoomerDeployable), Some(avatar))
|
boomer.AssignOwnership(player)
|
||||||
if !boomer.OwnerName.contains(name) || boomer.Faction != faction =>
|
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach { case (currElem, curr, maxElem, max) =>
|
||||||
val bguid = boomer.GUID
|
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, maxElem, max))
|
||||||
val faction = player.Faction
|
events ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(Service.defaultPlayerGUID, currElem, curr))
|
||||||
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)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
case _ => ; //pointless trigger?
|
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(boomer), zone))
|
||||||
}
|
events ! AvatarServiceMessage(factionChannel, AvatarAction.SetEmpire(Service.defaultPlayerGUID, bguid, faction))
|
||||||
case _ => ;
|
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 _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package net.psforever.objects.zones
|
package net.psforever.objects.zones
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
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 net.psforever.objects.{Avatar, Default, Player}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
@ -65,22 +65,18 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
|
||||||
case Zone.Corpse.Add(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 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
|
//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) {
|
val (canBeCorpse, control) = playerMap.find { case (a, _) => a.name == player.Name } match {
|
||||||
playerMap.find { case (a, _) => a.name == player.Name } match {
|
case Some((a, Some(p))) if p eq player =>
|
||||||
case Some((a, Some(p))) if p eq player =>
|
PopulationRelease(a, playerMap)
|
||||||
PopulationRelease(a, playerMap)
|
context.stop(player.Actor)
|
||||||
(true, false)
|
(true, Some(player.Actor))
|
||||||
case Some((a, None)) =>
|
case Some((_, None)) =>
|
||||||
(true, true)
|
(true, None)
|
||||||
case _ =>
|
case _ =>
|
||||||
(false, false)
|
(player.Zone == Zone.Nowhere || player.Zone == zone, None)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
if(canBeCorpse && CorpseAdd(player, corpseList)) {
|
||||||
(player.Zone == Zone.Nowhere, true)
|
player.Actor = context.actorOf(Props(classOf[CorpseControl], player), name = s"corpse_of_${GetPlayerControlName(player, control)}")
|
||||||
}
|
|
||||||
if(canBeCorpse && CorpseAdd(player, corpseList) && playerNotFound) {
|
|
||||||
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), name = s"corpse_of_${GetPlayerControlName(player, None)}")
|
|
||||||
player.Zone = zone
|
player.Zone = zone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
assert(zone.Corpses.isEmpty)
|
assert(zone.Corpses.isEmpty)
|
||||||
zone.Population ! Zone.Corpse.Add(player)
|
zone.Population ! Zone.Corpse.Add(player)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(500, "ms"))
|
||||||
assert(zone.Corpses.size == 1)
|
assert(zone.Corpses.size == 1)
|
||||||
assert(zone.Corpses.head == player)
|
assert(zone.Corpses.head == player)
|
||||||
}
|
}
|
||||||
|
|
@ -466,12 +466,12 @@ class ZonePopulationTest extends ActorTest {
|
||||||
zone.Actor ! Zone.Init()
|
zone.Actor ! Zone.Init()
|
||||||
expectNoMessage(200 milliseconds)
|
expectNoMessage(200 milliseconds)
|
||||||
zone.Population ! Zone.Corpse.Add(player)
|
zone.Population ! Zone.Corpse.Add(player)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(500, "ms"))
|
||||||
|
|
||||||
assert(zone.Corpses.size == 1)
|
assert(zone.Corpses.size == 1)
|
||||||
assert(zone.Corpses.head == player)
|
assert(zone.Corpses.head == player)
|
||||||
zone.Population ! Zone.Corpse.Remove(player)
|
zone.Population ! Zone.Corpse.Remove(player)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(200, "ms"))
|
||||||
assert(zone.Corpses.isEmpty)
|
assert(zone.Corpses.isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,14 +489,14 @@ class ZonePopulationTest extends ActorTest {
|
||||||
zone.Population ! Zone.Corpse.Add(player1)
|
zone.Population ! Zone.Corpse.Add(player1)
|
||||||
zone.Population ! Zone.Corpse.Add(player2)
|
zone.Population ! Zone.Corpse.Add(player2)
|
||||||
zone.Population ! Zone.Corpse.Add(player3)
|
zone.Population ! Zone.Corpse.Add(player3)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(500, "ms"))
|
||||||
|
|
||||||
assert(zone.Corpses.size == 3)
|
assert(zone.Corpses.size == 3)
|
||||||
assert(zone.Corpses.head == player1)
|
assert(zone.Corpses.head == player1)
|
||||||
assert(zone.Corpses(1) == player2)
|
assert(zone.Corpses(1) == player2)
|
||||||
assert(zone.Corpses(2) == player3)
|
assert(zone.Corpses(2) == player3)
|
||||||
zone.Population ! Zone.Corpse.Remove(player2)
|
zone.Population ! Zone.Corpse.Remove(player2)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(200, "ms"))
|
||||||
assert(zone.Corpses.size == 2)
|
assert(zone.Corpses.size == 2)
|
||||||
assert(zone.Corpses.head == player1)
|
assert(zone.Corpses.head == player1)
|
||||||
assert(zone.Corpses(1) == player3)
|
assert(zone.Corpses(1) == player3)
|
||||||
|
|
@ -512,7 +512,7 @@ class ZonePopulationTest extends ActorTest {
|
||||||
|
|
||||||
assert(zone.Corpses.isEmpty)
|
assert(zone.Corpses.isEmpty)
|
||||||
zone.Population ! Zone.Corpse.Add(player)
|
zone.Population ! Zone.Corpse.Add(player)
|
||||||
expectNoMessage(Duration.create(100, "ms"))
|
expectNoMessage(Duration.create(200, "ms"))
|
||||||
assert(zone.Corpses.isEmpty)
|
assert(zone.Corpses.isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue