unified code paths for dropping an item, and reinforced code path for picking an item back up; LocalService will handle some of the work now; ActionResultMessage embraces its simplistic nature (we don't have enough error messages)

This commit is contained in:
FateJH 2018-05-22 19:13:59 -04:00
parent fdf05337fd
commit f6f7ad5617
12 changed files with 192 additions and 126 deletions

View file

@ -386,27 +386,15 @@ object Zone {
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
}
/**
* Message to relinguish an item and place in on the ground.
* @param item the piece of `Equipment`
* @param pos where it is dropped
* @param orient in which direction it is facing when dropped
*/
final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
object Ground {
final case class DropItem(item : Equipment, pos : Vector3, orient : Vector3)
final case class ItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
final case class CanNotDropItem(item : Equipment)
/**
* Message to attempt to acquire an item from the ground (before somoene else?).
* @param player who wants the piece of `Equipment`
* @param item_guid the unique identifier of the piece of `Equipment`
*/
final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID)
/**
* Message to give an item from the ground to a specific user.
* @param player who wants the piece of `Equipment`
* @param item the piece of `Equipment`
*/
final case class ItemFromGround(player : Player, item : Equipment)
final case class PickupItem(item_guid : PlanetSideGUID)
final case class ItemInHand(item : Equipment)
final case class CanNotPickupItem(item_guid : PlanetSideGUID)
}
object Vehicle {
final case class Spawn(vehicle : Vehicle)

View file

@ -50,10 +50,10 @@ class ZoneActor(zone : Zone) extends Actor {
zone.Population forward msg
//frwd to Ground Actor
case msg @ Zone.DropItemOnGround =>
case msg @ Zone.Ground.DropItem =>
zone.Ground forward msg
case msg @ Zone.GetItemOnGround =>
case msg @ Zone.Ground.PickupItem =>
zone.Ground forward msg
//frwd to Vehicle Actor

View file

@ -16,18 +16,22 @@ class ZoneGroundActor(equipmentOnGround : ListBuffer[Equipment]) extends Actor {
//private[this] val log = org.log4s.getLogger
def receive : Receive = {
case Zone.DropItemOnGround(item, pos, orient) =>
item.Position = pos
item.Orientation = orient
equipmentOnGround += item
case Zone.GetItemOnGround(player, item_guid) =>
FindItemOnGround(item_guid) match {
case Some(item) =>
sender ! Zone.ItemFromGround(player, item)
case Zone.Ground.DropItem(item, pos, orient) =>
sender ! (FindItemOnGround(item.GUID) match {
case None =>
org.log4s.getLogger.warn(s"item on ground $item_guid was requested by $player for pickup but was not found")
}
equipmentOnGround += item
Zone.Ground.ItemOnGround(item, pos, orient)
case Some(_) =>
Zone.Ground.CanNotDropItem(item)
})
case Zone.Ground.PickupItem(item_guid) =>
sender ! (FindItemOnGround(item_guid) match {
case Some(item) =>
Zone.Ground.ItemInHand(item)
case None =>
Zone.Ground.CanNotPickupItem(item_guid)
})
case _ => ;
}

View file

@ -19,13 +19,18 @@ final case class ActionResultMessage(successful : Boolean,
}
object ActionResultMessage extends Marshallable[ActionResultMessage] {
def apply() : ActionResultMessage = {
ActionResultMessage(true, None)
}
/**
* A message where the result is always a pass.
* @return an `ActionResultMessage` object
*/
def Pass : ActionResultMessage = ActionResultMessage(true, None)
def apply(error : Long) : ActionResultMessage = {
ActionResultMessage(false, Some(error))
}
/**
* A message where the result is always a failure.
* @param error the error code
* @return an `ActionResultMessage` object
*/
def Fail(error : Long) : ActionResultMessage = ActionResultMessage(false, Some(error))
implicit val codec : Codec[ActionResultMessage] = (
("successful" | bool) >>:~ { res =>

View file

@ -40,6 +40,14 @@ final case class ObjectDetachMessage(parent_guid : PlanetSideGUID,
}
object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] {
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient : Vector3) : ObjectDetachMessage = {
ObjectDetachMessage(parent_guid, child_guid, pos, orient.x, orient.y, orient.z)
}
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient_z : Float) : ObjectDetachMessage = {
ObjectDetachMessage(parent_guid, child_guid, pos, 0, 0, orient_z)
}
implicit val codec : Codec[ObjectDetachMessage] = (
("parent_guid" | PlanetSideGUID.codec) ::
("child_guid" | PlanetSideGUID.codec) ::

View file

@ -38,7 +38,7 @@ class ActionResultMessageTest extends Specification {
}
"encode (pass, minimal)" in {
val msg = ActionResultMessage()
val msg = ActionResultMessage.Pass
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_pass
@ -52,7 +52,7 @@ class ActionResultMessageTest extends Specification {
}
"encode (fail, minimal)" in {
val msg = ActionResultMessage(1)
val msg = ActionResultMessage.Fail(1)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_fail

View file

@ -26,10 +26,24 @@ class ObjectDetachMessageTest extends Specification {
}
}
"encode" in {
"encode (1)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
"encode (2)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), Vector3(0f, 0f, 270f))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
"encode (3)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 270f)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -477,7 +477,7 @@ class ZoneGroundTest extends ActorTest {
assert(zone.EquipmentOnGround.isEmpty)
assert(item.Position == Vector3.Zero)
assert(item.Orientation == Vector3.Zero)
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
zone.Ground ! Zone.Ground.DropItem(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
@ -490,17 +490,16 @@ class ZoneGroundTest extends ActorTest {
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10))
zone.Ground ! Zone.Ground.PickupItem(PlanetSideGUID(10))
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround.isEmpty)
assert(reply.isInstanceOf[Zone.ItemFromGround])
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player)
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
assert(reply.isInstanceOf[Zone.Ground.ItemInHand])
assert(reply.asInstanceOf[Zone.Ground.ItemInHand].item == item)
}
"get item from ground (failure)" in {
@ -508,11 +507,11 @@ class ZoneGroundTest extends ActorTest {
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid
zone.Ground ! Zone.Ground.PickupItem(PlanetSideGUID(11)) //wrong guid
expectNoMsg(Duration.create(500, "ms"))
}
}

View file

@ -255,7 +255,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) =>
if(tplayer_guid != guid) {
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3(0,0,0), 0f, 0f, 0f))
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
sendResponse(
ObjectCreateMessage(
ammo_id,
@ -405,7 +405,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case LocalServiceResponse(_, guid, reply) =>
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) }
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(-1) }
reply match {
case LocalResponse.DoorOpens(door_guid) =>
if(tplayer_guid != guid) {
@ -415,16 +415,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalResponse.DoorCloses(door_guid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(door_guid, 17))
case LocalResponse.DropItem(id, item_guid, data) =>
if(tplayer_guid != guid) {
sendResponse(ObjectCreateMessage(id, item_guid, data))
}
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
if(player.GUID != guid) {
if(tplayer_guid != guid) {
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
}
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
if(player.GUID != guid) {
if(tplayer_guid != guid) {
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
}
@ -465,7 +470,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) =>
sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), 0, 0, pad_orientation_z))
sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), pad_orientation_z))
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(tplayer_guid != guid) {
@ -844,12 +849,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
//drop items on ground
val pos = tplayer.Position
val orient = tplayer.Orientation
val orient = Vector3(0,0, tplayer.Orientation.z)
((dropHolsters ++ dropInventory).map(_.obj) ++ drop).foreach(obj => {
continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z))
sendResponse(ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z))
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
//TODO make a sound when dropping stuff
continent.Ground ! Zone.Ground.DropItem(obj, pos, orient)
})
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true))
}
@ -938,12 +941,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
//drop stuff on ground
val pos = tplayer.Position
val orient = tplayer.Orientation
val orient = Vector3(0,0, tplayer.Orientation.z)
((dropHolsters ++ dropInventory).map(_.obj)).foreach(obj => {
continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z))
sendResponse(ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z))
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
continent.Ground ! Zone.Ground.DropItem(obj, pos, orient)
})
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
@ -1177,7 +1177,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad) =>
val vdef = vehicle.Definition
if(vehicle.Seats(0).isOccupied) {
sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), 0, 0, pad.Orientation.z))
sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), pad.Orientation.z))
}
ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, GlobalDefinitions.isFlightVehicle(vdef):Int)
@ -1375,6 +1375,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
RequestSanctuaryZoneSpawn(player, zone_number)
}
case Zone.Ground.ItemOnGround(item, pos, orient) =>
item.Position = pos
item.Orientation = Vector3(0,0, orient.z) //only one kind of rotation is important
val exclusionId = player.Find(item) match {
case Some(slotNum) =>
player.Slot(slotNum).Equipment = None
sendResponse(ObjectDetachMessage(player.GUID, item.GUID, pos, orient.z))
sendResponse(ActionResultMessage.Pass)
player.GUID //we're dropping it; don't need to see it dropped again
case None =>
PlanetSideGUID(0) //object is being introduced into the world upon drop
}
localService ! LocalServiceMessage(continent.Id, LocalAction.DropItem(exclusionId, item))
case Zone.Ground.CanNotDropItem(item) =>
log.warn(s"DropItem: $player tried to drop a $item on the ground, but he missed")
player.Find(item) match {
case None => //item in limbo
taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID)
case Some(_) => ;
}
case Zone.Ground.ItemInHand(item) =>
player.Fit(item) match {
case Some(slotNum) =>
val item_guid = item.GUID
val player_guid = player.GUID
player.Slot(slotNum).Equipment = item
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
item_guid,
ObjectCreateMessageParent(player_guid, slotNum),
definition.Packet.DetailedConstructorData(item).get
)
)
avatarService ! AvatarServiceMessage(continent.Id, if(player.VisibleSlots.contains(slotNum)) {
AvatarAction.EquipmentInHand(player_guid, player_guid, slotNum, item)
}
else {
AvatarAction.ObjectDelete(player_guid, item_guid)
})
sendResponse(ActionResultMessage.Pass)
case None =>
continent.Ground ! Zone.Ground.DropItem(item, item.Position, item.Orientation) //restore previous state
}
case Zone.Ground.CanNotPickupItem(item_guid) =>
continent.GUID(item_guid) match {
case Some(item) =>
log.warn(s"DropItem: finding a $item on the ground was suggested, but $player can not reach it")
case None =>
log.warn(s"DropItem: finding an item ($item_guid) on the ground was suggested, but $player can not see it")
}
case InterstellarCluster.ClientInitializationComplete() =>
StopBundlingPackets()
LivePlayerList.Add(sessionId, avatar)
@ -1467,31 +1523,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TacticsMessage
StopBundlingPackets()
case Zone.ItemFromGround(tplayer, item) =>
val obj_guid = item.GUID
val player_guid = tplayer.GUID
tplayer.Fit(item) match {
case Some(slot) =>
tplayer.Slot(slot).Equipment = item
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(player_guid, obj_guid))
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj_guid,
ObjectCreateMessageParent(player_guid, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
sendResponse(ActionResultMessage())
if(tplayer.VisibleSlots.contains(slot)) {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, player_guid, slot, item))
}
case None =>
continent.Ground ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
}
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
progressBarUpdate.cancel
if(progressBarValue.isDefined) {
@ -1607,18 +1638,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
import scala.concurrent.ExecutionContext.Implicits.global
clientKeepAlive.cancel
clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient())
log.warn(PacketCoding.DecodePacket(hex"d2327e7b8a972b95113881003710").toString)
case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) =>
log.info("Handling " + msg)
sendResponse(ActionResultMessage(true, None))
sendResponse(ActionResultMessage.Pass)
self ! ListAccountCharacters
case msg @ CharacterRequestMessage(charId, action) =>
log.info("Handling " + msg)
action match {
case CharacterRequestAction.Delete =>
sendResponse(ActionResultMessage(false, Some(1)))
sendResponse(ActionResultMessage.Fail(1))
case CharacterRequestAction.Select =>
//TODO check if can spawn on last continent/location from player?
//TODO if yes, get continent guid accessors
@ -1828,7 +1858,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(50 milliseconds, self, UnregisterCorpseOnVehicleDisembark(player))
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0, 0, 0))
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0))
//sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
}
@ -1989,8 +2019,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val tailReloadValue : Int = if(xs.isEmpty) { 0 } else { xs.map(_.obj.asInstanceOf[AmmoBox].Capacity).reduceLeft(_ + _) }
val sumReloadValue : Int = originalBoxCapacity + tailReloadValue
val previousBox = tool.AmmoSlot.Box //current magazine in tool
sendResponse(ObjectDetachMessage(tool.GUID, previousBox.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))
sendResponse(ObjectDetachMessage(player.GUID, box.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))
sendResponse(ObjectDetachMessage(tool.GUID, previousBox.GUID, Vector3.Zero, 0f))
sendResponse(ObjectDetachMessage(player.GUID, box.GUID, Vector3.Zero, 0f))
obj.Inventory -= x.start //remove replacement ammo from inventory
val ammoSlotIndex = tool.FireMode.AmmoSlotIndex
tool.AmmoSlots(ammoSlotIndex).Box = box //put replacement ammo in tool
@ -2158,27 +2188,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(EmoteMsg(avatar_guid, emote))
case msg @ DropItemMessage(item_guid) =>
log.info("DropItem: " + msg)
player.FreeHand.Equipment match {
case Some(item) =>
if(item.GUID == item_guid) {
val orient : Vector3 = Vector3(0f, 0f, player.Orientation.z)
player.FreeHand.Equipment = None
continent.Ground ! Zone.DropItemOnGround(item, player.Position, orient)
sendResponse(ObjectDetachMessage(player.GUID, item.GUID, player.Position, 0f, 0f, player.Orientation.z))
val objDef = item.Definition
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, player.Position, orient, objDef.ObjectId, item.GUID, objDef.Packet.ConstructorData(item).get))
}
else {
log.warn(s"item in hand was ${item.GUID} but trying to drop $item_guid; nothing will be dropped")
log.info(s"DropItem: $msg")
continent.GUID(item_guid) match {
case Some(item : Equipment) =>
player.FreeHand.Equipment match {
case Some(_) =>
if(item.GUID == item_guid) {
continent.Ground ! Zone.Ground.DropItem(item, player.Position, player.Orientation)
}
case None =>
log.warn(s"DropItem: $player wanted to drop a $item, but it wasn't at hand")
}
case Some(obj) => //TODO LLU
log.warn(s"DropItem: $player wanted to drop a $obj, but that isn't possible")
case None =>
log.error(s"$player wanted to drop an item, but it was not in hand")
log.warn(s"DropItem: $player wanted to drop an item ($item_guid), but it was nowhere to be found")
}
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
log.info("PickupItem: " + msg)
continent.Ground ! Zone.GetItemOnGround(player, item_guid)
log.info(s"PickupItem: $msg")
continent.GUID(item_guid) match {
case Some(item : Equipment) =>
player.Fit(item) match {
case Some(_) =>
continent.Ground ! Zone.Ground.PickupItem(item_guid)
case None => //skip
sendResponse(ActionResultMessage.Fail(16)) //error code?
}
case _ =>
log.warn(s"PickupItem: $player requested an item that doesn't exist in this zone; assume client-side garbage data")
sendResponse(ObjectDeleteMessage(item_guid, 0))
}
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
log.info("Reload: " + msg)
@ -3847,14 +3887,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(InventoryItem(item2, destIndex)) => //yes, swap
//cleanly shuffle items around to avoid losing icons
//the next ObjectDetachMessage is necessary to avoid icons being lost, but only as part of this swap
sendResponse(ObjectDetachMessage(source_guid, item_guid, Vector3.Zero, 0f, 0f, 0f))
sendResponse(ObjectDetachMessage(source_guid, item_guid, Vector3.Zero, 0f))
val item2_guid = item2.GUID
destination.Slot(destIndex).Equipment = None //remove the swap item from destination
(indexSlot.Equipment = item2) match {
case Some(_) => //item and item2 swapped places successfully
log.info(s"MoveItem: $item2 swapped to $source @ $index")
//remove item2 from destination
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, Vector3.Zero, 0f, 0f, 0f))
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, Vector3.Zero, 0f))
destination match {
case obj : Vehicle =>
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid))
@ -3897,8 +3937,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val pos = source.Position
val sourceOrientZ = source.Orientation.z
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
continent.Actor ! Zone.DropItemOnGround(item2, pos, orient)
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, 0f, 0f, sourceOrientZ)) //ground
continent.Ground ! Zone.Ground.DropItem(item2, pos, orient)
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, sourceOrientZ)) //ground
val objDef = item2.Definition
destination match {
case obj : Vehicle =>
@ -3956,15 +3996,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param item the item
*/
def NormalItemDrop(obj : PlanetSideGameObject with Container, zone : Zone, service : ActorRef)(item : Equipment) : Unit = {
val itemGUID = item.GUID
val ang = obj.Orientation.z
val pos = obj.Position
val orient = Vector3(0f, 0f, ang)
item.Position = pos
item.Orientation = orient
zone.Ground ! Zone.DropItemOnGround(item, pos, orient)
val itemDef = item.Definition
service ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentOnGround(Service.defaultPlayerGUID, pos, orient, itemDef.ObjectId, itemGUID, itemDef.Packet.ConstructorData(item).get))
continent.Ground ! Zone.Ground.DropItem(item, obj.Position, Vector3(0f, 0f, obj.Orientation.z))
}
/**

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package services.local
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.zones.Zone
@ -12,6 +13,7 @@ object LocalAction {
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment) extends Action
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package services.local
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
import net.psforever.types.Vector3
@ -9,6 +10,7 @@ object LocalResponse {
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
final case class DropItem(item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response

View file

@ -2,6 +2,7 @@
package services.local
import akka.actor.{Actor, Props}
import net.psforever.packet.game.objectcreate.{DroppedItemData, PlacementData}
import services.local.support.{DoorCloseActor, HackClearActor}
import services.{GenericEventBus, Service}
@ -46,6 +47,17 @@ class LocalService extends Actor {
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid))
)
case LocalAction.DropItem(player_guid, item) =>
val definition = item.Definition
val objectData = DroppedItemData(
PlacementData(item.Position, item.Orientation),
definition.Packet.ConstructorData(item).get
)
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid,
LocalResponse.DropItem(definition.ObjectId, item.GUID, objectData)
)
)
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))