working vehicle loadouts from repair/rearm silos; code documentation and expanded tests

This commit is contained in:
FateJH 2018-05-16 00:27:25 -04:00
parent 2a4fe4865e
commit a513f4996e
24 changed files with 601 additions and 200 deletions

View file

@ -468,6 +468,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
case VehicleResponse.InventoryState2(obj_guid, parent_guid, value) =>
if(tplayer_guid != guid) {
sendResponse(InventoryStateMessage(obj_guid, 0, parent_guid, value))
}
case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) =>
sendResponse(DismountVehicleMsg(guid, unk1, unk2))
if(tplayer_guid == guid) {
@ -819,7 +824,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
//TODO optimizations against replacing Equipment with the exact same Equipment and potentially for recycling existing Equipment
log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}")
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true))
val dropPred = DropPredicate(tplayer)
val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred)
val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred)
@ -838,7 +843,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
taskResolver ! GUIDTask.UnregisterEquipment(elem.obj)(continent.GUID)
})
//report change
sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, 0))
sendResponse(ArmorChangedMessage(tplayer.GUID, exosuit, subtype))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 4, tplayer.Armor))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor))
@ -882,44 +887,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
})
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
case Terminal.VehicleLoadout(definition, weapons, inventory) =>
log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}")
log.info(s"Vehicle: $definition")
log.info(s"Weapons (${weapons.size}): $weapons")
log.info(s"Inventory (${inventory.size}): $inventory")
LocalVehicle match {
FindLocalVehicle match {
case Some(vehicle) =>
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, true))
val (_, afterInventory) = inventory.partition( DropPredicate(tplayer) ) //dropped items are lost
//common action - remove old inventory
//remove old inventory
val deleteEquipment : (Int,Equipment)=>Unit = DeleteEquipmentFromVehicle(vehicle)
vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) =>
deleteEquipment(index, obj)
taskResolver ! GUIDTask.UnregisterEquipment(obj)(continent.GUID)
})
vehicle.Inventory.Clear().foreach({ case InventoryItem(obj, index) => deleteEquipment(index, obj) })
val stowEquipment : (Int,Equipment)=>TaskResolver.GiveTask = StowNewEquipmentInVehicle(vehicle)
(if(vehicle.Definition == definition) {
//vehicles are the same type; transfer over weapons
vehicle.Weapons
.filter({ case (_, slot) => slot.Equipment.nonEmpty })
.foreach({ case (_, slot) =>
val equipment = slot.Equipment.get
slot.Equipment = None
val equipment_guid = equipment.GUID
sendResponse(ObjectDeleteMessage(equipment_guid, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, equipment_guid))
taskResolver ! GUIDTask.UnregisterEquipment(equipment)(continent.GUID)
})
//vehicles are the same type; transfer over weapon ammo
//TODO ammo switching? no vehicle weapon does that currently but ...
//TODO want to completely swap weapons, but holster icon vanishes temporarily after swap
//TODO BFR arms must be swapped properly
val channel = s"${vehicle.Actor}"
weapons.foreach({ case InventoryItem(obj, index) =>
//create weapons and share with everyone
taskResolver ! PutNewWeaponInVehicleSlot(vehicle, obj.asInstanceOf[Tool], index)
val savedWeapon = obj.asInstanceOf[Tool]
val existingWeapon = vehicle.Weapons(index).Equipment.get.asInstanceOf[Tool]
(0 until existingWeapon.MaxAmmoSlot).foreach({ index =>
val existingBox = existingWeapon.AmmoSlots(index).Box
existingBox.Capacity = savedWeapon.AmmoSlots(index).Box.Capacity
//use VehicleAction.InventoryState2; VehicleAction.InventoryState temporarily glitches ammo count in ui
vehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState2(PlanetSideGUID(0), existingBox.GUID, existingWeapon.GUID, existingBox.Capacity))
})
})
afterInventory
}
else {
//do not transfer over weapons
//do not transfer over weapon ammo
if(vehicle.Definition.TrunkSize == definition.TrunkSize && vehicle.Definition.TrunkOffset == definition.TrunkOffset) {
afterInventory
}
@ -934,6 +932,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
case None =>
log.error(s"can not apply the loadout - can not find a vehicle")
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Loadout, false))
}
case Terminal.LearnCertification(cert, cost) =>
@ -2123,7 +2122,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None =>
None
})
.orElse(LocalVehicle match {
.orElse(FindLocalVehicle match {
case Some(parent) =>
findFunc(parent)
case None =>
@ -2735,46 +2734,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
def PutNewWeaponInVehicleSlot(target : Vehicle, obj : Tool, index : Int) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(
new Task() {
private val localTarget = target
private val localIndex = index
private val localObject = obj
private val localAnnounce = self
private val localAvatarService = avatarService
private val localVehicleService = vehicleService
override def isComplete : Task.Resolution.Value = {
if(localTarget.Slot(localIndex).Equipment.contains(localObject)) {
Task.Resolution.Success
}
else {
Task.Resolution.Incomplete
}
}
def Execute(resolver : ActorRef) : Unit = {
localTarget.Slot(localIndex).Equipment = localObject
resolver ! scala.util.Success(this)
}
override def onSuccess() : Unit = {
val definition = localObject.Definition
if(localTarget.VisibleSlots.contains(localIndex)) {
localAvatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentInHand(localTarget.GUID, localTarget.GUID, localIndex, localObject))
}
val channel = s"${localTarget.Actor}"
(0 until localObject.MaxAmmoSlot).foreach({ index =>
val box = localObject.AmmoSlots(index).Box
val boxDef = box.Definition
val boxdata = boxDef.Packet.DetailedConstructorData(box).get
localVehicleService ! VehicleServiceMessage(channel, VehicleAction.InventoryState(PlanetSideGUID(0), box, localObject.GUID, index, boxdata))
})
}
}, List(GUIDTask.RegisterTool(obj)(continent.GUID)))
}
/**
* Construct tasking that coordinates the following:<br>
* 1) Remove a new piece of `Equipment` from where it is currently stored.<br>
@ -3418,6 +3377,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
/**
* Get the current `Vehicle` object that the player is riding/driving.
* The vehicle must be found solely through use of `player.VehicleSeated`.
* @return the vehicle
*/
def FindLocalVehicle : Option[Vehicle] = {
player.VehicleSeated match {
case Some(vehicle_guid) =>
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
Some(obj)
case _ =>
None
}
case None =>
None
}
}
/**
* Given an object that contains an item (`Equipment`) in its `Inventory` at a certain location,
* remove it permanently.
@ -3428,7 +3406,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/
private def DeleteEquipment(obj : PlanetSideGameObject with Container)(start : Int, item : Equipment) : Unit = {
val item_guid = item.GUID
obj.Inventory -= start
obj.Slot(start).Equipment = None
//obj.Inventory -= start
taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID)
sendResponse(ObjectDeleteMessage(item_guid, 0))
}
@ -3762,20 +3741,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
def LocalVehicle : Option[Vehicle] = {
player.VehicleSeated match {
case Some(vehicle_guid) =>
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
Some(obj)
case _ =>
None
}
case None =>
None
}
}
/**
* Perform specific operations depending on the target of deployment.
* @param obj the object that has deployed

View file

@ -16,6 +16,7 @@ object VehicleAction {
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action
final case class InventoryState2(player_guid : PlanetSideGUID, obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action
final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action

View file

@ -17,6 +17,7 @@ object VehicleResponse {
final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response
final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response

View file

@ -60,6 +60,10 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
)
case VehicleAction.InventoryState2(player_guid, obj_guid, parent_guid, value) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState2(obj_guid, parent_guid, value))
)
case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid))

View file

@ -136,6 +136,22 @@ class InventoryStateTest extends ActorTest {
}
}
class InventoryState2Test extends ActorTest {
ServiceManager.boot(system)
val tool = Tool(GlobalDefinitions.beamer)
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13)
val cdata = tool.Definition.Packet.ConstructorData(tool).get
"VehicleService" should {
"pass InventoryState2" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
service ! VehicleServiceMessage("test", VehicleAction.InventoryState2(PlanetSideGUID(10), PlanetSideGUID(11), PlanetSideGUID(12), 13))
expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.InventoryState2(PlanetSideGUID(11), PlanetSideGUID(12), 13)))
}
}
}
class KickPassengerTest extends ActorTest {
ServiceManager.boot(system)