diff --git a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala index 4d90ef8f..4eea8159 100644 --- a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala @@ -104,7 +104,7 @@ object ExoSuitDefinition { MAX.MaxArmor = 650 MAX.InventoryScale = InventoryTile.Tile1612 MAX.InventoryOffset = 6 - MAX.Holster(2, EquipmentSize.Max) + MAX.Holster(0, EquipmentSize.Max) MAX.Holster(4, EquipmentSize.Melee) def apply(suitType : ExoSuitType.Value) : ExoSuitDefinition = { diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index cbb0655e..aef64f37 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -101,6 +101,24 @@ object GlobalDefinitions { val flamethrower_ammo = AmmoBoxDefinition(Ammo.flamethrower_ammo) + val dualcycler_ammo = AmmoBoxDefinition(Ammo.dualcycler_ammo) + + val pounder_ammo = AmmoBoxDefinition(Ammo.pounder_ammo) + + val burster_ammo = AmmoBoxDefinition(Ammo.burster_ammo) + + val scattercannon_ammo = AmmoBoxDefinition(Ammo.scattercannon_ammo) + + val falcon_ammo = AmmoBoxDefinition(Ammo.falcon_ammo) + + val sparrow_ammo = AmmoBoxDefinition(Ammo.sparrow_ammo) + + val quasar_ammo = AmmoBoxDefinition(Ammo.quasar_ammo) + + val comet_ammo = AmmoBoxDefinition(Ammo.comet_ammo) + + val starfire_ammo = AmmoBoxDefinition(Ammo.starfire_ammo) + val health_canister = AmmoBoxDefinition(Ammo.health_canister) val armor_canister = AmmoBoxDefinition(Ammo.armor_canister) @@ -256,23 +274,23 @@ object GlobalDefinitions { val flamethrower = ToolDefinition(ObjectClass.flamethrower) - val trhev_dualcycler = ToolDefinition(ObjectClass.trhev_dualcycler) //TODO + val trhev_dualcycler = ToolDefinition(ObjectClass.trhev_dualcycler) - val trhev_pounder = ToolDefinition(ObjectClass.trhev_pounder) //TODO + val trhev_pounder = ToolDefinition(ObjectClass.trhev_pounder) - val trhev_burster = ToolDefinition(ObjectClass.trhev_burster) //TODO + val trhev_burster = ToolDefinition(ObjectClass.trhev_burster) - val nchev_scattercannon = ToolDefinition(ObjectClass.nchev_scattercannon) //TODO + val nchev_scattercannon = ToolDefinition(ObjectClass.nchev_scattercannon) - val nchev_falcon = ToolDefinition(ObjectClass.nchev_falcon) //TODO + val nchev_falcon = ToolDefinition(ObjectClass.nchev_falcon) - val nchev_sparrow = ToolDefinition(ObjectClass.nchev_sparrow) //TODO + val nchev_sparrow = ToolDefinition(ObjectClass.nchev_sparrow) - val vshev_quasar = ToolDefinition(ObjectClass.vshev_quasar) //TODO + val vshev_quasar = ToolDefinition(ObjectClass.vshev_quasar) - val vshev_comet = ToolDefinition(ObjectClass.vshev_comet) //TODO + val vshev_comet = ToolDefinition(ObjectClass.vshev_comet) - val vshev_starfire = ToolDefinition(ObjectClass.vshev_starfire) //TODO + val vshev_starfire = ToolDefinition(ObjectClass.vshev_starfire) val medicalapplicator = ToolDefinition(ObjectClass.medicalapplicator) @@ -637,6 +655,21 @@ object GlobalDefinitions { } } + def MAXArms(subtype : Int, faction : PlanetSideEmpire.Value) : ToolDefinition = { + if(subtype == 1) { + AIMAX(faction) + } + else if(subtype == 2) { + AVMAX(faction) + } + else if(subtype == 3) { + AAMAX(faction) + } + else { + suppressor //there are no common pool MAX arms + } + } + def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = { faction match { case PlanetSideEmpire.TR => trhev_dualcycler @@ -842,6 +875,33 @@ object GlobalDefinitions { flamethrower_ammo.Capacity = 100 flamethrower_ammo.Tile = InventoryTile.Tile44 + dualcycler_ammo.Capacity = 100 + dualcycler_ammo.Tile = InventoryTile.Tile44 + + pounder_ammo.Capacity = 50 + pounder_ammo.Tile = InventoryTile.Tile44 + + burster_ammo.Capacity = 100 + burster_ammo.Tile = InventoryTile.Tile44 + + scattercannon_ammo.Capacity = 50 + scattercannon_ammo.Tile = InventoryTile.Tile44 + + falcon_ammo.Capacity = 50 + falcon_ammo.Tile = InventoryTile.Tile44 + + sparrow_ammo.Capacity = 50 + sparrow_ammo.Tile = InventoryTile.Tile44 + + quasar_ammo.Capacity = 60 + quasar_ammo.Tile = InventoryTile.Tile44 + + comet_ammo.Capacity = 50 + comet_ammo.Tile = InventoryTile.Tile44 + + starfire_ammo.Capacity = 50 + starfire_ammo.Tile = InventoryTile.Tile44 + health_canister.Capacity = 100 health_canister.Tile = InventoryTile.Tile33 @@ -1397,24 +1457,85 @@ object GlobalDefinitions { flamethrower.FireModes(1).Magazine = 100 flamethrower.FireModes(1).Chamber = 50 flamethrower.Tile = InventoryTile.Tile63 -//TODO + trhev_dualcycler.Size = EquipmentSize.Max - trhev_dualcycler.AmmoTypes += bullet_9mm + trhev_dualcycler.AmmoTypes += dualcycler_ammo trhev_dualcycler.FireModes += new FireModeDefinition trhev_dualcycler.FireModes.head.AmmoTypeIndices += 0 trhev_dualcycler.FireModes.head.AmmoSlotIndex = 0 -//TODO + trhev_dualcycler.FireModes.head.Magazine = 200 + trhev_pounder.Size = EquipmentSize.Max - trhev_pounder.AmmoTypes += bullet_9mm + trhev_pounder.AmmoTypes += pounder_ammo trhev_pounder.FireModes += new FireModeDefinition trhev_pounder.FireModes.head.AmmoTypeIndices += 0 trhev_pounder.FireModes.head.AmmoSlotIndex = 0 -//TODO + trhev_pounder.FireModes.head.Magazine = 30 + trhev_pounder.FireModes += new FireModeDefinition + trhev_pounder.FireModes(1).AmmoTypeIndices += 0 + trhev_pounder.FireModes(1).AmmoSlotIndex = 0 + trhev_pounder.FireModes(1).Magazine = 30 + trhev_burster.Size = EquipmentSize.Max - trhev_burster.AmmoTypes += bullet_9mm + trhev_burster.AmmoTypes += burster_ammo trhev_burster.FireModes += new FireModeDefinition trhev_burster.FireModes.head.AmmoTypeIndices += 0 trhev_burster.FireModes.head.AmmoSlotIndex = 0 + trhev_burster.FireModes.head.Magazine = 40 + + nchev_scattercannon.Size = EquipmentSize.Max + nchev_scattercannon.AmmoTypes += scattercannon_ammo + nchev_scattercannon.FireModes += new FireModeDefinition + nchev_scattercannon.FireModes.head.AmmoTypeIndices += 0 + nchev_scattercannon.FireModes.head.AmmoSlotIndex = 0 + nchev_scattercannon.FireModes.head.Magazine = 40 + nchev_scattercannon.FireModes += new FireModeDefinition + nchev_scattercannon.FireModes(1).AmmoTypeIndices += 0 + nchev_scattercannon.FireModes(1).AmmoSlotIndex = 0 + nchev_scattercannon.FireModes(1).Magazine = 40 + nchev_scattercannon.FireModes += new FireModeDefinition + nchev_scattercannon.FireModes(2).AmmoTypeIndices += 0 + nchev_scattercannon.FireModes(2).AmmoSlotIndex = 0 + nchev_scattercannon.FireModes(2).Magazine = 40 + + nchev_falcon.Size = EquipmentSize.Max + nchev_falcon.AmmoTypes += falcon_ammo + nchev_falcon.FireModes += new FireModeDefinition + nchev_falcon.FireModes.head.AmmoTypeIndices += 0 + nchev_falcon.FireModes.head.AmmoSlotIndex = 0 + nchev_falcon.FireModes.head.Magazine = 20 + + nchev_sparrow.Size = EquipmentSize.Max + nchev_sparrow.AmmoTypes += sparrow_ammo + nchev_sparrow.FireModes += new FireModeDefinition + nchev_sparrow.FireModes.head.AmmoTypeIndices += 0 + nchev_sparrow.FireModes.head.AmmoSlotIndex = 0 + nchev_sparrow.FireModes.head.Magazine = 12 + + vshev_quasar.Size = EquipmentSize.Max + vshev_quasar.AmmoTypes += quasar_ammo + vshev_quasar.FireModes += new FireModeDefinition + vshev_quasar.FireModes.head.AmmoTypeIndices += 0 + vshev_quasar.FireModes.head.AmmoSlotIndex = 0 + vshev_quasar.FireModes.head.Magazine = 120 + vshev_quasar.FireModes += new FireModeDefinition + vshev_quasar.FireModes(1).AmmoTypeIndices += 0 + vshev_quasar.FireModes(1).AmmoSlotIndex = 0 + vshev_quasar.FireModes(1).Magazine = 120 + + vshev_comet.Size = EquipmentSize.Max + vshev_comet.AmmoTypes += comet_ammo + vshev_comet.FireModes += new FireModeDefinition + vshev_comet.FireModes.head.AmmoTypeIndices += 0 + vshev_comet.FireModes.head.AmmoSlotIndex = 0 + vshev_comet.FireModes.head.Magazine = 10 + + vshev_starfire.Size = EquipmentSize.Max + vshev_starfire.AmmoTypes += starfire_ammo + vshev_starfire.FireModes += new FireModeDefinition + vshev_starfire.FireModes.head.AmmoTypeIndices += 0 + vshev_starfire.FireModes.head.AmmoSlotIndex = 0 + vshev_starfire.FireModes.head.Magazine = 8 medicalapplicator.Size = EquipmentSize.Pistol medicalapplicator.AmmoTypes += health_canister @@ -1571,28 +1692,28 @@ object GlobalDefinitions { battlewagon_weapon_systema.FireModes += new FireModeDefinition battlewagon_weapon_systema.FireModes.head.AmmoTypeIndices += 0 battlewagon_weapon_systema.FireModes.head.AmmoSlotIndex = 0 - battlewagon_weapon_systema.FireModes.head.Magazine = 235 + battlewagon_weapon_systema.FireModes.head.Magazine = 240 battlewagon_weapon_systemb.Size = EquipmentSize.VehicleWeapon battlewagon_weapon_systemb.AmmoTypes += bullet_15mm battlewagon_weapon_systemb.FireModes += new FireModeDefinition battlewagon_weapon_systemb.FireModes.head.AmmoTypeIndices += 0 battlewagon_weapon_systemb.FireModes.head.AmmoSlotIndex = 0 - battlewagon_weapon_systemb.FireModes.head.Magazine = 235 + battlewagon_weapon_systemb.FireModes.head.Magazine = 240 battlewagon_weapon_systemc.Size = EquipmentSize.VehicleWeapon battlewagon_weapon_systemc.AmmoTypes += bullet_15mm battlewagon_weapon_systemc.FireModes += new FireModeDefinition battlewagon_weapon_systemc.FireModes.head.AmmoTypeIndices += 0 battlewagon_weapon_systemc.FireModes.head.AmmoSlotIndex = 0 - battlewagon_weapon_systemc.FireModes.head.Magazine = 235 + battlewagon_weapon_systemc.FireModes.head.Magazine = 240 battlewagon_weapon_systemd.Size = EquipmentSize.VehicleWeapon battlewagon_weapon_systemd.AmmoTypes += bullet_15mm battlewagon_weapon_systemd.FireModes += new FireModeDefinition battlewagon_weapon_systemd.FireModes.head.AmmoTypeIndices += 0 battlewagon_weapon_systemd.FireModes.head.AmmoSlotIndex = 0 - battlewagon_weapon_systemd.FireModes.head.Magazine = 235 + battlewagon_weapon_systemd.FireModes.head.Magazine = 240 thunderer_weapon_systema.Size = EquipmentSize.VehicleWeapon thunderer_weapon_systema.AmmoTypes += gauss_cannon_ammo @@ -1724,7 +1845,7 @@ object GlobalDefinitions { prowler_weapon_systemB.FireModes += new FireModeDefinition prowler_weapon_systemB.FireModes.head.AmmoTypeIndices += 0 prowler_weapon_systemB.FireModes.head.AmmoSlotIndex = 0 - prowler_weapon_systemB.FireModes.head.Magazine = 235 + prowler_weapon_systemB.FireModes.head.Magazine = 240 vanguard_weapon_system.Size = EquipmentSize.VehicleWeapon vanguard_weapon_system.AmmoTypes += bullet_150mm @@ -1820,7 +1941,7 @@ object GlobalDefinitions { vulture_nose_weapon_system.FireModes += new FireModeDefinition vulture_nose_weapon_system.FireModes.head.AmmoTypeIndices += 0 vulture_nose_weapon_system.FireModes.head.AmmoSlotIndex = 0 - vulture_nose_weapon_system.FireModes.head.Magazine = 75 //80? + vulture_nose_weapon_system.FireModes.head.Magazine = 75 vulture_bomb_bay.Size = EquipmentSize.VehicleWeapon vulture_bomb_bay.AmmoTypes += liberator_bomb diff --git a/common/src/main/scala/net/psforever/objects/Loadout.scala b/common/src/main/scala/net/psforever/objects/Loadout.scala index 340cb466..1d2a2d2c 100644 --- a/common/src/main/scala/net/psforever/objects/Loadout.scala +++ b/common/src/main/scala/net/psforever/objects/Loadout.scala @@ -98,7 +98,7 @@ object Loadout { packageSimplifications(player.Holsters()), packageSimplifications(player.Inventory.Items.values.toList), player.ExoSuit, - determineSubtype(player) + DetermineSubtype(player) ) } @@ -158,9 +158,9 @@ object Loadout { */ final case class ShorthandKit(kdef : KitDefinition) extends Simplification - private def determineSubtype(player : Player) : Int = { + def DetermineSubtype(player : Player) : Int = { if(player.ExoSuit == ExoSuitType.MAX) { - player.Slot(2).Equipment match { + player.Slot(0).Equipment match { case Some(item) => item.Definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar => diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala index 369a6b66..90efc893 100644 --- a/common/src/main/scala/net/psforever/objects/Player.scala +++ b/common/src/main/scala/net/psforever/objects/Player.scala @@ -162,7 +162,7 @@ class Player(private val name : String, def MaxArmor : Int = ExoSuitDefinition.Select(exosuit).MaxArmor - def VisibleSlots : Set[Int] = if(exosuit == ExoSuitType.MAX) { Set(2) } else { Set(0,1,2,3,4) } + def VisibleSlots : Set[Int] = if(exosuit == ExoSuitType.MAX) { Set(0) } else { Set(0,1,2,3,4) } override def Slot(slot : Int) : EquipmentSlot = { if(inventory.Offset <= slot && slot <= inventory.LastIndex) { diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala index ce4084ad..5984dd56 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala @@ -21,8 +21,17 @@ object EquipmentTerminalDefinition { val suits : Map[String, (ExoSuitType.Value, Int)] = Map( "standard_issue_armor" -> (ExoSuitType.Standard, 0), "lite_armor" -> (ExoSuitType.Agile, 0), - "med_armor" -> (ExoSuitType.Reinforced, 0) - //TODO max and infiltration suit + "med_armor" -> (ExoSuitType.Reinforced, 0), + "stealth_armor" -> (ExoSuitType.Infiltration, 0), + "trhev_antiaircraft" -> (ExoSuitType.MAX, 3), + "trhev_antipersonnel" -> (ExoSuitType.MAX, 1), + "trhev_antivehicular" -> (ExoSuitType.MAX, 2), + "nchev_antiaircraft" -> (ExoSuitType.MAX, 3), + "nchev_antipersonnel" -> (ExoSuitType.MAX, 1), + "nchev_antivehicular" -> (ExoSuitType.MAX, 2), + "vshev_antiaircraft" -> (ExoSuitType.MAX, 3), + "vshev_antipersonnel" -> (ExoSuitType.MAX, 1), + "vshev_antivehicular" -> (ExoSuitType.MAX, 2) ) import net.psforever.objects.GlobalDefinitions._ @@ -51,6 +60,17 @@ object EquipmentTerminalDefinition { "oicw_ammo" -> MakeAmmoBox(oicw_ammo), //scorpion missile "flamethrower_ammo" -> MakeAmmoBox(flamethrower_ammo) ) + val maxAmmo : Map[String, () => Equipment] = Map( + "dualcycler_ammo" -> MakeAmmoBox(dualcycler_ammo), + "pounder_ammo" -> MakeAmmoBox(pounder_ammo), + "burster_ammo" -> MakeAmmoBox(burster_ammo), + "scattercannon_ammo" -> MakeAmmoBox(scattercannon_ammo), + "falcon_ammo" -> MakeAmmoBox(falcon_ammo), + "sparrow_ammo" -> MakeAmmoBox(sparrow_ammo), + "quasar_ammo" -> MakeAmmoBox(quasar_ammo), + "comet_ammo" -> MakeAmmoBox(comet_ammo), + "starfire_ammo" -> MakeAmmoBox(starfire_ammo) + ) /** * A `Map` of operations for producing the `AmmoBox` `Equipment` for infantry-held utilities. * key - an identification string sent by the client diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala index cdaee2e1..973d7664 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala @@ -58,7 +58,12 @@ class OrderTerminalDefinition extends EquipmentTerminalDefinition(612) { case Some((suit, subtype)) => Terminal.BuyExosuit(suit, subtype) case None => - Terminal.NoDeal() + maxAmmo.get(msg.item_name) match { + case Some(item) => + Terminal.BuyEquipment(item()) + case None => + Terminal.NoDeal() + } } case _ => Terminal.NoDeal() diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala index 05b190e1..6f806f26 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player import net.psforever.objects.definition.ImplantDefinition +import net.psforever.objects.inventory.InventoryItem import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.types.{TransactionType, Vector3} diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala index aabb9dcd..6c901153 100644 --- a/pslogin/src/main/scala/Maps.scala +++ b/pslogin/src/main/scala/Maps.scala @@ -65,6 +65,10 @@ object Maps { LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal))) LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal))) LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal))) + LocalObject(ServerObjectBuilder(842, Terminal.Constructor(order_terminal))) + LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal))) + LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal))) + LocalObject(ServerObjectBuilder(845, Terminal.Constructor(order_terminal))) LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal))) LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal))) LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal))) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index f9b59404..a1d56bb6 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -321,7 +321,6 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateDetailedMessage(item_type, item_guid, ObjectCreateMessageParent(vehicle_guid, slot), item_data) )) -// sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, item_guid, slot))) } case VehicleResponse.UnloadVehicle(vehicle_guid) => @@ -331,30 +330,6 @@ class WorldSessionActor extends Actor with MDCContextAware { if(player.GUID != guid) { //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, 0))) -// sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(vehicle_guid, item_guid, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) - //... -// continent.GUID(vehicle_guid) match { -// case Some(veh : Vehicle) => -// veh.PassengerInSeat(player) match { -// case Some(seat_num) => -// veh.Seat(seat_num).get.ControlledWeapon match { -// case Some(weapon_num) => -// veh.Weapons.get(weapon_num) match { -// case Some(mount) => -// mount.Equipment match { -// case Some(wep : Tool) => -// val ammo = wep.AmmoSlot -// sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(ammo.Box.GUID, wep.GUID, ammo.Magazine))) -// case _ => ; -// } -// case _ => ; -// } -// case _ => ; -// } -// case _ => ; -// } -// case _ => ; -// } } case VehicleResponse.VehicleState(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) => @@ -430,7 +405,7 @@ class WorldSessionActor extends Actor with MDCContextAware { AccessContents(obj) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num)) - case Mountable.CanMount(obj : Mountable, seat_num) => + case Mountable.CanMount(obj : Mountable, _) => log.warn(s"MountVehicleMsg: $obj is some generic mountable object and nothing will happen") case Mountable.CanNotMount(obj, seat_num) => @@ -439,24 +414,33 @@ class WorldSessionActor extends Actor with MDCContextAware { case Terminal.TerminalMessage(tplayer, msg, order) => order match { - case Terminal.BuyExosuit(exosuit, subtype) => - if(tplayer.ExoSuit == exosuit) { //just refresh armor points - //we should never actually reach this point through conventional in-game methods - sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true))) + case Terminal.BuyExosuit(exosuit, subtype) => //refresh armor points + if(tplayer.ExoSuit == exosuit) { + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))) + if(Loadout.DetermineSubtype(tplayer) != subtype) { + //special case: MAX suit switching to a different MAX suit; we need to change the main weapon + sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(tplayer.GUID, exosuit, subtype))) + avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype)) + val arms = tplayer.Slot(0).Equipment.get + val putTask = PutEquipmentInSlot(tplayer, Tool(GlobalDefinitions.MAXArms(subtype, tplayer.Faction)), 0) + taskResolver ! DelayedObjectHeld(tplayer, 0, List(TaskResolver.GiveTask(putTask.task, putTask.subs :+ RemoveEquipmentFromSlot(tplayer, arms, 0)))) + } + //outside of the MAX condition above, we should seldom reach this point through conventional methods tplayer.Armor = tplayer.MaxArmor sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(tplayer.GUID, 4, tplayer.Armor))) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor)) } else { //load a complete new exo-suit and shuffle the inventory around - //TODO if we're transitioning into a MAX suit, the subtype dictates the type of arm(s) if the holster list is empty + val originalSuit = tplayer.ExoSuit //save inventory before it gets cleared (empty holsters) sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true))) - val beforeHolsters = clearHolsters(tplayer.Holsters().iterator) - val beforeInventory = tplayer.Inventory.Clear() + val dropPred = DropPredicate(tplayer) + val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred) + val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred) //change suit (clear inventory and change holster sizes; note: holsters must be empty before this point) Player.SuitSetup(tplayer, exosuit) tplayer.Armor = tplayer.MaxArmor - //delete everything + //delete everything not dropped (beforeHolsters ++ beforeInventory).foreach({ elem => sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(elem.obj.GUID, 0))) }) @@ -468,10 +452,28 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ArmorChanged(tplayer.GUID, exosuit, subtype)) sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(tplayer.GUID, 4, tplayer.Armor))) avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor)) - //fill holsters - val (afterHolsters, toInventory) = beforeHolsters.partition(elem => elem.obj.Size == tplayer.Slot(elem.start).Size) - afterHolsters.foreach({elem => tplayer.Slot(elem.start).Equipment = elem.obj }) - val finalInventory = fillEmptyHolsters(tplayer.Holsters().iterator, toInventory ++ beforeInventory) + val finalInventory = if(exosuit == ExoSuitType.MAX) { + //MAX weapon to be placed in first pistol slot; slot to be drawn + taskResolver ! DelayedObjectHeld(tplayer, 0, List(PutEquipmentInSlot(tplayer, Tool(GlobalDefinitions.MAXArms(subtype, tplayer.Faction)), 0))) + //fill melee slot + fillEmptyHolsters(List(tplayer.Slot(4)).iterator, beforeHolsters) ++ beforeInventory + } + else { + //remove potential MAX weapon + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectHeld(tplayer.GUID, Player.HandsDownSlot)) + val normalWeapons = if(originalSuit == ExoSuitType.MAX) { + val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max) + maxWeapons.foreach(entry => { taskResolver ! GUIDTask.UnregisterEquipment(entry.obj)(continent.GUID) }) + normalWeapons + } + else { + beforeHolsters + } + //fill holsters + val (afterHolsters, toInventory) = normalWeapons.partition(elem => elem.obj.Size == tplayer.Slot(elem.start).Size) + afterHolsters.foreach({elem => tplayer.Slot(elem.start).Equipment = elem.obj }) + fillEmptyHolsters(tplayer.Holsters().iterator, toInventory ++ beforeInventory) + } //draw holsters tplayer.VisibleSlots.foreach({index => tplayer.Slot(index).Equipment match { @@ -527,17 +529,17 @@ class WorldSessionActor extends Actor with MDCContextAware { //drop items on ground val pos = tplayer.Position val orient = tplayer.Orientation - drop.foreach(obj => { - obj.Position = pos - obj.Orientation = orient - val definition = obj.Definition + ((dropHolsters ++ dropInventory).map(_.obj) ++ drop).foreach(obj => { + continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z)) +// val definition = obj.Definition sendResponse( PacketCoding.CreateGamePacket(0, - ObjectCreateMessage( - definition.ObjectId, - obj.GUID, - DroppedItemData(PlacementData(pos, Vector3(0f, 0f, orient.z)), definition.Packet.ConstructorData(obj).get) - ) + ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z) +// ObjectCreateMessage( +// definition.ObjectId, +// obj.GUID, +// DroppedItemData(PlacementData(pos, Vector3(0f, 0f, orient.z)), definition.Packet.ConstructorData(obj).get) +// ) ) ) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, obj)) @@ -569,13 +571,16 @@ class WorldSessionActor extends Actor with MDCContextAware { //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(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true))) - val beforeHolsters = clearHolsters(tplayer.Holsters().iterator) - val beforeInventory = tplayer.Inventory.Clear() + val dropPred = DropPredicate(tplayer) + val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred) + val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred) + val (_, afterHolsters) = holsters.partition(dropPred) //dropped items are lost + val (_, afterInventory) = inventory.partition(dropPred) //dropped items are lost val beforeFreeHand = tplayer.FreeHand.Equipment //change suit (clear inventory and change holster sizes; note: holsters must be empty before this point) Player.SuitSetup(tplayer, exosuit) tplayer.Armor = tplayer.MaxArmor - //delete everything + //delete everything (not dropped) beforeHolsters.foreach({ elem => avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(tplayer.GUID, elem.obj.GUID)) }) @@ -606,14 +611,33 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => ; } //draw holsters - holsters.foreach(entry => { + if(exosuit == ExoSuitType.MAX) { + tplayer.DrawnSlot = 0 + val (maxWeapons, otherWeapons) = afterHolsters.partition(entry => { entry.obj.Size == EquipmentSize.Max }) + taskResolver ! DelayedObjectHeld(tplayer, 0, List(PutEquipmentInSlot(tplayer, maxWeapons.head.obj, 0))) + otherWeapons + } + else { + afterHolsters + }.foreach(entry => { taskResolver ! PutEquipmentInSlot(tplayer, entry.obj, entry.start) }) //put items into inventory - inventory.foreach(entry => { + afterInventory.foreach(entry => { taskResolver ! PutEquipmentInSlot(tplayer, entry.obj, entry.start) }) - //TODO drop items on ground + //drop stuff on ground + val pos = tplayer.Position + val orient = tplayer.Orientation + ((dropHolsters ++ dropInventory).map(_.obj)).foreach(obj => { + continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z)) + sendResponse( + PacketCoding.CreateGamePacket(0, + ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z) + ) + ) + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, obj)) + }) sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true))) case Terminal.LearnCertification(cert, cost) => @@ -1028,7 +1052,8 @@ class WorldSessionActor extends Actor with MDCContextAware { import net.psforever.objects.GlobalDefinitions._ player = Player("TestCharacter"+sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) //player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) - player.Position = Vector3(3523.039f, 2855.5078f, 90.859375f) + //player.Position = Vector3(3523.039f, 2855.5078f, 90.859375f) + player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) player.Orientation = Vector3(0f, 0f, 90f) player.Certifications += CertificationType.StandardAssault player.Certifications += CertificationType.MediumAssault @@ -1051,6 +1076,8 @@ class WorldSessionActor extends Actor with MDCContextAware { player.Certifications += CertificationType.AirSupport player.Certifications += CertificationType.GalaxyGunship player.Certifications += CertificationType.Phantasm + player.Certifications += CertificationType.UniMAX + player.Certifications += CertificationType.InfiltrationSuit AwardBattleExperiencePoints(player, 1000000L) // player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting player.Slot(0).Equipment = Tool(GlobalDefinitions.StandardPistol(player.Faction)) @@ -1342,13 +1369,11 @@ class WorldSessionActor extends Actor with MDCContextAware { } case msg @ ObjectHeldMessage(avatar_guid, held_holsters, unk1) => - val before = player.DrawnSlot - val after = player.DrawnSlot = held_holsters - if(before != after) { - val slot = if(after == Player.HandsDownSlot) { before } else { after } - avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, slot)) - } log.info("ObjectHeld: " + msg) + val before = player.DrawnSlot + if((player.DrawnSlot = held_holsters) != before) { + avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, held_holsters)) + } case msg @ AvatarJumpMessage(state) => //log.info("AvatarJump: " + msg) @@ -2133,6 +2158,45 @@ class WorldSessionActor extends Actor with MDCContextAware { ) } + /** + * After some subtasking is completed, draw a particular slot, as if an `ObjectHeldMessage` packet was sent/received.
+ *
+ * The resulting `Task` is most useful for sequencing MAX weaponry when combined with the proper subtasks. + * @param player the player + * @param index the slot to be drawn + * @param priorTasking subtasks that needs to be accomplished first + * @return a `TaskResolver.GiveTask` message + */ + private def DelayedObjectHeld(player : Player, index : Int, priorTasking : List[TaskResolver.GiveTask]) : TaskResolver.GiveTask = { + TaskResolver.GiveTask( + new Task() { + private val localPlayer = player + private val localSlot = index + private val localAnnounce = self + private val localService = avatarService + + override def isComplete : Task.Resolution.Value = { + if(localPlayer.DrawnSlot == localSlot) { + Task.Resolution.Success + } + else { + Task.Resolution.Incomplete + } + } + + def Execute(resolver : ActorRef) : Unit = { + localPlayer.DrawnSlot = localSlot + resolver ! scala.util.Success(this) + } + + override def onSuccess() : Unit = { + localAnnounce ! ResponseToSelf(PacketCoding.CreateGamePacket(0, ObjectHeldMessage(localPlayer.GUID, localSlot, true))) + localService ! AvatarServiceMessage(localPlayer.Continent, AvatarAction.ObjectHeld(localPlayer.GUID, localSlot)) + } + }, priorTasking + ) + } + /** * After a client has connected to the server, their account is used to generate a list of characters. * On the character selection screen, each of these characters is made to exist temporarily when one is selected. @@ -2286,6 +2350,18 @@ class WorldSessionActor extends Actor with MDCContextAware { }) } + /** + * A predicate used to determine if an `InventoryItem` object contains `Equipment` that should be dropped. + * Used to filter through lists of object data before it is placed into a player's inventory. + * @param tplayer the player + * @return true if the item is to be dropped; false, otherwise + */ + def DropPredicate(tplayer : Player) : (InventoryItem => Boolean) = entry => { //drop if Cavern equipment, or is another faction's exclusive equipment + val objDef = entry.obj.Definition + val faction = GlobalDefinitions.isFactionEquipment(objDef) + GlobalDefinitions.isCavernEquipment(objDef) || (faction != tplayer.Faction && faction != PlanetSideEmpire.NEUTRAL) + } + def failWithError(error : String) = { log.error(error) sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))