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 Size
} }
/**
* Determine what `Equipment` is stowed in the given position.
* @return the `Equipment` in this slot
*/
def Equipment : Option[Equipment] = tool 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] = { def Equipment_=(assignEquipment : Equipment) : Option[Equipment] = {
Equipment = Some(assignEquipment) 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] = { 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(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)) { if(tool.isEmpty && EquipmentSize.isEqual(size, assignEquipment.get.Size)) {

View file

@ -1117,10 +1117,12 @@ object GlobalDefinitions {
val val
flail_targeting_laser = SimpleItemDefinition(SItem.flail_targeting_laser) flail_targeting_laser = SimpleItemDefinition(SItem.flail_targeting_laser)
flail_targeting_laser.Packet = new CommandDetonaterConverter flail_targeting_laser.Packet = new CommandDetonaterConverter
flail_targeting_laser.Tile = InventoryTile.Tile33
val val
command_detonater = SimpleItemDefinition(SItem.command_detonater) command_detonater = SimpleItemDefinition(SItem.command_detonater)
command_detonater.Packet = new CommandDetonaterConverter command_detonater.Packet = new CommandDetonaterConverter
command_detonater.Tile = InventoryTile.Tile33
val val
ace = ConstructionItemDefinition(CItem.Unit.ace) 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> * Test whether a given piece of `Equipment` would collide with any stowed content in the inventory.
* <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 start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested * @param w the width of the `Equipment` to be tested
* @param h the height 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]) Success(List.empty[Int])
} }
else { else {
def check : (Int,Int,Int) => Try[List[Int]] = if(items.size < w * h) { CheckCollisionsAsList } else { CheckCollisionsAsGrid } CheckCollisionsVar(start, w, h) match {
check(start, w, h) 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 start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested * @param w the width of the `Equipment` to be tested
* @param h the height 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 * @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 actualSlot = start - offset
val startx : Int = actualSlot % width val startx : Int = actualSlot % width
val starty : 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")) Failure(new IndexOutOfBoundsException(s"requested region escapes the $bounds edge of the grid inventory - $startx, $starty; $w x $h"))
} }
else { else {
val collisions : mutable.Set[Int] = mutable.Set[Int]() val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
items.values.foreach({ item : InventoryItem => items.values.foreach({ item : InventoryItem =>
val actualItemStart : Int = item.start - offset val actualItemStart : Int = item.start - offset
val itemx : Int = actualItemStart % width 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 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 } val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.height > starty } else { itemy <= starth }
if(clipsOnX && clipsOnY) { if(clipsOnX && clipsOnY) {
collisions += item.GUID.guid collisions += item
} }
}) })
Success(collisions.toList) Success(collisions.toList)
@ -183,10 +203,10 @@ class GridInventory {
* @param start the cell index to test this `Equipment` for insertion * @param start the cell index to test this `Equipment` for insertion
* @param w the width of the `Equipment` to be tested * @param w the width of the `Equipment` to be tested
* @param h the height 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 * @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 val actualSlot = start - offset
if(actualSlot < 0 || actualSlot >= grid.length || (actualSlot % width) + w > width || (actualSlot / width) + h > height) { if(actualSlot < 0 || actualSlot >= grid.length || (actualSlot % width) + w > width || (actualSlot / width) + h > height) {
val startx : Int = actualSlot % width 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")) Failure(new IndexOutOfBoundsException(s"requested region escapes the $bounds edge of the grid inventory - $startx, $starty; $w x $h"))
} }
else { else {
val collisions : mutable.Set[Int] = mutable.Set[Int]() val collisions : mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
var curr = actualSlot var curr = actualSlot
for(_ <- 0 until h) { for(_ <- 0 until h) {
for(col <- 0 until w) { for(col <- 0 until w) {
if(grid(curr + col) > -1) { if(grid(curr + col) > -1) {
collisions += items(grid(curr + col)).GUID.guid collisions += items(grid(curr + col))
} }
} }
curr += width curr += width

View file

@ -4,40 +4,57 @@ package net.psforever.objects.inventory
import net.psforever.objects.OffhandEquipmentSlot import net.psforever.objects.OffhandEquipmentSlot
import net.psforever.objects.equipment.{Equipment, EquipmentSize} import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import scala.util.{Failure, Success}
/** /**
* A slot-like interface for a specific grid position in an inventory. * 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`. * The size is typically 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`. * The capacity of this `EquipmentSlot` is essentially treated as 1x1.
* An object added to the underlying inventory from here can only be added with its initial point at `slot`. * Upon insertions, however, the capacity temporarily is treated as the size of the item being inserted (unless `None`).
* An object found at `slot`, however, can be removed even if the starting cell is prior to `slot.` * 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) { 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] = { override def Equipment_=(assignEquipment : Option[Equipment]) : Option[Equipment] = {
assignEquipment match { assignEquipment match {
case Some(equip) => 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 => case None =>
inv -= slot inv -= slot
} }
Equipment 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] = { override def Equipment : Option[Equipment] = {
inv.Items.find({ case ((_, item : InventoryItem)) => item.start == slot }) match { inv.CheckCollisionsAsGrid(slot,1,1) match {
case Some((_, item : InventoryItem)) => case Success(list) =>
Some(item.obj) list.headOption match {
case None => case Some(found) =>
Some(found.obj)
case None =>
None
}
case Failure(_) =>
None None
} }
} }
} }

View file

@ -59,14 +59,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45 obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(1 :: Nil) val list0 = obj.CheckCollisionsAsList(0, w, h)
obj.CheckCollisionsAsList(1, w, h) mustEqual Success(1 :: Nil) list0 match {
obj.CheckCollisionsAsList(2, w, h) mustEqual Success(1 :: Nil) case scala.util.Success(list) => list.length mustEqual 1
obj.CheckCollisionsAsList(3, w, h) mustEqual Success(Nil) case scala.util.Failure(_) => ko
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(1 :: Nil) }
obj.CheckCollisionsAsGrid(1, w, h) mustEqual Success(1 :: Nil) val list1 = obj.CheckCollisionsAsList(1, w, h)
obj.CheckCollisionsAsGrid(2, w, h) mustEqual Success(1 :: Nil) list1 match {
obj.CheckCollisionsAsGrid(3, w, h) mustEqual Success(Nil) 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() obj.Clear()
ok ok
} }
@ -77,14 +93,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45 obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(3, w, h) mustEqual Success(1 :: Nil) val list0 = obj.CheckCollisionsAsList(3, w, h)
obj.CheckCollisionsAsList(2, w, h) mustEqual Success(1 :: Nil) list0 match {
obj.CheckCollisionsAsList(1, w, h) mustEqual Success(1 :: Nil) case scala.util.Success(list) => list.length mustEqual 1
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(Nil) case scala.util.Failure(_) => ko
obj.CheckCollisionsAsGrid(3, w, h) mustEqual Success(1 :: Nil) }
obj.CheckCollisionsAsGrid(2, w, h) mustEqual Success(1 :: Nil) val list1 = obj.CheckCollisionsAsList(2, w, h)
obj.CheckCollisionsAsGrid(1, w, h) mustEqual Success(1 :: Nil) list1 match {
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(Nil) 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() obj.Clear()
ok ok
} }
@ -95,14 +127,30 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45 obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(1 :: Nil) val list0 = obj.CheckCollisionsAsList(0, w, h)
obj.CheckCollisionsAsList(9, w, h) mustEqual Success(1 :: Nil) list0 match {
obj.CheckCollisionsAsList(18, w, h) mustEqual Success(1 :: Nil) case scala.util.Success(list) => list.length mustEqual 1
obj.CheckCollisionsAsList(27, w, h) mustEqual Success(Nil) case scala.util.Failure(_) => ko
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(1 :: Nil) }
obj.CheckCollisionsAsGrid(9, w, h) mustEqual Success(1 :: Nil) val list1 = obj.CheckCollisionsAsList(9, w, h)
obj.CheckCollisionsAsGrid(18, w, h) mustEqual Success(1 :: Nil) list1 match {
obj.CheckCollisionsAsGrid(27, w, h) mustEqual Success(Nil) 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() obj.Clear()
ok ok
} }
@ -113,14 +161,100 @@ class InventoryTest extends Specification {
obj.Capacity mustEqual 45 obj.Capacity mustEqual 45
val w = bullet9mmBox2.Tile.width val w = bullet9mmBox2.Tile.width
val h = bullet9mmBox2.Tile.height val h = bullet9mmBox2.Tile.height
obj.CheckCollisionsAsList(27, w, h) mustEqual Success(1 :: Nil) val list0 = obj.CheckCollisionsAsList(27, w, h)
obj.CheckCollisionsAsList(19, w, h) mustEqual Success(1 :: Nil) list0 match {
obj.CheckCollisionsAsList(9, w, h) mustEqual Success(1 :: Nil) case scala.util.Success(list) => list.length mustEqual 1
obj.CheckCollisionsAsList(0, w, h) mustEqual Success(Nil) case scala.util.Failure(_) => ko
obj.CheckCollisionsAsGrid(27, w, h) mustEqual Success(1 :: Nil) }
obj.CheckCollisionsAsGrid(19, w, h) mustEqual Success(1 :: Nil) val list1 = obj.CheckCollisionsAsList(18, w, h)
obj.CheckCollisionsAsGrid(9, w, h) mustEqual Success(1 :: Nil) list1 match {
obj.CheckCollisionsAsGrid(0, w, h) mustEqual Success(Nil) 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() obj.Clear()
ok ok
} }

View file

@ -21,6 +21,7 @@ import net.psforever.packet.game.objectcreate._
import net.psforever.types._ import net.psforever.types._
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.util.Success
class WorldSessionActor extends Actor with MDCContextAware { class WorldSessionActor extends Actor with MDCContextAware {
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
@ -857,50 +858,68 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Find(item_guid) match { player.Find(item_guid) match {
case Some(index) => case Some(index) =>
val indexSlot = player.Slot(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 destSlot = player.Slot(dest)
val item = indexSlot.Equipment.get
val destItem = destSlot.Equipment
indexSlot.Equipment = None
destSlot.Equipment = None
(destSlot.Equipment = item) match { val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) {
case Some(_) => //move item destSlot.Equipment match {
log.info(s"MoveItem: $item_guid moved from $avatar_guid_1 @ $index to $avatar_guid_1 @ $dest") case Some(found) =>
//continue on to the code following the next match statement after resolving the match statement Some(InventoryItem(found, dest))
destItem match { case None =>
case Some(item2) => //second item to swap? None
(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
} }
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) => case msg @ ChangeAmmoMessage(item_guid, unk1) =>