corrected missing Tile size for CUD and Laze Pointer; changed MoveItem tests to account for spatial inconsistency between modular holster slots and regional inventory cells

This commit is contained in:
FateJH 2017-08-18 09:40:29 -04:00
parent 7af2e4b572
commit 02ee06fd97
6 changed files with 314 additions and 108 deletions

View file

@ -21,12 +21,26 @@ class EquipmentSlot {
Size
}
/**
* Determine what `Equipment` is stowed in the given position.
* @return the `Equipment` in this slot
*/
def Equipment : Option[Equipment] = tool
/**
* Attempt to stow an item at the given position.
* @param assignEquipment the change in `Equipment` for this slot
* @return the `Equipment` in this slot
*/
def Equipment_=(assignEquipment : Equipment) : Option[Equipment] = {
Equipment = Some(assignEquipment)
}
/**
* Attempt to stow an item at the given position.
* @param assignEquipment the change in `Equipment` for this slot
* @return the `Equipment` in this slot
*/
def Equipment_=(assignEquipment : Option[Equipment]) : Option[Equipment] = {
if(assignEquipment.isDefined) { //if new equipment is defined, don't put it in the slot if the slot is being used
if(tool.isEmpty && EquipmentSize.isEqual(size, assignEquipment.get.Size)) {

View file

@ -1117,10 +1117,12 @@ object GlobalDefinitions {
val
flail_targeting_laser = SimpleItemDefinition(SItem.flail_targeting_laser)
flail_targeting_laser.Packet = new CommandDetonaterConverter
flail_targeting_laser.Tile = InventoryTile.Tile33
val
command_detonater = SimpleItemDefinition(SItem.command_detonater)
command_detonater.Packet = new CommandDetonaterConverter
command_detonater.Tile = InventoryTile.Tile33
val
ace = ConstructionItemDefinition(CItem.Unit.ace)

View file

@ -116,12 +116,7 @@ class GridInventory {
}
/**
* Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.<br>
* <br>
* If there are fewer items stored in the inventory than there are cells required to represent the testing item,
* test the collision by iterating through the list of items.
* If there are more items, check that each cell that would be used for the testing items tile does not collide.
* The "testing item" in this case has already been transformed into its tile dimensions.
* Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.
* @param start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested
* @param h the height of the `Equipment` to be tested
@ -132,8 +127,33 @@ class GridInventory {
Success(List.empty[Int])
}
else {
def check : (Int,Int,Int) => Try[List[Int]] = if(items.size < w * h) { CheckCollisionsAsList } else { CheckCollisionsAsGrid }
check(start, w, h)
CheckCollisionsVar(start, w, h) match {
case Success(list) =>
Success(list.map({ f => f.obj.GUID.guid }))
case Failure(ex) =>
Failure(ex)
}
}
}
/**
* Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.<br>
* <br>
* If there are fewer items stored in the inventory than there are cells required to represent the testing item,
* test the collision by iterating through the list of items.
* If there are more items, check that each cell that would be used for the testing items tile does not collide.
* The "testing item" in this case has already been transformed into its tile dimensions.
* @param start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested
* @param h the height of the `Equipment` to be tested
* @return a `List` of existing items that an item of this scale would overlap if inserted
*/
def CheckCollisionsVar(start : Int, w : Int, h : Int) : Try[List[InventoryItem]] = {
if(items.size < w * h) {
CheckCollisionsAsList(start, w, h)
}
else {
CheckCollisionsAsGrid(start, w, h)
}
}
@ -145,10 +165,10 @@ class GridInventory {
* @param start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested
* @param h the height of the `Equipment` to be tested
* @return a `List` of GUID values for all existing contents that this item would overlap if inserted
* @return a `List` of existing items that an item of this scale would overlap if inserted
* @throws IndexOutOfBoundsException if the region extends outside of the grid boundaries
*/
def CheckCollisionsAsList(start : Int, w : Int, h : Int) : Try[List[Int]] = {
def CheckCollisionsAsList(start : Int, w : Int, h : Int) : Try[List[InventoryItem]] = {
val actualSlot = start - offset
val startx : Int = actualSlot % width
val starty : Int = actualSlot / width
@ -159,7 +179,7 @@ class GridInventory {
Failure(new IndexOutOfBoundsException(s"requested region escapes the $bounds edge of the grid inventory - $startx, $starty; $w x $h"))
}
else {
val collisions : mutable.Set[Int] = mutable.Set[Int]()
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
items.values.foreach({ item : InventoryItem =>
val actualItemStart : Int = item.start - offset
val itemx : Int = actualItemStart % width
@ -168,7 +188,7 @@ class GridInventory {
val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.width > startx } else { itemx <= startw }
val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.height > starty } else { itemy <= starth }
if(clipsOnX && clipsOnY) {
collisions += item.GUID.guid
collisions += item
}
})
Success(collisions.toList)
@ -183,10 +203,10 @@ class GridInventory {
* @param start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested
* @param h the height of the `Equipment` to be tested
* @return a `List` of GUID values for all existing contents that this item would overlap if inserted
* @return a `List` of existing items that an item of this scale would overlap if inserted
* @throws IndexOutOfBoundsException if the region extends outside of the grid boundaries
*/
def CheckCollisionsAsGrid(start : Int, w : Int, h : Int) : Try[List[Int]] = {
def CheckCollisionsAsGrid(start : Int, w : Int, h : Int) : Try[List[InventoryItem]] = {
val actualSlot = start - offset
if(actualSlot < 0 || actualSlot >= grid.length || (actualSlot % width) + w > width || (actualSlot / width) + h > height) {
val startx : Int = actualSlot % width
@ -196,12 +216,12 @@ class GridInventory {
Failure(new IndexOutOfBoundsException(s"requested region escapes the $bounds edge of the grid inventory - $startx, $starty; $w x $h"))
}
else {
val collisions : mutable.Set[Int] = mutable.Set[Int]()
val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
var curr = actualSlot
for(_ <- 0 until h) {
for(col <- 0 until w) {
if(grid(curr + col) > -1) {
collisions += items(grid(curr + col)).GUID.guid
collisions += items(grid(curr + col))
}
}
curr += width

View file

@ -4,40 +4,57 @@ package net.psforever.objects.inventory
import net.psforever.objects.OffhandEquipmentSlot
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import scala.util.{Failure, Success}
/**
* A slot-like interface for a specific grid position in an inventory.
* The size is bound to anything that can be stowed, which encompasses most all `Equipment`.
* Furthermore, rather than operating on a fixed-size slot, this "slot" represents an inventory region that either includes `slot` or starts at `slot`.
* An object added to the underlying inventory from here can only be added with its initial point at `slot`.
* An object found at `slot`, however, can be removed even if the starting cell is prior to `slot.`
* The size is typically bound to anything that can be stowed which encompasses most all `Equipment`.
* The capacity of this `EquipmentSlot` is essentially treated as 1x1.
* Upon insertions, however, the capacity temporarily is treated as the size of the item being inserted (unless `None`).
* This allows a proper check for insertion collision.<br>
* <br>
* Rather than operating on a fixed-size slot, this "slot" represents an inventory region that either includes `slot` or starts at `slot`.
* When determining the contents of the inventory at `slot`, only that singular cell is checked.
* When removing an item from `slot`, the item in inventory only has to be positioned in such a way that overlaps with `slot`.
* When adding an item to `slot`, `slot` is treated as the upper left corner (the initial point) of a larger capacity region.<br>
* <br>
* The following diagrams demonstrate the coordinate association:<br>
* `&nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - - - - -`<br>
* `&nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - r r x - &nbsp;&nbsp;&nbsp; - - - - -`<br>
* `&nbsp;&nbsp;&nbsp; - - s - - &nbsp;&nbsp;&nbsp; - r r x - &nbsp;&nbsp;&nbsp; - - i i -`<br>
* `&nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - x x x - &nbsp;&nbsp;&nbsp; - - i i -`<br>
* `&nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - - - - - &nbsp;&nbsp;&nbsp; - - - - -`<br>
* ... where 's' is the 1x1 slot,
* 'r' is the corner of any 2x2 item that can be removed ('x' is a potential affected edge),
* and 'i' is the region checked for a 2x2 insertion into `slot`.
*/
class InventoryEquipmentSlot(private val slot : Int, private val inv : GridInventory) extends OffhandEquipmentSlot(EquipmentSize.Inventory) {
/**
* Attempt to stow an item into the inventory at the given position.
* @param assignEquipment the change in `Equipment` for this slot
* @return the `Equipment` in this slot
*/
override def Equipment_=(assignEquipment : Option[Equipment]) : Option[Equipment] = {
assignEquipment match {
case Some(equip) =>
inv += slot -> equip
val tile = equip.Definition.Tile
inv.CheckCollisionsVar(slot, tile.Width, tile.Height) match {
case Success(Nil) => inv += slot -> equip
case _ => ;
}
case None =>
inv -= slot
}
Equipment
}
/**
* Determine what `Equipment`, if any, is stowed in the inventory in the given position.
* @return the `Equipment` in this slot
*/
override def Equipment : Option[Equipment] = {
inv.Items.find({ case ((_, item : InventoryItem)) => item.start == slot }) match {
case Some((_, item : InventoryItem)) =>
Some(item.obj)
case None =>
inv.CheckCollisionsAsGrid(slot,1,1) match {
case Success(list) =>
list.headOption match {
case Some(found) =>
Some(found.obj)
case None =>
None
}
case Failure(_) =>
None
}
}
}

View file

@ -59,14 +59,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(1, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(2, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(3, w, h) mustEqual Success(Nil)
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(1, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(2, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(3, w, h) mustEqual Success(Nil)
val list0 = obj.CheckCollisionsAsList(0, w, h)
list0 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list1 = obj.CheckCollisionsAsList(1, w, h)
list1 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list2 = obj.CheckCollisionsAsList(2, w, h)
list2 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list3 = obj.CheckCollisionsAsList(3, w, h)
list3 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
obj.CheckCollisionsAsGrid(0, w, h) mustEqual list0
obj.CheckCollisionsAsGrid(1, w, h) mustEqual list1
obj.CheckCollisionsAsGrid(2, w, h) mustEqual list2
obj.CheckCollisionsAsGrid(3, w, h) mustEqual list3
obj.Clear()
ok
}
@ -77,14 +93,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(3, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(2, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(1, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(Nil)
obj.CheckCollisionsAsGrid(3, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(2, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(1, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(Nil)
val list0 = obj.CheckCollisionsAsList(3, w, h)
list0 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list1 = obj.CheckCollisionsAsList(2, w, h)
list1 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list2 = obj.CheckCollisionsAsList(1, w, h)
list2 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list3 = obj.CheckCollisionsAsList(0, w, h)
list3 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
obj.CheckCollisionsAsGrid(3, w, h) mustEqual list0
obj.CheckCollisionsAsGrid(2, w, h) mustEqual list1
obj.CheckCollisionsAsGrid(1, w, h) mustEqual list2
obj.CheckCollisionsAsGrid(0, w, h) mustEqual list3
obj.Clear()
ok
}
@ -95,14 +127,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(9, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(18, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(27, w, h) mustEqual Success(Nil)
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(9, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(18, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(27, w, h) mustEqual Success(Nil)
val list0 = obj.CheckCollisionsAsList(0, w, h)
list0 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list1 = obj.CheckCollisionsAsList(9, w, h)
list1 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list2 = obj.CheckCollisionsAsList(18, w, h)
list2 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list3 = obj.CheckCollisionsAsList(27, w, h)
list3 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
obj.CheckCollisionsAsGrid(0, w, h) mustEqual list0
obj.CheckCollisionsAsGrid(9, w, h) mustEqual list1
obj.CheckCollisionsAsGrid(18, w, h) mustEqual list2
obj.CheckCollisionsAsGrid(27, w, h) mustEqual list3
obj.Clear()
ok
}
@ -113,14 +161,100 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(27, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(19, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(9, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(Nil)
obj.CheckCollisionsAsGrid(27, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(19, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(9, w, h) mustEqual Success(1 :: Nil)
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(Nil)
val list0 = obj.CheckCollisionsAsList(27, w, h)
list0 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list1 = obj.CheckCollisionsAsList(18, w, h)
list1 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list2 = obj.CheckCollisionsAsList(9, w, h)
list2 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list3 = obj.CheckCollisionsAsList(0, w, h)
list3 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
obj.CheckCollisionsAsGrid(27, w, h) mustEqual list0
obj.CheckCollisionsAsGrid(18, w, h) mustEqual list1
obj.CheckCollisionsAsGrid(9, w, h) mustEqual list2
obj.CheckCollisionsAsGrid(0, w, h) mustEqual list3
obj.Clear()
ok
}
"check for item collision (diagonal insert)" in {
/*
Number indicates upper-left corner of attempted 3x3 insertion by list#
0 - - - - - 2 - - - - -
- 1 - - - 3 - - - - - -
- - - - - - - - - - - -
- - - X X X - - - - - -
- - - X X X - - - - - -
- 5 - X X 7 - - - - - -
4 - - - - - 6 - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - -
*/
val obj : GridInventory = GridInventory(12, 9)
obj += 39 -> bullet9mmBox1
obj.Capacity mustEqual 99 //108 - 9
val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height
val list0 = obj.CheckCollisionsAsList(0, w, h)
list0 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
val list1 = obj.CheckCollisionsAsList(13, w, h)
list1 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list2 = obj.CheckCollisionsAsList(6, w, h)
list2 match {
case scala.util.Success(list) =>list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
val list3 = obj.CheckCollisionsAsList(17, w, h)
list3 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list4 = obj.CheckCollisionsAsList(72, w, h)
list4 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
val list5 = obj.CheckCollisionsAsList(61, w, h)
list5 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
val list6 = obj.CheckCollisionsAsList(78, w, h)
list6 match {
case scala.util.Success(list) => list.isEmpty mustEqual true
case scala.util.Failure(_) => ko
}
val list7 = obj.CheckCollisionsAsList(65, w, h)
list7 match {
case scala.util.Success(list) => list.length mustEqual 1
case scala.util.Failure(_) => ko
}
obj.CheckCollisionsAsGrid(0, w, h) mustEqual list0
obj.CheckCollisionsAsGrid(13, w, h) mustEqual list1
obj.CheckCollisionsAsGrid(6, w, h) mustEqual list2
obj.CheckCollisionsAsGrid(17, w, h) mustEqual list3
obj.CheckCollisionsAsGrid(72, w, h) mustEqual list4
obj.CheckCollisionsAsGrid(61, w, h) mustEqual list5
obj.CheckCollisionsAsGrid(78, w, h) mustEqual list6
obj.CheckCollisionsAsGrid(65, w, h) mustEqual list7
obj.Clear()
ok
}

View file

@ -21,6 +21,7 @@ import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import scala.annotation.tailrec
import scala.util.Success
class WorldSessionActor extends Actor with MDCContextAware {
private[this] val log = org.log4s.getLogger
@ -857,50 +858,68 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Find(item_guid) match {
case Some(index) =>
val indexSlot = player.Slot(index)
var itemOpt = indexSlot.Equipment //use this to short circuit
val item = itemOpt.get
val destSlot = player.Slot(dest)
val item = indexSlot.Equipment.get
val destItem = destSlot.Equipment
indexSlot.Equipment = None
destSlot.Equipment = None
(destSlot.Equipment = item) match {
case Some(_) => //move item
log.info(s"MoveItem: $item_guid moved from $avatar_guid_1 @ $index to $avatar_guid_1 @ $dest")
//continue on to the code following the next match statement after resolving the match statement
destItem match {
case Some(item2) => //second item to swap?
(indexSlot.Equipment = destItem) match {
case Some(_) => //yes, swap
log.info(s"MoveItem: ${item2.GUID} swapped to $avatar_guid_1 @ $index")
//we must shuffle items around cleanly to avoid causing icons to "disappear"
if(index == Player.FreeHandSlot) { //temporarily put in safe location, A -> C
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground
}
else {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item.GUID, Player.FreeHandSlot))) //free hand
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item2.GUID, index))) //B -> A
if(0 <= index && index < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, index, item2))
}
case None => //can't complete the swap; drop the other item on the ground
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, player.Position, 0f, 0f, player.Orientation.z))) //ground
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, player.Position, player.Orientation, item2))
}
case None => ; //just move item over
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(avatar_guid_1, item_guid, dest)))
if(0 <= dest && dest < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item))
}
case None => //restore original contents
indexSlot.Equipment = item
destSlot.Equipment = destItem
val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) {
destSlot.Equipment match {
case Some(found) =>
Some(InventoryItem(found, dest))
case None =>
None
}
}
case None => ;
else {
val tile = item.Definition.Tile
player.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height) match {
case Success(Nil) => None //no item swap
case Success(entry :: Nil) => Some(entry) //one item to swap
case Success(_) | scala.util.Failure(_) => itemOpt = None; None //abort item move altogether
}
}
if(itemOpt.isDefined) {
log.info(s"MoveItem: $item_guid moved from $avatar_guid_1 @ $index to $avatar_guid_1 @ $dest")
indexSlot.Equipment = None
destItem match { //do we have a swap item?
case Some(entry) => //yes, swap
val item2 = entry.obj
player.Slot(entry.start).Equipment = None //remove item2 to make room for item
destSlot.Equipment = item //in case dest and index could block each other
(indexSlot.Equipment = entry.obj) match {
case Some(_) => //item and item2 swapped places successfully
log.info(s"MoveItem: ${item2.GUID} swapped to $avatar_guid_1 @ $index")
//we must shuffle items around cleanly to avoid causing icons to "disappear"
if(index == Player.FreeHandSlot) { //temporarily put in safe location, A -> C
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground
}
else {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item.GUID, Player.FreeHandSlot))) //free hand
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(player.GUID, item2.GUID, index))) //B -> A
if(0 <= index && index < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, index, item2))
}
case None => //item2 does not fit; drop on ground
val pos = player.Position
val orient = player.Orientation
DropItemOnGround(item2, pos, player.Orientation)
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, orient.z))) //ground
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2))
}
case None => //just move item over
destSlot.Equipment = item
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(avatar_guid_1, item_guid, dest)))
if(0 <= dest && dest < 5) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item))
}
}
case None =>
log.info(s"MoveItem: $avatar_guid_1 wanted to move the item $item_guid but could not find it")
}
case msg @ ChangeAmmoMessage(item_guid, unk1) =>