diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
index c7a39da0..b56dc405 100644
--- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala
+++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
@@ -257,21 +257,28 @@ object AvatarActor {
}
/**
- * Transform from encoded inventory data as a CLOB - character large object - into individual items.
- * Install those items into positions in a target container
- * in the same positions in which they were previously recorded.
- *
- * There is no guarantee that the structure of the retained container data encoded in the CLOB
- * will fit the current dimensions of the container.
- * No tests are performed.
- * A partial decompression of the CLOB may occur.
- * @param container the container in which to place the pieces of equipment produced from the CLOB
- * @param clob the inventory data in string form
- * @param log a reference to a logging context
- */
- def buildContainedEquipmentFromClob(container: Container, clob: String, log: org.log4s.Logger): Unit = {
+ * Transform from encoded inventory data as a CLOB - character large object - into individual items.
+ * Install those items into positions in a target container
+ * in the same positions in which they were previously recorded.
+ *
+ * There is no guarantee that the structure of the retained container data encoded in the CLOB
+ * will fit the current dimensions of the container.
+ * No tests are performed.
+ * A partial decompression of the CLOB may occur.
+ * @param container the container in which to place the pieces of equipment produced from the CLOB
+ * @param clob the inventory data in string form
+ * @param log a reference to a logging context
+ * @param restoreAmmo by default, when `false`, use the maximum ammunition for all ammunition boixes and for all tools;
+ * if `true`, load the last saved ammunition count for all ammunition boxes and for all tools
+ */
+ def buildContainedEquipmentFromClob(
+ container: Container,
+ clob: String,
+ log: org.log4s.Logger,
+ restoreAmmo: Boolean = false
+ ): Unit = {
clob.split("/").filter(_.trim.nonEmpty).foreach { value =>
- val (objectType, objectIndex, objectId, toolAmmo) = value.split(",") match {
+ val (objectType, objectIndex, objectId, ammoData) = value.split(",") match {
case Array(a, b: String, c: String) => (a, b.toInt, c.toInt, None)
case Array(a, b: String, c: String, d) => (a, b.toInt, c.toInt, Some(d))
case _ =>
@@ -281,11 +288,30 @@ object AvatarActor {
objectType match {
case "Tool" =>
- container.Slot(objectIndex).Equipment =
- Tool(DefinitionUtil.idToDefinition(objectId).asInstanceOf[ToolDefinition])
+ val tool = Tool(DefinitionUtil.idToDefinition(objectId).asInstanceOf[ToolDefinition])
+ //previous ammunition loaded into each sub-magazine
+ ammoData foreach { toolAmmo =>
+ toolAmmo.split("_").drop(1).foreach { value =>
+ val (ammoSlots, ammoTypeIndex, ammoBoxDefinition, ammoCount) = value.split("-") match {
+ case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt, None)
+ case Array(a: String, b: String, c: String, d:String) => (a.toInt, b.toInt, c.toInt, Some(d.toInt))
+ }
+ val fireMode = tool.AmmoSlots(ammoSlots)
+ fireMode.AmmoTypeIndex = ammoTypeIndex
+ fireMode.Box = AmmoBox(AmmoBoxDefinition(ammoBoxDefinition))
+ ammoCount.collect {
+ case count if restoreAmmo => fireMode.Magazine = count
+ }
+ }
+ }
+ container.Slot(objectIndex).Equipment = tool
case "AmmoBox" =>
- container.Slot(objectIndex).Equipment =
- AmmoBox(DefinitionUtil.idToDefinition(objectId).asInstanceOf[AmmoBoxDefinition])
+ val box = AmmoBox(DefinitionUtil.idToDefinition(objectId).asInstanceOf[AmmoBoxDefinition])
+ container.Slot(objectIndex).Equipment = box
+ //previous capacity of ammunition box
+ ammoData.collect {
+ case count if restoreAmmo => box.Capacity = count.toInt
+ }
case "ConstructionItem" =>
container.Slot(objectIndex).Equipment = ConstructionItem(
DefinitionUtil.idToDefinition(objectId).asInstanceOf[ConstructionItemDefinition]
@@ -301,18 +327,6 @@ object AvatarActor {
case name =>
log.error(s"failing to add unknown equipment to a container - $name")
}
-
- toolAmmo foreach { toolAmmo =>
- toolAmmo.split("_").drop(1).foreach { value =>
- val (ammoSlots, ammoTypeIndex, ammoBoxDefinition) = value.split("-") match {
- case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt)
- }
- container.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).AmmoTypeIndex =
- ammoTypeIndex
- container.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).Box =
- AmmoBox(AmmoBoxDefinition(ammoBoxDefinition))
- }
- }
}
}
@@ -427,9 +441,11 @@ object AvatarActor {
val ammoInfo: String = equipment match {
case tool: Tool =>
tool.AmmoSlots.zipWithIndex.collect {
- case (ammoSlot, index2) if ammoSlot.AmmoTypeIndex != 0 =>
- s"_$index2-${ammoSlot.AmmoTypeIndex}-${ammoSlot.AmmoType.id}"
+ case (ammoSlot, index2) =>
+ s"_$index2-${ammoSlot.AmmoTypeIndex}-${ammoSlot.AmmoType.id}-${ammoSlot.Magazine}"
}.mkString
+ case ammo: AmmoBox =>
+ s"${ammo.Capacity}"
case _ =>
""
}
@@ -1795,7 +1811,7 @@ class AvatarActor(
saved <- AvatarActor.loadSavedAvatarData(avatarId)
} yield (loadouts, implants, certs, locker, friends, ignored, shortcuts, saved)
result.onComplete {
- case Success((_loadouts, implants, certs, locker, friendsList, ignoredList, shortcutList, saved)) =>
+ case Success((_loadouts, implants, certs, lockerInv, friendsList, ignoredList, shortcutList, saved)) =>
//shortcuts must have a hotbar option for each implant
// val implantShortcuts = shortcutList.filter {
// case Some(e) => e.purpose == 0
@@ -1821,7 +1837,7 @@ class AvatarActor(
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
shortcuts = shortcutList,
- locker = locker,
+ locker = lockerInv,
people = MemberLists(
friend = friendsList,
ignored = ignoredList
@@ -2273,7 +2289,12 @@ class AvatarActor(
def storeLocker(): Unit = {
log.debug(s"saving locker contents belonging to ${avatar.name}")
- pushLockerClobToDataBase(AvatarActor.encodeLockerClob(avatar.locker))
+ pushLockerClobToDataBase(AvatarActor.encodeLockerClob(avatar.locker)).onComplete {
+ case Failure(e) =>
+ saveLockerFunc = doNotStoreLocker
+ log.error(e)("db failure")
+ case _ => ()
+ }
}
def pushLockerClobToDataBase(items: String): Database.ctx.Result[Database.ctx.RunActionResult] = {
@@ -2435,35 +2456,27 @@ class AvatarActor(
}
def loadLocker(charId: Long): Future[LockerContainer] = {
- val locker = Avatar.makeLocker()
- var notLoaded: Boolean = false
import ctx._
- val out = ctx.run(query[persistence.Locker]
- .filter(_.avatarId == lift(charId)))
- .map { entry =>
- notLoaded = false
- entry.foreach { contents => AvatarActor.buildContainedEquipmentFromClob(locker, contents.items, log) }
- }
- .map { _ => locker }
- out.onComplete {
+ val locker = Avatar.makeLocker()
+ saveLockerFunc = storeLocker
+ val out = Promise[LockerContainer]()
+ ctx.run(query[persistence.Locker].filter(_.avatarId == lift(charId)))
+ .onComplete {
+ case Success(entry) if entry.nonEmpty =>
+ AvatarActor.buildContainedEquipmentFromClob(locker, entry.head.items, log, restoreAmmo = true)
+ out.completeWith(Future(locker))
case Success(_) =>
- saveLockerFunc = storeLocker
- case Failure(_) =>
- notLoaded = true
+ //no locker, or maybe default empty locker?
+ ctx.run(query[persistence.Locker].insert(_.avatarId -> lift(avatar.id), _.items -> lift("")))
+ .onComplete {
+ _ => out.completeWith(Future(locker))
+ }
+ case Failure(e) =>
+ saveLockerFunc = doNotStoreLocker
+ log.error(e)("db failure")
+ out.tryFailure(e)
}
- if (notLoaded) {
- //default empty locker
- ctx.run(query[persistence.Locker]
- .insert(_.avatarId -> lift(avatar.id), _.items -> lift("")))
- .onComplete {
- case Success(_) =>
- saveLockerFunc = storeLocker
- case Failure(e) =>
- saveLockerFunc = doNotStoreLocker
- log.error(e)("db failure")
- }
- }
- out
+ out.future
}
diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
index 60dc17df..7318e6be 100644
--- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
@@ -1707,9 +1707,9 @@ class ZoningOperations(
if (hasHealthUponLogin) {
player.Spawn()
player.Health = health
- player.Armor = results.armor
player.ExoSuit = ExoSuitType(results.exosuitNum)
- AvatarActor.buildContainedEquipmentFromClob(player, results.loadout, log)
+ player.Armor = results.armor
+ AvatarActor.buildContainedEquipmentFromClob(player, results.loadout, log, restoreAmmo = true)
if (player.ExoSuit == ExoSuitType.MAX) {
player.DrawnSlot = 0
player.ResistArmMotion(PlayerControl.maxRestriction)
diff --git a/src/main/scala/net/psforever/objects/AmmoBox.scala b/src/main/scala/net/psforever/objects/AmmoBox.scala
index f0372601..0a40f836 100644
--- a/src/main/scala/net/psforever/objects/AmmoBox.scala
+++ b/src/main/scala/net/psforever/objects/AmmoBox.scala
@@ -5,9 +5,11 @@ import net.psforever.objects.definition.AmmoBoxDefinition
import net.psforever.objects.equipment.{Ammo, Equipment}
import net.psforever.types.PlanetSideEmpire
-class AmmoBox(private val ammoDef: AmmoBoxDefinition, cap: Option[Int] = None) extends Equipment {
- private var capacity = if (cap.isDefined) { AmmoBox.limitCapacity(cap.get, 1) }
- else { FullCapacity }
+class AmmoBox(private val ammoDef: AmmoBoxDefinition, private val cap: Option[Int] = None) extends Equipment {
+ private var capacity = cap match {
+ case Some(toCount) => AmmoBox.limitCapacity(toCount, min=1)
+ case _ => FullCapacity
+ }
def AmmoType: Ammo.Value = ammoDef.AmmoType
diff --git a/src/main/scala/net/psforever/objects/inventory/LocallyRegisteredInventory.scala b/src/main/scala/net/psforever/objects/inventory/LocallyRegisteredInventory.scala
index 452b8c63..dbb90df8 100644
--- a/src/main/scala/net/psforever/objects/inventory/LocallyRegisteredInventory.scala
+++ b/src/main/scala/net/psforever/objects/inventory/LocallyRegisteredInventory.scala
@@ -17,7 +17,7 @@ import scala.util.{Failure, Success}
* The equipment must not already be registered to another unique number system for that reason.
* Upon being removed, the removed equipment is unregistered.
* The registration system adds another unspoken layer to `Capacity`
- * as it imposes a total object count to the inventory.
+ * as it imposes a total object count to the inventory based on he number of unique identifiers available.
* @see `NumberSourceHub`
* @see `RandomSelector`
* @see `SpecificNumberSource`
@@ -32,7 +32,7 @@ class LocallyRegisteredInventory(numbers: Iterable[Int])
numHub
}
- override def Insert(start : Int, obj : Equipment) : Boolean = {
+ override def Insert(start: Int, obj: Equipment): Boolean = {
if(!obj.HasGUID) {
registerEquipment(obj) match {
case true if super.Insert(start, obj) =>
@@ -49,7 +49,7 @@ class LocallyRegisteredInventory(numbers: Iterable[Int])
}
}
- override def InsertQuickly(start : Int, obj : Equipment) : Boolean = {
+ override def InsertQuickly(start: Int, obj: Equipment): Boolean = {
if(!obj.HasGUID) {
registerEquipment(obj) match {
case true if super.InsertQuickly(start, obj) =>
@@ -66,7 +66,7 @@ class LocallyRegisteredInventory(numbers: Iterable[Int])
}
}
- override def Remove(guid : PlanetSideGUID) : Boolean = {
+ override def Remove(guid: PlanetSideGUID): Boolean = {
hub(guid) match {
case Some(obj: Equipment) if super.Remove(guid) =>
unregisterEquipment(obj)
@@ -75,7 +75,7 @@ class LocallyRegisteredInventory(numbers: Iterable[Int])
}
}
- override def Remove(index : Int) : Boolean = {
+ override def Remove(index: Int): Boolean = {
Slot(index).Equipment match {
case Some(obj: Equipment) if super.Remove(obj.GUID) =>
unregisterEquipment(obj)
@@ -84,7 +84,7 @@ class LocallyRegisteredInventory(numbers: Iterable[Int])
}
}
- override def Clear() : List[InventoryItem] = {
+ override def Clear(): List[InventoryItem] = {
val items = super.Clear()
items.foreach { item => unregisterEquipment(item.obj) }
items