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 80376a530..ddeff7e31 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -2924,9 +2924,11 @@ class ZoningOperations( 0 seconds } else { //for other zones ... + //Searhus lock benefit also gives biolab faster respawn + val searhusBenefit = Zones.zones.find(_.Number == 9).exists(_.benefitRecipient == player.Faction) //biolabs have/grant benefits val cryoBenefit: Float = toSpawnPoint.Owner match { - case b: Building if b.hasLatticeBenefit(LatticeBenefit.BioLaboratory) => 0.5f + case b: Building if b.hasLatticeBenefit(LatticeBenefit.BioLaboratory) || (b.BuildingType == StructureType.Facility && !b.CaptureTerminalIsHacked && searhusBenefit) => 0.5f case _ => 1f } //TODO cumulative death penalty diff --git a/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala index b363fcee4..fa0a41787 100644 --- a/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala +++ b/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala @@ -5,6 +5,7 @@ import net.psforever.objects.equipment.{Equipment, EquipmentSlot} import net.psforever.objects.{PlanetSideGameObject, Vehicle} import net.psforever.packet.game.objectcreate._ import net.psforever.types.{DriveState, PlanetSideGUID, VehicleFormat} +import net.psforever.zones.Zones import scala.util.{Failure, Success, Try} @@ -14,6 +15,8 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() { override def ConstructorData(obj: Vehicle): Try[VehicleData] = { val health = StatConverter.Health(obj.Health, obj.MaxHealth) + val boosted = if (Zones.zones.find(_.Number == 3).exists(_.benefitRecipient == obj.Faction)) true + else false if (health > 0) { //active Success( VehicleData( @@ -32,7 +35,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() { case None => PlanetSideGUID(0) } ), - unk3 = false, + boostMaxHealth = boosted, health, unk4 = false, no_mount_points = false, @@ -59,7 +62,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() { v5 = None, guid = PlanetSideGUID(0) ), - unk3 = false, + boostMaxHealth = boosted, health = 0, unk4 = false, no_mount_points = true, diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala index 8e4d07bc1..7bbb1123c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala @@ -10,6 +10,7 @@ import net.psforever.objects.zones.Zone import net.psforever.services.Service import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.Vector3 +import net.psforever.zones.Zones import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -44,6 +45,12 @@ class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnC ) //appear below the trench and doors vehicle.WhichSide = pad.WhichSide vehicle.Cloaked = vehicle.Definition.CanCloak && driver.Cloaked + // increase MaxHealth by 10% if driver has Cyssor empire armor benefit + if (Zones.zones.find(_.Number == 3).exists(_.benefitRecipient == driver.Faction)) { + val boosted = Math.round(vehicle.MaxHealth * 1.1).toInt + vehicle.MaxHealth = boosted + vehicle.Health = boosted + } temp = Some(order) val result = ask(pad.Zone.Transport, Zone.Vehicle.Spawn(vehicle)) diff --git a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala index 3efb93c97..232a3c8c5 100644 --- a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala @@ -58,7 +58,7 @@ trait RepairableEntity extends Repairable { */ protected def CanPerformRepairs(target: Repairable.Target, player: Player, item: Tool): Boolean = { val definition = target.Definition - definition.Repairable && target.Health < definition.MaxHealth && (definition.RepairIfDestroyed || !target.Destroyed) && + definition.Repairable && target.Health < target.MaxHealth && (definition.RepairIfDestroyed || !target.Destroyed) && (target.Faction == player.Faction || target.Faction == PlanetSideEmpire.NEUTRAL) && item.Magazine > 0 && player.isAlive && Vector3.Distance(target.Position, player.Position) < definition.RepairDistance } diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 0b69cc36a..b01b2604b 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -56,7 +56,8 @@ import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.prop.DamageWithPosition import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation} -import net.psforever.packet.game.PropertyOverrideMessage +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock, ZoneLockBenefit, ZoneLockZone} +import net.psforever.packet.game.{EmpireBenefitsMessage, PropertyOverrideMessage} import net.psforever.services.Service import net.psforever.zones.Zones @@ -682,11 +683,15 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { if (perks.values.forall(_.isEmpty)) {/*do nothing*/} else { - val msg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, + val overrideMsg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, List(PropertyOverrideMessage.GameProperty("purchase_exempt_vs", perks(PlanetSideEmpire.VS)), PropertyOverrideMessage.GameProperty("purchase_exempt_tr", perks(PlanetSideEmpire.TR)), PropertyOverrideMessage.GameProperty("purchase_exempt_nc", perks(PlanetSideEmpire.NC)))))))) - building.Actor ! BuildingActor.HomeLockBenefits(msg) + building.Actor ! BuildingActor.HomeLockBenefits(overrideMsg) + } + val benefitMsg = BuildEmpireBenefits() + if (benefitMsg.zoneLocks.nonEmpty) { + building.Actor ! BuildingActor.HomeLockBenefits(benefitMsg) } } @@ -716,12 +721,71 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { if (perks.values.forall(_.isEmpty)) {/*do nothing*/} else { - val msg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, + val overrideMsg = PropertyOverrideMessage(List(PropertyOverrideMessage.GamePropertyScope(0, List(PropertyOverrideMessage.GamePropertyTarget(343, List(PropertyOverrideMessage.GameProperty("purchase_exempt_vs", perks(PlanetSideEmpire.VS)), PropertyOverrideMessage.GameProperty("purchase_exempt_tr", perks(PlanetSideEmpire.TR)), PropertyOverrideMessage.GameProperty("purchase_exempt_nc", perks(PlanetSideEmpire.NC)))))))) - PlayerControl.sendResponse(player.Zone, player.Name, msg) + PlayerControl.sendResponse(player.Zone, player.Name, overrideMsg) } + + val benefitMsg = BuildEmpireBenefits() + if (benefitMsg.zoneLocks.nonEmpty) { + PlayerControl.sendResponse(player.Zone, player.Name, benefitMsg) + } + } + + def BuildEmpireBenefits(): EmpireBenefitsMessage = { + val locks = scala.collection.mutable.ArrayBuffer[ZoneLock]() + val benefits = scala.collection.mutable.ArrayBuffer[ZoneBenefit]() + val homeSets: Map[PlanetSideEmpire.Value, Set[Int]] = Map( + PlanetSideEmpire.TR -> Set(1, 2), + PlanetSideEmpire.VS -> Set(5, 6), + PlanetSideEmpire.NC -> Set(7, 10) + ) + val benefitOfZones: Map[Int, Int] = Map( + 3 -> 6, // Cyssor gives benefit 6 (+10% armor bonus to vehicles) + 4 -> 1, // Ishundar gives benefit 1 (vehicle shields) + 9 -> 3 // Searhus gives benefit 3 (faster respawn) + ) + val homePerkBenefits: Map[PlanetSideEmpire.Value, Int] = Map( + PlanetSideEmpire.TR -> 7, + PlanetSideEmpire.NC -> 8, + PlanetSideEmpire.VS -> 9 + ) + def isLockedBy(homeSet: Set[Int], empire: PlanetSideEmpire.Value): Boolean = + Zones.zones.filter(z => homeSet.contains(z.Number)).forall(_.benefitRecipient == empire) + + // home zone perks + homeSets.foreach { case (owner, set) => + PlanetSideEmpire.values.filterNot(_ == PlanetSideEmpire.NEUTRAL).foreach { empire => + if (owner != empire && isLockedBy(set, empire)) { + locks += ZoneLock(empire, s"lock-${owner.toString.toLowerCase}-homes") + benefits += ZoneBenefit(empire, ZoneLockBenefit(homePerkBenefits(owner))) + } + } + } + + benefitOfZones.foreach { case (zoneNum, benefitId) => + Zones.zones.find(_.Number == zoneNum).foreach { z => + z.benefitRecipient match { + case PlanetSideEmpire.NEUTRAL => + //nothing + case empire => + locks += ZoneLock(empire, s"lock-z$zoneNum") + benefits += ZoneBenefit(empire, ZoneLockBenefit(benefitId)) + } + } + } + // all four islands together give benefit 4 (vehicle repair) + val islandZones: Set[Int] = Set(29, 30, 31, 32) + val islandBenefit: Int = 4 + PlanetSideEmpire.values.filterNot(_ == PlanetSideEmpire.NEUTRAL).foreach { empire => + if (isLockedBy(islandZones, empire)) { + locks += ZoneLock(empire, ZoneLockZone.i1_i2_i3_i4) + benefits += ZoneBenefit(empire, ZoneLockBenefit(islandBenefit)) + } + } + EmpireBenefitsMessage(locks.toVector, benefits.toVector) } } diff --git a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 0253d2b80..54c4e3352 100644 --- a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -557,7 +557,7 @@ object GamePacketOpcode extends Enumeration { case 0xd4 => game.GenericObjectActionAtPositionMessage.decode case 0xd5 => game.PropertyOverrideMessage.decode case 0xd6 => game.WarpgateLinkOverrideMessage.decode - case 0xd7 => noDecoder(EmpireBenefitsMessage) + case 0xd7 => game.EmpireBenefitsMessage.decode // 0xd8 case 0xd8 => noDecoder(ForceEmpireMessage) case 0xd9 => game.BroadcastWarpgateUpdateMessage.decode diff --git a/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala new file mode 100644 index 000000000..24a62a6d5 --- /dev/null +++ b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala @@ -0,0 +1,110 @@ +// Copyright (c) 2025 PSForever +package net.psforever.packet.game + +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock} +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import net.psforever.types.PlanetSideEmpire +import scodec.Codec +import scodec.codecs._ + +import scala.language.implicitConversions + +/** + * EmpireBenefitsMessage + * + * zoneLocks gives the client information about which empire locks what continent. + * This produces a chat message. + * zoneBenefits tells the client what empire has which benefits enabled. + * This has to match zoneLocks to work properly. + */ +final case class EmpireBenefitsMessage( + zoneLocks: Vector[ZoneLock], + zoneBenefits: Vector[ZoneBenefit] + ) extends PlanetSideGamePacket { + type Packet = EmpireBenefitsMessage + def opcode = GamePacketOpcode.EmpireBenefitsMessage + def encode = EmpireBenefitsMessage.encode(this) +} + +object EmpireBenefitsMessage extends Marshallable[EmpireBenefitsMessage] { + + /** + * ZoneLockZone + * + * Available Types of Zones + * + * These zones can be used to notify the client of a lock. + */ + object ZoneLockZone extends Enumeration { + type Type = String + + val i1: ZoneLockZone.Value = Value("lock-i1") // Extinction Continental Lock + val i2: ZoneLockZone.Value = Value("lock-i2") // Ascension Continental Lock + val i3: ZoneLockZone.Value = Value("lock-i3") // Desolation Continental Lock + val i4: ZoneLockZone.Value = Value("lock-i4") // Nexus Continental Lock + val i1_i2_i3_i4: ZoneLockZone.Value = Value("lock-i1-i2-i3-i4") // Oshur Cluster Lock + val z3: ZoneLockZone.Value = Value("lock-z3") // Cyssor Continental Lock + val z4: ZoneLockZone.Value = Value("lock-z4") // Ishundar Continental Lock + val z9: ZoneLockZone.Value = Value("lock-z9") // Searhus Continental Lock + val tr_homes: ZoneLockZone.Value = Value("lock-tr-homes") // TR Home Continent Lock + val nc_homes: ZoneLockZone.Value = Value("lock-nc-homes") // NC Home Continent Lock + val vs_homes: ZoneLockZone.Value = Value("lock-vs-homes") // VS Home Continent Lock + + implicit def valueToType(v: ZoneLockZone.Value): Type = v.toString + implicit val codec: Codec[Type] = PacketHelpers.encodedStringAligned(6) + } + + /** + * ZoneLockBenefit + * + * Available Types of Benefits + * + * Benefits 0, 2 and 5 are unknown. Benefits for i1 to i4 are unknown and mapped incorrectly here. + */ + object ZoneLockBenefit extends Enumeration { + type Type = Value + + val i1: ZoneLockBenefit.Value = Value(-1) // Extinction Continental Lock + val i2: ZoneLockBenefit.Value = Value(-2) // Ascension Continental Lock + val i3: ZoneLockBenefit.Value = Value(-3) // Desolation Continental Lock + val i4: ZoneLockBenefit.Value = Value(-4) // Nexus Continental Lock + + // val unk0: ZoneLockBenefit.Value = Value(0) + val z4: ZoneLockBenefit.Value = Value(1) // Ishundar Continental Lock + // val unk2: ZoneLockBenefit.Value = Value(2) + val z9: ZoneLockBenefit.Value = Value(3) // Searhus Continental Lock + val i1_i2_i3_i4: ZoneLockBenefit.Value = Value(4) // Oshur Cluster Lock + // val unk5: ZoneLockBenefit.Value = Value(5) + val z3: ZoneLockBenefit.Value = Value(6) // Cyssor Continental Lock + val tr_homes: ZoneLockBenefit.Value = Value(7) // TR Home Continent Lock + val nc_homes: ZoneLockBenefit.Value = Value(8) // NC Home Continent Lock + val vs_homes: ZoneLockBenefit.Value = Value(9) // VS Home Continent Lock + + implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uint16L) + } + + final case class ZoneLock( + empire: PlanetSideEmpire.Type, + zone: ZoneLockZone.Type, + ) + + final case class ZoneBenefit( + empire: PlanetSideEmpire.Type, + value: ZoneLockBenefit.Type + ) + + private implicit val zoneLockCodec: Codec[ZoneLock] = ( + ("empire" | PlanetSideEmpire.codec) :: + ("zone" | ZoneLockZone.codec) + ).as[ZoneLock] + + private implicit val zoneBenefitCodec: Codec[ZoneBenefit] = ( + ("empire" | PlanetSideEmpire.codec) :: + ("benefit" | ZoneLockBenefit.codec) + ).as[ZoneBenefit] + + implicit val codec: Codec[EmpireBenefitsMessage] = ( + ("zoneLocks" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneLockCodec)) :: + ("zoneBenefits" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneBenefitCodec)) + ).as[EmpireBenefitsMessage] +} diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala index 67d102c91..f6524b324 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala @@ -44,7 +44,7 @@ final case class VariantVehicleData(unk: Int) extends SpecificVehicleData(Vehicl * -jammered - vehicles will not be jammered by setting this field
* -player_guid the vehicle's (official) owner; * a living player in the game world on the same continent as the vehicle who may mount the driver mount - * @param unk3 na + * @param boostMaxHealth vehicle gets 10% more armor from vehicle armor benefit given by Cyssor empire lock * @param health the amount of health the vehicle has, as a percentage of a filled bar (255) * @param unk4 na * @param no_mount_points do not display entry points for the seats @@ -65,7 +65,7 @@ final case class VariantVehicleData(unk: Int) extends SpecificVehicleData(Vehicl final case class VehicleData( pos: PlacementData, data: CommonFieldData, - unk3: Boolean, + boostMaxHealth: Boolean, health: Int, unk4: Boolean, no_mount_points: Boolean, @@ -106,7 +106,7 @@ object VehicleData extends Marshallable[VehicleData] { cloak: Boolean, inventory: Option[InventoryData] ): VehicleData = { - VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, None, inventory)( + VehicleData(pos, basic, boostMaxHealth = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, None, inventory)( VehicleFormat.Normal ) } @@ -128,7 +128,7 @@ object VehicleData extends Marshallable[VehicleData] { format: UtilityVehicleData, inventory: Option[InventoryData] ): VehicleData = { - VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)( + VehicleData(pos, basic, boostMaxHealth = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)( VehicleFormat.Utility ) } @@ -150,7 +150,7 @@ object VehicleData extends Marshallable[VehicleData] { format: VariantVehicleData, inventory: Option[InventoryData] ): VehicleData = { - VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)( + VehicleData(pos, basic, boostMaxHealth = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)( VehicleFormat.Variant ) } diff --git a/src/test/scala/game/EmpireBenefitsMessageTest.scala b/src/test/scala/game/EmpireBenefitsMessageTest.scala new file mode 100644 index 000000000..9f86281d6 --- /dev/null +++ b/src/test/scala/game/EmpireBenefitsMessageTest.scala @@ -0,0 +1,182 @@ +// Copyright (c) 2025 PSForever +package game + +import net.psforever.packet._ +import net.psforever.packet.game.EmpireBenefitsMessage +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock, ZoneLockBenefit, ZoneLockZone} +import net.psforever.types.PlanetSideEmpire +import org.specs2.mutable._ +import scodec.bits._ + +class EmpireBenefitsMessageTest extends Specification { + + val sample1: ByteVector = ByteVector.fromValidHex( + "d7" + // header + "04000000" + // count uint32L + "21c06c6f636b2d7a3321c06c6f636b2d7a3464006c6f636b2d69312d69322d69332d6934a1c06c6f636b2d7a39" + + "04000000" + // count uint32L + "004000600410020300" + ) + + val sample2: ByteVector = ByteVector.fromValidHex( + "d7" + + "05000000 23406c6f636b2d76732d686f6d657321c06c6f636b2d7a3321c06c6f636b2d7a3461c06c6f636b2d7a3964006c6f636b2d69312d69322d69332d6934" + + "05000000 004000600024010300410000" + ) + + val sample3: ByteVector = ByteVector.fromValidHex( + "d7" + + "05000000 21c06c6f636b2d7a3321c06c6f636b2d7a3423406c6f636b2d6e632d686f6d657361c06c6f636b2d7a39a4006c6f636b2d69312d69322d69332d6934" + + "05000000 004000600020010300810000" + ) + + val sample4: ByteVector = ByteVector.fromValidHex( + "d7" + + "06000000 a3406c6f636b2d6e632d686f6d6573a3406c6f636b2d74722d686f6d6573a1c06c6f636b2d7a33a1c06c6f636b2d7a34a1c06c6f636b2d7a39a4006c6f636b2d69312d69322d69332d6934" + + "06000000 80402030081002060081c0208000" + ) + + private val sample1_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.i1_i2_i3_i4), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z9) + ) + + private val sample1_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.i1_i2_i3_i4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z9) + ) + + private val sample2_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.vs_homes), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample2_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.vs_homes), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.i1_i2_i3_i4) + ) + + private val sample3_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.nc_homes), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample3_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.nc_homes), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.i1_i2_i3_i4) + ) + + private val sample4_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.nc_homes), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.tr_homes), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample4_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.i1_i2_i3_i4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.tr_homes), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.nc_homes) + ) + + "decode sample1" in { + PacketCoding.decodePacket(sample1).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample1_expectedLocks + b mustEqual sample1_expectedBenefits + case _ => + ko + } + } + + "encode sample1" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample1_expectedLocks, + zoneBenefits = sample1_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample1 + } + + "decode sample2" in { + PacketCoding.decodePacket(sample2).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample2_expectedLocks + b mustEqual sample2_expectedBenefits + case _ => + ko + } + } + + "encode sample2" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample2_expectedLocks, + zoneBenefits = sample2_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample2 + } + + "decode sample3" in { + PacketCoding.decodePacket(sample3).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample3_expectedLocks + b mustEqual sample3_expectedBenefits + case _ => + ko + } + } + + "encode sample3" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample3_expectedLocks, + zoneBenefits = sample3_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample3 + } + + "decode sample4" in { + PacketCoding.decodePacket(sample4).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample4_expectedLocks + b mustEqual sample4_expectedBenefits + case _ => + ko + } + } + + "encode sample4" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample4_expectedLocks, + zoneBenefits = sample4_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample4 + } +} diff --git a/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala b/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala index f384ea651..08df24820 100644 --- a/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala +++ b/src/test/scala/game/objectcreatevehicle/MountedVehiclesTest.scala @@ -39,7 +39,7 @@ class MountedVehiclesTest extends Specification { vdata.no_mount_points mustEqual false vdata.driveState mustEqual DriveState.Mobile vdata.cloak mustEqual false - vdata.unk3 mustEqual false + vdata.boostMaxHealth mustEqual false vdata.unk4 mustEqual false vdata.unk5 mustEqual false vdata.unk6 mustEqual false diff --git a/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala b/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala index 57dedbdb0..d1bdaa164 100644 --- a/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala +++ b/src/test/scala/game/objectcreatevehicle/UtilityVehiclesTest.scala @@ -36,7 +36,7 @@ class UtilityVehiclesTest extends Specification { ant.driveState mustEqual DriveState.Mobile ant.health mustEqual 255 ant.cloak mustEqual false - ant.unk3 mustEqual false + ant.boostMaxHealth mustEqual false ant.unk4 mustEqual false ant.unk5 mustEqual false ant.unk6 mustEqual false @@ -67,7 +67,7 @@ class UtilityVehiclesTest extends Specification { ams.vehicle_format_data mustEqual Some(UtilityVehicleData(60)) ams.health mustEqual 236 ams.cloak mustEqual true - ams.unk3 mustEqual false + ams.boostMaxHealth mustEqual false ams.unk4 mustEqual false ams.unk5 mustEqual false ams.unk6 mustEqual true @@ -120,7 +120,7 @@ class UtilityVehiclesTest extends Specification { case _ => ko } - ams.unk3 mustEqual false + ams.boostMaxHealth mustEqual false ams.health mustEqual 255 ams.unk4 mustEqual false ams.no_mount_points mustEqual false @@ -320,7 +320,7 @@ class UtilityVehiclesTest extends Specification { Some(Vector3(27.3375f, -0.78749996f, 0.1125f)) ), CommonFieldData(PlanetSideEmpire.TR, false, false, false, None, false, Some(false), None, PlanetSideGUID(3087)), - unk3 = false, + boostMaxHealth = false, health = 255, unk4 = false, no_mount_points = false,