From 5de1c4e202bd222c95c532ace7de30296859d7de Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 4 Oct 2017 22:12:05 -0400 Subject: [PATCH] created Definition for cert_terminal, involving the definition itself and an entry in GlobalDefinitions; wired up the three terminals in the closest HART building such that the player can get and let go of certifications; fixed typo in certification enum; added a comment to PSAM --- .../psforever/objects/GlobalDefinitions.scala | 6 +- .../terminals/CertTerminalDefinition.scala | 100 ++++++++++++++++++ .../terminals/OrderTerminalDefinition.scala | 12 ++- .../objects/terminals/Terminal.scala | 10 +- .../terminals/TerminalDefinition.scala | 16 ++- .../game/PlanetsideAttributeMessage.scala | 3 +- .../psforever/types/CertificationType.scala | 6 +- pslogin/src/main/scala/PsLogin.scala | 9 +- .../src/main/scala/WorldSessionActor.scala | 27 +++++ 9 files changed, 166 insertions(+), 23 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/terminals/CertTerminalDefinition.scala diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 21bfbd07..4fccc47f 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -6,7 +6,7 @@ import net.psforever.objects.definition.converter.{CommandDetonaterConverter, Lo import net.psforever.objects.equipment.CItem.DeployedItem import net.psforever.objects.equipment._ import net.psforever.objects.inventory.InventoryTile -import net.psforever.objects.terminals.OrderTerminalDefinition +import net.psforever.objects.terminals.{CertTerminalDefinition, OrderTerminalDefinition} import net.psforever.packet.game.objectcreate.ObjectClass import net.psforever.types.PlanetSideEmpire @@ -1239,5 +1239,7 @@ object GlobalDefinitions { fury.TrunkOffset = 30 val - orderTerminal = new OrderTerminalDefinition + order_terminal = new OrderTerminalDefinition + val + cert_terminal = new CertTerminalDefinition } diff --git a/common/src/main/scala/net/psforever/objects/terminals/CertTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/terminals/CertTerminalDefinition.scala new file mode 100644 index 00000000..cc617ecb --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/terminals/CertTerminalDefinition.scala @@ -0,0 +1,100 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage +import net.psforever.types.CertificationType + +/** + * The definition for any `Terminal` that is of a type "cert_terminal" (certification terminal). + * `Learn` and `Sell` `CertificationType` entries, gaining access to different `Equipment` and `Vehicles`. + */ +class CertTerminalDefinition extends TerminalDefinition(171) { + Name = "cert_terminal" + + /** + * The certifications available. + * All entries are listed on page (tab) number 0. + */ + private val certificationList : Map[String, (CertificationType.Value, Int)] = Map( + "medium_assault" -> (CertificationType.MediumAssault, 2), + "reinforced_armo" -> (CertificationType.ReinforcedExoSuit, 3), + "quad_all" -> (CertificationType.ATV, 1), + "switchblade" -> (CertificationType.Switchblade, 1), + "harasser" -> (CertificationType.Harasser, 1), + "anti_vehicular" -> (CertificationType.AntiVehicular, 3), + "heavy_assault" -> (CertificationType.HeavyAssault, 4), + "sniper" -> (CertificationType.Sniping, 3), + "special_assault" -> (CertificationType.SpecialAssault, 3), + "special_assault_2" -> (CertificationType.EliteAssault, 1), + "infiltration_suit" -> (CertificationType.InfiltrationSuit, 2), + "max_anti_personnel" -> (CertificationType.AIMAX, 3), + "max_anti_vehicular" -> (CertificationType.AVMAX, 3), + "max_anti_aircraft" -> (CertificationType.AAMAX, 2), + "max_all" -> (CertificationType.UniMAX, 6), + "air_cavalry_scout" -> (CertificationType.AirCavalryScout, 3), + "air_cavalry_assault" -> (CertificationType.AirCavalryAssault, 2), + "air_cavalry_interceptor" -> (CertificationType.AirCavalryInterceptor, 2), + "air_support" -> (CertificationType.AirSupport, 3), + "gunship" -> (CertificationType.GalaxyGunship, 2), + "phantasm" -> (CertificationType.Phantasm, 3), + "armored_assault1" -> (CertificationType.ArmoredAssault1, 2), + "armored_assault2" -> (CertificationType.ArmoredAssault2, 1), + "flail" -> (CertificationType.Flail, 1), + "assault_buggy" -> (CertificationType.AssaultBuggy, 3), + "ground_support" -> (CertificationType.GroundSupport, 2), + "ground_transport" -> (CertificationType.GroundTransport, 2), + "light_scout" -> (CertificationType.LightScout, 5), + "Repair" -> (CertificationType.Engineering, 3), + "combat_engineering" -> (CertificationType.CombatEngineering, 2), + "ce_offense" -> (CertificationType.AssaultEngineering, 3), + "ce_defense" -> (CertificationType.FortificationEngineering, 3), + "ce_advanced" -> (CertificationType.AdvancedEngineering, 5), + "Hacking" -> (CertificationType.Hacking, 3), + "advanced_hacking" -> (CertificationType.AdvancedHacking, 2), + "expert_hacking" -> (CertificationType.ExpertHacking, 2), + "virus_hacking" -> (CertificationType.DataCorruption, 3), + "electronics_expert" -> (CertificationType.ElectronicsExpert, 4), + "Medical" -> (CertificationType.Medical, 3), + "advanced_medical" -> (CertificationType.AdvancedMedical, 2) + //TODO bfr certification entries + ) + + /** + * Process a `TransactionType.Learn` action by the user. + * @param player the player + * @param msg the original packet carrying the request + * @return an actionable message that explains how to process the request + */ + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { //Learn + certificationList.get(msg.item_name) match { + case Some((cert, cost)) => + Terminal.LearnCertification(cert, cost) + case None => + Terminal.NoDeal() + } + } + + /** + * Process a `TransactionType.Sell` action by the user. + * @param player the player + * @param msg the original packet carrying the request + * @return an actionable message that explains how to process the request + */ + def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + certificationList.get(msg.item_name) match { + case Some((cert, cost)) => + Terminal.SellCertification(cert, cost) + case None => + Terminal.NoDeal() + } + } + + /** + * This action is not supported by this type of `Terminal`. + * @param player the player + * @param msg the original packet carrying the request + * @return `Terminal.NoEvent` always + */ + def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() +} diff --git a/common/src/main/scala/net/psforever/objects/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/terminals/OrderTerminalDefinition.scala index ee431ff7..0eb171fc 100644 --- a/common/src/main/scala/net/psforever/objects/terminals/OrderTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/terminals/OrderTerminalDefinition.scala @@ -10,6 +10,11 @@ import net.psforever.packet.game.ItemTransactionMessage import scala.annotation.switch +/** + * The definition for any `Terminal` that is of a type "order_terminal". + * `Buy` and `Sell` `Equipment` items and `AmmoBox` items. + * Change `ExoSuitType` and retrieve `InfantryLoadout` entries. + */ class OrderTerminalDefinition extends TerminalDefinition(612) { Name = "order_terminal" @@ -21,10 +26,10 @@ class OrderTerminalDefinition extends TerminalDefinition(612) { /** * Process a `TransactionType.Buy` action by the user. + * Either attempt to purchase equipment or attempt to switch directly to a different exo-suit. * @param player the player * @param msg the original packet carrying the request - * @return an actionable message that explains how to process the request; - * either you attempt to purchase equipment or attempt to switch directly to a different exo-suit + * @return an actionable message that explains how to process the request */ def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { (msg.item_page : @switch) match { @@ -64,6 +69,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) { /** * Process a `TransactionType.Sell` action by the user. * There is no specific `order_terminal` tab associated with this action. + * Additionally, the equipment to be sold ia almost always in the player's `FreeHand` slot. * Selling `Equipment` is always permitted. * @param player the player * @param msg the original packet carrying the request @@ -81,7 +87,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) { * @param msg the original packet carrying the request * @return an actionable message that explains how to process the request */ - def InfantryLoadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { case Some(loadout) => diff --git a/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala index 81f464fe..f5d0bd53 100644 --- a/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala @@ -66,14 +66,14 @@ class Terminal(tdef : TerminalDefinition) extends PlanetSideGameObject { */ def Request(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { msg.transaction_type match { - case TransactionType.Buy => + case TransactionType.Buy | TransactionType.Learn => tdef.Buy(player, msg) case TransactionType.Sell => tdef.Sell(player, msg) case TransactionType.InfantryLoadout => - tdef.InfantryLoadout(player, msg) + tdef.Loadout(player, msg) case _ => Terminal.NoDeal() @@ -132,6 +132,12 @@ object Terminal { */ //TODO if there are exceptions, find them final case class SellEquipment() extends Exchange + + import net.psforever.types.CertificationType + final case class LearnCertification(cert : CertificationType.Value, cost : Int) extends Exchange + + final case class SellCertification(cert : CertificationType.Value, cost : Int) extends Exchange + /** * Recover a former exo-suit and `Equipment` configuration that the `Player` possessed. * A result of a processed request. diff --git a/common/src/main/scala/net/psforever/objects/terminals/TerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/terminals/TerminalDefinition.scala index a82fb739..a14836fa 100644 --- a/common/src/main/scala/net/psforever/objects/terminals/TerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/terminals/TerminalDefinition.scala @@ -7,8 +7,6 @@ import net.psforever.objects.equipment.Equipment import net.psforever.packet.game.ItemTransactionMessage import net.psforever.types.ExoSuitType -import scala.collection.immutable.HashMap - /** * The definition for any `Terminal`. * @param objectId the object's identifier number @@ -17,7 +15,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec Name = "terminal" /** - * The unimplemented functionality for this `Terminal`'s `TransactionType.Buy` activity. + * The unimplemented functionality for this `Terminal`'s `TransactionType.Buy` and `TransactionType.Learn` activity. */ def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange @@ -29,7 +27,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec /** * The unimplemented functionality for this `Terminal`'s `TransactionType.InfantryLoadout` activity. */ - def InfantryLoadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange + def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange /** * A `Map` of information for changing exo-suits. @@ -49,7 +47,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * key - an identification string sent by the client * value - a curried function that builds the object */ - protected val infantryAmmunition : HashMap[String, ()=>Equipment] = HashMap( + protected val infantryAmmunition : Map[String, ()=>Equipment] = Map( "9mmbullet" -> MakeAmmoBox(bullet_9mm), "9mmbullet_AP" -> MakeAmmoBox(bullet_9mm_AP), "shotgun_shell" -> MakeAmmoBox(shotgun_shell), @@ -75,7 +73,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * key - an identification string sent by the client * value - a curried function that builds the object */ - protected val supportAmmunition : HashMap[String, ()=>Equipment] = HashMap( + protected val supportAmmunition : Map[String, ()=>Equipment] = Map( "health_canister" -> MakeAmmoBox(health_canister), "armor_canister" -> MakeAmmoBox(armor_canister), "upgrade_canister" -> MakeAmmoBox(upgrade_canister) @@ -86,7 +84,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * key - an identification string sent by the client * value - a curried function that builds the object */ - protected val vehicleAmmunition : HashMap[String, ()=>Equipment] = HashMap( + protected val vehicleAmmunition : Map[String, ()=>Equipment] = Map( "35mmbullet" -> MakeAmmoBox(bullet_35mm), "hellfire_ammo" -> MakeAmmoBox(hellfire_ammo), "liberator_bomb" -> MakeAmmoBox(liberator_bomb), @@ -129,7 +127,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * key - an identification string sent by the client * value - a curried function that builds the object */ - protected val infantryWeapons : HashMap[String, ()=>Equipment] = HashMap( + protected val infantryWeapons : Map[String, ()=>Equipment] = Map( "ilc9" -> MakeTool(ilc9, bullet_9mm), "repeater" -> MakeTool(repeater, bullet_9mm), "isp" -> MakeTool(isp, shotgun_shell), //amp @@ -173,7 +171,7 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * key - an identification string sent by the client * value - a curried function that builds the object */ - protected val supportWeapons : HashMap[String, ()=>Equipment] = HashMap( + protected val supportWeapons : Map[String, ()=>Equipment] = Map( "medkit" -> MakeKit(medkit), "super_medkit" -> MakeKit(super_medkit), "super_armorkit" -> MakeKit(super_armorkit), diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala index 125e521d..0b640c68 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -20,7 +20,7 @@ import scodec.codecs._ * `17 - BEP. Value seems to be the same as BattleExperienceMessage`
* `18 - CEP.`
* `19 - Anchors. Value is 0 to disengage, 1 to engage.`
- * `24 - Certifications with value :`
+ * `24 - Learn certifications with value :`
* 01 : Medium Assault
* 02 : Heavy Assault
* 03 : Special Assault
@@ -66,6 +66,7 @@ import scodec.codecs._ * 43 : Fortification Engineering
* 44 : Assault Engineering
* 45 : Advanced Engineering (= Fortification Engineering + Assault Engineering) Must have Combat Engineering
+ * `25 - Forget certifications (same order as 24)` * `29 - Visible ?! That's not the cloaked effect, Maybe for spectator mode ?. Value is 0 to visible, 1 to invisible.`
* `31 - Info under avatar name : 0 = LFS, 1 = Looking For Squad Members`
* `32 - Info under avatar name : 0 = Looking For Squad Members, 1 = LFS`
diff --git a/common/src/main/scala/net/psforever/types/CertificationType.scala b/common/src/main/scala/net/psforever/types/CertificationType.scala index d06e2d19..ea4ca3ab 100644 --- a/common/src/main/scala/net/psforever/types/CertificationType.scala +++ b/common/src/main/scala/net/psforever/types/CertificationType.scala @@ -30,9 +30,9 @@ object CertificationType extends Enumeration { AntiVehicular, Sniping, EliteAssault, - AirCalvaryScout, - AirCalvaryInterceptor, - AirCalvaryAssault, + AirCavalryScout, + AirCavalryInterceptor, + AirCavalryAssault, //10 AirSupport, ATV, diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index c1403fc4..f71cce11 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -222,9 +222,12 @@ object PsLogin { def createContinents() : List[Zone] = { val map13 = new ZoneMap("map13") { import net.psforever.objects.GlobalDefinitions._ - LocalObject(TerminalObjectBuilder(orderTerminal, 853)) - LocalObject(TerminalObjectBuilder(orderTerminal, 855)) - LocalObject(TerminalObjectBuilder(orderTerminal, 860)) + LocalObject(TerminalObjectBuilder(cert_terminal, 186)) + LocalObject(TerminalObjectBuilder(cert_terminal, 187)) + LocalObject(TerminalObjectBuilder(cert_terminal, 188)) + LocalObject(TerminalObjectBuilder(order_terminal, 853)) + LocalObject(TerminalObjectBuilder(order_terminal, 855)) + LocalObject(TerminalObjectBuilder(order_terminal, 860)) } val home3 = Zone("home3", map13, 13) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 07393e4c..d759a8f3 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -312,6 +312,7 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, obj)) }) } + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true))) case Terminal.BuyEquipment(item) => ; tplayer.Fit(item) match { @@ -382,6 +383,31 @@ class WorldSessionActor extends Actor with MDCContextAware { PutEquipmentInSlot(tplayer, entry.obj, entry.start) }) //TODO drop items on ground + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage (msg.terminal_guid, TransactionType.InfantryLoadout, true))) + + case Terminal.LearnCertification(cert, cost) => + if(!player.Certifications.contains(cert)) { + log.info(s"$tplayer is learning the $cert certification for $cost points") + tplayer.Certifications += cert + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(tplayer.GUID, 24, cert.id.toLong))) + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true))) + } + else { + log.warn(s"$tplayer already knows the $cert certification, so he can't learn it") + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false))) + } + + case Terminal.SellCertification(cert, cost) => + if(player.Certifications.contains(cert)) { + log.info(s"$tplayer is forgetting the $cert certification for $cost points") + tplayer.Certifications -= cert + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(tplayer.GUID, 25, cert.id.toLong))) + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Sell, true))) + } + else { + log.warn(s"$tplayer doesn't know what a $cert certification is, so he can't forget it") + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false))) + } case Terminal.NoDeal() => log.warn(s"$tplayer made a request but the terminal rejected the order $msg") @@ -477,6 +503,7 @@ class WorldSessionActor extends Actor with MDCContextAware { LivePlayerList.Assign(continent.Number, sessionId, guid) sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(guid,0,0))) sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))) + sendResponse(PacketCoding.CreateGamePacket(0, ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None))) //CC on case Zone.ItemFromGround(tplayer, item) => val obj_guid = item.GUID