diff --git a/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala b/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala new file mode 100644 index 000000000..394ba9c9f --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects + +/** + * Used to initialize the value of a re-usable `Cancellable` object. + * By convention, it always acts like it has been cancelled before and can be cancelled. + * Should be replaced with pertinent `Cancellable` logic through the initialization of an executor. + */ +object DefaultCancellable { + import akka.actor.Cancellable + + protected class InternalCancellable extends Cancellable { + override def cancel : Boolean = true + override def isCancelled : Boolean = true + } + + final val obj : Cancellable = new InternalCancellable +} diff --git a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala index 0ef2ba55f..4eea81599 100644 --- a/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/ExoSuitDefinition.scala @@ -65,7 +65,7 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) { object ExoSuitDefinition { final val Standard = ExoSuitDefinition(ExoSuitType.Standard) Standard.MaxArmor = 50 - Standard.InventoryScale = new InventoryTile(9,6) + Standard.InventoryScale = InventoryTile.Tile96 Standard.InventoryOffset = 6 Standard.Holster(0, EquipmentSize.Pistol) Standard.Holster(2, EquipmentSize.Rifle) @@ -73,7 +73,7 @@ object ExoSuitDefinition { final val Agile = ExoSuitDefinition(ExoSuitType.Agile) Agile.MaxArmor = 100 - Agile.InventoryScale = new InventoryTile(9,9) + Agile.InventoryScale = InventoryTile.Tile99 Agile.InventoryOffset = 6 Agile.Holster(0, EquipmentSize.Pistol) Agile.Holster(1, EquipmentSize.Pistol) @@ -83,7 +83,7 @@ object ExoSuitDefinition { final val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced) Reinforced.permission = 1 Reinforced.MaxArmor = 200 - Reinforced.InventoryScale = new InventoryTile(12,9) + Reinforced.InventoryScale = InventoryTile.Tile1209 Reinforced.InventoryOffset = 6 Reinforced.Holster(0, EquipmentSize.Pistol) Reinforced.Holster(1, EquipmentSize.Pistol) @@ -94,7 +94,7 @@ object ExoSuitDefinition { final val Infiltration = ExoSuitDefinition(ExoSuitType.Standard) Infiltration.permission = 1 Infiltration.MaxArmor = 0 - Infiltration.InventoryScale = new InventoryTile(6,6) + Infiltration.InventoryScale = InventoryTile.Tile66 Infiltration.InventoryOffset = 6 Infiltration.Holster(0, EquipmentSize.Pistol) Infiltration.Holster(4, EquipmentSize.Melee) @@ -102,7 +102,7 @@ object ExoSuitDefinition { final val MAX = ExoSuitDefinition(ExoSuitType.MAX) MAX.permission = 1 MAX.MaxArmor = 650 - MAX.InventoryScale = new InventoryTile(16,12) + MAX.InventoryScale = InventoryTile.Tile1612 MAX.InventoryOffset = 6 MAX.Holster(0, EquipmentSize.Max) MAX.Holster(4, EquipmentSize.Melee) diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index dba9b469e..c8ac95d62 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2,13 +2,14 @@ package net.psforever.objects import net.psforever.objects.definition._ -import net.psforever.objects.definition.converter.{CommandDetonaterConverter, LockerContainerConverter, REKConverter} +import net.psforever.objects.definition.converter._ import net.psforever.objects.serverobject.doors.DoorDefinition import net.psforever.objects.equipment.CItem.DeployedItem import net.psforever.objects.equipment._ import net.psforever.objects.inventory.InventoryTile import net.psforever.objects.serverobject.locks.IFFLockDefinition -import net.psforever.objects.serverobject.terminals.{CertTerminalDefinition, OrderTerminalDefinition} +import net.psforever.objects.serverobject.terminals._ +import net.psforever.objects.vehicles.SeatArmorRestriction import net.psforever.types.PlanetSideEmpire object GlobalDefinitions { @@ -163,6 +164,33 @@ object GlobalDefinitions { } } + def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = { + faction match { + case PlanetSideEmpire.TR => trhev_dualcycler + case PlanetSideEmpire.NC => nchev_scattercannon + case PlanetSideEmpire.VS => vshev_quasar + case PlanetSideEmpire.NEUTRAL => suppressor //there is no common pool MAX arms + } + } + + def AVMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = { + faction match { + case PlanetSideEmpire.TR => trhev_pounder + case PlanetSideEmpire.NC => nchev_falcon + case PlanetSideEmpire.VS => vshev_comet + case PlanetSideEmpire.NEUTRAL => suppressor //there is no common pool MAX arms + } + } + + def AAMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = { + faction match { + case PlanetSideEmpire.TR => trhev_burster + case PlanetSideEmpire.NC => nchev_sparrow + case PlanetSideEmpire.VS => vshev_starfire + case PlanetSideEmpire.NEUTRAL => suppressor //there is no common pool MAX arms + } + } + /** * Using the definition for a piece of `Equipment` determine if it is a grenade-type weapon. * Only the normal grenades count; the grenade packs are excluded. @@ -196,17 +224,17 @@ object GlobalDefinitions { /** * Using the definition for a piece of `Equipment` determine with which faction it aligns if it is a weapon. * Only checks `Tool` objects. - * Useful for determining if some item has to be dropped during an activity like `InfantryLoadout` switching. + * Useful for determining if some item has to be dropped during an activity like `Loadout` switching. * @param edef the `EquipmentDefinition` of the item * @return the faction alignment, or `Neutral` */ def isFactionWeapon(edef : EquipmentDefinition) : PlanetSideEmpire.Value = { edef match { - case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` => + case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` => PlanetSideEmpire.TR - case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` => + case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` => PlanetSideEmpire.NC - case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` => + case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` => PlanetSideEmpire.VS case _ => PlanetSideEmpire.NEUTRAL @@ -221,11 +249,11 @@ object GlobalDefinitions { */ def isFactionEquipment(edef : EquipmentDefinition) : PlanetSideEmpire.Value = { edef match { - case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | `striker_missile_ammo` => + case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | `striker_missile_ammo` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` => PlanetSideEmpire.TR - case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `hunter_seeker_missile` => + case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `hunter_seeker_missile` | `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` => PlanetSideEmpire.NC - case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `energy_cell` | `lancer_cartridge` => + case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `energy_cell` | `lancer_cartridge` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` => PlanetSideEmpire.VS case _ => PlanetSideEmpire.NEUTRAL @@ -234,7 +262,7 @@ object GlobalDefinitions { /** * Using the definition for a piece of `Equipment` determine whether it is a "cavern weapon." - * Useful for determining if some item has to be dropped during an activity like `InfantryLoadout` switching. + * Useful for determining if some item has to be dropped during an activity like `Loadout` switching. * @param edef the `EquipmentDefinition` of the item * @return `true`, if it is; otherwise, `false` */ @@ -457,6 +485,9 @@ object GlobalDefinitions { bullet_35mm.Capacity = 100 bullet_35mm.Tile = InventoryTile.Tile44 + val ancient_ammo_vehicle = AmmoBoxDefinition(Ammo.ancient_ammo_vehicle) +// + val aphelion_laser_ammo = AmmoBoxDefinition(Ammo.aphelion_laser_ammo) aphelion_laser_ammo.Capacity = 165 @@ -487,6 +518,11 @@ object GlobalDefinitions { skyguard_flak_cannon_ammo.Capacity = 200 skyguard_flak_cannon_ammo.Tile = InventoryTile.Tile44 + val + firebird_missile = AmmoBoxDefinition(ObjectClass.firebird_missile) + firebird_missile.Capacity = 50 + firebird_missile.Tile = InventoryTile.Tile44 + val flux_cannon_thresher_battery = AmmoBoxDefinition(Ammo.flux_cannon_thresher_battery) flux_cannon_thresher_battery.Capacity = 150 @@ -630,7 +666,7 @@ object GlobalDefinitions { val chainblade = ToolDefinition(ObjectClass.chainblade) chainblade.Size = EquipmentSize.Melee - chainblade.AmmoTypes += Ammo.melee_ammo + chainblade.AmmoTypes += melee_ammo chainblade.FireModes += new FireModeDefinition chainblade.FireModes.head.AmmoTypeIndices += 0 chainblade.FireModes.head.AmmoSlotIndex = 0 @@ -643,7 +679,7 @@ object GlobalDefinitions { val magcutter = ToolDefinition(ObjectClass.magcutter) magcutter.Size = EquipmentSize.Melee - magcutter.AmmoTypes += Ammo.melee_ammo + magcutter.AmmoTypes += melee_ammo magcutter.FireModes += new FireModeDefinition magcutter.FireModes.head.AmmoTypeIndices += 0 magcutter.FireModes.head.AmmoSlotIndex = 0 @@ -656,7 +692,7 @@ object GlobalDefinitions { val forceblade = ToolDefinition(ObjectClass.forceblade) forceblade.Size = EquipmentSize.Melee - forceblade.AmmoTypes += Ammo.melee_ammo + forceblade.AmmoTypes += melee_ammo forceblade.FireModes += new FireModeDefinition forceblade.FireModes.head.AmmoTypeIndices += 0 forceblade.FireModes.head.AmmoSlotIndex = 0 @@ -671,7 +707,7 @@ object GlobalDefinitions { val katana = ToolDefinition(ObjectClass.katana) katana.Size = EquipmentSize.Melee - katana.AmmoTypes += Ammo.melee_ammo + katana.AmmoTypes += melee_ammo katana.FireModes += new FireModeDefinition katana.FireModes.head.AmmoTypeIndices += 0 katana.FireModes.head.AmmoSlotIndex = 0 @@ -686,7 +722,7 @@ object GlobalDefinitions { val frag_grenade = ToolDefinition(ObjectClass.frag_grenade) frag_grenade.Size = EquipmentSize.Pistol - frag_grenade.AmmoTypes += Ammo.frag_grenade_ammo + frag_grenade.AmmoTypes += frag_grenade_ammo frag_grenade.FireModes += new FireModeDefinition frag_grenade.FireModes.head.AmmoTypeIndices += 0 frag_grenade.FireModes.head.AmmoSlotIndex = 0 @@ -700,7 +736,7 @@ object GlobalDefinitions { val plasma_grenade = ToolDefinition(ObjectClass.plasma_grenade) plasma_grenade.Size = EquipmentSize.Pistol - plasma_grenade.AmmoTypes += Ammo.plasma_grenade_ammo + plasma_grenade.AmmoTypes += plasma_grenade_ammo plasma_grenade.FireModes += new FireModeDefinition plasma_grenade.FireModes.head.AmmoTypeIndices += 0 plasma_grenade.FireModes.head.AmmoSlotIndex = 0 @@ -714,7 +750,7 @@ object GlobalDefinitions { val jammer_grenade = ToolDefinition(ObjectClass.jammer_grenade) jammer_grenade.Size = EquipmentSize.Pistol - jammer_grenade.AmmoTypes += Ammo.jammer_grenade_ammo + jammer_grenade.AmmoTypes += jammer_grenade_ammo jammer_grenade.FireModes += new FireModeDefinition jammer_grenade.FireModes.head.AmmoTypeIndices += 0 jammer_grenade.FireModes.head.AmmoSlotIndex = 0 @@ -728,8 +764,8 @@ object GlobalDefinitions { val repeater = ToolDefinition(ObjectClass.repeater) repeater.Size = EquipmentSize.Pistol - repeater.AmmoTypes += Ammo.bullet_9mm - repeater.AmmoTypes += Ammo.bullet_9mm_AP + repeater.AmmoTypes += bullet_9mm + repeater.AmmoTypes += bullet_9mm_AP repeater.FireModes += new FireModeDefinition repeater.FireModes.head.AmmoTypeIndices += 0 repeater.FireModes.head.AmmoTypeIndices += 1 @@ -740,8 +776,8 @@ object GlobalDefinitions { val isp = ToolDefinition(ObjectClass.isp) //mag-scatter isp.Size = EquipmentSize.Pistol - isp.AmmoTypes += Ammo.shotgun_shell - isp.AmmoTypes += Ammo.shotgun_shell_AP + isp.AmmoTypes += shotgun_shell + isp.AmmoTypes += shotgun_shell_AP isp.FireModes += new FireModeDefinition isp.FireModes.head.AmmoTypeIndices += 0 isp.FireModes.head.AmmoTypeIndices += 1 @@ -752,7 +788,7 @@ object GlobalDefinitions { val beamer = ToolDefinition(ObjectClass.beamer) beamer.Size = EquipmentSize.Pistol - beamer.AmmoTypes += Ammo.energy_cell + beamer.AmmoTypes += energy_cell beamer.FireModes += new FireModeDefinition beamer.FireModes.head.AmmoTypeIndices += 0 beamer.FireModes.head.AmmoSlotIndex = 0 @@ -766,8 +802,8 @@ object GlobalDefinitions { val ilc9 = ToolDefinition(ObjectClass.ilc9) //amp ilc9.Size = EquipmentSize.Pistol - ilc9.AmmoTypes += Ammo.bullet_9mm - ilc9.AmmoTypes += Ammo.bullet_9mm_AP + ilc9.AmmoTypes += bullet_9mm + ilc9.AmmoTypes += bullet_9mm_AP ilc9.FireModes += new FireModeDefinition ilc9.FireModes.head.AmmoTypeIndices += 0 ilc9.FireModes.head.AmmoTypeIndices += 1 @@ -778,8 +814,8 @@ object GlobalDefinitions { val suppressor = ToolDefinition(ObjectClass.suppressor) suppressor.Size = EquipmentSize.Rifle - suppressor.AmmoTypes += Ammo.bullet_9mm - suppressor.AmmoTypes += Ammo.bullet_9mm_AP + suppressor.AmmoTypes += bullet_9mm + suppressor.AmmoTypes += bullet_9mm_AP suppressor.FireModes += new FireModeDefinition suppressor.FireModes.head.AmmoTypeIndices += 0 suppressor.FireModes.head.AmmoTypeIndices += 1 @@ -790,12 +826,12 @@ object GlobalDefinitions { val punisher = ToolDefinition(ObjectClass.punisher) punisher.Size = EquipmentSize.Rifle - punisher.AmmoTypes += Ammo.bullet_9mm - punisher.AmmoTypes += Ammo.bullet_9mm_AP - punisher.AmmoTypes += Ammo.rocket - punisher.AmmoTypes += Ammo.frag_cartridge - punisher.AmmoTypes += Ammo.jammer_cartridge - punisher.AmmoTypes += Ammo.plasma_cartridge + punisher.AmmoTypes += bullet_9mm + punisher.AmmoTypes += bullet_9mm_AP + punisher.AmmoTypes += rocket + punisher.AmmoTypes += frag_cartridge + punisher.AmmoTypes += jammer_cartridge + punisher.AmmoTypes += plasma_cartridge punisher.FireModes += new FireModeDefinition punisher.FireModes.head.AmmoTypeIndices += 0 punisher.FireModes.head.AmmoTypeIndices += 1 @@ -813,8 +849,8 @@ object GlobalDefinitions { val flechette = ToolDefinition(ObjectClass.flechette) //sweeper flechette.Size = EquipmentSize.Rifle - flechette.AmmoTypes += Ammo.shotgun_shell - flechette.AmmoTypes += Ammo.shotgun_shell_AP + flechette.AmmoTypes += shotgun_shell + flechette.AmmoTypes += shotgun_shell_AP flechette.FireModes += new FireModeDefinition flechette.FireModes.head.AmmoTypeIndices += 0 flechette.FireModes.head.AmmoTypeIndices += 1 @@ -825,8 +861,8 @@ object GlobalDefinitions { val cycler = ToolDefinition(ObjectClass.cycler) cycler.Size = EquipmentSize.Rifle - cycler.AmmoTypes += Ammo.bullet_9mm - cycler.AmmoTypes += Ammo.bullet_9mm_AP + cycler.AmmoTypes += bullet_9mm + cycler.AmmoTypes += bullet_9mm_AP cycler.FireModes += new FireModeDefinition cycler.FireModes.head.AmmoTypeIndices += 0 cycler.FireModes.head.AmmoTypeIndices += 1 @@ -837,8 +873,8 @@ object GlobalDefinitions { val gauss = ToolDefinition(ObjectClass.gauss) gauss.Size = EquipmentSize.Rifle - gauss.AmmoTypes += Ammo.bullet_9mm - gauss.AmmoTypes += Ammo.bullet_9mm_AP + gauss.AmmoTypes += bullet_9mm + gauss.AmmoTypes += bullet_9mm_AP gauss.FireModes += new FireModeDefinition gauss.FireModes.head.AmmoTypeIndices += 0 gauss.FireModes.head.AmmoTypeIndices += 1 @@ -849,7 +885,7 @@ object GlobalDefinitions { val pulsar = ToolDefinition(ObjectClass.pulsar) pulsar.Size = EquipmentSize.Rifle - pulsar.AmmoTypes += Ammo.energy_cell + pulsar.AmmoTypes += energy_cell pulsar.FireModes += new FireModeDefinition pulsar.FireModes.head.AmmoTypeIndices += 0 pulsar.FireModes.head.AmmoSlotIndex = 0 @@ -863,7 +899,7 @@ object GlobalDefinitions { val anniversary_guna = ToolDefinition(ObjectClass.anniversary_guna) //tr stinger anniversary_guna.Size = EquipmentSize.Pistol - anniversary_guna.AmmoTypes += Ammo.anniversary_ammo + anniversary_guna.AmmoTypes += anniversary_ammo anniversary_guna.FireModes += new FireModeDefinition anniversary_guna.FireModes.head.AmmoTypeIndices += 0 anniversary_guna.FireModes.head.AmmoSlotIndex = 0 @@ -878,7 +914,7 @@ object GlobalDefinitions { val anniversary_gun = ToolDefinition(ObjectClass.anniversary_gun) //nc spear anniversary_gun.Size = EquipmentSize.Pistol - anniversary_gun.AmmoTypes += Ammo.anniversary_ammo + anniversary_gun.AmmoTypes += anniversary_ammo anniversary_gun.FireModes += new FireModeDefinition anniversary_gun.FireModes.head.AmmoTypeIndices += 0 anniversary_gun.FireModes.head.AmmoSlotIndex = 0 @@ -893,7 +929,7 @@ object GlobalDefinitions { val anniversary_gunb = ToolDefinition(ObjectClass.anniversary_gunb) //vs eraser anniversary_gunb.Size = EquipmentSize.Pistol - anniversary_gunb.AmmoTypes += Ammo.anniversary_ammo + anniversary_gunb.AmmoTypes += anniversary_ammo anniversary_gunb.FireModes += new FireModeDefinition anniversary_gunb.FireModes.head.AmmoTypeIndices += 0 anniversary_gunb.FireModes.head.AmmoSlotIndex = 0 @@ -908,7 +944,7 @@ object GlobalDefinitions { val spiker = ToolDefinition(ObjectClass.spiker) spiker.Size = EquipmentSize.Pistol - spiker.AmmoTypes += Ammo.ancient_ammo_combo + spiker.AmmoTypes += ancient_ammo_combo spiker.FireModes += new FireModeDefinition spiker.FireModes.head.AmmoTypeIndices += 0 spiker.FireModes.head.AmmoSlotIndex = 0 @@ -918,8 +954,8 @@ object GlobalDefinitions { val mini_chaingun = ToolDefinition(ObjectClass.mini_chaingun) mini_chaingun.Size = EquipmentSize.Rifle - mini_chaingun.AmmoTypes += Ammo.bullet_9mm - mini_chaingun.AmmoTypes += Ammo.bullet_9mm_AP + mini_chaingun.AmmoTypes += bullet_9mm + mini_chaingun.AmmoTypes += bullet_9mm_AP mini_chaingun.FireModes += new FireModeDefinition mini_chaingun.FireModes.head.AmmoTypeIndices += 0 mini_chaingun.FireModes.head.AmmoTypeIndices += 1 @@ -930,8 +966,8 @@ object GlobalDefinitions { val r_shotgun = ToolDefinition(ObjectClass.r_shotgun) //jackhammer r_shotgun.Size = EquipmentSize.Rifle - r_shotgun.AmmoTypes += Ammo.shotgun_shell - r_shotgun.AmmoTypes += Ammo.shotgun_shell_AP + r_shotgun.AmmoTypes += shotgun_shell + r_shotgun.AmmoTypes += shotgun_shell_AP r_shotgun.FireModes += new FireModeDefinition r_shotgun.FireModes.head.AmmoTypeIndices += 0 r_shotgun.FireModes.head.AmmoTypeIndices += 1 @@ -948,7 +984,7 @@ object GlobalDefinitions { val lasher = ToolDefinition(ObjectClass.lasher) lasher.Size = EquipmentSize.Rifle - lasher.AmmoTypes += Ammo.energy_cell + lasher.AmmoTypes += energy_cell lasher.FireModes += new FireModeDefinition lasher.FireModes.head.AmmoTypeIndices += 0 lasher.FireModes.head.AmmoSlotIndex = 0 @@ -962,7 +998,7 @@ object GlobalDefinitions { val maelstrom = ToolDefinition(ObjectClass.maelstrom) maelstrom.Size = EquipmentSize.Rifle - maelstrom.AmmoTypes += Ammo.maelstrom_ammo + maelstrom.AmmoTypes += maelstrom_ammo maelstrom.FireModes += new FireModeDefinition maelstrom.FireModes.head.AmmoTypeIndices += 0 maelstrom.FireModes.head.AmmoSlotIndex = 0 @@ -980,7 +1016,7 @@ object GlobalDefinitions { val phoenix = ToolDefinition(ObjectClass.phoenix) //decimator phoenix.Size = EquipmentSize.Rifle - phoenix.AmmoTypes += Ammo.phoenix_missile + phoenix.AmmoTypes += phoenix_missile phoenix.FireModes += new FireModeDefinition phoenix.FireModes.head.AmmoTypeIndices += 0 phoenix.FireModes.head.AmmoSlotIndex = 0 @@ -994,7 +1030,7 @@ object GlobalDefinitions { val striker = ToolDefinition(ObjectClass.striker) striker.Size = EquipmentSize.Rifle - striker.AmmoTypes += Ammo.striker_missile_ammo + striker.AmmoTypes += striker_missile_ammo striker.FireModes += new FireModeDefinition striker.FireModes.head.AmmoTypeIndices += 0 striker.FireModes.head.AmmoSlotIndex = 0 @@ -1008,7 +1044,7 @@ object GlobalDefinitions { val hunterseeker = ToolDefinition(ObjectClass.hunterseeker) //phoenix hunterseeker.Size = EquipmentSize.Rifle - hunterseeker.AmmoTypes += Ammo.hunter_seeker_missile + hunterseeker.AmmoTypes += hunter_seeker_missile hunterseeker.FireModes += new FireModeDefinition hunterseeker.FireModes.head.AmmoTypeIndices += 0 hunterseeker.FireModes.head.AmmoSlotIndex = 0 @@ -1022,7 +1058,7 @@ object GlobalDefinitions { val lancer = ToolDefinition(ObjectClass.lancer) lancer.Size = EquipmentSize.Rifle - lancer.AmmoTypes += Ammo.lancer_cartridge + lancer.AmmoTypes += lancer_cartridge lancer.FireModes += new FireModeDefinition lancer.FireModes.head.AmmoTypeIndices += 0 lancer.FireModes.head.AmmoSlotIndex = 0 @@ -1032,8 +1068,8 @@ object GlobalDefinitions { val rocklet = ToolDefinition(ObjectClass.rocklet) rocklet.Size = EquipmentSize.Rifle - rocklet.AmmoTypes += Ammo.rocket - rocklet.AmmoTypes += Ammo.frag_cartridge + rocklet.AmmoTypes += rocket + rocklet.AmmoTypes += frag_cartridge rocklet.FireModes += new FireModeDefinition rocklet.FireModes.head.AmmoTypeIndices += 0 rocklet.FireModes.head.AmmoTypeIndices += 1 @@ -1050,9 +1086,9 @@ object GlobalDefinitions { val thumper = ToolDefinition(ObjectClass.thumper) thumper.Size = EquipmentSize.Rifle - thumper.AmmoTypes += Ammo.frag_cartridge - thumper.AmmoTypes += Ammo.plasma_cartridge - thumper.AmmoTypes += Ammo.jammer_cartridge + thumper.AmmoTypes += frag_cartridge + thumper.AmmoTypes += plasma_cartridge + thumper.AmmoTypes += jammer_cartridge thumper.FireModes += new FireModeDefinition thumper.FireModes.head.AmmoTypeIndices += 0 thumper.FireModes.head.AmmoTypeIndices += 1 @@ -1070,7 +1106,7 @@ object GlobalDefinitions { val radiator = ToolDefinition(ObjectClass.radiator) radiator.Size = EquipmentSize.Rifle - radiator.AmmoTypes += Ammo.ancient_ammo_combo + radiator.AmmoTypes += ancient_ammo_combo radiator.FireModes += new FireModeDefinition radiator.FireModes.head.AmmoTypeIndices += 0 radiator.FireModes.head.AmmoSlotIndex = 0 @@ -1084,7 +1120,7 @@ object GlobalDefinitions { val heavy_sniper = ToolDefinition(ObjectClass.heavy_sniper) //hsr heavy_sniper.Size = EquipmentSize.Rifle - heavy_sniper.AmmoTypes += Ammo.bolt + heavy_sniper.AmmoTypes += bolt heavy_sniper.FireModes += new FireModeDefinition heavy_sniper.FireModes.head.AmmoTypeIndices += 0 heavy_sniper.FireModes.head.AmmoSlotIndex = 0 @@ -1094,7 +1130,7 @@ object GlobalDefinitions { val bolt_driver = ToolDefinition(ObjectClass.bolt_driver) bolt_driver.Size = EquipmentSize.Rifle - bolt_driver.AmmoTypes += Ammo.bolt + bolt_driver.AmmoTypes += bolt bolt_driver.FireModes += new FireModeDefinition bolt_driver.FireModes.head.AmmoTypeIndices += 0 bolt_driver.FireModes.head.AmmoSlotIndex = 0 @@ -1104,7 +1140,7 @@ object GlobalDefinitions { val oicw = ToolDefinition(ObjectClass.oicw) //scorpion oicw.Size = EquipmentSize.Rifle - oicw.AmmoTypes += Ammo.oicw_ammo + oicw.AmmoTypes += oicw_ammo oicw.FireModes += new FireModeDefinition oicw.FireModes.head.AmmoTypeIndices += 0 oicw.FireModes.head.AmmoSlotIndex = 0 @@ -1118,7 +1154,7 @@ object GlobalDefinitions { val flamethrower = ToolDefinition(ObjectClass.flamethrower) flamethrower.Size = EquipmentSize.Rifle - flamethrower.AmmoTypes += Ammo.flamethrower_ammo + flamethrower.AmmoTypes += flamethrower_ammo flamethrower.FireModes += new FireModeDefinition flamethrower.FireModes.head.AmmoTypeIndices += 0 flamethrower.FireModes.head.AmmoSlotIndex = 0 @@ -1131,10 +1167,37 @@ object GlobalDefinitions { flamethrower.FireModes(1).Chamber = 50 flamethrower.Tile = InventoryTile.Tile63 + val + trhev_dualcycler = ToolDefinition(ObjectClass.trhev_dualcycler) + + val + trhev_pounder = ToolDefinition(ObjectClass.trhev_pounder) + + val + trhev_burster = ToolDefinition(ObjectClass.trhev_burster) + + val + nchev_scattercannon = ToolDefinition(ObjectClass.nchev_scattercannon) + + val + nchev_falcon = ToolDefinition(ObjectClass.nchev_falcon) + + val + nchev_sparrow = ToolDefinition(ObjectClass.nchev_sparrow) + + val + vshev_quasar = ToolDefinition(ObjectClass.vshev_quasar) + + val + vshev_comet = ToolDefinition(ObjectClass.vshev_comet) + + val + vshev_starfire = ToolDefinition(ObjectClass.vshev_starfire) + val medicalapplicator = ToolDefinition(ObjectClass.medicalapplicator) medicalapplicator.Size = EquipmentSize.Pistol - medicalapplicator.AmmoTypes += Ammo.health_canister + medicalapplicator.AmmoTypes += health_canister medicalapplicator.FireModes += new FireModeDefinition medicalapplicator.FireModes.head.AmmoTypeIndices += 0 medicalapplicator.FireModes.head.AmmoSlotIndex = 0 @@ -1148,8 +1211,8 @@ object GlobalDefinitions { val nano_dispenser = ToolDefinition(ObjectClass.nano_dispenser) nano_dispenser.Size = EquipmentSize.Rifle - nano_dispenser.AmmoTypes += Ammo.armor_canister - nano_dispenser.AmmoTypes += Ammo.upgrade_canister + nano_dispenser.AmmoTypes += armor_canister + nano_dispenser.AmmoTypes += upgrade_canister nano_dispenser.FireModes += new FireModeDefinition nano_dispenser.FireModes.head.AmmoTypeIndices += 0 nano_dispenser.FireModes.head.AmmoTypeIndices += 1 @@ -1160,7 +1223,7 @@ object GlobalDefinitions { val bank = ToolDefinition(ObjectClass.bank) bank.Size = EquipmentSize.Pistol - bank.AmmoTypes += Ammo.armor_canister + bank.AmmoTypes += armor_canister bank.FireModes += new FireModeDefinition bank.FireModes.head.AmmoTypeIndices += 0 bank.FireModes.head.AmmoSlotIndex = 0 @@ -1179,7 +1242,7 @@ object GlobalDefinitions { val trek = ToolDefinition(ObjectClass.trek) trek.Size = EquipmentSize.Pistol - trek.AmmoTypes += Ammo.trek_ammo + trek.AmmoTypes += trek_ammo trek.FireModes += new FireModeDefinition trek.FireModes.head.AmmoTypeIndices += 0 trek.FireModes.head.AmmoSlotIndex = 0 @@ -1222,7 +1285,7 @@ object GlobalDefinitions { val fury_weapon_systema = ToolDefinition(ObjectClass.fury_weapon_systema) fury_weapon_systema.Size = EquipmentSize.VehicleWeapon - fury_weapon_systema.AmmoTypes += Ammo.hellfire_ammo + fury_weapon_systema.AmmoTypes += hellfire_ammo fury_weapon_systema.FireModes += new FireModeDefinition fury_weapon_systema.FireModes.head.AmmoTypeIndices += 0 fury_weapon_systema.FireModes.head.AmmoSlotIndex = 0 @@ -1231,30 +1294,486 @@ object GlobalDefinitions { val quadassault_weapon_system = ToolDefinition(ObjectClass.quadassault_weapon_system) quadassault_weapon_system.Size = EquipmentSize.VehicleWeapon - quadassault_weapon_system.AmmoTypes += Ammo.bullet_12mm + quadassault_weapon_system.AmmoTypes += bullet_12mm quadassault_weapon_system.FireModes += new FireModeDefinition quadassault_weapon_system.FireModes.head.AmmoTypeIndices += 0 quadassault_weapon_system.FireModes.head.AmmoSlotIndex = 0 - quadassault_weapon_system.FireModes.head.Magazine = 100 + quadassault_weapon_system.FireModes.head.Magazine = 150 + + val + scythe = ToolDefinition(ObjectClass.scythe) //TODO resolve ammo slot/pool discrepancy + scythe.Size = EquipmentSize.VehicleWeapon + scythe.AmmoTypes += ancient_ammo_vehicle + scythe.AmmoTypes += ancient_ammo_vehicle + scythe.FireModes += new FireModeDefinition + scythe.FireModes.head.AmmoTypeIndices += 0 + scythe.FireModes.head.AmmoSlotIndex = 0 + scythe.FireModes.head.Magazine = 250 + scythe.FireModes += new FireModeDefinition + scythe.FireModes(1).AmmoTypeIndices += 0 + scythe.FireModes(1).AmmoSlotIndex = 1 //note: the scythe has two magazines using a single pool; however, it can not ammo-switch or mode-switch + scythe.FireModes(1).Magazine = 250 val chaingun_p = ToolDefinition(ObjectClass.chaingun_p) chaingun_p.Size = EquipmentSize.VehicleWeapon - chaingun_p.AmmoTypes += Ammo.bullet_12mm + chaingun_p.AmmoTypes += bullet_12mm chaingun_p.FireModes += new FireModeDefinition chaingun_p.FireModes.head.AmmoTypeIndices += 0 chaingun_p.FireModes.head.AmmoSlotIndex = 0 chaingun_p.FireModes.head.Magazine = 150 + val + skyguard_weapon_system = ToolDefinition(ObjectClass.skyguard_weapon_system) + skyguard_weapon_system.Size = EquipmentSize.VehicleWeapon + skyguard_weapon_system.AmmoTypes += skyguard_flak_cannon_ammo + skyguard_weapon_system.AmmoTypes += bullet_12mm + skyguard_weapon_system.FireModes += new FireModeDefinition + skyguard_weapon_system.FireModes.head.AmmoTypeIndices += 0 + skyguard_weapon_system.FireModes.head.AmmoSlotIndex = 0 + skyguard_weapon_system.FireModes.head.Magazine = 40 + skyguard_weapon_system.FireModes += new FireModeDefinition + skyguard_weapon_system.FireModes(1).AmmoTypeIndices += 1 + skyguard_weapon_system.FireModes(1).AmmoSlotIndex = 1 + skyguard_weapon_system.FireModes(1).Magazine = 1 //TODO check + + val + grenade_launcher_marauder = ToolDefinition(ObjectClass.grenade_launcher_marauder) + grenade_launcher_marauder.Size = EquipmentSize.VehicleWeapon + grenade_launcher_marauder.AmmoTypes += heavy_grenade_mortar + grenade_launcher_marauder.FireModes += new FireModeDefinition + grenade_launcher_marauder.FireModes.head.AmmoTypeIndices += 0 + grenade_launcher_marauder.FireModes.head.AmmoSlotIndex = 0 + grenade_launcher_marauder.FireModes.head.Magazine = 50 + + val + advanced_missile_launcher_t = ToolDefinition(ObjectClass.advanced_missile_launcher_t) + advanced_missile_launcher_t.Size = EquipmentSize.VehicleWeapon + advanced_missile_launcher_t.AmmoTypes += firebird_missile + advanced_missile_launcher_t.FireModes += new FireModeDefinition + advanced_missile_launcher_t.FireModes.head.AmmoTypeIndices += 0 + advanced_missile_launcher_t.FireModes.head.AmmoSlotIndex = 0 + advanced_missile_launcher_t.FireModes.head.Magazine = 40 + + val + flux_cannon_thresher = ToolDefinition(ObjectClass.flux_cannon_thresher) + flux_cannon_thresher.Size = EquipmentSize.VehicleWeapon + flux_cannon_thresher.AmmoTypes += flux_cannon_thresher_battery + flux_cannon_thresher.FireModes += new FireModeDefinition + flux_cannon_thresher.FireModes.head.AmmoTypeIndices += 0 + flux_cannon_thresher.FireModes.head.AmmoSlotIndex = 0 + flux_cannon_thresher.FireModes.head.Magazine = 100 + + val + mediumtransport_weapon_systemA = ToolDefinition(ObjectClass.mediumtransport_weapon_systemA) + mediumtransport_weapon_systemA.Size = EquipmentSize.VehicleWeapon + mediumtransport_weapon_systemA.AmmoTypes += bullet_20mm + mediumtransport_weapon_systemA.FireModes += new FireModeDefinition + mediumtransport_weapon_systemA.FireModes.head.AmmoTypeIndices += 0 + mediumtransport_weapon_systemA.FireModes.head.AmmoSlotIndex = 0 + mediumtransport_weapon_systemA.FireModes.head.Magazine = 150 + + val + mediumtransport_weapon_systemB = ToolDefinition(ObjectClass.mediumtransport_weapon_systemB) + mediumtransport_weapon_systemB.Size = EquipmentSize.VehicleWeapon + mediumtransport_weapon_systemB.AmmoTypes += bullet_20mm + mediumtransport_weapon_systemB.FireModes += new FireModeDefinition + mediumtransport_weapon_systemB.FireModes.head.AmmoTypeIndices += 0 + mediumtransport_weapon_systemB.FireModes.head.AmmoSlotIndex = 0 + mediumtransport_weapon_systemB.FireModes.head.Magazine = 150 + + val + battlewagon_weapon_systema = ToolDefinition(ObjectClass.battlewagon_weapon_systema) + battlewagon_weapon_systema.Size = EquipmentSize.VehicleWeapon + battlewagon_weapon_systema.AmmoTypes += bullet_15mm + 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 + + val + battlewagon_weapon_systemb = ToolDefinition(ObjectClass.battlewagon_weapon_systemb) + 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 + + val + battlewagon_weapon_systemc = ToolDefinition(ObjectClass.battlewagon_weapon_systemc) + 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 + + val + battlewagon_weapon_systemd = ToolDefinition(ObjectClass.battlewagon_weapon_systemd) + 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 + + val + thunderer_weapon_systema = ToolDefinition(ObjectClass.thunderer_weapon_systema) + thunderer_weapon_systema.Size = EquipmentSize.VehicleWeapon + thunderer_weapon_systema.AmmoTypes += gauss_cannon_ammo + thunderer_weapon_systema.FireModes += new FireModeDefinition + thunderer_weapon_systema.FireModes.head.AmmoTypeIndices += 0 + thunderer_weapon_systema.FireModes.head.AmmoSlotIndex = 0 + thunderer_weapon_systema.FireModes.head.Magazine = 15 + + val + thunderer_weapon_systemb = ToolDefinition(ObjectClass.thunderer_weapon_systemb) + thunderer_weapon_systemb.Size = EquipmentSize.VehicleWeapon + thunderer_weapon_systemb.AmmoTypes += gauss_cannon_ammo + thunderer_weapon_systemb.FireModes += new FireModeDefinition + thunderer_weapon_systemb.FireModes.head.AmmoTypeIndices += 0 + thunderer_weapon_systemb.FireModes.head.AmmoSlotIndex = 0 + thunderer_weapon_systemb.FireModes.head.Magazine = 15 + + val + aurora_weapon_systema = ToolDefinition(ObjectClass.aurora_weapon_systema) + aurora_weapon_systema.Size = EquipmentSize.VehicleWeapon + aurora_weapon_systema.AmmoTypes += fluxpod_ammo + aurora_weapon_systema.FireModes += new FireModeDefinition + aurora_weapon_systema.FireModes.head.AmmoTypeIndices += 0 + aurora_weapon_systema.FireModes.head.AmmoSlotIndex = 0 + aurora_weapon_systema.FireModes.head.Magazine = 12 + aurora_weapon_systema.FireModes += new FireModeDefinition + aurora_weapon_systema.FireModes(1).AmmoTypeIndices += 0 + aurora_weapon_systema.FireModes(1).AmmoSlotIndex = 1 + aurora_weapon_systema.FireModes(1).Magazine = 12 + + val + aurora_weapon_systemb = ToolDefinition(ObjectClass.aurora_weapon_systemb) + aurora_weapon_systemb.Size = EquipmentSize.VehicleWeapon + aurora_weapon_systemb.AmmoTypes += fluxpod_ammo + aurora_weapon_systemb.FireModes += new FireModeDefinition + aurora_weapon_systemb.FireModes.head.AmmoTypeIndices += 0 + aurora_weapon_systemb.FireModes.head.AmmoSlotIndex = 0 + aurora_weapon_systemb.FireModes.head.Magazine = 12 + aurora_weapon_systemb.FireModes += new FireModeDefinition + aurora_weapon_systemb.FireModes(1).AmmoTypeIndices += 0 + aurora_weapon_systemb.FireModes(1).AmmoSlotIndex = 1 + aurora_weapon_systemb.FireModes(1).Magazine = 12 + + val + apc_weapon_systema = ToolDefinition(ObjectClass.apc_weapon_systema) + apc_weapon_systema.Size = EquipmentSize.VehicleWeapon + apc_weapon_systema.AmmoTypes += bullet_75mm + apc_weapon_systema.FireModes += new FireModeDefinition + apc_weapon_systema.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systema.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systema.FireModes.head.Magazine = 50 + + val + apc_weapon_systemb = ToolDefinition(ObjectClass.apc_weapon_systemb) + apc_weapon_systemb.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemb.AmmoTypes += bullet_75mm + apc_weapon_systemb.FireModes += new FireModeDefinition + apc_weapon_systemb.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemb.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemb.FireModes.head.Magazine = 50 + + val + apc_ballgun_r = ToolDefinition(ObjectClass.apc_ballgun_r) + apc_ballgun_r.Size = EquipmentSize.VehicleWeapon + apc_ballgun_r.AmmoTypes += bullet_12mm + apc_ballgun_r.FireModes += new FireModeDefinition + apc_ballgun_r.FireModes.head.AmmoTypeIndices += 0 + apc_ballgun_r.FireModes.head.AmmoSlotIndex = 0 + apc_ballgun_r.FireModes.head.Magazine = 150 + + val + apc_ballgun_l = ToolDefinition(ObjectClass.apc_ballgun_l) + apc_ballgun_l.Size = EquipmentSize.VehicleWeapon + apc_ballgun_l.AmmoTypes += bullet_12mm + apc_ballgun_l.FireModes += new FireModeDefinition + apc_ballgun_l.FireModes.head.AmmoTypeIndices += 0 + apc_ballgun_l.FireModes.head.AmmoSlotIndex = 0 + apc_ballgun_l.FireModes.head.Magazine = 150 + + val + apc_weapon_systemc_tr = ToolDefinition(ObjectClass.apc_weapon_systemc_tr) + apc_weapon_systemc_tr.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemc_tr.AmmoTypes += bullet_15mm + apc_weapon_systemc_tr.FireModes += new FireModeDefinition + apc_weapon_systemc_tr.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemc_tr.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemc_tr.FireModes.head.Magazine = 150 + + val + apc_weapon_systemd_tr = ToolDefinition(ObjectClass.apc_weapon_systemd_tr) + apc_weapon_systemd_tr.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemd_tr.AmmoTypes += bullet_15mm + apc_weapon_systemd_tr.FireModes += new FireModeDefinition + apc_weapon_systemd_tr.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemd_tr.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemd_tr.FireModes.head.Magazine = 150 + + val + apc_weapon_systemc_nc = ToolDefinition(ObjectClass.apc_weapon_systemc_nc) + apc_weapon_systemc_nc.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemc_nc.AmmoTypes += bullet_20mm + apc_weapon_systemc_nc.FireModes += new FireModeDefinition + apc_weapon_systemc_nc.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemc_nc.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemc_nc.FireModes.head.Magazine = 150 + + val + apc_weapon_systemd_nc = ToolDefinition(ObjectClass.apc_weapon_systemd_nc) + apc_weapon_systemd_nc.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemd_nc.AmmoTypes += bullet_20mm + apc_weapon_systemd_nc.FireModes += new FireModeDefinition + apc_weapon_systemd_nc.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemd_nc.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemd_nc.FireModes.head.Magazine = 150 + + val + apc_weapon_systemc_vs = ToolDefinition(ObjectClass.apc_weapon_systemc_vs) + apc_weapon_systemc_vs.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemc_vs.AmmoTypes += flux_cannon_thresher_battery + apc_weapon_systemc_vs.FireModes += new FireModeDefinition + apc_weapon_systemc_vs.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemc_vs.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemc_vs.FireModes.head.Magazine = 100 + + val + apc_weapon_systemd_vs = ToolDefinition(ObjectClass.apc_weapon_systemd_vs) + apc_weapon_systemd_vs.Size = EquipmentSize.VehicleWeapon + apc_weapon_systemd_vs.AmmoTypes += flux_cannon_thresher_battery + apc_weapon_systemd_vs.FireModes += new FireModeDefinition + apc_weapon_systemd_vs.FireModes.head.AmmoTypeIndices += 0 + apc_weapon_systemd_vs.FireModes.head.AmmoSlotIndex = 0 + apc_weapon_systemd_vs.FireModes.head.Magazine = 100 + + val + lightning_weapon_system = ToolDefinition(ObjectClass.lightning_weapon_system) + lightning_weapon_system.Size = EquipmentSize.VehicleWeapon + lightning_weapon_system.AmmoTypes += bullet_75mm + lightning_weapon_system.AmmoTypes += bullet_25mm + lightning_weapon_system.FireModes += new FireModeDefinition + lightning_weapon_system.FireModes.head.AmmoTypeIndices += 0 + lightning_weapon_system.FireModes.head.AmmoSlotIndex = 0 + lightning_weapon_system.FireModes.head.Magazine = 20 + lightning_weapon_system.FireModes += new FireModeDefinition + lightning_weapon_system.FireModes(1).AmmoTypeIndices += 1 + lightning_weapon_system.FireModes(1).AmmoSlotIndex = 1 + lightning_weapon_system.FireModes(1).Magazine = 1 //TODO check + + val + prowler_weapon_systemA = ToolDefinition(ObjectClass.prowler_weapon_systemA) + prowler_weapon_systemA.Size = EquipmentSize.VehicleWeapon + prowler_weapon_systemA.AmmoTypes += bullet_105mm + prowler_weapon_systemA.FireModes += new FireModeDefinition + prowler_weapon_systemA.FireModes.head.AmmoTypeIndices += 0 + prowler_weapon_systemA.FireModes.head.AmmoSlotIndex = 0 + prowler_weapon_systemA.FireModes.head.Magazine = 20 + + val + prowler_weapon_systemB = ToolDefinition(ObjectClass.prowler_weapon_systemB) + prowler_weapon_systemB.Size = EquipmentSize.VehicleWeapon + prowler_weapon_systemB.AmmoTypes += bullet_15mm + 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 + + val + vanguard_weapon_system = ToolDefinition(ObjectClass.vanguard_weapon_system) + vanguard_weapon_system.Size = EquipmentSize.VehicleWeapon + vanguard_weapon_system.AmmoTypes += bullet_150mm + vanguard_weapon_system.AmmoTypes += bullet_20mm + vanguard_weapon_system.FireModes += new FireModeDefinition + vanguard_weapon_system.FireModes.head.AmmoTypeIndices += 0 + vanguard_weapon_system.FireModes.head.AmmoSlotIndex = 0 + vanguard_weapon_system.FireModes.head.Magazine = 10 + vanguard_weapon_system.FireModes += new FireModeDefinition + vanguard_weapon_system.FireModes(1).AmmoTypeIndices += 1 + vanguard_weapon_system.FireModes(1).AmmoSlotIndex = 1 + vanguard_weapon_system.FireModes(1).Magazine = 1 //TODO check + + val + particle_beam_magrider = ToolDefinition(ObjectClass.particle_beam_magrider) + particle_beam_magrider.Size = EquipmentSize.VehicleWeapon + particle_beam_magrider.AmmoTypes += pulse_battery + particle_beam_magrider.FireModes += new FireModeDefinition + particle_beam_magrider.FireModes.head.AmmoTypeIndices += 0 + particle_beam_magrider.FireModes.head.AmmoSlotIndex = 0 + particle_beam_magrider.FireModes.head.Magazine = 150 + + val + heavy_rail_beam_magrider = ToolDefinition(ObjectClass.heavy_rail_beam_magrider) + heavy_rail_beam_magrider.Size = EquipmentSize.VehicleWeapon + heavy_rail_beam_magrider.AmmoTypes += heavy_rail_beam_battery + heavy_rail_beam_magrider.FireModes += new FireModeDefinition + heavy_rail_beam_magrider.FireModes.head.AmmoTypeIndices += 0 + heavy_rail_beam_magrider.FireModes.head.AmmoSlotIndex = 0 + heavy_rail_beam_magrider.FireModes.head.Magazine = 25 + + val + flail_weapon = ToolDefinition(ObjectClass.flail_weapon) + flail_weapon.Size = EquipmentSize.VehicleWeapon + flail_weapon.AmmoTypes += ancient_ammo_vehicle + flail_weapon.FireModes += new FireModeDefinition + flail_weapon.FireModes.head.AmmoTypeIndices += 0 + flail_weapon.FireModes.head.AmmoSlotIndex = 0 + flail_weapon.FireModes.head.Magazine = 100 + + val + rotarychaingun_mosquito = ToolDefinition(ObjectClass.rotarychaingun_mosquito) + rotarychaingun_mosquito.Size = EquipmentSize.VehicleWeapon + rotarychaingun_mosquito.AmmoTypes += bullet_12mm + rotarychaingun_mosquito.FireModes += new FireModeDefinition + rotarychaingun_mosquito.FireModes.head.AmmoTypeIndices += 0 + rotarychaingun_mosquito.FireModes.head.AmmoSlotIndex = 0 + rotarychaingun_mosquito.FireModes.head.Magazine = 150 + + val + lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system) + lightgunship_weapon_system.Size = EquipmentSize.VehicleWeapon + lightgunship_weapon_system.AmmoTypes += bullet_20mm + lightgunship_weapon_system.AmmoTypes += reaver_rocket + lightgunship_weapon_system.FireModes += new FireModeDefinition + lightgunship_weapon_system.FireModes.head.AmmoTypeIndices += 0 + lightgunship_weapon_system.FireModes.head.AmmoSlotIndex = 0 + lightgunship_weapon_system.FireModes.head.Magazine = 150 + lightgunship_weapon_system.FireModes += new FireModeDefinition + lightgunship_weapon_system.FireModes(1).AmmoTypeIndices += 1 + lightgunship_weapon_system.FireModes(1).AmmoSlotIndex = 1 + lightgunship_weapon_system.FireModes(1).Magazine = 1 //TODO check + + val + wasp_weapon_system = ToolDefinition(ObjectClass.wasp_weapon_system) + wasp_weapon_system.Size = EquipmentSize.VehicleWeapon + wasp_weapon_system.AmmoTypes += wasp_gun_ammo + wasp_weapon_system.AmmoTypes += wasp_rocket_ammo + wasp_weapon_system.FireModes += new FireModeDefinition + wasp_weapon_system.FireModes.head.AmmoTypeIndices += 0 + wasp_weapon_system.FireModes.head.AmmoSlotIndex = 0 + wasp_weapon_system.FireModes.head.Magazine = 30 + wasp_weapon_system.FireModes += new FireModeDefinition + wasp_weapon_system.FireModes(1).AmmoTypeIndices += 1 + wasp_weapon_system.FireModes(1).AmmoSlotIndex = 1 + wasp_weapon_system.FireModes(1).Magazine = 1 //TODO check + + val + liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system) + liberator_weapon_system.Size = EquipmentSize.VehicleWeapon + liberator_weapon_system.AmmoTypes += bullet_35mm + liberator_weapon_system.FireModes += new FireModeDefinition + liberator_weapon_system.FireModes.head.AmmoTypeIndices += 0 + liberator_weapon_system.FireModes.head.AmmoSlotIndex = 0 + liberator_weapon_system.FireModes.head.Magazine = 100 + + val + liberator_bomb_bay = ToolDefinition(ObjectClass.liberator_bomb_bay) + liberator_bomb_bay.Size = EquipmentSize.VehicleWeapon + liberator_bomb_bay.AmmoTypes += liberator_bomb + liberator_bomb_bay.FireModes += new FireModeDefinition + liberator_bomb_bay.FireModes.head.AmmoTypeIndices += 0 + liberator_bomb_bay.FireModes.head.AmmoSlotIndex = 0 + liberator_bomb_bay.FireModes.head.Magazine = 10 + liberator_bomb_bay.FireModes += new FireModeDefinition + liberator_bomb_bay.FireModes(1).AmmoTypeIndices += 0 + liberator_bomb_bay.FireModes(1).AmmoSlotIndex = 0 + liberator_bomb_bay.FireModes(1).Magazine = 10 + + val + liberator_25mm_cannon = ToolDefinition(ObjectClass.liberator_25mm_cannon) + liberator_25mm_cannon.Size = EquipmentSize.VehicleWeapon + liberator_25mm_cannon.AmmoTypes += bullet_25mm + liberator_25mm_cannon.FireModes += new FireModeDefinition + liberator_25mm_cannon.FireModes.head.AmmoTypeIndices += 0 + liberator_25mm_cannon.FireModes.head.AmmoSlotIndex = 0 + liberator_25mm_cannon.FireModes.head.Magazine = 150 + + val + vulture_nose_weapon_system = ToolDefinition(ObjectClass.vulture_nose_weapon_system) + vulture_nose_weapon_system.Size = EquipmentSize.VehicleWeapon + vulture_nose_weapon_system.AmmoTypes += bullet_35mm + 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? + + val + vulture_bomb_bay = ToolDefinition(ObjectClass.vulture_bomb_bay) + vulture_bomb_bay.Size = EquipmentSize.VehicleWeapon + vulture_bomb_bay.AmmoTypes += liberator_bomb + vulture_bomb_bay.FireModes += new FireModeDefinition + vulture_bomb_bay.FireModes.head.AmmoTypeIndices += 0 + vulture_bomb_bay.FireModes.head.AmmoSlotIndex = 0 + vulture_bomb_bay.FireModes.head.Magazine = 10 + + val + vulture_tail_cannon = ToolDefinition(ObjectClass.vulture_tail_cannon) + vulture_tail_cannon.Size = EquipmentSize.VehicleWeapon + vulture_tail_cannon.AmmoTypes += bullet_25mm + vulture_tail_cannon.FireModes += new FireModeDefinition + vulture_tail_cannon.FireModes.head.AmmoTypeIndices += 0 + vulture_tail_cannon.FireModes.head.AmmoSlotIndex = 0 + vulture_tail_cannon.FireModes.head.Magazine = 100 + + val + cannon_dropship_20mm = ToolDefinition(ObjectClass.cannon_dropship_20mm) + cannon_dropship_20mm.Size = EquipmentSize.VehicleWeapon + cannon_dropship_20mm.AmmoTypes += bullet_20mm + cannon_dropship_20mm.FireModes += new FireModeDefinition + cannon_dropship_20mm.FireModes.head.AmmoTypeIndices += 0 + cannon_dropship_20mm.FireModes.head.AmmoSlotIndex = 0 + cannon_dropship_20mm.FireModes.head.Magazine = 250 + + val + dropship_rear_turret = ToolDefinition(ObjectClass.dropship_rear_turret) + dropship_rear_turret.Size = EquipmentSize.VehicleWeapon + dropship_rear_turret.AmmoTypes += bullet_20mm + dropship_rear_turret.FireModes += new FireModeDefinition + dropship_rear_turret.FireModes.head.AmmoTypeIndices += 0 + dropship_rear_turret.FireModes.head.AmmoSlotIndex = 0 + dropship_rear_turret.FireModes.head.Magazine = 250 + + val + galaxy_gunship_cannon = ToolDefinition(ObjectClass.galaxy_gunship_cannon) + galaxy_gunship_cannon.Size = EquipmentSize.VehicleWeapon + galaxy_gunship_cannon.AmmoTypes += heavy_grenade_mortar + galaxy_gunship_cannon.FireModes += new FireModeDefinition + galaxy_gunship_cannon.FireModes.head.AmmoTypeIndices += 0 + galaxy_gunship_cannon.FireModes.head.AmmoSlotIndex = 0 + galaxy_gunship_cannon.FireModes.head.Magazine = 50 + + val + galaxy_gunship_tailgun = ToolDefinition(ObjectClass.galaxy_gunship_tailgun) + galaxy_gunship_tailgun.Size = EquipmentSize.VehicleWeapon + galaxy_gunship_tailgun.AmmoTypes += bullet_35mm + galaxy_gunship_tailgun.FireModes += new FireModeDefinition + galaxy_gunship_tailgun.FireModes.head.AmmoTypeIndices += 0 + galaxy_gunship_tailgun.FireModes.head.AmmoSlotIndex = 0 + galaxy_gunship_tailgun.FireModes.head.Magazine = 200 + + val + galaxy_gunship_gun = ToolDefinition(ObjectClass.galaxy_gunship_gun) + galaxy_gunship_gun.Size = EquipmentSize.VehicleWeapon + galaxy_gunship_gun.AmmoTypes += bullet_35mm + galaxy_gunship_gun.FireModes += new FireModeDefinition + galaxy_gunship_gun.FireModes.head.AmmoTypeIndices += 0 + galaxy_gunship_gun.FireModes.head.AmmoSlotIndex = 0 + galaxy_gunship_gun.FireModes.head.Magazine = 200 + val fury = VehicleDefinition(ObjectClass.fury) fury.Seats += 0 -> new SeatDefinition() fury.Seats(0).Bailable = true fury.Seats(0).ControlledWeapon = 1 + fury.Weapons += 1 -> fury_weapon_systema fury.MountPoints += 1 -> 0 fury.MountPoints += 2 -> 0 - fury.Weapons += 1 -> fury_weapon_systema - fury.TrunkSize = InventoryTile(11, 11) + fury.TrunkSize = InventoryTile.Tile1111 fury.TrunkOffset = 30 val @@ -1262,10 +1781,10 @@ object GlobalDefinitions { quadassault.Seats += 0 -> new SeatDefinition() quadassault.Seats(0).Bailable = true quadassault.Seats(0).ControlledWeapon = 1 + quadassault.Weapons += 1 -> quadassault_weapon_system quadassault.MountPoints += 1 -> 0 quadassault.MountPoints += 2 -> 0 - quadassault.Weapons += 1 -> quadassault_weapon_system - quadassault.TrunkSize = InventoryTile(11, 11) + quadassault.TrunkSize = InventoryTile.Tile1111 quadassault.TrunkOffset = 30 val @@ -1273,10 +1792,10 @@ object GlobalDefinitions { quadstealth.CanCloak = true quadstealth.Seats += 0 -> new SeatDefinition() quadstealth.Seats(0).Bailable = true + quadstealth.CanCloak = true quadstealth.MountPoints += 1 -> 0 quadstealth.MountPoints += 2 -> 0 - quadstealth.CanCloak = true - quadstealth.TrunkSize = InventoryTile(11, 11) + quadstealth.TrunkSize = InventoryTile.Tile1111 quadstealth.TrunkOffset = 30 val @@ -1286,12 +1805,542 @@ object GlobalDefinitions { two_man_assault_buggy.Seats += 1 -> new SeatDefinition() two_man_assault_buggy.Seats(1).Bailable = true two_man_assault_buggy.Seats(1).ControlledWeapon = 2 + two_man_assault_buggy.Weapons += 2 -> chaingun_p two_man_assault_buggy.MountPoints += 1 -> 0 two_man_assault_buggy.MountPoints += 2 -> 1 - two_man_assault_buggy.Weapons += 2 -> chaingun_p - two_man_assault_buggy.TrunkSize = InventoryTile(11, 11) + two_man_assault_buggy.TrunkSize = InventoryTile.Tile1111 two_man_assault_buggy.TrunkOffset = 30 + val + skyguard = VehicleDefinition(ObjectClass.skyguard) + skyguard.Seats += 0 -> new SeatDefinition() + skyguard.Seats(0).Bailable = true + skyguard.Seats += 1 -> new SeatDefinition() + skyguard.Seats(1).Bailable = true + skyguard.Seats(1).ControlledWeapon = 2 + skyguard.Weapons += 2 -> skyguard_weapon_system + skyguard.MountPoints += 1 -> 0 + skyguard.MountPoints += 2 -> 0 + skyguard.MountPoints += 3 -> 1 + skyguard.TrunkSize = InventoryTile.Tile1511 + skyguard.TrunkOffset = 30 + + val + threemanheavybuggy = VehicleDefinition(ObjectClass.threemanheavybuggy) + threemanheavybuggy.Seats += 0 -> new SeatDefinition() + threemanheavybuggy.Seats(0).Bailable = true + threemanheavybuggy.Seats += 1 -> new SeatDefinition() + threemanheavybuggy.Seats(1).Bailable = true + threemanheavybuggy.Seats(1).ControlledWeapon = 3 + threemanheavybuggy.Seats += 2 -> new SeatDefinition() + threemanheavybuggy.Seats(2).Bailable = true + threemanheavybuggy.Seats(2).ControlledWeapon = 4 + threemanheavybuggy.Weapons += 3 -> chaingun_p + threemanheavybuggy.Weapons += 4 -> grenade_launcher_marauder + threemanheavybuggy.MountPoints += 1 -> 0 + threemanheavybuggy.MountPoints += 2 -> 1 + threemanheavybuggy.MountPoints += 3 -> 2 + threemanheavybuggy.TrunkSize = InventoryTile.Tile1511 + threemanheavybuggy.TrunkOffset = 30 + + val + twomanheavybuggy = VehicleDefinition(ObjectClass.twomanheavybuggy) + twomanheavybuggy.Seats += 0 -> new SeatDefinition() + twomanheavybuggy.Seats(0).Bailable = true + twomanheavybuggy.Seats += 1 -> new SeatDefinition() + twomanheavybuggy.Seats(1).Bailable = true + twomanheavybuggy.Seats(1).ControlledWeapon = 2 + twomanheavybuggy.Weapons += 2 -> advanced_missile_launcher_t + twomanheavybuggy.MountPoints += 1 -> 0 + twomanheavybuggy.MountPoints += 2 -> 1 + twomanheavybuggy.TrunkSize = InventoryTile.Tile1511 + twomanheavybuggy.TrunkOffset = 30 + + val + twomanhoverbuggy = VehicleDefinition(ObjectClass.twomanhoverbuggy) + twomanhoverbuggy.Seats += 0 -> new SeatDefinition() + twomanhoverbuggy.Seats(0).Bailable = true + twomanhoverbuggy.Seats += 1 -> new SeatDefinition() + twomanhoverbuggy.Seats(1).Bailable = true + twomanhoverbuggy.Seats(1).ControlledWeapon = 2 + twomanhoverbuggy.Weapons += 2 -> flux_cannon_thresher + twomanhoverbuggy.MountPoints += 1 -> 0 + twomanhoverbuggy.MountPoints += 2 -> 1 + twomanhoverbuggy.TrunkSize = InventoryTile.Tile1511 + twomanhoverbuggy.TrunkOffset = 30 + + val + mediumtransport = VehicleDefinition(ObjectClass.mediumtransport) + mediumtransport.Seats += 0 -> new SeatDefinition() + mediumtransport.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + mediumtransport.Seats += 1 -> new SeatDefinition() + mediumtransport.Seats(1).ControlledWeapon = 5 + mediumtransport.Seats += 2 -> new SeatDefinition() + mediumtransport.Seats(2).ControlledWeapon = 6 + mediumtransport.Seats += 3 -> new SeatDefinition() + mediumtransport.Seats += 4 -> new SeatDefinition() + mediumtransport.Weapons += 5 -> mediumtransport_weapon_systemA + mediumtransport.Weapons += 6 -> mediumtransport_weapon_systemB + mediumtransport.MountPoints += 1 -> 0 + mediumtransport.MountPoints += 2 -> 1 + mediumtransport.MountPoints += 3 -> 2 + mediumtransport.MountPoints += 4 -> 3 + mediumtransport.MountPoints += 5 -> 4 + mediumtransport.TrunkSize = InventoryTile.Tile1515 + mediumtransport.TrunkOffset = 30 + + val + battlewagon = VehicleDefinition(ObjectClass.battlewagon) + battlewagon.Seats += 0 -> new SeatDefinition() + battlewagon.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + battlewagon.Seats += 1 -> new SeatDefinition() + battlewagon.Seats(1).ControlledWeapon = 5 + battlewagon.Seats += 2 -> new SeatDefinition() + battlewagon.Seats(2).ControlledWeapon = 6 + battlewagon.Seats += 3 -> new SeatDefinition() + battlewagon.Seats(3).ControlledWeapon = 7 + battlewagon.Seats += 4 -> new SeatDefinition() + battlewagon.Seats(4).ControlledWeapon = 8 + battlewagon.Weapons += 5 -> battlewagon_weapon_systema + battlewagon.Weapons += 6 -> battlewagon_weapon_systemb + battlewagon.Weapons += 7 -> battlewagon_weapon_systemc + battlewagon.Weapons += 8 -> battlewagon_weapon_systemd + battlewagon.MountPoints += 1 -> 0 + battlewagon.MountPoints += 2 -> 1 + battlewagon.MountPoints += 3 -> 2 + battlewagon.MountPoints += 4 -> 3 + battlewagon.MountPoints += 5 -> 4 + battlewagon.TrunkSize = InventoryTile.Tile1515 + battlewagon.TrunkOffset = 30 + + val + thunderer = VehicleDefinition(ObjectClass.thunderer) + thunderer.Seats += 0 -> new SeatDefinition() + thunderer.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + thunderer.Seats += 1 -> new SeatDefinition() + thunderer.Seats(1).ControlledWeapon = 5 + thunderer.Seats += 2 -> new SeatDefinition() + thunderer.Seats(2).ControlledWeapon = 6 + thunderer.Seats += 3 -> new SeatDefinition() + thunderer.Seats += 4 -> new SeatDefinition() + thunderer.Weapons += 5 -> thunderer_weapon_systema + thunderer.Weapons += 6 -> thunderer_weapon_systemb + thunderer.MountPoints += 1 -> 0 + thunderer.MountPoints += 2 -> 1 + thunderer.MountPoints += 3 -> 2 + thunderer.MountPoints += 4 -> 3 + thunderer.MountPoints += 5 -> 4 + thunderer.TrunkSize = InventoryTile.Tile1515 + thunderer.TrunkOffset = 30 + + val + aurora = VehicleDefinition(ObjectClass.aurora) + aurora.Seats += 0 -> new SeatDefinition() + aurora.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + aurora.Seats += 1 -> new SeatDefinition() + aurora.Seats(1).ControlledWeapon = 5 + aurora.Seats += 2 -> new SeatDefinition() + aurora.Seats(2).ControlledWeapon = 6 + aurora.Seats += 3 -> new SeatDefinition() + aurora.Seats += 4 -> new SeatDefinition() + aurora.Weapons += 5 -> aurora_weapon_systema + aurora.Weapons += 6 -> aurora_weapon_systemb + aurora.MountPoints += 1 -> 0 + aurora.MountPoints += 2 -> 1 + aurora.MountPoints += 3 -> 2 + aurora.MountPoints += 4 -> 3 + aurora.MountPoints += 5 -> 4 + aurora.TrunkSize = InventoryTile.Tile1515 + aurora.TrunkOffset = 30 + + val + apc_tr = VehicleDefinition(ObjectClass.apc_tr) + apc_tr.Seats += 0 -> new SeatDefinition() + apc_tr.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + apc_tr.Seats += 1 -> new SeatDefinition() + apc_tr.Seats(1).ControlledWeapon = 11 + apc_tr.Seats += 2 -> new SeatDefinition() + apc_tr.Seats(2).ControlledWeapon = 12 + apc_tr.Seats += 3 -> new SeatDefinition() + apc_tr.Seats += 4 -> new SeatDefinition() + apc_tr.Seats += 5 -> new SeatDefinition() + apc_tr.Seats(5).ControlledWeapon = 15 + apc_tr.Seats += 6 -> new SeatDefinition() + apc_tr.Seats(6).ControlledWeapon = 16 + apc_tr.Seats += 7 -> new SeatDefinition() + apc_tr.Seats(7).ControlledWeapon = 13 + apc_tr.Seats += 8 -> new SeatDefinition() + apc_tr.Seats(8).ControlledWeapon = 14 + apc_tr.Seats += 9 -> new SeatDefinition() + apc_tr.Seats(9).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_tr.Seats += 10 -> new SeatDefinition() + apc_tr.Seats(10).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_tr.Weapons += 11 -> apc_weapon_systemc_tr + apc_tr.Weapons += 12 -> apc_weapon_systemb + apc_tr.Weapons += 13 -> apc_weapon_systema + apc_tr.Weapons += 14 -> apc_weapon_systemd_tr + apc_tr.Weapons += 15 -> apc_ballgun_r + apc_tr.Weapons += 16 -> apc_ballgun_l + apc_tr.MountPoints += 1 -> 0 + apc_tr.MountPoints += 2 -> 0 + apc_tr.MountPoints += 3 -> 1 + apc_tr.MountPoints += 4 -> 2 + apc_tr.MountPoints += 5 -> 3 + apc_tr.MountPoints += 6 -> 4 + apc_tr.MountPoints += 7 -> 5 + apc_tr.MountPoints += 8 -> 6 + apc_tr.MountPoints += 9 -> 7 + apc_tr.MountPoints += 10 -> 8 + apc_tr.MountPoints += 11 -> 9 + apc_tr.MountPoints += 12 -> 10 + apc_tr.TrunkSize = InventoryTile.Tile2016 + apc_tr.TrunkOffset = 30 + + val + apc_nc = VehicleDefinition(ObjectClass.apc_nc) + apc_nc.Seats += 0 -> new SeatDefinition() + apc_nc.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + apc_nc.Seats += 1 -> new SeatDefinition() + apc_nc.Seats(1).ControlledWeapon = 11 + apc_nc.Seats += 2 -> new SeatDefinition() + apc_nc.Seats(2).ControlledWeapon = 12 + apc_nc.Seats += 3 -> new SeatDefinition() + apc_nc.Seats += 4 -> new SeatDefinition() + apc_nc.Seats += 5 -> new SeatDefinition() + apc_nc.Seats(5).ControlledWeapon = 15 + apc_nc.Seats += 6 -> new SeatDefinition() + apc_nc.Seats(6).ControlledWeapon = 16 + apc_nc.Seats += 7 -> new SeatDefinition() + apc_nc.Seats(7).ControlledWeapon = 13 + apc_nc.Seats += 8 -> new SeatDefinition() + apc_nc.Seats(8).ControlledWeapon = 14 + apc_nc.Seats += 9 -> new SeatDefinition() + apc_nc.Seats(9).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_nc.Seats += 10 -> new SeatDefinition() + apc_nc.Seats(10).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_nc.Weapons += 11 -> apc_weapon_systemc_nc + apc_nc.Weapons += 12 -> apc_weapon_systemb + apc_nc.Weapons += 13 -> apc_weapon_systema + apc_nc.Weapons += 14 -> apc_weapon_systemd_nc + apc_nc.Weapons += 15 -> apc_ballgun_r + apc_nc.Weapons += 16 -> apc_ballgun_l + apc_nc.MountPoints += 1 -> 0 + apc_nc.MountPoints += 2 -> 0 + apc_nc.MountPoints += 3 -> 1 + apc_nc.MountPoints += 4 -> 2 + apc_nc.MountPoints += 5 -> 3 + apc_nc.MountPoints += 6 -> 4 + apc_nc.MountPoints += 7 -> 5 + apc_nc.MountPoints += 8 -> 6 + apc_nc.MountPoints += 9 -> 7 + apc_nc.MountPoints += 10 -> 8 + apc_nc.MountPoints += 11 -> 9 + apc_nc.MountPoints += 12 -> 10 + apc_nc.TrunkSize = InventoryTile.Tile2016 + apc_nc.TrunkOffset = 30 + + val + apc_vs = VehicleDefinition(ObjectClass.apc_vs) + apc_vs.Seats += 0 -> new SeatDefinition() + apc_vs.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + apc_vs.Seats += 1 -> new SeatDefinition() + apc_vs.Seats(1).ControlledWeapon = 11 + apc_vs.Seats += 2 -> new SeatDefinition() + apc_vs.Seats(2).ControlledWeapon = 12 + apc_vs.Seats += 3 -> new SeatDefinition() + apc_vs.Seats += 4 -> new SeatDefinition() + apc_vs.Seats += 5 -> new SeatDefinition() + apc_vs.Seats(5).ControlledWeapon = 15 + apc_vs.Seats += 6 -> new SeatDefinition() + apc_vs.Seats(6).ControlledWeapon = 16 + apc_vs.Seats += 7 -> new SeatDefinition() + apc_vs.Seats(7).ControlledWeapon = 13 + apc_vs.Seats += 8 -> new SeatDefinition() + apc_vs.Seats(8).ControlledWeapon = 14 + apc_vs.Seats += 9 -> new SeatDefinition() + apc_vs.Seats(9).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_vs.Seats += 10 -> new SeatDefinition() + apc_vs.Seats(10).ArmorRestriction = SeatArmorRestriction.MaxOnly + apc_vs.Weapons += 11 -> apc_weapon_systemc_vs + apc_vs.Weapons += 12 -> apc_weapon_systemb + apc_vs.Weapons += 13 -> apc_weapon_systema + apc_vs.Weapons += 14 -> apc_weapon_systemd_vs + apc_vs.Weapons += 15 -> apc_ballgun_r + apc_vs.Weapons += 16 -> apc_ballgun_l + apc_vs.MountPoints += 1 -> 0 + apc_vs.MountPoints += 2 -> 0 + apc_vs.MountPoints += 3 -> 1 + apc_vs.MountPoints += 4 -> 2 + apc_vs.MountPoints += 5 -> 3 + apc_vs.MountPoints += 6 -> 4 + apc_vs.MountPoints += 7 -> 5 + apc_vs.MountPoints += 8 -> 6 + apc_vs.MountPoints += 9 -> 7 + apc_vs.MountPoints += 10 -> 8 + apc_vs.MountPoints += 11 -> 9 + apc_vs.MountPoints += 12 -> 10 + apc_vs.TrunkSize = InventoryTile.Tile2016 + apc_vs.TrunkOffset = 30 + + val + lightning = VehicleDefinition(ObjectClass.lightning) + lightning.Seats += 0 -> new SeatDefinition() + lightning.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + lightning.Seats(0).ControlledWeapon = 1 + lightning.Weapons += 1 -> lightning_weapon_system + lightning.MountPoints += 1 -> 0 + lightning.MountPoints += 2 -> 0 + lightning.TrunkSize = InventoryTile.Tile1511 + lightning.TrunkOffset = 30 + + val + prowler = VehicleDefinition(ObjectClass.prowler) + prowler.Seats += 0 -> new SeatDefinition() + prowler.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + prowler.Seats += 1 -> new SeatDefinition() + prowler.Seats(1).ControlledWeapon = 3 + prowler.Seats += 2 -> new SeatDefinition() + prowler.Seats(2).ControlledWeapon = 4 + prowler.Weapons += 3 -> prowler_weapon_systemA + prowler.Weapons += 4 -> prowler_weapon_systemB + prowler.MountPoints += 1 -> 0 + prowler.MountPoints += 2 -> 1 + prowler.MountPoints += 3 -> 2 + prowler.TrunkSize = InventoryTile.Tile1511 + prowler.TrunkOffset = 30 + + val + vanguard = VehicleDefinition(ObjectClass.vanguard) + vanguard.Seats += 0 -> new SeatDefinition() + vanguard.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + vanguard.Seats += 1 -> new SeatDefinition() + vanguard.Seats(1).ControlledWeapon = 2 + vanguard.Weapons += 2 -> vanguard_weapon_system + vanguard.MountPoints += 1 -> 0 + vanguard.MountPoints += 2 -> 1 + vanguard.TrunkSize = InventoryTile.Tile1511 + vanguard.TrunkOffset = 30 + + val + magrider = VehicleDefinition(ObjectClass.magrider) + magrider.Seats += 0 -> new SeatDefinition() + magrider.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + magrider.Seats(0).ControlledWeapon = 2 + magrider.Seats += 1 -> new SeatDefinition() + magrider.Seats(1).ControlledWeapon = 3 + magrider.Weapons += 2 -> particle_beam_magrider + magrider.Weapons += 3 -> heavy_rail_beam_magrider + magrider.MountPoints += 1 -> 0 + magrider.MountPoints += 2 -> 1 + magrider.TrunkSize = InventoryTile.Tile1511 + magrider.TrunkOffset = 30 + + private val utilityConverter = new UtilityVehicleConverter + val + ant = VehicleDefinition(ObjectClass.ant) + ant.Seats += 0 -> new SeatDefinition() + ant.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + ant.MountPoints += 1 -> 0 + ant.MountPoints += 2 -> 0 + ant.Packet = utilityConverter + + val + ams = VehicleDefinition(ObjectClass.ams) + ams.Seats += 0 -> new SeatDefinition() + ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + ams.MountPoints += 1 -> 0 + ams.MountPoints += 2 -> 0 + ams.Packet = utilityConverter + + private val variantConverter = new VariantVehicleConverter + val + router = VehicleDefinition(ObjectClass.router) + router.Seats += 0 -> new SeatDefinition() + router.MountPoints += 1 -> 0 + router.TrunkSize = InventoryTile.Tile1511 + router.TrunkOffset = 30 + router.Packet = variantConverter + + val + switchblade = VehicleDefinition(ObjectClass.switchblade) + switchblade.Seats += 0 -> new SeatDefinition() + switchblade.Seats(0).ControlledWeapon = 1 + switchblade.Weapons += 1 -> scythe + switchblade.MountPoints += 1 -> 0 + switchblade.MountPoints += 2 -> 0 + switchblade.TrunkSize = InventoryTile.Tile1511 + switchblade.TrunkOffset = 30 + switchblade.Packet = variantConverter + + val + flail = VehicleDefinition(ObjectClass.flail) + flail.Seats += 0 -> new SeatDefinition() + flail.Seats(0).ControlledWeapon = 1 + flail.Weapons += 1 -> flail_weapon + flail.MountPoints += 1 -> 0 + flail.TrunkSize = InventoryTile.Tile1511 + flail.TrunkOffset = 30 + flail.Packet = variantConverter + + val + mosquito = VehicleDefinition(ObjectClass.mosquito) + mosquito.Seats += 0 -> new SeatDefinition() + mosquito.Seats(0).Bailable = true + mosquito.Seats(0).ControlledWeapon = 1 + mosquito.Weapons += 1 -> rotarychaingun_mosquito + mosquito.MountPoints += 1 -> 0 + mosquito.MountPoints += 2 -> 0 + mosquito.TrunkSize = InventoryTile.Tile1111 + mosquito.TrunkOffset = 30 + mosquito.Packet = variantConverter + + val + lightgunship = VehicleDefinition(ObjectClass.lightgunship) + lightgunship.Seats += 0 -> new SeatDefinition() + lightgunship.Seats(0).Bailable = true + lightgunship.Seats(0).ControlledWeapon = 1 + lightgunship.Weapons += 1 -> lightgunship_weapon_system + lightgunship.MountPoints += 1 -> 0 + lightgunship.MountPoints += 2 -> 0 + lightgunship.TrunkSize = InventoryTile.Tile1511 + lightgunship.TrunkOffset = 30 + lightgunship.Packet = variantConverter + + val + wasp = VehicleDefinition(ObjectClass.wasp) + wasp.Seats += 0 -> new SeatDefinition() + wasp.Seats(0).Bailable = true + wasp.Seats(0).ControlledWeapon = 1 + wasp.Weapons += 1 -> wasp_weapon_system + wasp.MountPoints += 1 -> 0 + wasp.MountPoints += 2 -> 0 + wasp.TrunkSize = InventoryTile.Tile1111 + wasp.TrunkOffset = 30 + wasp.Packet = variantConverter + + val + liberator = VehicleDefinition(ObjectClass.liberator) + liberator.Seats += 0 -> new SeatDefinition() + liberator.Seats(0).ControlledWeapon = 3 + liberator.Seats += 1 -> new SeatDefinition() + liberator.Seats(1).ControlledWeapon = 4 + liberator.Seats += 2 -> new SeatDefinition() + liberator.Seats(2).ControlledWeapon = 5 + liberator.Weapons += 3 -> liberator_weapon_system + liberator.Weapons += 4 -> liberator_bomb_bay + liberator.Weapons += 5 -> liberator_25mm_cannon + liberator.MountPoints += 1 -> 0 + liberator.MountPoints += 2 -> 1 + liberator.MountPoints += 3 -> 1 + liberator.MountPoints += 4 -> 2 + liberator.TrunkSize = InventoryTile.Tile1515 + liberator.TrunkOffset = 30 + liberator.Packet = variantConverter + + val + vulture = VehicleDefinition(ObjectClass.vulture) + vulture.Seats += 0 -> new SeatDefinition() + vulture.Seats(0).ControlledWeapon = 3 + vulture.Seats += 1 -> new SeatDefinition() + vulture.Seats(1).ControlledWeapon = 4 + vulture.Seats += 2 -> new SeatDefinition() + vulture.Seats(2).ControlledWeapon = 5 + vulture.Weapons += 3 -> vulture_nose_weapon_system + vulture.Weapons += 4 -> vulture_bomb_bay + vulture.Weapons += 5 -> vulture_tail_cannon + vulture.MountPoints += 1 -> 0 + vulture.MountPoints += 2 -> 1 + vulture.MountPoints += 3 -> 1 + vulture.MountPoints += 4 -> 2 + vulture.TrunkSize = InventoryTile.Tile1611 + vulture.TrunkOffset = 30 + vulture.Packet = variantConverter + + val + dropship = VehicleDefinition(ObjectClass.dropship) + dropship.Seats += 0 -> new SeatDefinition() + dropship.Seats += 1 -> new SeatDefinition() + dropship.Seats(1).Bailable = true + dropship.Seats(1).ControlledWeapon = 12 + dropship.Seats += 2 -> new SeatDefinition() + dropship.Seats(2).Bailable = true + dropship.Seats(2).ControlledWeapon = 13 + dropship.Seats += 3 -> new SeatDefinition() + dropship.Seats(3).Bailable = true + dropship.Seats += 4 -> new SeatDefinition() + dropship.Seats(4).Bailable = true + dropship.Seats += 5 -> new SeatDefinition() + dropship.Seats(5).Bailable = true + dropship.Seats += 6 -> new SeatDefinition() + dropship.Seats(6).Bailable = true + dropship.Seats += 7 -> new SeatDefinition() + dropship.Seats(7).Bailable = true + dropship.Seats += 8 -> new SeatDefinition() + dropship.Seats(8).Bailable = true + dropship.Seats += 9 -> new SeatDefinition() + dropship.Seats(9).Bailable = true + dropship.Seats(9).ArmorRestriction = SeatArmorRestriction.MaxOnly + dropship.Seats += 10 -> new SeatDefinition() + dropship.Seats(10).Bailable = true + dropship.Seats(10).ArmorRestriction = SeatArmorRestriction.MaxOnly + dropship.Seats += 11 -> new SeatDefinition() + dropship.Seats(11).Bailable = true + dropship.Seats(11).ControlledWeapon = 14 + dropship.Weapons += 12 -> cannon_dropship_20mm + dropship.Weapons += 13 -> cannon_dropship_20mm + dropship.Weapons += 14 -> dropship_rear_turret + dropship.MountPoints += 1 -> 0 + dropship.MountPoints += 2 -> 11 + dropship.MountPoints += 3 -> 1 + dropship.MountPoints += 4 -> 2 + dropship.MountPoints += 5 -> 3 + dropship.MountPoints += 6 -> 4 + dropship.MountPoints += 7 -> 5 + dropship.MountPoints += 8 -> 6 + dropship.MountPoints += 9 -> 7 + dropship.MountPoints += 10 -> 8 + dropship.MountPoints += 11 -> 9 + dropship.MountPoints += 12 -> 10 + dropship.TrunkSize = InventoryTile.Tile1612 + dropship.TrunkOffset = 30 + dropship.Packet = variantConverter + + val + galaxy_gunship = VehicleDefinition(ObjectClass.galaxy_gunship) + galaxy_gunship.Seats += 0 -> new SeatDefinition() + galaxy_gunship.Seats += 1 -> new SeatDefinition() + galaxy_gunship.Seats(1).ControlledWeapon = 6 + galaxy_gunship.Seats += 2 -> new SeatDefinition() + galaxy_gunship.Seats(2).ControlledWeapon = 7 + galaxy_gunship.Seats += 3 -> new SeatDefinition() + galaxy_gunship.Seats(3).ControlledWeapon = 8 + galaxy_gunship.Seats += 4 -> new SeatDefinition() + galaxy_gunship.Seats(4).ControlledWeapon = 9 + galaxy_gunship.Seats += 5 -> new SeatDefinition() + galaxy_gunship.Seats(5).ControlledWeapon = 10 + galaxy_gunship.Weapons += 6 -> galaxy_gunship_cannon + galaxy_gunship.Weapons += 7 -> galaxy_gunship_cannon + galaxy_gunship.Weapons += 8 -> galaxy_gunship_tailgun + galaxy_gunship.Weapons += 9 -> galaxy_gunship_gun + galaxy_gunship.Weapons += 10 -> galaxy_gunship_gun + galaxy_gunship.MountPoints += 1 -> 0 + galaxy_gunship.MountPoints += 2 -> 3 + galaxy_gunship.MountPoints += 3 -> 1 + galaxy_gunship.MountPoints += 4 -> 2 + galaxy_gunship.MountPoints += 5 -> 4 + galaxy_gunship.MountPoints += 6 -> 5 + galaxy_gunship.TrunkSize = InventoryTile.Tile1816 + galaxy_gunship.TrunkOffset = 30 + galaxy_gunship.Packet = variantConverter + + val + lodestar = VehicleDefinition(ObjectClass.lodestar) + lodestar.Seats += 0 -> new SeatDefinition() + lodestar.MountPoints += 1 -> 0 + lodestar.TrunkSize = InventoryTile.Tile1612 + lodestar.TrunkOffset = 30 + lodestar.Packet = variantConverter + val phantasm = VehicleDefinition(ObjectClass.phantasm) phantasm.CanCloak = true @@ -1304,14 +2353,30 @@ object GlobalDefinitions { phantasm.Seats(3).Bailable = true phantasm.Seats += 4 -> new SeatDefinition() phantasm.Seats(4).Bailable = true - phantasm.MountPoints += 1 -> 0 //TODO add and check all - phantasm.TrunkSize = InventoryTile(11, 8) - phantasm.TrunkOffset = 30 //TODO check + phantasm.MountPoints += 1 -> 0 + phantasm.MountPoints += 2 -> 1 + phantasm.MountPoints += 3 -> 2 + phantasm.MountPoints += 4 -> 3 + phantasm.MountPoints += 5 -> 4 + phantasm.TrunkSize = InventoryTile.Tile1107 + phantasm.TrunkOffset = 30 + phantasm.Packet = variantConverter val order_terminal = new OrderTerminalDefinition val cert_terminal = new CertTerminalDefinition + val + ground_vehicle_terminal = new GroundVehicleTerminalDefinition + val + air_vehicle_terminal = new AirVehicleTerminalDefinition + val + dropship_vehicle_terminal = new DropshipVehicleTerminalDefinition + val + vehicle_terminal_combined = new VehicleTerminalCombinedDefinition + + val + spawn_pad = new ObjectDefinition(800) { Name = "spawn_pad" } val lock_external = new IFFLockDefinition diff --git a/common/src/main/scala/net/psforever/objects/InfantryLoadout.scala b/common/src/main/scala/net/psforever/objects/Loadout.scala similarity index 81% rename from common/src/main/scala/net/psforever/objects/InfantryLoadout.scala rename to common/src/main/scala/net/psforever/objects/Loadout.scala index ac1c27df7..355a7d171 100644 --- a/common/src/main/scala/net/psforever/objects/InfantryLoadout.scala +++ b/common/src/main/scala/net/psforever/objects/Loadout.scala @@ -11,7 +11,7 @@ import scala.annotation.tailrec /** * From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.
*
- * `InfantryLoadout` objects are composed of the following information, as if a blueprint:
+ * `Loadout` objects are composed of the following information, as if a blueprint:
* - the avatar's current exo-suit
* - the type of specialization, called a "subtype" (mechanized assault exo-suits only)
* - the contents of the avatar's occupied holster slots
@@ -28,25 +28,24 @@ import scala.annotation.tailrec * Even a whole blueprint can be denied if the user lacks the necessary exo-suit certification. * A completely new piece of `Equipment` is constructed when the `Loadout` is regurgitated.
*
- * The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `InfantryLoadout` entries. + * The fifth tab on an `order_terminal` window is for "Favorite" blueprints for `Loadout` entries. * The ten-long list is initialized with `FavoritesMessage` packets. * Specific entries are loaded or removed using `FavoritesRequest` packets. * @param player the player * @param label the name by which this inventory will be known when displayed in a Favorites list */ -class InfantryLoadout(player : Player, private val label : String) { +class Loadout(player : Player, private val label : String) { /** the exo-suit */ private val exosuit : ExoSuitType.Value = player.ExoSuit /** the MAX specialization, to differentiate the three types of MAXes who all use the same exo-suit name */ private val subtype = if(exosuit == ExoSuitType.MAX) { - import net.psforever.packet.game.objectcreate.ObjectClass - player.Holsters().head.Equipment.get.Definition.ObjectId match { - case ObjectClass.trhev_dualcycler | ObjectClass.nchev_scattercannon | ObjectClass.vshev_quasar => + player.Holsters().head.Equipment.get.Definition match { + case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon | GlobalDefinitions.vshev_quasar => 1 - case ObjectClass.trhev_pounder | ObjectClass.nchev_falcon | ObjectClass.vshev_comet => + case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet => 2 - case ObjectClass.trhev_burster | ObjectClass.nchev_sparrow | ObjectClass.vshev_starfire => + case GlobalDefinitions.trhev_burster | GlobalDefinitions.nchev_sparrow | GlobalDefinitions.vshev_starfire => 3 case _ => 0 @@ -56,14 +55,14 @@ class InfantryLoadout(player : Player, private val label : String) { 0 } /** simplified representation of the holster `Equipment` */ - private val holsters : List[InfantryLoadout.SimplifiedEntry] = - InfantryLoadout.packageSimplifications(player.Holsters()) + private val holsters : List[Loadout.SimplifiedEntry] = + Loadout.packageSimplifications(player.Holsters()) /** simplified representation of the inventory `Equipment` */ - private val inventory : List[InfantryLoadout.SimplifiedEntry] = - InfantryLoadout.packageSimplifications(player.Inventory.Items.values.toList) + private val inventory : List[Loadout.SimplifiedEntry] = + Loadout.packageSimplifications(player.Inventory.Items.values.toList) /** - * The label by which this `InfantryLoadout` is called. + * The label by which this `Loadout` is called. * @return the label */ def Label : String = label @@ -87,26 +86,26 @@ class InfantryLoadout(player : Player, private val label : String) { def Subtype : Int = subtype /** - * The `Equipment` in the `Player`'s holster slots when this `InfantryLoadout` is created. + * The `Equipment` in the `Player`'s holster slots when this `Loadout` is created. * @return a `List` of the holster item blueprints */ - def Holsters : List[InfantryLoadout.SimplifiedEntry] = holsters + def Holsters : List[Loadout.SimplifiedEntry] = holsters /** - * The `Equipment` in the `Player`'s inventory region when this `InfantryLoadout` is created. + * The `Equipment` in the `Player`'s inventory region when this `Loadout` is created. * @return a `List` of the inventory item blueprints */ - def Inventory : List[InfantryLoadout.SimplifiedEntry] = inventory + def Inventory : List[Loadout.SimplifiedEntry] = inventory } -object InfantryLoadout { +object Loadout { /** * A basic `Trait` connecting all of the `Equipment` blueprints. */ sealed trait Simplification /** - * An entry in the `InfantryLoadout`, wrapping around a slot index and what is in the slot index. + * An entry in the `Loadout`, wrapping around a slot index and what is in the slot index. * @param item the `Equipment` * @param index the slot number where the `Equipment` is to be stowed * @see `InventoryItem` @@ -163,7 +162,7 @@ object InfantryLoadout { * @return a `List` of simplified `Equipment` */ private def packageSimplifications(equipment : List[InventoryItem]) : List[SimplifiedEntry] = { - recursiveInventorySimplifications(equipment.iterator) + equipment.map(entry => { SimplifiedEntry(buildSimplification(entry.obj), entry.start) }) } /** @@ -193,23 +192,6 @@ object InfantryLoadout { } } - /** - * Traverse a `Player`'s inventory and transform `Equipment` into simplified blueprints. - * @param iter an `Iterator` - * @param list an updating `List` of simplified `Equipment` blueprints; - * empty, by default - * @return a `List` of simplified `Equipment` blueprints - */ - @tailrec private def recursiveInventorySimplifications(iter : Iterator[InventoryItem], list : List[SimplifiedEntry] = Nil) : List[SimplifiedEntry] = { - if(!iter.hasNext) { - list - } - else { - val entry = iter.next - recursiveInventorySimplifications(iter, list :+ SimplifiedEntry(buildSimplification(entry.obj), entry.start)) - } - } - /** * Ammunition slots are internal connection points where `AmmoBox` units and their characteristics represent a `Tool`'s magazine. * Their simplification process has a layer of complexity that ensures that the content of the slot matches the type of content that should be in the slot. @@ -235,7 +217,7 @@ object InfantryLoadout { else { ShorthandAmmotSlot( entry.AmmoTypeIndex, - ShorthandAmmoBox(AmmoBoxDefinition(entry.Tool.Definition.AmmoTypes(entry.Definition.AmmoTypeIndices.head).id), 1) + ShorthandAmmoBox(entry.Tool.AmmoTypes(entry.Definition.AmmoTypeIndices.head), 1) ) } recursiveFireModeSimplications(iter, list :+ fmodeSimp) diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala index 15572f514..37c6a2859 100644 --- a/common/src/main/scala/net/psforever/objects/Player.scala +++ b/common/src/main/scala/net/psforever/objects/Player.scala @@ -32,7 +32,7 @@ class Player(private val name : String, private var drawnSlot : Int = Player.HandsDownSlot private var lastDrawnSlot : Int = 0 - private val loadouts : Array[Option[InfantryLoadout]] = Array.fill[Option[InfantryLoadout]](10)(None) + private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None) private var bep : Long = 0 private var cep : Long = 0 @@ -230,10 +230,10 @@ class Player(private val name : String, } def SaveLoadout(label : String, line : Int) : Unit = { - loadouts(line) = Some(new InfantryLoadout(this, label)) + loadouts(line) = Some(new Loadout(this, label)) } - def LoadLoadout(line : Int) : Option[InfantryLoadout] = loadouts(line) + def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line) def DeleteLoadout(line : Int) : Unit = { loadouts(line) = None @@ -551,7 +551,7 @@ object Player { player.ExoSuit = eSuit //inventory player.Inventory.Clear() - player.Inventory.Resize(esuitDef.InventoryScale.width, esuitDef.InventoryScale.height) + player.Inventory.Resize(esuitDef.InventoryScale.Width, esuitDef.InventoryScale.Height) player.Inventory.Offset = esuitDef.InventoryOffset //holsters (0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) }) diff --git a/common/src/main/scala/net/psforever/objects/Tool.scala b/common/src/main/scala/net/psforever/objects/Tool.scala index 85b2c484c..068f13a0d 100644 --- a/common/src/main/scala/net/psforever/objects/Tool.scala +++ b/common/src/main/scala/net/psforever/objects/Tool.scala @@ -1,9 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import net.psforever.objects.definition.{AmmoBoxDefinition, ToolDefinition} +import net.psforever.objects.definition.ToolDefinition import net.psforever.objects.equipment.{Ammo, Equipment, FireModeDefinition, FireModeSwitch} -import net.psforever.packet.game.PlanetSideGUID import scala.annotation.tailrec @@ -13,14 +12,15 @@ import scala.annotation.tailrec * "Tool" is a very mechanical name while this class is intended for various weapons and support items. * The primary trait of a `Tool` is that it has something that counts as an "ammunition," * depleted as the `Tool` is used, replaceable as long as one has an appropriate type of `AmmoBox` object. - * (The former is always called "consuming;" the latter, "reloading.")
- *
+ * (The former is always called "consuming;" the latter, "reloading.") * Some weapons Chainblade have ammunition but do not consume it. * @param toolDef the `ObjectDefinition` that constructs this item and maintains some of its immutable fields */ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireModeSwitch[FireModeDefinition] { + /** index of the current fire mode on the `ToolDefinition`'s list of fire modes */ private var fireModeIndex : Int = 0 - private val ammoSlot : List[Tool.FireModeSlot] = Tool.LoadDefinition(this) + /** current ammunition slot being used by this fire mode */ + private val ammoSlots : List[Tool.FireModeSlot] = Tool.LoadDefinition(this) def FireModeIndex : Int = fireModeIndex @@ -36,24 +36,24 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode FireMode } - def AmmoTypeIndex : Int = ammoSlot(fireModeIndex).AmmoTypeIndex + def AmmoTypeIndex : Int = FireMode.AmmoTypeIndices(AmmoSlot.AmmoTypeIndex) def AmmoTypeIndex_=(index : Int) : Int = { - ammoSlot(fireModeIndex).AmmoTypeIndex = index % FireMode.AmmoTypeIndices.length + AmmoSlot.AmmoTypeIndex = index % FireMode.AmmoTypeIndices.length AmmoTypeIndex } - def AmmoType : Ammo.Value = toolDef.AmmoTypes(AmmoTypeIndex) + def AmmoType : Ammo.Value = toolDef.AmmoTypes(AmmoTypeIndex).AmmoType def NextAmmoType : Ammo.Value = { - AmmoTypeIndex = AmmoTypeIndex + 1 + AmmoSlot.AmmoTypeIndex = AmmoSlot.AmmoTypeIndex + 1 AmmoType } - def Magazine : Int = ammoSlot(fireModeIndex).Magazine + def Magazine : Int = AmmoSlot.Magazine def Magazine_=(mag : Int) : Int = { - ammoSlot(fireModeIndex).Magazine = Math.min(Math.max(0, mag), MaxMagazine) + AmmoSlot.Magazine = Math.min(Math.max(0, mag), MaxMagazine) Magazine } @@ -61,15 +61,15 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode def NextDischarge : Int = math.min(Magazine, FireMode.Chamber) - def AmmoSlots : List[Tool.FireModeSlot] = ammoSlot + def AmmoSlot : Tool.FireModeSlot = ammoSlots(FireMode.AmmoSlotIndex) - def MaxAmmoSlot : Int = ammoSlot.length + def AmmoSlots : List[Tool.FireModeSlot] = ammoSlots + + def MaxAmmoSlot : Int = ammoSlots.length def Definition : ToolDefinition = toolDef - override def toString : String = { - Tool.toString(this) - } + override def toString : String = Tool.toString(this) } object Tool { @@ -77,12 +77,6 @@ object Tool { new Tool(toolDef) } - def apply(guid : PlanetSideGUID, toolDef : ToolDefinition) : Tool = { - val obj = new Tool(toolDef) - obj.GUID = guid - obj - } - /** * Use the `*Definition` that was provided to this object to initialize its fields and settings. * @param tool the `Tool` being initialized @@ -90,10 +84,10 @@ object Tool { def LoadDefinition(tool : Tool) : List[FireModeSlot] = { val tdef : ToolDefinition = tool.Definition val maxSlot = tdef.FireModes.maxBy(fmode => fmode.AmmoSlotIndex).AmmoSlotIndex - buildFireModes(tool, (0 to maxSlot).iterator, tdef.FireModes.toList) + buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList) } - @tailrec private def buildFireModes(tool : Tool, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = { + @tailrec private def buildFireModes(tdef : ToolDefinition, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = { if(!iter.hasNext) { list } @@ -101,9 +95,9 @@ object Tool { val index = iter.next fmodes.filter(fmode => fmode.AmmoSlotIndex == index) match { case fmode :: _ => - buildFireModes(tool, iter, fmodes, list :+ new FireModeSlot(tool, fmode)) + buildFireModes(tdef, iter, fmodes, list :+ new FireModeSlot(tdef, fmode)) case Nil => - throw new IllegalArgumentException(s"tool ${tool.Definition.Name} ammo slot #$index is missing a fire mode specification; do not skip") + throw new IllegalArgumentException(s"tool ${tdef.Name} ammo slot #$index is missing a fire mode specification; do not skip") } } } @@ -113,32 +107,45 @@ object Tool { } /** - * A hidden class that manages the specifics of the given ammunition for the current fire mode of this tool. - * It operates much closer to an "ammunition feed" rather than a fire mode. - * The relationship to fire modes is at least one-to-one and at most one-to-many. + * The `FireModeSlot` can be called the "magazine feed," an abstracted "ammunition slot." + * Most weapons will have only one ammunition slot and swap different ammunition into it as needed. + * In general to swap ammunition means to unload the onld ammunition and load the new ammunition. + * Many weapons also have one ammunition slot and multiple fire modes using the same list of ammunition + * This slot manages either of two ammunitions where one does not need to unload to be swapped to the other; + * however, the fire mod has most likely been changed. + * The Punisher - + * six ammunition types in total, + * two uniquely different types without unloading, + * two exclusive groups of ammunition divided into 2 cycled types and 4 cycled types - + * is an example of a weapon that benefits from this implementation. */ - class FireModeSlot(private val tool : Tool, private val fdef : FireModeDefinition) { - /* - By way of demonstration: - Suppressors have one fire mode, two types of ammunition, one slot (2) - MA Pistols have two fire modes, one type of ammunition, one slot (1) - Jackhammers have two fire modes, two types of ammunition, one slot (2) - Punishers have two fire modes, five types of ammunition, two slots (2, 3) - */ - - /** if this fire mode has multiple types of ammunition */ - private var ammoTypeIndex : Int = fdef.AmmoTypeIndices.head - /** a reference to the actual `AmmoBox` of this slot; will not synch up with `AmmoType` immediately */ - private var box : AmmoBox = AmmoBox(AmmoBoxDefinition(AmmoType)) //defaults to box of one round of the default type for this slot + class FireModeSlot(private val tdef : ToolDefinition, private val fdef : FireModeDefinition) { + /** + * if this fire mode has multiple types of ammunition + * this is the index of the fire mode's ammo List, not a reference to the tool's ammo List + */ + private var ammoTypeIndex : Int = 0 + /** a reference to the actual `AmmoBox` of this slot */ + private var box : AmmoBox = AmmoBox(tdef.AmmoTypes(ammoTypeIndex), fdef.Magazine) def AmmoTypeIndex : Int = ammoTypeIndex def AmmoTypeIndex_=(index : Int) : Int = { - ammoTypeIndex = index + ammoTypeIndex = index % fdef.AmmoTypeIndices.length AmmoTypeIndex } - def AmmoType : Ammo.Value = tool.Definition.AmmoTypes(ammoTypeIndex) + /** + * This is a reference to the `Ammo.Value` whose `AmmoBoxDefinition` should be loaded into `box`. + * It may not be the correct `Ammo.Value` whose `AmmoBoxDefinition` is loaded into `box` such as is the case during ammunition swaps. + * Generally, convert from this index, to the index in the fire mode's ammunition list, to the index in the `ToolDefinition`'s ammunition list. + * @return the `Ammo` type that should be loaded into the magazine right now + */ + def AmmoType : Ammo.Value = tdef.AmmoTypes(fdef.AmmoTypeIndices(ammoTypeIndex)).AmmoType + + def AllAmmoTypes : List[Ammo.Value] = { + fdef.AmmoTypeIndices.map(index => tdef.AmmoTypes(fdef.AmmoTypeIndices(index)).AmmoType).toList + } def Magazine : Int = box.Capacity @@ -159,7 +166,7 @@ object Tool { } } - def Tool : Tool = tool + def Tool : ToolDefinition = tdef def Definition : FireModeDefinition = fdef } diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala index 19f0101db..1d6acb114 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala @@ -3,7 +3,7 @@ package net.psforever.objects import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.equipment.{Equipment, EquipmentSize} -import net.psforever.objects.inventory.GridInventory +import net.psforever.objects.inventory.{GridInventory, InventoryTile} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState} import net.psforever.packet.game.PlanetSideGUID @@ -480,8 +480,12 @@ object Vehicle { vehicle.Utilities += Utility.Select(i, vehicle) } //trunk - vehicle.trunk.Resize(vdef.TrunkSize.width, vdef.TrunkSize.height) - vehicle.trunk.Offset = vdef.TrunkOffset + vdef.TrunkSize match { + case InventoryTile.None => ; + case dim => + vehicle.trunk.Resize(dim.Width, dim.Height) + vehicle.trunk.Offset = vdef.TrunkOffset + } vehicle } diff --git a/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala index 2dbb7303e..f40e3946a 100644 --- a/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala @@ -2,17 +2,17 @@ package net.psforever.objects.definition import net.psforever.objects.definition.converter.ToolConverter -import net.psforever.objects.equipment.{Ammo, FireModeDefinition} +import net.psforever.objects.equipment.FireModeDefinition import scala.collection.mutable class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) { - private val ammoTypes : mutable.ListBuffer[Ammo.Value] = new mutable.ListBuffer[Ammo.Value] + private val ammoTypes : mutable.ListBuffer[AmmoBoxDefinition] = new mutable.ListBuffer[AmmoBoxDefinition] private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition] Name = "tool" Packet = new ToolConverter() - def AmmoTypes : mutable.ListBuffer[Ammo.Value] = ammoTypes + def AmmoTypes : mutable.ListBuffer[AmmoBoxDefinition] = ammoTypes def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes } diff --git a/common/src/main/scala/net/psforever/objects/equipment/Ammo.scala b/common/src/main/scala/net/psforever/objects/equipment/Ammo.scala index e7228abcb..55bf072a4 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/Ammo.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/Ammo.scala @@ -43,7 +43,7 @@ object Ammo extends Enumeration { final val fluxpod_ammo = Value(310) final val frag_cartridge = Value(327) final val frag_grenade_ammo = Value(331) - final val gauss_cannon_ammo = Value(345) + final val gauss_cannon_ammo = Value(347) final val grenade = Value(370) final val health_canister = Value(389) final val heavy_grenade_mortar = Value(391) diff --git a/common/src/main/scala/net/psforever/objects/equipment/FireModeSwitch.scala b/common/src/main/scala/net/psforever/objects/equipment/FireModeSwitch.scala index 6a3596cc5..82bc4ff8a 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/FireModeSwitch.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/FireModeSwitch.scala @@ -18,4 +18,4 @@ trait FireModeSwitch[Mode] { def FireMode : Mode def NextFireMode : Mode -} \ No newline at end of file +} diff --git a/common/src/main/scala/net/psforever/objects/inventory/GridInventory.scala b/common/src/main/scala/net/psforever/objects/inventory/GridInventory.scala index b513ea553..f5db0b38a 100644 --- a/common/src/main/scala/net/psforever/objects/inventory/GridInventory.scala +++ b/common/src/main/scala/net/psforever/objects/inventory/GridInventory.scala @@ -68,7 +68,7 @@ class GridInventory { * @return the number of free cells */ def Capacity : Int = { - TotalCapacity - items.values.foldLeft(0)((cnt, item) => cnt + (item.obj.Tile.width * item.obj.Tile.height)) + TotalCapacity - items.values.foldLeft(0)((cnt, item) => cnt + (item.obj.Tile.Width * item.obj.Tile.Height)) } /** @@ -112,7 +112,7 @@ class GridInventory { */ def CheckCollisions(start : Int, item : Equipment) : Try[List[Int]] = { val tile : InventoryTile = item.Tile - CheckCollisions(start, tile.width, tile.height) + CheckCollisions(start, tile.Width, tile.Height) } /** @@ -185,8 +185,8 @@ class GridInventory { val itemx : Int = actualItemStart % width val itemy : Int = actualItemStart / width val tile = item.obj.Tile - val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.width > startx } else { itemx <= startw } - val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.height > starty } else { itemy <= starth } + val clipsOnX : Boolean = if(itemx < startx) { itemx + tile.Width > startx } else { itemx <= startw } + val clipsOnY : Boolean = if(itemy < starty) { itemy + tile.Height > starty } else { itemy <= starth } if(clipsOnX && clipsOnY) { collisions += item } @@ -237,8 +237,8 @@ class GridInventory { * @return the grid index of the upper left corner where equipment to which the `tile` belongs should be placed */ def Fit(tile : InventoryTile) : Option[Int] = { - val tWidth = tile.width - val tHeight = tile.height + val tWidth = tile.Width + val tHeight = tile.Height val gridIter = (0 until (grid.length - (tHeight - 1) * width)) .filter(cell => grid(cell) == -1 && (width - cell%width >= tWidth)) .iterator @@ -325,7 +325,7 @@ class GridInventory { val card = InventoryItem(obj, start) items += key -> card val tile = obj.Tile - SetCells(start, tile.width, tile.height, key) + SetCells(start, tile.Width, tile.Height, key) true case _ => false @@ -348,7 +348,7 @@ class GridInventory { items.remove(key) match { case Some(item) => val tile = item.obj.Tile - SetCells(item.start, tile.width, tile.height) + SetCells(item.start, tile.Width, tile.Height) true case None => false @@ -362,7 +362,7 @@ class GridInventory { case Some(index) => val item = items.remove(index).get val tile = item.obj.Tile - SetCells(item.start, tile.width, tile.height) + SetCells(item.start, tile.Width, tile.Height) true case None => false @@ -492,11 +492,11 @@ object GridInventory { (a, b) => { val aTile = a.obj.Tile val bTile = b.obj.Tile - if(aTile.width == bTile.width) { - aTile.height > bTile.height + if(aTile.Width == bTile.Width) { + aTile.Height > bTile.Height } else { - aTile.width > bTile.width + aTile.Width > bTile.Width } } @@ -513,9 +513,9 @@ object GridInventory { private def sortKnapsack(list : List[InventoryItem], width : Int, height : Int) : Unit = { val root = new KnapsackNode(0, 0, width, height) list.foreach(item => { - findKnapsackSpace(root, item.obj.Tile.width, item.obj.Tile.height) match { + findKnapsackSpace(root, item.obj.Tile.Width, item.obj.Tile.Height) match { case Some(node) => - splitKnapsackSpace(node, item.obj.Tile.width, item.obj.Tile.height) + splitKnapsackSpace(node, item.obj.Tile.Width, item.obj.Tile.Height) item.start = node.y * width + node.x case _ => ; item.start = -1 diff --git a/common/src/main/scala/net/psforever/objects/inventory/InventoryTile.scala b/common/src/main/scala/net/psforever/objects/inventory/InventoryTile.scala index c860a0c32..ea0b58c3e 100644 --- a/common/src/main/scala/net/psforever/objects/inventory/InventoryTile.scala +++ b/common/src/main/scala/net/psforever/objects/inventory/InventoryTile.scala @@ -8,7 +8,7 @@ package net.psforever.objects.inventory * @param height the height of the tile * @throws IllegalArgumentException if either the width or the height are less than zero */ -class InventoryTile(val width : Int, val height : Int) { +class InventoryTile(private val width : Int, private val height : Int) { if(width < 0 || height < 0) throw new IllegalArgumentException(s"tile has no area - width: $width, height: $height") @@ -19,15 +19,27 @@ class InventoryTile(val width : Int, val height : Int) { object InventoryTile { final val None = InventoryTile(0,0) //technically invalid; used to indicate a vehicle with no trunk - final val Tile11 = InventoryTile(1,1) //placeholder size + final val Tile11 = InventoryTile(1,1) //occasional placeholder final val Tile22 = InventoryTile(2,2) //grenades, boomer trigger final val Tile23 = InventoryTile(2,3) //canister ammo final val Tile42 = InventoryTile(4,2) //medkit final val Tile33 = InventoryTile(3,3) //ammo box, pistols, ace final val Tile44 = InventoryTile(4,4) //large ammo box final val Tile55 = InventoryTile(5,5) //bfr ammo box + final val Tile66 = InventoryTile(6,6) //standard assault inventory final val Tile63 = InventoryTile(6,3) //rifles final val Tile93 = InventoryTile(9,3) //long-body weapons + final val Tile96 = InventoryTile(9,6) //standard exo-suit + final val Tile99 = InventoryTile(9,9) //agile exo-suit + final val Tile1107 = InventoryTile(11, 7) //uncommon small trunk capacity - phantasm + final val Tile1111 = InventoryTile(11,11) //common small trunk capacity + final val Tile1209 = InventoryTile(12, 9) //reinforced exo-suit + final val Tile1511 = InventoryTile(15,11) //common medium trunk capacity + final val Tile1515 = InventoryTile(15,15) //common large trunk capacity + final val Tile1611 = InventoryTile(16,11) //uncommon medium trunk capacity - vulture + final val Tile1612 = InventoryTile(16,12) //MAX; uncommon medium trunk capacity - lodestar + final val Tile1816 = InventoryTile(18,16) //uncommon massive trunk capacity - galaxy_gunship + final val Tile2016 = InventoryTile(20,16) //uncommon massive trunk capacity - apc def apply(w : Int, h : Int) : InventoryTile = { new InventoryTile(w, h) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala index f44975444..b81d8e0f6 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala @@ -24,7 +24,7 @@ abstract class PlanetSideServerObject extends PlanetSideGameObject { * @return the current internal `ActorRef` */ def Actor_=(control : ActorRef) : ActorRef = { - if(actor == ActorRef.noSender) { + if(actor == ActorRef.noSender || control == ActorRef.noSender) { actor = control } actor diff --git a/common/src/main/scala/net/psforever/objects/serverobject/builders/VehicleSpawnPadObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/serverobject/builders/VehicleSpawnPadObjectBuilder.scala new file mode 100644 index 000000000..d50ecd248 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/builders/VehicleSpawnPadObjectBuilder.scala @@ -0,0 +1,35 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.builders + +import akka.actor.Props +import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} + +/** + * Wrapper `Class` designed to instantiate a `VehicleSpawnPad` server object. + * @param spdef an `ObjectDefinition` object ... + * @param id the globally unique identifier to which this `VehicleSpawnPad` will be registered + */ +class VehicleSpawnPadObjectBuilder(private val spdef : ObjectDefinition, private val id : Int) extends ServerObjectBuilder[VehicleSpawnPad] { + import akka.actor.ActorContext + import net.psforever.objects.guid.NumberPoolHub + + def Build(implicit context : ActorContext, guid : NumberPoolHub) : VehicleSpawnPad = { + val obj = VehicleSpawnPad(spdef) + guid.register(obj, id) //non-Actor GUID registration + obj.Actor = context.actorOf(Props(classOf[VehicleSpawnControl], obj), s"${spdef.Name}_${obj.GUID.guid}") + obj + } +} + +object VehicleSpawnPadObjectBuilder { + /** + * Overloaded constructor for a `DoorObjectBuilder`. + * @param spdef an `ObjectDefinition` object + * @param id a globally unique identifier + * @return a `VehicleSpawnPadObjectBuilder` object + */ + def apply(spdef : ObjectDefinition, id : Int) : VehicleSpawnPadObjectBuilder = { + new VehicleSpawnPadObjectBuilder(spdef, id) + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala new file mode 100644 index 000000000..412774bf1 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -0,0 +1,203 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.pad + +import akka.actor.{Actor, ActorRef, Cancellable} +import net.psforever.objects.{DefaultCancellable, Player, Vehicle} +import net.psforever.types.Vector3 + +import scala.concurrent.duration._ + +/** + * An `Actor` that handles messages being dispatched to a specific `VehicleSpawnPad`.
+ *
+ * A spawn pad receives vehicle orders from an attached `Terminal` object. + * At the time when the order is received, the player who submitted the order is completely visible + * and waiting back by the said `Terminal` from where the order was submitted. + * Assuming no other orders are currently being processed, the repeated self message will retrieve this as the next order. + * The player character is first made transparent with a `GenericObjectActionMessage` packet. + * The vehicle model itself is then introduced to the game and three things happen with the following order, more or less:
+ * 1. the vehicle is attached to a lifting platform that is designed to introduce the vehicle;
+ * 2. the player is seated in the vehicle's driver seat (seat 0) and is thus declared the owner;
+ * 3. various properties of the player, the vehicle, and the spawn pad itself are set `PlanetsideAttributesMessage`.
+ * When this step is finished, the lifting platform raises the vehicle and the mounted player into the game world. + * The vehicle detaches and is made to roll off the spawn pad a certain distance before being released to user control. + * That is what is supposed to happen within a certain measure of timing.
+ *
+ * The orders that are submitted to the spawn pad must be composed of at least three elements: + * 1. a player, specifically the one that submitted the order and will be declared the "owner;" + * 2. a vehicle; + * 3. a callback location for sending messages. + * @param pad the `VehicleSpawnPad` object being governed + */ +class VehicleSpawnControl(pad : VehicleSpawnPad) extends Actor { + /** an executor for progressing a vehicle order through the normal spawning logic */ + private var process : Cancellable = DefaultCancellable.obj + /** a list of vehicle orders that have been submitted for this spawn pad */ + private var orders : List[VehicleSpawnControl.OrderEntry] = List.empty[VehicleSpawnControl.OrderEntry] + /** the current vehicle order being acted upon */ + private var trackedOrder : Option[VehicleSpawnControl.OrderEntry] = None + /** how many times a spawned vehicle (spatially) disrupted the next vehicle from being spawned */ + private var blockingViolations : Int = 0 + private[this] val log = org.log4s.getLogger + private[this] def trace(msg : String) : Unit = log.trace(msg) + + + def receive : Receive = { + case VehicleSpawnPad.VehicleOrder(player, vehicle) => + trace(s"order from $player for $vehicle received") + orders = orders :+ VehicleSpawnControl.OrderEntry(player, vehicle, sender) + if(trackedOrder.isEmpty && orders.length == 1) { + self ! VehicleSpawnControl.Process.GetOrder + } + + case VehicleSpawnControl.Process.GetOrder => + process.cancel + blockingViolations = 0 + val (completeOrder, remainingOrders) : (Option[VehicleSpawnControl.OrderEntry], List[VehicleSpawnControl.OrderEntry]) = orders match { + case x :: Nil => + (Some(x), Nil) + case x :: b => + (Some(x), b) + case Nil => + (None, Nil) + } + orders = remainingOrders + completeOrder match { + case Some(entry) => + trace(s"processing order $entry") + trackedOrder = completeOrder + import scala.concurrent.ExecutionContext.Implicits.global + process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.concealPlayerTimeout, self, VehicleSpawnControl.Process.ConcealPlayer) + case None => ; + } + + case VehicleSpawnControl.Process.ConcealPlayer => + process.cancel + trackedOrder match { + case Some(entry) => + if(entry.player.isAlive && entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender && entry.player.VehicleSeated.isEmpty) { + trace(s"hiding player: ${entry.player}") + entry.sendTo ! VehicleSpawnPad.ConcealPlayer + import scala.concurrent.ExecutionContext.Implicits.global + process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.loadVehicleTimeout, self, VehicleSpawnControl.Process.LoadVehicle) + } + else { + trace("integral component lost; abort order fulfillment") + //TODO Unregister vehicle ... somehow + trackedOrder = None + self ! VehicleSpawnControl.Process.GetOrder + } + case None => + self ! VehicleSpawnControl.Process.GetOrder + } + + case VehicleSpawnControl.Process.LoadVehicle => + process.cancel + trackedOrder match { + case Some(entry) => + if(entry.vehicle.Actor != ActorRef.noSender && entry.sendTo != ActorRef.noSender) { + trace(s"loading vehicle: ${entry.vehicle} defined in order") + entry.sendTo ! VehicleSpawnPad.LoadVehicle(entry.vehicle, pad) + import scala.concurrent.ExecutionContext.Implicits.global + process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated) + } + else { + trace("owner or vehicle lost; abort order fulfillment") + //TODO Unregister vehicle ... somehow + trackedOrder = None + self ! VehicleSpawnControl.Process.GetOrder + } + + case None => + self ! VehicleSpawnControl.Process.GetOrder + } + + case VehicleSpawnControl.Process.AwaitSeated => + process.cancel + trackedOrder match { + case Some(entry) => + if(entry.sendTo != ActorRef.noSender) { + trace("owner seated in vehicle") + import scala.concurrent.ExecutionContext.Implicits.global + process = if(entry.player.VehicleOwned.contains(entry.vehicle.GUID)) { + entry.sendTo ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle) + context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance) + } + else { + context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitSeatedTimeout, self, VehicleSpawnControl.Process.AwaitSeated) + } + } + else { + trace("owner lost; abort order fulfillment") + trackedOrder = None + self ! VehicleSpawnControl.Process.GetOrder + } + case None => + self ! VehicleSpawnControl.Process.GetOrder + } + + //TODO raise spawn pad rails from ground + + //TODO start auto drive away + + //TODO release auto drive away + + case VehicleSpawnControl.Process.AwaitClearance => + process.cancel + trackedOrder match { + case Some(entry) => + if(entry.sendTo == ActorRef.noSender || entry.vehicle.Actor == ActorRef.noSender) { + trace("integral component lost, but order fulfilled; process next order") + trackedOrder = None + self ! VehicleSpawnControl.Process.GetOrder + } + else if(Vector3.DistanceSquared(entry.vehicle.Position, pad.Position) > 100.0f) { //10m away from pad + trace("pad cleared; process next order") + trackedOrder = None + entry.sendTo ! VehicleSpawnPad.SpawnPadUnblocked(entry.vehicle.GUID) + self ! VehicleSpawnControl.Process.GetOrder + } + else { + trace(s"pad blocked by ${entry.vehicle} ...") + blockingViolations += 1 + entry.sendTo ! VehicleSpawnPad.SpawnPadBlockedWarning(entry.vehicle, blockingViolations) + import scala.concurrent.ExecutionContext.Implicits.global + process = context.system.scheduler.scheduleOnce(VehicleSpawnControl.awaitClearanceTimeout, self, VehicleSpawnControl.Process.AwaitClearance) + } + case None => + self ! VehicleSpawnControl.Process.GetOrder + } + + case _ => ; + } +} + +object VehicleSpawnControl { + final val concealPlayerTimeout : FiniteDuration = 2000000000L nanoseconds //2s + final val loadVehicleTimeout : FiniteDuration = 1000000000L nanoseconds //1s + final val awaitSeatedTimeout : FiniteDuration = 1000000000L nanoseconds //1s + final val awaitClearanceTimeout : FiniteDuration = 5000000000L nanoseconds //5s + + /** + * An `Enumeration` of the stages of a full vehicle spawning process, associated with certain messages passed. + * Some stages are currently TEMPORARY. + * @see VehicleSpawnPad + */ + object Process extends Enumeration { + val + GetOrder, + ConcealPlayer, + LoadVehicle, + AwaitSeated, + AwaitClearance + = Value + } + + /** + * An entry that stores vehicle spawn pad spawning tasks (called "orders"). + * @param player the player + * @param vehicle the vehicle + * @param sendTo a callback `Actor` associated with the player (in other words, `WorldSessionActor`) + */ + private final case class OrderEntry(player : Player, vehicle : Vehicle, sendTo : ActorRef) +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala new file mode 100644 index 000000000..f17ed5690 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala @@ -0,0 +1,89 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.pad + +import net.psforever.objects.{Player, Vehicle} +import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.packet.game.PlanetSideGUID + +/** + * A structure-owned server object that is a "spawn pad" for vehicles.
+ *
+ * Spawn pads have no purpose on their own but + * maintain the operative queue that introduces the vehicle into the game world and applies initial activity to it and + * maintain a position and a direction where the vehicle will be made to appear (as a `PlanetSideServerObject`). + * The actual functionality managed by this object is wholly found on its accompanying `Actor`. + * @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields + * @see `VehicleSpawnControl` + */ +class VehicleSpawnPad(spDef : ObjectDefinition) extends PlanetSideServerObject { + def Definition : ObjectDefinition = spDef +} + +object VehicleSpawnPad { + + /** + * Communicate to the spawn pad that it should enqueue the following vehicle. + * This is the entry point to vehicle spawn pad functionality. + * @param player the player who submitted the order (the "owner") + * @param vehicle the vehicle produced from the order + */ + final case class VehicleOrder(player : Player, vehicle : Vehicle) + + /** + * The first callback step in spawning the vehicle. + * An packet `GenericObjectActionMessage(/player/, 36)`, when used on a player character, + * will cause that player character's model to fade into transparency. + */ + final case class ConcealPlayer() + + /** + * A callback step in spawning the vehicle. + * The vehicle is properly introduced into the game world. + * If information about the vehicle itself that is important to its spawning has not yet been set, + * this callback is the last ideal situation to set that properties without having to adjust the vehicle visually. + * The primary operation that should occur is a content-appropriate `ObjectCreateMessage` packet and + * having the player sit down in the driver's seat (seat 0) of the vehicle. + * @param vehicle the vehicle being spawned + * @param pad the pad + */ + final case class LoadVehicle(vehicle : Vehicle, pad : VehicleSpawnPad) + + /** + * A TEMPORARY callback step in spawning the vehicle. + * From a state of transparency, while the vehicle is attached to the lifting platform of the spawn pad, + * the player designated the "owner" by callback is made to sit in the driver's seat (always seat 0). + * This message is the next step after that. + * @param vehicle the vehicle being spawned + */ + final case class PlayerSeatedInVehicle(vehicle : Vehicle) + + /** + * A TEMPORARY callback step in (successfully) spawning the vehicle. + * While the vehicle is still occupying the pad just after being spawned and its driver seat mounted, + * that vehicle is considered blocking the pad from being used for further spawning operations. + * This message allows the user to be made known about this blockage. + * @param vehicle the vehicle + * @param warning_count the number of times a warning period has occurred + */ + final case class SpawnPadBlockedWarning(vehicle : Vehicle, warning_count : Int) + + /** + * A TEMPORARY callback step in (successfully) spawning the vehicle. + * While the vehicle is still occupying the pad just after being spawned and its driver seat mounted, + * that vehicle is considered blocking the pad from being used for further spawning operations. + * A timeout will begin counting until the vehicle is despawned automatically for its driver's negligence. + * This message is used to clear the deconstruction countdown, primarily. + * @param vehicle_guid the vehicle + */ + final case class SpawnPadUnblocked(vehicle_guid : PlanetSideGUID) + + /** + * Overloaded constructor. + * @param spDef the spawn pad's definition entry + * @return a `VehicleSpawnPad` object + */ + def apply(spDef : ObjectDefinition) : VehicleSpawnPad = { + new VehicleSpawnPad(spDef) + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala new file mode 100644 index 000000000..fd62de60c --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/AirVehicleTerminalDefinition.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class AirVehicleTerminalDefinition extends TerminalDefinition(43) { + Name = "air_vehicle_terminal" + + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + flight1Vehicles.get(msg.item_name) match { + case Some(vehicle) => + Terminal.BuyVehicle(vehicle(), Nil) + case None => + Terminal.NoDeal() + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala new file mode 100644 index 000000000..263067491 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/BFRTerminalDefinition.scala @@ -0,0 +1,19 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class BFRTerminalDefinition extends TerminalDefinition(143) { + Name = "bfr_terminal" + + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + bfrVehicles.get(msg.item_name) match { + case Some(vehicle) => + //Terminal.BuyVehicle(vehicle, Nil) + Terminal.NoDeal() + case None => + Terminal.NoDeal() + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala index 8f990196e..a1583aa0c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/CertTerminalDefinition.scala @@ -81,7 +81,7 @@ class CertTerminalDefinition extends TerminalDefinition(171) { * @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 = { + override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { certificationList.get(msg.item_name) match { case Some((cert, cost)) => Terminal.SellCertification(cert, cost) @@ -89,12 +89,4 @@ class CertTerminalDefinition extends TerminalDefinition(171) { 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/serverobject/terminals/DropshipVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala new file mode 100644 index 000000000..7176f9785 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/DropshipVehicleTerminalDefinition.scala @@ -0,0 +1,19 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class DropshipVehicleTerminalDefinition extends TerminalDefinition(263) { + private val flightVehicles = flight1Vehicles ++ flight2Vehicles + Name = "dropship_vehicle_terminal" + + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + flightVehicles.get(msg.item_name) match { + case Some(vehicle) => + Terminal.BuyVehicle(vehicle(), Nil) + case None => + Terminal.NoDeal() + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala new file mode 100644 index 000000000..1087c575a --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/GroundVehicleTerminalDefinition.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class GroundVehicleTerminalDefinition extends TerminalDefinition(386) { + Name = "ground_vehicle_terminal" + + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + groundVehicles.get(msg.item_name) match { + case Some(vehicle) => + Terminal.BuyVehicle(vehicle(), Nil) + case None => + Terminal.NoDeal() + } + } +} 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 cea418f0f..2735b1bb3 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 @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.terminals -import net.psforever.objects.InfantryLoadout.Simplification +import net.psforever.objects.Loadout.Simplification import net.psforever.objects.{Player, Tool} import net.psforever.objects.definition._ import net.psforever.objects.equipment.Equipment @@ -13,7 +13,7 @@ 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. + * Change `ExoSuitType` and retrieve `Loadout` entries. */ class OrderTerminalDefinition extends TerminalDefinition(612) { Name = "order_terminal" @@ -75,19 +75,19 @@ 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 Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + override def Sell(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { Terminal.SellEquipment() } /** * Process a `TransactionType.InfantryLoadout` action by the user. - * `InfantryLoadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings. + * `Loadout` objects are blueprints composed of exo-suit specifications and simplified `Equipment`-to-slot mappings. * If a valid loadout is found, its data is transformed back into actual `Equipment` for return to 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 Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + override def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { if(msg.item_page == 4) { //Favorites tab player.LoadLoadout(msg.unk1) match { case Some(loadout) => @@ -105,7 +105,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) { /** * Accept a simplified blueprint for some piece of `Equipment` and create an actual piece of `Equipment` based on it. - * Used specifically for the reconstruction of `Equipment` via an `InfantryLoadout`. + * Used specifically for the reconstruction of `Equipment` via an `Loadout`. * @param entry the simplified blueprint * @return some `Equipment` object * @see `TerminalDefinition.MakeTool`
@@ -115,7 +115,7 @@ class OrderTerminalDefinition extends TerminalDefinition(612) { * `TerminalDefinition.MakeKit` */ private def BuildSimplifiedPattern(entry : Simplification) : Equipment = { - import net.psforever.objects.InfantryLoadout._ + import net.psforever.objects.Loadout._ entry match { case obj : ShorthandTool => val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef }) 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 c6cba9f0c..554f09603 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 @@ -136,10 +136,24 @@ object Terminal { final case class SellEquipment() extends Exchange import net.psforever.types.CertificationType + + /** + * Provide the certification type unlocked by the player. + * @param cert the certification unlocked + * @param cost the certification point cost + */ final case class LearnCertification(cert : CertificationType.Value, cost : Int) extends Exchange + /** + * Provide the certification type freed-up by the player. + * @param cert the certification returned + * @param cost the certification point cost + */ final case class SellCertification(cert : CertificationType.Value, cost : Int) extends Exchange + import net.psforever.objects.Vehicle + final case class BuyVehicle(vehicle : Vehicle, loadout: List[Any]) 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/serverobject/terminals/TerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala index 5cf8ff3d4..aa98b0f74 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala @@ -8,10 +8,11 @@ import net.psforever.packet.game.ItemTransactionMessage import net.psforever.types.ExoSuitType /** - * The definition for any `Terminal`. + * The basic definition for any `Terminal`. * @param objectId the object's identifier number */ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objectId) { + private[this] val log = org.log4s.getLogger("TerminalDefinition") Name = "terminal" /** @@ -22,12 +23,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec /** * The unimplemented functionality for this `Terminal`'s `TransactionType.Sell` activity. */ - def Sell(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange + def Sell(player: Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() /** * The unimplemented functionality for this `Terminal`'s `TransactionType.InfantryLoadout` activity. */ - def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange + def Loadout(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() /** * A `Map` of information for changing exo-suits. @@ -128,42 +129,42 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * value - a curried function that builds the object */ protected val infantryWeapons : Map[String, ()=>Equipment] = Map( - "ilc9" -> MakeTool(ilc9, bullet_9mm), - "repeater" -> MakeTool(repeater, bullet_9mm), - "isp" -> MakeTool(isp, shotgun_shell), //amp - "beamer" -> MakeTool(beamer, energy_cell), - "suppressor" -> MakeTool(suppressor, bullet_9mm), - "anniversary_guna" -> MakeTool(anniversary_guna, anniversary_ammo), //tr stinger - "anniversary_gun" -> MakeTool(anniversary_gun, anniversary_ammo), //nc spear - "anniversary_gunb" -> MakeTool(anniversary_gunb, anniversary_ammo), //vs eraser - "cycler" -> MakeTool(cycler, bullet_9mm), - "gauss" -> MakeTool(gauss, bullet_9mm), - "pulsar" -> MakeTool(pulsar, energy_cell), - "punisher" -> MakeTool(punisher, List(bullet_9mm, rocket)), - "flechette" -> MakeTool(flechette, shotgun_shell), - "spiker" -> MakeTool(spiker, ancient_ammo_combo), - "frag_grenade" -> MakeTool(frag_grenade, frag_grenade_ammo), - "jammer_grenade" -> MakeTool(jammer_grenade, jammer_grenade_ammo), - "plasma_grenade" -> MakeTool(plasma_grenade, plasma_grenade_ammo), - "katana" -> MakeTool(katana, melee_ammo), - "chainblade" -> MakeTool(chainblade, melee_ammo), - "magcutter" -> MakeTool(magcutter, melee_ammo), - "forceblade" -> MakeTool(forceblade, melee_ammo), - "mini_chaingun" -> MakeTool(mini_chaingun, bullet_9mm), - "r_shotgun" -> MakeTool(r_shotgun, shotgun_shell), //jackhammer - "lasher" -> MakeTool(lasher, energy_cell), - "maelstrom" -> MakeTool(maelstrom, maelstrom_ammo), - "striker" -> MakeTool(striker, striker_missile_ammo), - "hunterseeker" -> MakeTool(hunterseeker, hunter_seeker_missile), //phoenix - "lancer" -> MakeTool(lancer, lancer_cartridge), - "phoenix" -> MakeTool(phoenix, phoenix_missile), //decimator - "rocklet" -> MakeTool(rocklet, rocket), - "thumper" -> MakeTool(thumper, frag_cartridge), - "radiator" -> MakeTool(radiator, ancient_ammo_combo), - "heavy_sniper" -> MakeTool(heavy_sniper, bolt), //hsr - "bolt_driver" -> MakeTool(bolt_driver, bolt), - "oicw" -> MakeTool(oicw, oicw_ammo), //scorpion - "flamethrower" -> MakeTool(flamethrower, flamethrower_ammo) + "ilc9" -> MakeTool(ilc9), + "repeater" -> MakeTool(repeater), + "isp" -> MakeTool(isp), //amp + "beamer" -> MakeTool(beamer), + "suppressor" -> MakeTool(suppressor), + "anniversary_guna" -> MakeTool(anniversary_guna), //tr stinger + "anniversary_gun" -> MakeTool(anniversary_gun), //nc spear + "anniversary_gunb" -> MakeTool(anniversary_gunb), //vs eraser + "cycler" -> MakeTool(cycler), + "gauss" -> MakeTool(gauss), + "pulsar" -> MakeTool(pulsar), + "punisher" -> MakeTool(punisher), + "flechette" -> MakeTool(flechette), + "spiker" -> MakeTool(spiker), + "frag_grenade" -> MakeTool(frag_grenade), + "jammer_grenade" -> MakeTool(jammer_grenade), + "plasma_grenade" -> MakeTool(plasma_grenade), + "katana" -> MakeTool(katana), + "chainblade" -> MakeTool(chainblade), + "magcutter" -> MakeTool(magcutter), + "forceblade" -> MakeTool(forceblade), + "mini_chaingun" -> MakeTool(mini_chaingun), + "r_shotgun" -> MakeTool(r_shotgun), //jackhammer + "lasher" -> MakeTool(lasher), + "maelstrom" -> MakeTool(maelstrom), + "striker" -> MakeTool(striker), + "hunterseeker" -> MakeTool(hunterseeker), //phoenix + "lancer" -> MakeTool(lancer), + "phoenix" -> MakeTool(phoenix), //decimator + "rocklet" -> MakeTool(rocklet), + "thumper" -> MakeTool(thumper), + "radiator" -> MakeTool(radiator), + "heavy_sniper" -> MakeTool(heavy_sniper), //hsr + "bolt_driver" -> MakeTool(bolt_driver), + "oicw" -> MakeTool(oicw), //scorpion + "flamethrower" -> MakeTool(flamethrower) ) /** @@ -176,20 +177,98 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec "super_medkit" -> MakeKit(super_medkit), "super_armorkit" -> MakeKit(super_armorkit), "super_staminakit" -> MakeKit(super_staminakit), - "medicalapplicator" -> MakeTool(medicalapplicator, health_canister), + "medicalapplicator" -> MakeTool(medicalapplicator), "bank" -> MakeTool(bank, armor_canister), - "nano_dispenser" -> MakeTool(nano_dispenser, armor_canister), + "nano_dispenser" -> MakeTool(nano_dispenser), //TODO "ace" -> MakeConstructionItem(ace), //TODO "advanced_ace" -> MakeConstructionItem(advanced_ace), "remote_electronics_kit" -> MakeSimpleItem(remote_electronics_kit), - "trek" -> MakeTool(trek, trek_ammo), + "trek" -> MakeTool(trek), "command_detonater" -> MakeSimpleItem(command_detonater), "flail_targeting_laser" -> MakeSimpleItem(flail_targeting_laser) ) + /** + * A `Map` of operations for producing a ground-based `Vehicle`. + * key - an identification string sent by the client + * value - a curried function that builds the object + */ + protected val groundVehicles : Map[String, ()=>Vehicle] = Map( + "quadassault" -> MakeVehicle(quadassault), + "fury" -> MakeVehicle(fury), + "quadstealth" -> MakeVehicle(quadstealth), + "ant" -> MakeVehicle(ant), + "ams" -> MakeVehicle(ams), + "mediumtransport" -> MakeVehicle(mediumtransport), + "two_man_assault_buggy" -> MakeVehicle(two_man_assault_buggy), + "skyguard" -> MakeVehicle(skyguard), + "lightning" -> MakeVehicle(lightning), + "threemanheavybuggy" -> MakeVehicle(threemanheavybuggy), + "battlewagon" -> MakeVehicle(battlewagon), + "apc_tr" -> MakeVehicle(apc_tr), + "prowler" -> MakeVehicle(prowler), + "twomanheavybuggy" -> MakeVehicle(twomanheavybuggy), + "thunderer" -> MakeVehicle(thunderer), + "apc_nc" -> MakeVehicle(apc_nc), + "vanguard" -> MakeVehicle(vanguard), + "twomanhoverbuggy" -> MakeVehicle(twomanhoverbuggy), + "aurora" -> MakeVehicle(aurora), + "apc_vs" -> MakeVehicle(apc_vs), + "magrider" -> MakeVehicle(magrider), + "flail" -> MakeVehicle(flail), + "switchblade" -> MakeVehicle(switchblade), + "router" -> MakeVehicle(router) + ) + + /** + * A `Map` of operations for producing most flight-based `Vehicle`. + * key - an identification string sent by the client + * value - a curried function that builds the object + */ + protected val flight1Vehicles : Map[String, ()=>Vehicle] = Map( + "mosquito" -> MakeVehicle(mosquito), + "lightgunship" -> MakeVehicle(lightgunship), + "wasp" -> MakeVehicle(wasp), + "phantasm" -> MakeVehicle(phantasm), + "vulture" -> MakeVehicle(vulture), + "liberator" -> MakeVehicle(liberator) + ) + + /** + * A `Map` of operations for producing a flight-based `Vehicle` specific to the dropship terminal. + * key - an identification string sent by the client + * value - a curried function that builds the object + */ + protected val flight2Vehicles : Map[String, ()=>Vehicle] = Map( + "dropship" -> MakeVehicle(dropship), + "galaxy_gunship" -> MakeVehicle(galaxy_gunship), + "lodestar" -> MakeVehicle(lodestar) + ) + + /** + * A `Map` of operations for producing a ground-based `Vehicle` specific to the bfr terminal. + * key - an identification string sent by the client + * value - a curried function that builds the object + */ + protected val bfrVehicles : Map[String, ()=>Vehicle] = Map( +// "colossus_gunner" -> (()=>Unit), +// "colossus_flight" -> (()=>Unit), +// "peregrine_gunner" -> (()=>Unit), +// "peregrine_flight" -> (()=>Unit), +// "aphelion_gunner" -> (()=>Unit), +// "aphelion_flight" -> (()=>Unit) + ) + /** * Create a new `Tool` from provided `EquipmentDefinition` objects. - * @param tdef the `ToolDefinition` objects + * @param tdef the `ToolDefinition` object + * @return a partial function that, when called, creates the piece of `Equipment` + */ + protected def MakeTool(tdef : ToolDefinition)() : Tool = MakeTool(tdef, Nil) + + /** + * Create a new `Tool` from provided `EquipmentDefinition` objects. + * @param tdef the `ToolDefinition` object * @param adef an `AmmoBoxDefinition` object * @return a partial function that, when called, creates the piece of `Equipment` */ @@ -200,24 +279,55 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * Only use this function to create default `Tools` with the default parameters. * For example, loadouts can retain `Tool` information that utilizes alternate, valid ammunition types; * and, this method function will not construct a complete object if provided that information. - * @param tdef the `ToolDefinition` objects + * @param tdef the `ToolDefinition` object * @param adefs a `List` of `AmmoBoxDefinition` objects * @return a curried function that, when called, creates the piece of `Equipment` * @see `GlobalDefinitions` * @see `OrderTerminalDefinition.BuildSimplifiedPattern` */ - protected def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = { + protected def MakeTool(tdef : ToolDefinition, adefs : List[AmmoBoxDefinition])() : Tool = { val obj = Tool(tdef) - (0 until obj.MaxAmmoSlot).foreach(index => { - val aType = adefs(index) - val ammo = MakeAmmoBox(aType, Some(obj.Definition.FireModes(index).Magazine)) //make internal magazine, full - (obj.AmmoSlots(index).Box = ammo) match { - case Some(_) => ; //this means it worked - case None => - org.log4s.getLogger("TerminalDefinition").warn(s"plans do not match definition: trying to feed ${ammo.AmmoType} ammunition into Tool (${obj.Definition.ObjectId} @ $index)") + adefs match { + case _ :: _ => + LoadAmmunitionIntoWeapon(obj, adefs) + case Nil => ; //as-is + } + obj + } + + /** + * Given a weapon, and custom ammunition profiles, attempt to load those boxes of ammunition into the weapon.
+ *
+ * This is a customization function that should normally go unused. + * All of the information necessary to generate a `Tool` from a `Terminal` request should be available on the `ToolDefinition` object. + * The ammunition information, regardless of "customization," must satisfy the type limits of the original definition. + * As thus, to introduce very strange ammunition to a give `Tool`, + * either the definition must be modified or a different definition must be used. + * The custom ammunition is organized into order of ammunition slots based on the `FireModeDefinition` objects. + * That is: + * the first custom element is processed by the first ammunition slot; + * the second custom element is processed by the second ammunition slot; and, so forth. + * @param weapon the `Tool` object + * @param adefs a sequential `List` of ammunition to be loaded into weapon + * @see `AmmoBoxDefinition` + * @see `FireModeDefinition` + */ + private def LoadAmmunitionIntoWeapon(weapon : Tool, adefs : List[AmmoBoxDefinition]) : Unit = { + val definition = weapon.Definition + (0 until math.min(weapon.MaxAmmoSlot, adefs.length)).foreach(index => { + val ammoSlot = weapon.AmmoSlots(index) + adefs.lift(index) match { + case Some(aType) => + ammoSlot.AllAmmoTypes.indexOf(aType.AmmoType) match { + case -1 => + log.warn(s"terminal plans do not match definition: can not feed ${aType.AmmoType} ammunition into Tool (${definition.ObjectId} @ ammo $index)") + case n => + ammoSlot.AmmoTypeIndex = n + ammoSlot.Box = MakeAmmoBox(aType, Some(definition.FireModes(index).Magazine)) //make new internal magazine, full + } + case None => ; } }) - obj } /** @@ -229,11 +339,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * @see `GlobalDefinitions` */ protected def MakeAmmoBox(adef : AmmoBoxDefinition, capacity : Option[Int] = None)() : AmmoBox = { - val obj = AmmoBox(adef) - if(capacity.isDefined) { - obj.Capacity = capacity.get + capacity match { + case Some(cap) => + AmmoBox(adef, cap) + case None => + AmmoBox(adef) } - obj } /** @@ -259,4 +370,12 @@ abstract class TerminalDefinition(objectId : Int) extends ObjectDefinition(objec * @see `GlobalDefinitions` */ protected def MakeConstructionItem(cdef : ConstructionItemDefinition)() : ConstructionItem = ConstructionItem(cdef) + + /** + * Create a new `Vehicle` from provided `VehicleDefinition` objects. + * @param vdef the `VehicleDefinition` object + * @return a curried function that, when called, creates the `Vehicle` + * @see `GlobalDefinitions` + */ + protected def MakeVehicle(vdef : VehicleDefinition)() : Vehicle = Vehicle(vdef) } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala new file mode 100644 index 000000000..c069545a4 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/VehicleTerminalCombinedDefinition.scala @@ -0,0 +1,19 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.terminals + +import net.psforever.objects.Player +import net.psforever.packet.game.ItemTransactionMessage + +class VehicleTerminalCombinedDefinition extends TerminalDefinition(952) { + private val vehicles = groundVehicles ++ flight1Vehicles + Name = "vehicle_terminal_combined" + + def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = { + vehicles.get(msg.item_name) match { + case Some(vehicle) => + Terminal.BuyVehicle(vehicle(), Nil) + case None => + Terminal.NoDeal() + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala index 022aa53d7..3e2a63fc3 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala @@ -2,7 +2,6 @@ package net.psforever.objects.zones import akka.actor.Actor -import net.psforever.objects.serverobject.locks.IFFLock /** * na @@ -35,8 +34,9 @@ class ZoneActor(zone : Zone) extends Actor { } }) - //check door to locks association + //check door to lock association import net.psforever.objects.serverobject.doors.Door + import net.psforever.objects.serverobject.locks.IFFLock map.DoorToLock.foreach({ case((door_guid, lock_guid)) => try { if(!guid(door_guid).get.isInstanceOf[Door]) { @@ -45,7 +45,7 @@ class ZoneActor(zone : Zone) extends Actor { } catch { case _ : Exception => - slog.error(s"expected a door, but looking for uninitialized object $door_guid") + slog.error(s"expected a door at id $door_guid, but looking for uninitialized object") } try { if(!guid(lock_guid).get.isInstanceOf[IFFLock]) { @@ -54,7 +54,31 @@ class ZoneActor(zone : Zone) extends Actor { } catch { case _ : Exception => - slog.error(s"expected an IFF locks, but looking for uninitialized object $lock_guid") + slog.error(s"expected an IFF locks at id $lock_guid, but looking for uninitialized object") + } + }) + + //check vehicle terminal to spawn pad association + import net.psforever.objects.serverobject.pad.VehicleSpawnPad + import net.psforever.objects.serverobject.terminals.Terminal + map.TerminalToSpawnPad.foreach({ case ((term_guid, pad_guid)) => + try { + if(!guid(term_guid).get.isInstanceOf[Terminal]) { //TODO check is vehicle terminal + slog.error(s"expected id $term_guid to be a terminal, but it was not") + } + } + catch { + case _ : Exception => + slog.error(s"expected a terminal at id $term_guid, but looking for uninitialized object") + } + try { + if(!guid(pad_guid).get.isInstanceOf[VehicleSpawnPad]) { + slog.error(s"expected id $pad_guid to be a spawn pad, but it was not") + } + } + catch { + case _ : Exception => + slog.error(s"expected a spawn pad at id $pad_guid, but looking for uninitialized object") } }) } diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala index dbec207ca..35d38fe96 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala @@ -25,6 +25,7 @@ import net.psforever.objects.serverobject.builders.ServerObjectBuilder */ class ZoneMap(private val name : String) { private var localObjects : List[ServerObjectBuilder[_]] = List() + private var linkTerminalPad : Map[Int, Int] = Map() private var linkDoorLock : Map[Int, Int] = Map() private var linkObjectBase : Map[Int, Int] = Map() private var numBases : Int = 0 @@ -64,7 +65,13 @@ class ZoneMap(private val name : String) { def DoorToLock : Map[Int, Int] = linkDoorLock - def DoorToLock(door_guid : Int, lock_guid : Int) = { + def DoorToLock(door_guid : Int, lock_guid : Int) : Unit = { linkDoorLock = linkDoorLock ++ Map(door_guid -> lock_guid) } + + def TerminalToSpawnPad : Map[Int, Int] = linkTerminalPad + + def TerminalToSpawnPad(terminal_guid : Int, pad_guid : Int) : Unit = { + linkTerminalPad = linkTerminalPad ++ Map(terminal_guid -> pad_guid) + } } diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index bec753054..bfc13b89f 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -420,7 +420,7 @@ object GamePacketOpcode extends Enumeration { case 0x53 => noDecoder(DroppodLaunchRequestMessage) case 0x54 => game.HackMessage.decode case 0x55 => noDecoder(DroppodLaunchResponseMessage) - case 0x56 => noDecoder(GenericObjectActionMessage) + case 0x56 => game.GenericObjectActionMessage.decode case 0x57 => game.AvatarVehicleTimerMessage.decode // 0x58 case 0x58 => game.AvatarImplantMessage.decode diff --git a/common/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala b/common/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala new file mode 100644 index 000000000..0c15f526f --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala @@ -0,0 +1,39 @@ +// Copyright (c) 2017 PSForever +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import scodec.bits.BitVector +import scodec.{Attempt, Codec} +import scodec.codecs._ +import shapeless.{::, HNil} + +/** + * Dispatched by the server to enact an effect on some game object. + * (Write more some other time.) + * @param object_guid the target object + * @param code the action code + */ +final case class GenericObjectActionMessage(object_guid : PlanetSideGUID, + code : Int) + extends PlanetSideGamePacket { + type Packet = GenericObjectActionMessage + def opcode = GamePacketOpcode.GenericObjectActionMessage + def encode = GenericObjectActionMessage.encode(this) +} + +object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] { + implicit val codec : Codec[GenericObjectActionMessage] = ( + ("object_guid" | PlanetSideGUID.codec) :: + ("code" | uint8L) :: + ("ex" | bits) //"code" may extract at odd sizes + ).exmap[GenericObjectActionMessage] ( + { + case guid :: code :: _ :: HNil => + Attempt.Successful(GenericObjectActionMessage(guid, code)) + }, + { + case GenericObjectActionMessage(guid, code) => + Attempt.Successful(guid :: code :: BitVector.empty :: HNil) + } + ) +} 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 c61b03e7a..036bd5ff8 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -89,7 +89,7 @@ import scodec.codecs._ * `106 - Custom Head`
*
* Vehicles:
- * 0 - Vehicle health
+ * 0 - Vehicle base health
* 10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)
* 11 - Gunner seat(s) permissions (same)
* 12 - Passenger seat(s) permissions (same)
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala index 37b85c5a6..e9506fc3b 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala @@ -347,6 +347,8 @@ object ObjectClass { final val quadstealth_destroyed = 711 final val router = 741 final val router_destroyed = 742 + final val skyguard = 784 + final val skyguard_destroyed = 785 final val switchblade = 847 final val switchblade_destroyed = 848 final val threemanheavybuggy = 862 //marauder @@ -845,7 +847,7 @@ object ObjectClass { case ObjectClass.lancer => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.lasher => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.liberator_25mm_cannon => ConstructorData.genericCodec(WeaponData.codec, "weapon") - case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec(2), "weapon") + case ObjectClass.liberator_bomb_bay => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.liberator_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.lightgunship_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon") case ObjectClass.lightning_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon") @@ -888,7 +890,7 @@ object ObjectClass { case ObjectClass.rocklet => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.rotarychaingun_mosquito => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.scythe => ConstructorData.genericCodec(WeaponData.codec(2), "weapon") - case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec, "weapon") + case ObjectClass.skyguard_weapon_system => ConstructorData.genericCodec(WeaponData.codec(2), "weapon") case ObjectClass.spiker => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.spitfire_aa_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon") case ObjectClass.spitfire_weapon => ConstructorData.genericCodec(WeaponData.codec, "weapon") @@ -1245,6 +1247,8 @@ object ObjectClass { case ObjectClass.quadstealth_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage") case ObjectClass.router => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle") case ObjectClass.router_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage") + case ObjectClass.skyguard => ConstructorData.genericCodec(VehicleData.codec, "vehicle") + case ObjectClass.skyguard_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage") case ObjectClass.switchblade => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle") case ObjectClass.switchblade_destroyed => ConstructorData.genericCodec(DestroyedVehicleData.codec, "wreckage") case ObjectClass.threemanheavybuggy => ConstructorData.genericCodec(VehicleData.codec, "vehicle") diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala index 8d3a60651..1a3d8aed9 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala @@ -27,6 +27,81 @@ object Prefab { VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, driveState, false, false, false, Some(UtilityVehicleData(0)), None)(VehicleFormat.Utility) } + def apc_nc(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { + VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, + Some(InventoryData( + InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemd_nc, weapon4_guid, 14, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo4_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) + ) :: Nil + )) + )(VehicleFormat.Normal) + } + + def apc_tr(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { + VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, + Some(InventoryData( + InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemd_tr, weapon4_guid, 14, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) + ) :: Nil + )) + )(VehicleFormat.Normal) + } + + def apc_vs(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { + VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, + Some(InventoryData( + InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11, + WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_weapon_systemd_vs, weapon4_guid, 14, + WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo4_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) + ) :: + InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) + ) :: Nil + )) + )(VehicleFormat.Normal) + } + def aurora(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo11_guid : PlanetSideGUID, ammo12_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo21_guid : PlanetSideGUID, ammo22_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, Some(InventoryData( @@ -118,56 +193,6 @@ object Prefab { )(VehicleFormat.Variant) } - def juggernaut(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { - VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, - Some(InventoryData( - InventoryItemData(ObjectClass.apc_weapon_systemc_tr, weapon1_guid, 11, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo1_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemd_tr, weapon4_guid, 14, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_15mm, ammo4_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) - ) :: Nil - )) - )(VehicleFormat.Normal) - } - - def leviathan(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { - VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, - Some(InventoryData( - InventoryItemData(ObjectClass.apc_weapon_systemc_vs, weapon1_guid, 11, - WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo1_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemd_vs, weapon4_guid, 14, - WeaponData(0x6, 0x8, 0, ObjectClass.flux_cannon_thresher_battery, ammo4_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) - ) :: Nil - )) - )(VehicleFormat.Normal) - } - def liberator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), Some(InventoryData( @@ -187,8 +212,8 @@ object Prefab { def lightgunship(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), Some(InventoryData( - InventoryItemData(445, weapon_guid, 1, - WeaponData(0x6, 0x8, 0, 16, ammo1_guid, 0, AmmoBoxData(8), 722, ammo2_guid,1, AmmoBoxData(8)) + InventoryItemData(ObjectClass.lightgunship_weapon_system, weapon_guid, 1, + WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.reaver_rocket, ammo2_guid,1, AmmoBoxData(8)) ) :: Nil )) )(VehicleFormat.Variant) @@ -270,14 +295,6 @@ object Prefab { )(VehicleFormat.Normal) } - def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = { - VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), - Some(InventoryData( - InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil - )) - )(VehicleFormat.Variant) - } - def quadassault(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, None, Some(InventoryData( @@ -292,6 +309,24 @@ object Prefab { VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, false, false, false, None, None)(VehicleFormat.Normal) } + def router(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, terminal_guid : PlanetSideGUID) : VehicleData = { + VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.Mobile, false, false, false, Some(VariantVehicleData(0)), + Some(InventoryData( + InventoryItemData(ObjectClass.teleportpad_terminal, terminal_guid, 1, CommonTerminalData(faction, 2)) :: Nil + )) + )(VehicleFormat.Variant) + } + + def skyguard(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { + VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, + Some(InventoryData( + InventoryItemData(ObjectClass.skyguard_weapon_system, weapon_guid, 2, + WeaponData(0x6, 0x8, 0, ObjectClass.skyguard_flak_cannon_ammo, ammo1_guid, 0, AmmoBoxData(8), ObjectClass.bullet_12mm, ammo2_guid, 1, AmmoBoxData(8)) + ) :: Nil + )) + )(VehicleFormat.Normal) + } + def switchblade(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, driveState : DriveState.Value, weapon_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 0), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), Some(InventoryData( @@ -368,31 +403,6 @@ object Prefab { )(VehicleFormat.Normal) } - def vindicator(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID, weapon4_guid : PlanetSideGUID, ammo4_guid : PlanetSideGUID, weapon5_guid : PlanetSideGUID, ammo5_guid : PlanetSideGUID, weapon6_guid : PlanetSideGUID, ammo6_guid : PlanetSideGUID) : VehicleData = { - VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, None, - Some(InventoryData( - InventoryItemData(ObjectClass.apc_weapon_systemc_nc, weapon1_guid, 11, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo1_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemb, weapon2_guid, 12, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo2_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systema, weapon3_guid, 13, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_75mm, ammo3_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_weapon_systemd_nc, weapon4_guid, 14, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_20mm, ammo4_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_r, weapon5_guid, 15, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo5_guid, 0, AmmoBoxData(8)) - ) :: - InventoryItemData(ObjectClass.apc_ballgun_l, weapon6_guid, 16, - WeaponData(0x6, 0x8, 0, ObjectClass.bullet_12mm, ammo6_guid, 0, AmmoBoxData(8)) - ) :: Nil - )) - )(VehicleFormat.Normal) - } - def vulture(loc : PlacementData, faction : PlanetSideEmpire.Value, health : Int, weapon1_guid : PlanetSideGUID, ammo1_guid : PlanetSideGUID, weapon2_guid : PlanetSideGUID, ammo2_guid : PlanetSideGUID, weapon3_guid : PlanetSideGUID, ammo3_guid : PlanetSideGUID) : VehicleData = { VehicleData(CommonFieldData(loc, faction, 2), 0, health, false, false, DriveState.State7, true, false, false, Some(VariantVehicleData(0)), Some(InventoryData( diff --git a/common/src/main/scala/net/psforever/types/Vector3.scala b/common/src/main/scala/net/psforever/types/Vector3.scala index ac2b92fea..279b74110 100644 --- a/common/src/main/scala/net/psforever/types/Vector3.scala +++ b/common/src/main/scala/net/psforever/types/Vector3.scala @@ -4,11 +4,41 @@ package net.psforever.types import net.psforever.newcodecs._ import scodec.Codec import scodec.codecs._ -import shapeless.{::, HNil} final case class Vector3(x : Float, y : Float, - z : Float) + z : Float) { + /** + * Operator override for vector addition, treating `Vector3` objects as actual mathematical vectors. + * The application of this overload is "vector1 + vector2." + * @param vec the other `Vector3` object + * @return a new `Vector3` object with the summed values + */ + def +(vec : Vector3) : Vector3 = { + new Vector3(x + vec.x, y + vec.y, z + vec.z) + } + + /** + * Operator override for vector subtraction, treating `Vector3` objects as actual mathematical vectors. + * The application of this overload is "vector1 - vector2." + * @param vec the other `Vector3` object + * @return a new `Vector3` object with the difference values + */ + def -(vec : Vector3) : Vector3 = { + new Vector3(x - vec.x, y - vec.y, z - vec.z) + } + + /** + * Operator override for vector scaling, treating `Vector3` objects as actual mathematical vectors. + * The application of this overload is "vector * scalar" exclusively. + * "scalar * vector" is invalid. + * @param scalar the value to multiply this vector + * @return a new `Vector3` object + */ + def *(scalar : Float) : Vector3 = { + new Vector3(x*scalar, y*scalar, z*scalar) + } +} object Vector3 { implicit val codec_pos : Codec[Vector3] = ( @@ -28,4 +58,48 @@ object Vector3 { ("y" | floatL) :: ("z" | floatL) ).as[Vector3] + + /** + * Calculate the actual distance between two points. + * @param pos1 the first point + * @param pos2 the second point + * @return the distance + */ + def Distance(pos1 : Vector3, pos2 : Vector3) : Float = { + math.sqrt(DistanceSquared(pos1, pos2)).toFloat + } + + /** + * Calculate the squared distance between two points. + * Though some time is saved care must be taken that any comparative distance is also squared. + * @param pos1 the first point + * @param pos2 the second point + * @return the distance + */ + def DistanceSquared(pos1 : Vector3, pos2 : Vector3) : Float = { + val dvec : Vector3 = pos1 - pos2 + (dvec.x * dvec.x) + (dvec.y * dvec.y) + (dvec.z * dvec.z) + } + + /** + * Calculate the actual magnitude of a vector. + * @param vec the vector + * @return the magnitude + */ + def Magnitude(vec : Vector3) : Float = { + math.sqrt(MagnitudeSquared(vec)).toFloat + } + + /** + * Calculate the squared magnitude of a vector. + * Though some time is saved care must be taken that any comparative magnitude is also squared. + * @param vec the vector + * @return the magnitude + */ + def MagnitudeSquared(vec : Vector3) : Float = { + val dx : Float = vec.x + val dy : Float = vec.y + val dz : Float = vec.z + (dx * dx) + (dy * dy) + (dz * dz) + } } diff --git a/common/src/test/scala/Vector3Test.scala b/common/src/test/scala/Vector3Test.scala new file mode 100644 index 000000000..cedc7c066 --- /dev/null +++ b/common/src/test/scala/Vector3Test.scala @@ -0,0 +1,68 @@ +// Copyright (c) 2017 PSForever +import org.specs2.mutable._ +import net.psforever.types.Vector3 + +class Vector3Test extends Specification { + val vec = Vector3(1.3f, -2.6f, 3.9f) + + "Vector3" should { + "construct" in { + vec.x mustEqual 1.3f + vec.y mustEqual -2.6f + vec.z mustEqual 3.9f + } + + "calculate magnitude (like a vector) 1" in { + val obj = Vector3(2.0f, 0.0f, 0.0f) + Vector3.Magnitude(obj) mustEqual 2.0f + } + + "calculate magnitude (like a vector) 2" in { + val obj = Vector3(3.0f, 4.0f, 0.0f) + Vector3.Magnitude(obj) mustEqual 5.0f + } + + "calculate magnitude (like a vector) 3" in { + Vector3.Magnitude(vec) mustEqual 4.864155f + } + + "calculate square magnitude (like a vector)" in { + Vector3.MagnitudeSquared(vec) mustEqual 23.66f + } + + "calculate distance 1" in { + val obj1 = Vector3(0.0f, 0.0f, 0.0f) + val obj2 = Vector3(2.0f, 0.0f, 0.0f) + Vector3.Distance(obj1, obj2) mustEqual 2.0f + } + + "calculate distance 2" in { + val obj1 = Vector3(0.0f, 0.0f, 0.0f) + val obj2 = Vector3(2.0f, 0.0f, 0.0f) + Vector3.Distance(obj1, obj2) mustEqual Vector3.Magnitude(obj2) + } + + "calculate distance 3" in { + val obj1 = Vector3(3.0f, 4.0f, 5.0f) + val obj2 = Vector3(3.0f, 4.0f, 5.0f) + Vector3.Distance(obj1, obj2) mustEqual 0f + } + + "addition" in { + val obj1 = Vector3(3.0f, 4.0f, 5.0f) + val obj2 = Vector3(3.0f, 4.0f, 5.0f) + obj1 + obj2 mustEqual Vector3(6f, 8f, 10f) + } + + "subtraction" in { + val obj1 = Vector3(3.0f, 4.0f, 5.0f) + val obj2 = Vector3(3.0f, 4.0f, 5.0f) + obj1 - obj2 mustEqual Vector3(0f, 0f, 0f) + } + + "scalar" in { + vec * 3f mustEqual Vector3(3.8999999f, -7.7999997f, 11.700001f) + } + } +} + diff --git a/common/src/test/scala/game/GenericObjectActionMessageTest.scala b/common/src/test/scala/game/GenericObjectActionMessageTest.scala new file mode 100644 index 000000000..8ad103886 --- /dev/null +++ b/common/src/test/scala/game/GenericObjectActionMessageTest.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2017 PSForever +package game + +import org.specs2.mutable._ +import net.psforever.packet._ +import net.psforever.packet.game._ +import scodec.bits._ + +class GenericObjectActionMessageTest extends Specification { + val string = hex"56 B501 24" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case GenericObjectActionMessage(object_guid, action) => + object_guid mustEqual PlanetSideGUID(437) + action mustEqual 36 + case _ => + ko + } + } + + "encode" in { + val msg = GenericObjectActionMessage(PlanetSideGUID(437), 36) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } +} diff --git a/common/src/test/scala/objects/ConverterTest.scala b/common/src/test/scala/objects/ConverterTest.scala index 90e92a44e..b4e5ec24a 100644 --- a/common/src/test/scala/objects/ConverterTest.scala +++ b/common/src/test/scala/objects/ConverterTest.scala @@ -40,16 +40,15 @@ class ConverterTest extends Specification { "convert to packet" in { val tdef = ToolDefinition(1076) tdef.Size = EquipmentSize.Rifle - tdef.AmmoTypes += Ammo.shotgun_shell - tdef.AmmoTypes += Ammo.shotgun_shell_AP + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP tdef.FireModes += new FireModeDefinition tdef.FireModes.head.AmmoTypeIndices += 0 tdef.FireModes.head.AmmoTypeIndices += 1 tdef.FireModes.head.AmmoSlotIndex = 0 + tdef.FireModes.head.Magazine = 30 val obj : Tool = Tool(tdef) - val box = AmmoBox(PlanetSideGUID(90), new AmmoBoxDefinition(Ammo.shotgun_shell.id)) - obj.AmmoSlots.head.Box = box - obj.AmmoSlots.head.Magazine = 30 + obj.AmmoSlot.Box.GUID = PlanetSideGUID(90) obj.Definition.Packet.DetailedConstructorData(obj) match { case Success(pkt) => @@ -139,24 +138,22 @@ class ConverterTest extends Specification { Give the Player's Holster (2) the Tool Place the remaining AmmoBox into the Player's inventory in the third slot (8) */ - val bullet_9mm = AmmoBoxDefinition(28) - bullet_9mm.Capacity = 50 - val box1 = AmmoBox(PlanetSideGUID(90), bullet_9mm) - val box2 = AmmoBox(PlanetSideGUID(91), bullet_9mm) val tdef = ToolDefinition(1076) tdef.Name = "sample_weapon" tdef.Size = EquipmentSize.Rifle - tdef.AmmoTypes += Ammo.bullet_9mm + tdef.AmmoTypes += GlobalDefinitions.bullet_9mm tdef.FireModes += new FireModeDefinition tdef.FireModes.head.AmmoTypeIndices += 0 tdef.FireModes.head.AmmoSlotIndex = 0 tdef.FireModes.head.Magazine = 18 - val tool = Tool(PlanetSideGUID(92), tdef) - tool.AmmoSlots.head.Box = box1 + val tool = Tool(tdef) + tool.GUID = PlanetSideGUID(92) + tool.AmmoSlot.Box.GUID = PlanetSideGUID(90) val obj = Player(PlanetSideGUID(93), "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) obj.Slot(2).Equipment = tool obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(94) - obj.Inventory += 8 -> box2 + obj.Inventory += 8 -> AmmoBox(GlobalDefinitions.bullet_9mm) + obj.Slot(8).Equipment.get.GUID = PlanetSideGUID(91) obj } val converter = new CharacterSelectConverter @@ -263,7 +260,7 @@ class ConverterTest extends Specification { val fury_weapon_systema_def = ToolDefinition(ObjectClass.fury_weapon_systema) fury_weapon_systema_def.Size = EquipmentSize.VehicleWeapon - fury_weapon_systema_def.AmmoTypes += Ammo.hellfire_ammo + fury_weapon_systema_def.AmmoTypes += GlobalDefinitions.hellfire_ammo fury_weapon_systema_def.FireModes += new FireModeDefinition fury_weapon_systema_def.FireModes.head.AmmoTypeIndices += 0 fury_weapon_systema_def.FireModes.head.AmmoSlotIndex = 0 diff --git a/common/src/test/scala/objects/EquipmentTest.scala b/common/src/test/scala/objects/EquipmentTest.scala index 284d0b2b7..02847c63c 100644 --- a/common/src/test/scala/objects/EquipmentTest.scala +++ b/common/src/test/scala/objects/EquipmentTest.scala @@ -10,7 +10,6 @@ import net.psforever.objects.GlobalDefinitions._ import org.specs2.mutable._ class EquipmentTest extends Specification { - "AmmoBox" should { "define" in { val obj = AmmoBoxDefinition(86) @@ -19,8 +18,8 @@ class EquipmentTest extends Specification { obj.AmmoType mustEqual Ammo.aphelion_immolation_cannon_ammo obj.Capacity mustEqual 300 - obj.Tile.width mustEqual InventoryTile.Tile44.width - obj.Tile.height mustEqual InventoryTile.Tile44.height + obj.Tile.Width mustEqual InventoryTile.Tile44.Width + obj.Tile.Height mustEqual InventoryTile.Tile44.Height obj.ObjectId mustEqual 86 } @@ -58,8 +57,8 @@ class EquipmentTest extends Specification { val obj = ToolDefinition(1076) obj.Name = "sample_weapon" obj.Size = EquipmentSize.Rifle - obj.AmmoTypes += Ammo.shotgun_shell - obj.AmmoTypes += Ammo.shotgun_shell_AP + obj.AmmoTypes += GlobalDefinitions.shotgun_shell + obj.AmmoTypes += GlobalDefinitions.shotgun_shell_AP obj.FireModes += new FireModeDefinition obj.FireModes.head.AmmoTypeIndices += 0 obj.FireModes.head.AmmoTypeIndices += 1 @@ -74,9 +73,10 @@ class EquipmentTest extends Specification { obj.FireModes(1).Magazine = 18 obj.Tile = InventoryTile.Tile93 obj.ObjectId mustEqual 1076 + obj.Name mustEqual "sample_weapon" - obj.AmmoTypes.head mustEqual Ammo.shotgun_shell - obj.AmmoTypes(1) mustEqual Ammo.shotgun_shell_AP + obj.AmmoTypes.head mustEqual GlobalDefinitions.shotgun_shell + obj.AmmoTypes(1) mustEqual GlobalDefinitions.shotgun_shell_AP obj.FireModes.head.AmmoTypeIndices.head mustEqual 0 obj.FireModes.head.AmmoTypeIndices(1) mustEqual 1 obj.FireModes.head.AmmoSlotIndex mustEqual 0 @@ -89,8 +89,8 @@ class EquipmentTest extends Specification { obj.FireModes(1).Chamber mustEqual 3 obj.FireModes(1).Magazine mustEqual 18 obj.FireModes(1).ResetAmmoIndexOnSwap mustEqual false - obj.Tile.width mustEqual InventoryTile.Tile93.width - obj.Tile.height mustEqual InventoryTile.Tile93.height + obj.Tile.Width mustEqual InventoryTile.Tile93.Width + obj.Tile.Height mustEqual InventoryTile.Tile93.Height } "construct" in { @@ -118,8 +118,8 @@ class EquipmentTest extends Specification { //explanation: sample_weapon has two fire modes; adjusting the FireMode changes between them val tdef = ToolDefinition(1076) tdef.Size = EquipmentSize.Rifle - tdef.AmmoTypes += Ammo.shotgun_shell - tdef.AmmoTypes += Ammo.shotgun_shell_AP + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP tdef.FireModes += new FireModeDefinition tdef.FireModes.head.AmmoTypeIndices += 0 tdef.FireModes.head.AmmoSlotIndex = 0 @@ -149,8 +149,8 @@ class EquipmentTest extends Specification { //explanation: obj has one fire mode and two ammunitions; adjusting the AmmoType changes between them val tdef = ToolDefinition(1076) tdef.Size = EquipmentSize.Rifle - tdef.AmmoTypes += Ammo.shotgun_shell - tdef.AmmoTypes += Ammo.shotgun_shell_AP + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell + tdef.AmmoTypes += GlobalDefinitions.shotgun_shell_AP tdef.FireModes += new FireModeDefinition tdef.FireModes.head.AmmoTypeIndices += 0 tdef.FireModes.head.AmmoTypeIndices += 1 @@ -168,14 +168,54 @@ class EquipmentTest extends Specification { obj.AmmoTypeIndex mustEqual 0 obj.AmmoType mustEqual Ammo.shotgun_shell } + + "multiple ammo types and multiple fire modes, split (Punisher)" in { + val obj = Tool(GlobalDefinitions.punisher) + //ammo = 0, fmode = 0 + obj.FireModeIndex mustEqual 0 + obj.AmmoTypeIndex mustEqual 0 + obj.AmmoType mustEqual Ammo.bullet_9mm + //ammo = 2, fmode = 1 + obj.NextFireMode + obj.FireModeIndex mustEqual 1 + obj.AmmoTypeIndex mustEqual 2 + obj.AmmoType mustEqual Ammo.rocket + //ammo = 3, fmode = 1 + obj.NextAmmoType + obj.AmmoTypeIndex mustEqual 3 + obj.AmmoType mustEqual Ammo.frag_cartridge + //ammo = 4, fmode = 1 + obj.NextAmmoType + obj.AmmoTypeIndex mustEqual 4 + obj.AmmoType mustEqual Ammo.jammer_cartridge + //ammo = 0, fmode = 0 + obj.NextFireMode + obj.FireModeIndex mustEqual 0 + obj.AmmoTypeIndex mustEqual 0 + obj.AmmoType mustEqual Ammo.bullet_9mm + //ammo = 1, fmode = 0 + obj.NextAmmoType + obj.AmmoTypeIndex mustEqual 1 + obj.AmmoType mustEqual Ammo.bullet_9mm_AP + //ammo = 5, fmode = 1 + obj.NextFireMode + obj.NextAmmoType + obj.FireModeIndex mustEqual 1 + obj.AmmoTypeIndex mustEqual 5 + obj.AmmoType mustEqual Ammo.plasma_cartridge + //ammo = 2, fmode = 1 + obj.NextAmmoType + obj.AmmoTypeIndex mustEqual 2 + obj.AmmoType mustEqual Ammo.rocket + } } "Kit" should { "define" in { val sample = KitDefinition(Kits.medkit) sample.ObjectId mustEqual medkit.ObjectId - sample.Tile.width mustEqual medkit.Tile.width - sample.Tile.height mustEqual medkit.Tile.height + sample.Tile.Width mustEqual medkit.Tile.Width + sample.Tile.Height mustEqual medkit.Tile.Height } "construct" in { @@ -200,8 +240,8 @@ class EquipmentTest extends Specification { sample.Modes.head mustEqual DeployedItem.tank_traps sample.Modes(1) mustEqual DeployedItem.portable_manned_turret_tr sample.Modes(2) mustEqual DeployedItem.deployable_shield_generator - sample.Tile.width mustEqual InventoryTile.Tile63.width - sample.Tile.height mustEqual InventoryTile.Tile63.height + sample.Tile.Width mustEqual InventoryTile.Tile63.Width + sample.Tile.Height mustEqual InventoryTile.Tile63.Height } "construct" in { diff --git a/common/src/test/scala/objects/InventoryTest.scala b/common/src/test/scala/objects/InventoryTest.scala index 3af8a011d..34ec74914 100644 --- a/common/src/test/scala/objects/InventoryTest.scala +++ b/common/src/test/scala/objects/InventoryTest.scala @@ -57,8 +57,8 @@ class InventoryTest extends Specification { val obj : GridInventory = GridInventory(9, 6) obj += 0 -> bullet9mmBox1 obj.Capacity mustEqual 45 - val w = bullet9mmBox2.Tile.width - val h = bullet9mmBox2.Tile.height + val w = bullet9mmBox2.Tile.Width + val h = bullet9mmBox2.Tile.Height val list0 = obj.CheckCollisionsAsList(0, w, h) list0 match { case scala.util.Success(list) => list.length mustEqual 1 @@ -91,8 +91,8 @@ class InventoryTest extends Specification { val obj : GridInventory = GridInventory(9, 6) obj += 3 -> bullet9mmBox1 obj.Capacity mustEqual 45 - val w = bullet9mmBox2.Tile.width - val h = bullet9mmBox2.Tile.height + val w = bullet9mmBox2.Tile.Width + val h = bullet9mmBox2.Tile.Height val list0 = obj.CheckCollisionsAsList(3, w, h) list0 match { case scala.util.Success(list) => list.length mustEqual 1 @@ -125,8 +125,8 @@ class InventoryTest extends Specification { val obj : GridInventory = GridInventory(9, 6) obj += 0 -> bullet9mmBox1 obj.Capacity mustEqual 45 - val w = bullet9mmBox2.Tile.width - val h = bullet9mmBox2.Tile.height + val w = bullet9mmBox2.Tile.Width + val h = bullet9mmBox2.Tile.Height val list0 = obj.CheckCollisionsAsList(0, w, h) list0 match { case scala.util.Success(list) => list.length mustEqual 1 @@ -159,8 +159,8 @@ class InventoryTest extends Specification { val obj : GridInventory = GridInventory(9, 6) obj += 27 -> bullet9mmBox1 obj.Capacity mustEqual 45 - val w = bullet9mmBox2.Tile.width - val h = bullet9mmBox2.Tile.height + val w = bullet9mmBox2.Tile.Width + val h = bullet9mmBox2.Tile.Height val list0 = obj.CheckCollisionsAsList(27, w, h) list0 match { case scala.util.Success(list) => list.length mustEqual 1 @@ -205,8 +205,8 @@ class InventoryTest extends Specification { val obj : GridInventory = GridInventory(12, 9) obj += 39 -> bullet9mmBox1 obj.Capacity mustEqual 99 //108 - 9 - val w = bullet9mmBox2.Tile.width - val h = bullet9mmBox2.Tile.height + val w = bullet9mmBox2.Tile.Width + val h = bullet9mmBox2.Tile.Height val list0 = obj.CheckCollisionsAsList(0, w, h) list0 match { case scala.util.Success(list) => list.isEmpty mustEqual true diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala new file mode 100644 index 000000000..5005661f5 --- /dev/null +++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala @@ -0,0 +1,110 @@ +// Copyright (c) 2017 PSForever +package objects + +import akka.actor.{ActorRef, Props} +import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.objects.vehicles.VehicleControl +import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} +import org.specs2.mutable.Specification + +import scala.concurrent.duration.Duration + +class VehicleSpawnPadTest extends Specification { + "VehicleSpawnPadDefinition" should { + "define" in { + GlobalDefinitions.spawn_pad.ObjectId mustEqual 800 + } + } + + "VehicleSpawnPad" should { + "construct" in { + val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) + obj.Actor mustEqual ActorRef.noSender + obj.Definition mustEqual GlobalDefinitions.spawn_pad + } + } +} + +class VehicleSpawnControl1Test extends ActorTest() { + "VehicleSpawnControl" should { + "construct" in { + val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) + obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") + assert(obj.Actor != ActorRef.noSender) + } + } +} + +class VehicleSpawnControl2Test extends ActorTest() { + "VehicleSpawnControl" should { + "spawn a vehicle" in { + val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) + obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + player.Spawn + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(1) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle") + + obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + val reply = receiveOne(Duration.create(10000, "ms")) + assert(reply == VehicleSpawnPad.ConcealPlayer) //explicit: isInstanceOf does not work + + val reply2 = receiveOne(Duration.create(10000, "ms")) + assert(reply2.isInstanceOf[VehicleSpawnPad.LoadVehicle]) + assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].vehicle == vehicle) + assert(reply2.asInstanceOf[VehicleSpawnPad.LoadVehicle].pad == obj) + + player.VehicleOwned = vehicle + val reply3 = receiveOne(Duration.create(10000, "ms")) + assert(reply3.isInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle]) + assert(reply3.asInstanceOf[VehicleSpawnPad.PlayerSeatedInVehicle].vehicle == vehicle) + + val reply4 = receiveOne(Duration.create(10000, "ms")) + assert(reply4.isInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning]) + assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].vehicle == vehicle) + assert(reply4.asInstanceOf[VehicleSpawnPad.SpawnPadBlockedWarning].warning_count > 0) + + vehicle.Position = Vector3(11f, 0f, 0f) //greater than 10m + val reply5 = receiveOne(Duration.create(10000, "ms")) + assert(reply5.isInstanceOf[VehicleSpawnPad.SpawnPadUnblocked]) + assert(reply5.asInstanceOf[VehicleSpawnPad.SpawnPadUnblocked].vehicle_guid == vehicle.GUID) + } + } +} + +class VehicleSpawnControl3Test extends ActorTest() { + "VehicleSpawnControl" should { + "not spawn a vehicle if player is dead" in { + val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) + obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(1) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle") + + obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + val reply = receiveOne(Duration.create(5000, "ms")) + assert(reply == null) + } + } +} + +class VehicleSpawnControl4Test extends ActorTest() { + "VehicleSpawnControl" should { + "not spawn a vehicle if vehicle Actor is missing" in { + val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) + obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "door") + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + player.Spawn + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + vehicle.GUID = PlanetSideGUID(1) + + obj.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) + val reply = receiveOne(Duration.create(5000, "ms")) + assert(reply == null) + } + } +} diff --git a/common/src/test/scala/objects/VehicleTest.scala b/common/src/test/scala/objects/VehicleTest.scala index d898a66e5..a61ad4312 100644 --- a/common/src/test/scala/objects/VehicleTest.scala +++ b/common/src/test/scala/objects/VehicleTest.scala @@ -44,8 +44,8 @@ class VehicleTest extends Specification { fury.Weapons.size mustEqual 1 fury.Weapons.get(0) mustEqual None fury.Weapons.get(1) mustEqual Some(GlobalDefinitions.fury_weapon_systema) - fury.TrunkSize.width mustEqual 11 - fury.TrunkSize.height mustEqual 11 + fury.TrunkSize.Width mustEqual 11 + fury.TrunkSize.Height mustEqual 11 fury.TrunkOffset mustEqual 30 } } diff --git a/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala new file mode 100644 index 000000000..ab1bcca82 --- /dev/null +++ b/common/src/test/scala/objects/terminal/AirVehicleTerminalTest.scala @@ -0,0 +1,37 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.ActorRef +import net.psforever.objects.{GlobalDefinitions, Player, Tool} +import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} +import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} +import org.specs2.mutable.Specification + +class AirVehicleTerminalTest extends Specification { + "Air_Vehicle_Terminal" should { + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + + "construct" in { + val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) + terminal.Actor mustEqual ActorRef.noSender + } + + "player can buy a reaver ('lightgunship')" in { + val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) + reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] + reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship + reply2.loadout mustEqual Nil //TODO + } + + "player can not buy a fake vehicle ('reaver')" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "reaver", 0, PlanetSideGUID(0)) + + terminal.Request(player, msg) mustEqual Terminal.NoDeal() + } + } +} diff --git a/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala new file mode 100644 index 000000000..3446ec951 --- /dev/null +++ b/common/src/test/scala/objects/terminal/DropshipVehicleTerminalTest.scala @@ -0,0 +1,37 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.ActorRef +import net.psforever.objects.{GlobalDefinitions, Player} +import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} +import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} +import org.specs2.mutable.Specification + +class DropshipVehicleTerminalTest extends Specification { + "Dropship_Vehicle_Terminal" should { + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + + "construct" in { + val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) + terminal.Actor mustEqual ActorRef.noSender + } + + "player can buy a galaxy ('dropship')" in { + val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "dropship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) + reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] + reply2.vehicle.Definition mustEqual GlobalDefinitions.dropship + reply2.loadout mustEqual Nil //TODO + } + + "player can not buy a fake vehicle ('galaxy')" in { + val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "galaxy", 0, PlanetSideGUID(0)) + + terminal.Request(player, msg) mustEqual Terminal.NoDeal() + } + } +} diff --git a/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala new file mode 100644 index 000000000..0a56463b9 --- /dev/null +++ b/common/src/test/scala/objects/terminal/GroundVehicleTerminalTest.scala @@ -0,0 +1,37 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.ActorRef +import net.psforever.objects.{GlobalDefinitions, Player} +import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} +import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} +import org.specs2.mutable.Specification + +class GroundVehicleTerminalTest extends Specification { + "Ground_Vehicle_Terminal" should { + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + + "construct" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + terminal.Actor mustEqual ActorRef.noSender + } + + "player can buy a harasser ('two_man_assault_buggy')" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) + reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] + reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy + reply2.loadout mustEqual Nil //TODO + } + + "player can not buy a fake vehicle ('harasser')" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) + + terminal.Request(player, msg) mustEqual Terminal.NoDeal() + } + } +} diff --git a/common/src/test/scala/objects/terminal/TerminalControlTest.scala b/common/src/test/scala/objects/terminal/TerminalControlTest.scala index c5d8b0624..4d587292e 100644 --- a/common/src/test/scala/objects/terminal/TerminalControlTest.scala +++ b/common/src/test/scala/objects/terminal/TerminalControlTest.scala @@ -71,3 +71,40 @@ class CertTerminalControl3Test extends ActorTest() { assert(reply2.response == Terminal.SellCertification(CertificationType.MediumAssault, 2)) } } + +class VehicleTerminalControl1Test extends ActorTest() { + "TerminalControl can be used to buy a vehicle ('two_man_assault_buggy')" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) + + terminal.Actor ! Terminal.Request(player, msg) + val reply = receiveOne(Duration.create(500, "ms")) + assert(reply.isInstanceOf[Terminal.TerminalMessage]) + val reply2 = reply.asInstanceOf[Terminal.TerminalMessage] + assert(reply2.player == player) + assert(reply2.msg == msg) + assert(reply2.response.isInstanceOf[Terminal.BuyVehicle]) + val reply3 = reply2.response.asInstanceOf[Terminal.BuyVehicle] + assert(reply3.vehicle.Definition == GlobalDefinitions.two_man_assault_buggy) + assert(reply3.loadout == Nil) //TODO + } +} + +class VehicleTerminalControl2Test extends ActorTest() { + "TerminalControl can be used to warn about not buy a vehicle ('harasser')" in { + val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) + terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-cert-term") + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) + + terminal.Actor ! Terminal.Request(player, msg) + val reply = receiveOne(Duration.create(500, "ms")) + assert(reply.isInstanceOf[Terminal.TerminalMessage]) + val reply2 = reply.asInstanceOf[Terminal.TerminalMessage] + assert(reply2.player == player) + assert(reply2.msg == msg) + assert(reply2.response == Terminal.NoDeal()) + } +} diff --git a/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala new file mode 100644 index 000000000..722ec6adf --- /dev/null +++ b/common/src/test/scala/objects/terminal/VehicleTerminalCombinedTest.scala @@ -0,0 +1,47 @@ +// Copyright (c) 2017 PSForever +package objects.terminal + +import akka.actor.ActorRef +import net.psforever.objects.{GlobalDefinitions, Player} +import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} +import net.psforever.types.{CharacterGender, PlanetSideEmpire, TransactionType} +import org.specs2.mutable.Specification + +class VehicleTerminalCombinedTest extends Specification { + "Ground_Vehicle_Terminal" should { + val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + + "construct" in { + val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) + terminal.Actor mustEqual ActorRef.noSender + } + + "player can buy a ground vehicle, the harasser ('two_man_assault_buggy')" in { + val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) + reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] + reply2.vehicle.Definition mustEqual GlobalDefinitions.two_man_assault_buggy + reply2.loadout mustEqual Nil //TODO + } + + "player can buy a flying vehicle, the reaver ('lightgunship')" in { + val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "lightgunship", 0, PlanetSideGUID(0)) + val reply = terminal.Request(player, msg) + reply.isInstanceOf[Terminal.BuyVehicle] mustEqual true + val reply2 = reply.asInstanceOf[Terminal.BuyVehicle] + reply2.vehicle.Definition mustEqual GlobalDefinitions.lightgunship + reply2.loadout mustEqual Nil //TODO + } + + "player can not buy a fake vehicle ('harasser')" in { + val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) + val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) + + terminal.Request(player, msg) mustEqual Terminal.NoDeal() + } + } +} diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index 870f402ee..e63ef0a16 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -14,7 +14,8 @@ import com.typesafe.config.ConfigFactory import net.psforever.crypto.CryptoInterface import net.psforever.objects.zones._ import net.psforever.objects.guid.TaskResolver -import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockObjectBuilder, TerminalObjectBuilder} +import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockObjectBuilder, TerminalObjectBuilder, VehicleSpawnPadObjectBuilder} +import net.psforever.types.Vector3 import org.slf4j import org.fusesource.jansi.Ansi._ import org.fusesource.jansi.Ansi.Color._ @@ -244,6 +245,10 @@ object PsLogin { LocalObject(TerminalObjectBuilder(order_terminal, 853)) LocalObject(TerminalObjectBuilder(order_terminal, 855)) LocalObject(TerminalObjectBuilder(order_terminal, 860)) + LocalObject(TerminalObjectBuilder(ground_vehicle_terminal, 1063)) + LocalObject(VehicleSpawnPadObjectBuilder(spawn_pad, 500)) //TODO guid not correct + LocalObject(TerminalObjectBuilder(dropship_vehicle_terminal, 304)) + LocalObject(VehicleSpawnPadObjectBuilder(spawn_pad, 501)) //TODO guid not correct LocalBases = 30 @@ -251,8 +256,14 @@ object PsLogin { ObjectToBase(332, 29) ObjectToBase(556, 29) ObjectToBase(558, 29) + ObjectToBase(1063, 29) //TODO unowned courtyard terminal? + ObjectToBase(500, 29) //TODO unowned courtyard spawnpad? + ObjectToBase(304, 29) //TODO unowned courtyard terminal? + ObjectToBase(501, 29) //TODO unowned courtyard spawnpad? DoorToLock(330, 558) DoorToLock(332, 556) + TerminalToSpawnPad(1063, 500) + TerminalToSpawnPad(304, 501) } val home3 = new Zone("home3", map13, 13) { override def Init(implicit context : ActorContext) : Unit = { @@ -261,6 +272,19 @@ object PsLogin { import net.psforever.types.PlanetSideEmpire Base(2).get.Faction = PlanetSideEmpire.VS //HART building C Base(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower + + GUID(500) match { + case Some(pad) => + pad.Position = Vector3(3506.0f, 2820.0f, 92.0f) + pad.Orientation = Vector3(0f, 0f, 270.0f) + case None => ; + } + GUID(501) match { + case Some(pad) => + pad.Position = Vector3(3508.9844f, 2895.961f, 92.296875f) + pad.Orientation = Vector3(0f, 0f, 270.0f) + case None => ; + } } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9c40a2800..2808cc1cd 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -17,8 +17,9 @@ import net.psforever.objects.inventory.{GridInventory, InventoryItem} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.locks.IFFLock +import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.terminals.Terminal -import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, VehicleLockState} +import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState} import net.psforever.objects.zones.{InterstellarCluster, Zone} import net.psforever.packet.game.objectcreate._ import net.psforever.types._ @@ -45,25 +46,43 @@ class WorldSessionActor extends Actor with MDCContextAware { var continent : Zone = null var progressBarValue : Option[Float] = None - var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable - var progressBarUpdate : Cancellable = WorldSessionActor.DefaultCancellable + var clientKeepAlive : Cancellable = DefaultCancellable.obj + var progressBarUpdate : Cancellable = DefaultCancellable.obj override def postStop() = { if(clientKeepAlive != null) clientKeepAlive.cancel() + localService ! Service.Leave() + vehicleService ! Service.Leave() + avatarService ! Service.Leave() + LivePlayerList.Remove(sessionId) match { + case Some(tplayer) => + tplayer.VehicleSeated match { + case Some(vehicle_guid) => + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true)) + case None => ; + } + tplayer.VehicleOwned match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) match { + case Some(vehicle : Vehicle) => + vehicle.Owner = None + //TODO temporary solution; to un-own, permit driver seat to Empire access level + vehicle.PermissionGroup(10, VehicleLockState.Empire.id) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(tplayer.GUID, vehicle_guid, 10, VehicleLockState.Empire.id)) + case _ => ; + } + case None => ; + } - avatarService ! Service.Leave() - localService ! Service.Leave() - vehicleService ! Service.Leave() - LivePlayerList.Remove(sessionId) match { - case Some(tplayer) => - if(tplayer.HasGUID) { - val guid = tplayer.GUID - avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid)) - taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID) - //TODO normally, the actual player avatar persists a minute or so after the user disconnects - } - case None => ; + if(tplayer.HasGUID) { + val guid = tplayer.GUID + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid)) + taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID) + //TODO normally, the actual player avatar persists a minute or so after the user disconnects + } + + case None => ; } } @@ -124,6 +143,11 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(guid, suit, subtype))) } + case AvatarResponse.ConcealPlayer() => + if(player.GUID != guid) { + sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(guid, 36))) + } + case AvatarResponse.EquipmentInHand(slot, item) => if(player.GUID != guid) { val definition = item.Definition @@ -186,7 +210,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } else { val before = player.lastSeenStreamMessage(guid.guid) - val dist = WorldSessionActor.DistanceSquared(player.Position, msg.pos) + val dist = Vector3.DistanceSquared(player.Position, msg.pos) (msg.pos, now - before, dist) } @@ -280,9 +304,6 @@ class WorldSessionActor extends Actor with MDCContextAware { case VehicleResponse.MountVehicle(vehicle_guid, seat) => if(player.GUID != guid) { sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, guid, seat))) - if(player.VehicleOwned.contains(vehicle_guid)) { //simplistic vehicle ownership management - player.VehicleOwned = None - } } case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) => @@ -524,6 +545,19 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false))) } + case Terminal.BuyVehicle(vehicle, loadout) => + continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match { + case Some(pad_guid) => + val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad] + vehicle.Faction = tplayer.Faction + vehicle.Position = pad.Position + vehicle.Orientation = pad.Orientation + taskResolver ! RegisterNewVehicle(vehicle, pad) + sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true))) + case None => + log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it") + } + case Terminal.NoDeal() => log.warn(s"$tplayer made a request but the terminal rejected the order $msg") sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))) @@ -533,9 +567,21 @@ class WorldSessionActor extends Actor with MDCContextAware { reply match { case Vehicle.CanSeatPlayer(vehicle, seat_num) => log.info(s"MountVehicleMsg: ${player.GUID} mounts ${vehicle.GUID} @ $seat_num") + vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle.GUID) //clear all deconstruction timers val vehicle_guid : PlanetSideGUID = vehicle.GUID tplayer.VehicleSeated = Some(vehicle_guid) if(seat_num == 0) { //simplistic vehicle ownership management + vehicle.Owner match { + case Some(owner_guid) => + continent.GUID(owner_guid) match { + case Some(previous_owner : Player) => + if(previous_owner.VehicleOwned.contains(vehicle_guid)) { + previous_owner.VehicleOwned = None //simplistic ownership management, player loses vehicle ownership + } + case _ => ; + } + case None => ; + } player.VehicleOwned = Some(vehicle_guid) vehicle.Owner = Some(player.GUID) } @@ -558,12 +604,52 @@ class WorldSessionActor extends Actor with MDCContextAware { vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, vehicle_guid, seat_num)) case Vehicle.CannotSeatPlayer(vehicle, seat_num) => - val seat : Seat = vehicle.Seat(seat_num).get log.warn(s"MountVehicleMsg: player $tplayer attempted to board vehicle ${vehicle.GUID}'s seat $seat_num, but was not allowed") case _ => ; } + case VehicleSpawnPad.ConcealPlayer => + sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(player.GUID, 36))) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ConcealPlayer(player.GUID)) + + case VehicleSpawnPad.LoadVehicle(vehicle, _/*pad*/) => + val player_guid = player.GUID + val definition = vehicle.Definition + val objedtId = definition.ObjectId + val vehicle_guid = vehicle.GUID + val vdata = definition.Packet.ConstructorData(vehicle).get + sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(objedtId, vehicle_guid, vdata))) + continent.Transport ! Zone.SpawnVehicle(vehicle) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata)) + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 22, 1L))) //mount points off? + //sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid))) //fte and ownership? + //sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, player_guid, 0))) + vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //temporary drive away from pad delay + vehicle.Actor ! Vehicle.TrySeatPlayer(0, player) + + case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) => + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //sitting in the vehicle clears the drive away delay + val vehicle_guid = vehicle.GUID + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 22, 0L))) //mount points on? + //sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 0, vehicle.Definition.MaxHealth))) + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 68, 0L))) //??? + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(vehicle_guid, 113, 0L))) //??? + ReloadVehicleAccessPermissions(vehicle) + + case VehicleSpawnPad.SpawnPadBlockedWarning(vehicle, warning_count) => + if(warning_count > 2) { + sendResponse(PacketCoding.CreateGamePacket(0, TriggerSoundMessage(TriggeredSound.Unknown14, vehicle.Position, 20, 1f))) + sendResponse(PacketCoding.CreateGamePacket(0, + ChatMsg(ChatMessageType.CMT_TELL, true, "", "\\#FYour vehicle is blocking the spawn pad, and will be deconstructed if not moved.", None)) + ) + } + + case VehicleSpawnPad.SpawnPadUnblocked(vehicle_guid) => + //vehicle has moved away from spawn pad after initial spawn + vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel temporary drive away from pad delay + case ListAccountCharacters => import net.psforever.objects.definition.converter.CharacterSelectConverter val gen : AtomicInteger = new AtomicInteger(1) @@ -605,15 +691,8 @@ class WorldSessionActor extends Actor with MDCContextAware { failWithError(s"$tplayer failed to load anywhere") } - case VehicleLoaded(vehicle) => - val definition = vehicle.Definition - val objedtId = definition.ObjectId - val vehicle_guid = vehicle.GUID - val vdata = definition.Packet.ConstructorData(vehicle).get - sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(objedtId, vehicle_guid, vdata))) - ReloadVehicleAccessPermissions(vehicle) - continent.Transport ! Zone.SpawnVehicle(vehicle) - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player.GUID, vehicle, objedtId, vehicle_guid, vdata)) + case VehicleLoaded(_/*vehicle*/) => ; + //currently being handled by VehicleSpawnPad.LoadVehicle during testing phase case Zone.ClientInitialization(/*initList*/_) => //TODO iterate over initList; for now, just do this @@ -795,7 +874,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } var player : Player = null - var harasser : Vehicle = null //TODO used in testing def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => @@ -803,18 +881,9 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info(s"New world login to $server with Token:$token. $clientVersion") //TODO begin temp player character auto-loading; remove later import net.psforever.objects.GlobalDefinitions._ - val - beamer1 = Tool(beamer) - beamer1.AmmoSlots.head.Box = AmmoBox(energy_cell, 16) - val - suppressor1 = Tool(suppressor) - suppressor1.AmmoSlots.head.Box = AmmoBox(bullet_9mm, 25) - val - forceblade1 = Tool(forceblade) - forceblade1.AmmoSlots.head.Box = AmmoBox(melee_ammo) - player = Player("TestCharacter"+sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) - player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) + //player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) + player.Position = Vector3(3523.039f, 2855.5078f, 90.859375f) player.Orientation = Vector3(0f, 0f, 90f) player.Certifications += CertificationType.StandardAssault player.Certifications += CertificationType.MediumAssault @@ -823,9 +892,24 @@ class WorldSessionActor extends Actor with MDCContextAware { player.Certifications += CertificationType.ReinforcedExoSuit player.Certifications += CertificationType.ATV player.Certifications += CertificationType.Harasser - player.Slot(0).Equipment = beamer1 - player.Slot(2).Equipment = suppressor1 - player.Slot(4).Equipment = forceblade1 + // + player.Certifications += CertificationType.GroundSupport + player.Certifications += CertificationType.GroundTransport + player.Certifications += CertificationType.Flail + player.Certifications += CertificationType.Switchblade + player.Certifications += CertificationType.AssaultBuggy + player.Certifications += CertificationType.ArmoredAssault1 + player.Certifications += CertificationType.ArmoredAssault2 + player.Certifications += CertificationType.AirCavalryScout + player.Certifications += CertificationType.AirCavalryAssault + player.Certifications += CertificationType.AirCavalryInterceptor + player.Certifications += CertificationType.AirSupport + player.Certifications += CertificationType.GalaxyGunship + player.Certifications += CertificationType.Phantasm + //player.ExoSuit = ExoSuitType.Infiltrator + player.Slot(0).Equipment = Tool(beamer) + player.Slot(2).Equipment = Tool(suppressor) + player.Slot(4).Equipment = Tool(forceblade) player.Slot(6).Equipment = AmmoBox(bullet_9mm) player.Slot(9).Equipment = AmmoBox(bullet_9mm) player.Slot(12).Equipment = AmmoBox(bullet_9mm) @@ -917,21 +1001,6 @@ class WorldSessionActor extends Actor with MDCContextAware { }) ReloadVehicleAccessPermissions(vehicle) }) - //TODO begin temp vehicle auto-loading - import net.psforever.objects.GlobalDefinitions._ - if(continent.Vehicles.isEmpty) { - harasser = Vehicle(two_man_assault_buggy) - harasser.Position = Vector3(3674.8438f, 2730.789f, 91.15625f) - harasser.Faction = PlanetSideEmpire.VS - harasser.Orientation = Vector3(0f, 0f, 90f) - harasser.Weapons(2).Equipment.get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(bullet_12mm, 150) - harasser.Trunk += 30 -> AmmoBox(bullet_12mm, 100) - taskResolver ! RegisterNewVehicle(harasser) - } - else { - harasser = continent.Vehicles.head //subsequent players after first - } - //TODO end temp vehicle auto-loading avatarService ! Service.Join(player.Continent) localService ! Service.Join(player.Continent) vehicleService ! Service.Join(player.Continent) @@ -1129,6 +1198,7 @@ class WorldSessionActor extends Actor with MDCContextAware { continent.GUID(object_guid) match { case Some(vehicle : Vehicle) => if(player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) { + vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(object_guid) vehicleService ! VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) log.info(s"RequestDestroy: vehicle $object_guid") } @@ -1157,14 +1227,14 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(object_guid, 0))) log.info("ObjectDelete: " + msg) - case msg @ MoveItemMessage(item_guid, avatar_guid_1, avatar_guid_2, dest, unk1) => + case msg @ MoveItemMessage(item_guid, source_guid, destination_guid, dest, unk1) => player.Find(item_guid) match { case Some(index) => val indexSlot = player.Slot(index) - var itemOpt = indexSlot.Equipment //use this to short circuit + var itemOpt : Option[Equipment] = indexSlot.Equipment + //use this to short circuit val item = itemOpt.get val destSlot = player.Slot(dest) - val destItem = if((-1 < dest && dest < 5) || dest == Player.FreeHandSlot) { destSlot.Equipment match { case Some(found) => @@ -1181,20 +1251,21 @@ class WorldSessionActor extends Actor with MDCContextAware { case Success(_) | scala.util.Failure(_) => itemOpt = None; None //abort item move altogether } } - if(itemOpt.isDefined) { - log.info(s"MoveItem: $item_guid moved from $avatar_guid_1 @ $index to $avatar_guid_1 @ $dest") + log.info(s"MoveItem: $item_guid moved from $source_guid @ $index to $source_guid @ $dest") indexSlot.Equipment = None - destItem match { //do we have a swap item? + destItem match { + //do we have a swap item? case Some(entry) => //yes, swap val item2 = entry.obj player.Slot(entry.start).Equipment = None //remove item2 to make room for item destSlot.Equipment = item //in case dest and index could block each other (indexSlot.Equipment = entry.obj) match { case Some(_) => //item and item2 swapped places successfully - log.info(s"MoveItem: ${item2.GUID} swapped to $avatar_guid_1 @ $index") + log.info(s"MoveItem: ${item2.GUID} swapped to $source_guid @ $index") //we must shuffle items around cleanly to avoid causing icons to "disappear" - if(index == Player.FreeHandSlot) { //temporarily put in safe location, A -> C + if(index == Player.FreeHandSlot) { + //temporarily put in safe location, A -> C sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f))) //ground } else { @@ -1217,13 +1288,13 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => //just move item over destSlot.Equipment = item } - sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(avatar_guid_1, item_guid, dest))) + sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(source_guid, item_guid, dest))) if(0 <= dest && dest < 5) { avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(player.GUID, dest, item)) } } case None => - log.info(s"MoveItem: $avatar_guid_1 wanted to move the item $item_guid but could not find it") + log.info(s"MoveItem: $source_guid wanted to move the item $item_guid but could not find it") } case msg @ ChangeAmmoMessage(item_guid, unk1) => @@ -1384,6 +1455,9 @@ class WorldSessionActor extends Actor with MDCContextAware { player.VehicleSeated = None sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2))) //should be safe; replace with ObjectDetachMessage later vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2)) + if(obj.Seats.count(seat => seat.isOccupied) == 0) { + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m) + } } case None => log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid") @@ -1409,6 +1483,9 @@ class WorldSessionActor extends Actor with MDCContextAware { seat.Occupant = None tplayer.VehicleSeated = None vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2)) + if(obj.Seats.count(seat => seat.isOccupied) == 0) { + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m) + } case None => log.warn(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid") } @@ -1733,12 +1810,16 @@ class WorldSessionActor extends Actor with MDCContextAware { * @see `RegisterVehicle` * @return a `TaskResolver.GiveTask` message */ - def RegisterNewVehicle(obj : Vehicle) : TaskResolver.GiveTask = { + def RegisterNewVehicle(obj : Vehicle, pad : VehicleSpawnPad) : TaskResolver.GiveTask = { TaskResolver.GiveTask( new Task() { private val localVehicle = obj private val localAnnounce = vehicleService private val localSession : String = sessionId.toString + private val localPad = pad.Actor + private val localPlayer = player + private val localVehicleService = vehicleService + private val localZone = continent override def isComplete : Task.Resolution.Value = { if(localVehicle.Actor != ActorRef.noSender) { @@ -1751,6 +1832,8 @@ class WorldSessionActor extends Actor with MDCContextAware { def Execute(resolver : ActorRef) : Unit = { localAnnounce ! VehicleServiceMessage.GiveActorControl(obj, localSession) + localPad ! VehicleSpawnPad.VehicleOrder(localPlayer, localVehicle) + localVehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(localVehicle, localZone, 60L) resolver ! scala.util.Success(this) } }, List(RegisterVehicle(obj))) @@ -1945,35 +2028,4 @@ object WorldSessionActor { delta : Float, completeAction : () => Unit, tickAction : Option[() => Unit] = None) - /** - * A placeholder `Cancellable` object. - */ - private final val DefaultCancellable = new Cancellable() { - def cancel : Boolean = true - def isCancelled() : Boolean = true - } - - /** - * Calculate the actual distance between two points. - * @param pos1 the first point - * @param pos2 the second point - * @return the distance - */ - def Distance(pos1 : Vector3, pos2 : Vector3) : Float = { - math.sqrt(DistanceSquared(pos1, pos2)).toFloat - } - - /** - * Calculate the squared distance between two points. - * Though some time is saved care must be taken that any comparative distance is also squared. - * @param pos1 the first point - * @param pos2 the second point - * @return the distance - */ - def DistanceSquared(pos1 : Vector3, pos2 : Vector3) : Float = { - val dx : Float = pos1.x - pos2.x - val dy : Float = pos1.y - pos2.y - val dz : Float = pos1.z - pos2.z - (dx * dx) + (dy * dy) + (dz * dz) - } } diff --git a/pslogin/src/main/scala/services/avatar/AvatarAction.scala b/pslogin/src/main/scala/services/avatar/AvatarAction.scala index 451b38732..3b9f54e65 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarAction.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarAction.scala @@ -10,6 +10,7 @@ object AvatarAction { trait Action final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action + final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Action final case class EquipmentInHand(player_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item : Equipment) extends Action diff --git a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala index 87f37e841..75763c8c8 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala @@ -10,6 +10,7 @@ object AvatarResponse { trait Response final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response + final case class ConcealPlayer() extends Response //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response final case class EquipmentInHand(slot : Int, item : Equipment) extends Response final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item : Equipment) extends Response diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala index 0e3c6f16f..5148b2733 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarService.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala @@ -33,6 +33,10 @@ class AvatarService extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype)) ) + case AvatarAction.ConcealPlayer(player_guid) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer()) + ) case AvatarAction.EquipmentInHand(player_guid, slot, obj) => AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(slot, obj)) diff --git a/pslogin/src/main/scala/services/local/support/DoorCloseActor.scala b/pslogin/src/main/scala/services/local/support/DoorCloseActor.scala index a2ca622cf..10413da44 100644 --- a/pslogin/src/main/scala/services/local/support/DoorCloseActor.scala +++ b/pslogin/src/main/scala/services/local/support/DoorCloseActor.scala @@ -2,6 +2,7 @@ package services.local.support import akka.actor.{Actor, Cancellable} +import net.psforever.objects.DefaultCancellable import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.zones.Zone import net.psforever.packet.game.PlanetSideGUID @@ -16,7 +17,7 @@ import scala.concurrent.duration._ */ class DoorCloseActor() extends Actor { /** The periodic `Executor` that checks for doors to be closed */ - private var doorCloserTrigger : Cancellable = DoorCloseActor.DefaultCloser + private var doorCloserTrigger : Cancellable = DefaultCancellable.obj /** A `List` of currently open doors */ private var openDoors : List[DoorCloseActor.DoorEntry] = Nil //private[this] val log = org.log4s.getLogger @@ -98,11 +99,6 @@ object DoorCloseActor { /** The wait before an open door closes; as a `FiniteDuration` for `Executor` simplicity */ private final val timeout : FiniteDuration = timeout_time nanoseconds - private final val DefaultCloser : Cancellable = new Cancellable() { - override def cancel : Boolean = true - override def isCancelled : Boolean = true - } - /** * Message that carries information about a door that has been opened. * @param door the door object diff --git a/pslogin/src/main/scala/services/local/support/HackClearActor.scala b/pslogin/src/main/scala/services/local/support/HackClearActor.scala index 76a7e7f92..52a402804 100644 --- a/pslogin/src/main/scala/services/local/support/HackClearActor.scala +++ b/pslogin/src/main/scala/services/local/support/HackClearActor.scala @@ -2,6 +2,7 @@ package services.local.support import akka.actor.{Actor, Cancellable} +import net.psforever.objects.DefaultCancellable import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.zones.Zone import net.psforever.packet.game.PlanetSideGUID @@ -16,7 +17,7 @@ import scala.concurrent.duration._ */ class HackClearActor() extends Actor { /** The periodic `Executor` that checks for server objects to be unhacked */ - private var clearTrigger : Cancellable = HackClearActor.DefaultClearer + private var clearTrigger : Cancellable = DefaultCancellable.obj /** A `List` of currently hacked server objects */ private var hackedObjects : List[HackClearActor.HackEntry] = Nil //private[this] val log = org.log4s.getLogger @@ -99,11 +100,6 @@ object HackClearActor { /** The wait before a server object is to unhack; as a `FiniteDuration` for `Executor` simplicity */ private final val timeout : FiniteDuration = timeout_time nanoseconds - private final val DefaultClearer : Cancellable = new Cancellable() { - override def cancel : Boolean = true - override def isCancelled : Boolean = true - } - /** * Message that carries information about a server object that has been hacked. * @param target the server object diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala index 12842a95b..7f49f6eda 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala @@ -2,12 +2,13 @@ package services.vehicle import akka.actor.{Actor, ActorRef, Props} -import services.vehicle.support.{DeconstructionActor, VehicleContextActor} +import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor} import services.{GenericEventBus, Service} class VehicleService extends Actor { private val vehicleContext : ActorRef = context.actorOf(Props[VehicleContextActor], "vehicle-context-root") private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent") + private val vehicleDelayedDecon : ActorRef = context.actorOf(Props[DelayedDeconstructionActor], "vehicle-delayed-decon-agent") vehicleDecon ! DeconstructionActor.RequestTaskResolver private [this] val log = org.log4s.getLogger @@ -79,6 +80,14 @@ class VehicleService extends Actor { case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) => vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent) + //message to DelayedDeconstructionActor + case VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, zone, timeAlive) => + vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) + + //message to DelayedDeconstructionActor + case VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) => + vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) + //response from DeconstructionActor case DeconstructionActor.DeleteVehicle(vehicle_guid, zone_id) => VehicleEvents.publish( diff --git a/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala b/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala index c3e1c4804..48c03dab3 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleServiceMessage.scala @@ -3,11 +3,14 @@ package services.vehicle import net.psforever.objects.Vehicle import net.psforever.objects.zones.Zone +import net.psforever.packet.game.PlanetSideGUID final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action) object VehicleServiceMessage { + final case class DelayedVehicleDeconstruction(vehicle : Vehicle, continent : Zone, timeAlive : Long) final case class GiveActorControl(vehicle : Vehicle, actorName : String) final case class RevokeActorControl(vehicle : Vehicle) final case class RequestDeleteVehicle(vehicle : Vehicle, continent : Zone) + final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID) } diff --git a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala index 4d09b9272..f19d47aa9 100644 --- a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala +++ b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala @@ -2,7 +2,7 @@ package services.vehicle.support import akka.actor.{Actor, ActorRef, Cancellable} -import net.psforever.objects.Vehicle +import net.psforever.objects.{DefaultCancellable, Vehicle} import net.psforever.objects.guid.TaskResolver import net.psforever.objects.vehicles.Seat import net.psforever.objects.zones.Zone @@ -20,11 +20,13 @@ import scala.concurrent.duration._ * A reference to a vehicle should be passed to this object as soon as it is going to be cleaned-up from the game world. * Once accepted, only a few seconds will remain before the vehicle is deleted. * To ensure that no players are lost in the deletion, all occupants of the vehicle are kicked out. - * Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed. + * Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.
+ *
+ * This `Actor` is intended to sit on top of the event system that handles broadcast messaging. */ class DeconstructionActor extends Actor { /** The periodic `Executor` that scraps the next vehicle on the list */ - private var scrappingProcess : Cancellable = DeconstructionActor.DefaultProcess + private var scrappingProcess : Cancellable = DefaultCancellable.obj /** A `List` of currently doomed vehicles */ private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil /** The manager that helps unregister the vehicle from its current GUID scope */ @@ -171,11 +173,6 @@ object DeconstructionActor { /** The wait before completely deleting a vehicle; as a `FiniteDuration` for `Executor` simplicity */ private final val timeout : FiniteDuration = timeout_time nanoseconds - private final val DefaultProcess : Cancellable = new Cancellable() { - override def cancel : Boolean = true - override def isCancelled : Boolean = true - } - final case class RequestTaskResolver() /** diff --git a/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala new file mode 100644 index 000000000..2c508ad8d --- /dev/null +++ b/pslogin/src/main/scala/services/vehicle/support/DelayedDeconstructionActor.scala @@ -0,0 +1,104 @@ +// Copyright (c) 2017 PSForever +package services.vehicle.support + +import akka.actor.{Actor, Cancellable} +import net.psforever.objects.{DefaultCancellable, Vehicle} +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.PlanetSideGUID +import services.vehicle.VehicleServiceMessage + +import scala.concurrent.duration._ + +/** + * Maintain and curate a list of timed `vehicle` object deconstruction tasks.
+ *
+ * These tasks are queued or dismissed by player activity but they are executed independent of player activity. + * A common disconnected cause of deconstruction is neglect for an extended period of time. + * At that point, the original owner of the vehicle no longer matters. + * Deconstruction neglect, however, is averted by having someone become seated. + * A realized deconstruction is entirely based on a fixed interval after an unresolved request has been received. + * The actual process of deconstructing the vehicle and cleaning up its resources is performed by an external agent.
+ *
+ * This `Actor` is intended to sit on top of the event system that handles broadcast messaging. + */ +class DelayedDeconstructionActor extends Actor { + /** The periodic `Executor` that scraps the next vehicle on the list */ + private var monitor : Cancellable = DefaultCancellable.obj + /** A `List` of currently doomed vehicles */ + private var vehicles : List[DelayedDeconstructionActor.VehicleEntry] = Nil + private[this] val log = org.log4s.getLogger + private[this] def trace(msg : String) : Unit = log.trace(msg) + + + def receive : Receive = { + case DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) => + trace(s"delayed deconstruction order for $vehicle in $timeAlive") + vehicles = vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L) + if(vehicles.size == 1) { //we were the only entry so the event must be started from scratch + import scala.concurrent.ExecutionContext.Implicits.global + monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling) + } + + case DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) => + //all tasks for this vehicle are cleared from the queue + //clear any task that is no longer valid by determination of unregistered GUID + val before = vehicles.length + vehicles = vehicles.filter(entry => { !entry.vehicle.HasGUID || entry.vehicle.GUID != vehicle_guid }) + trace(s"attempting to clear deconstruction order for vehicle $vehicle_guid, found ${before - vehicles.length}") + if(vehicles.isEmpty) { + monitor.cancel + } + + case DelayedDeconstructionActor.PeriodicTaskCulling => + //filter the list of deconstruction tasks for any that are need to be triggered + monitor.cancel + val now : Long = System.nanoTime + val (vehiclesToDecon, vehiclesRemain) = vehicles.partition(entry => { now - entry.logTime >= entry.survivalTime }) + vehicles = vehiclesRemain + trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found") + vehiclesToDecon.foreach(entry => { context.parent ! VehicleServiceMessage.RequestDeleteVehicle(entry.vehicle, entry.zone) }) + if(vehiclesRemain.nonEmpty) { + import scala.concurrent.ExecutionContext.Implicits.global + monitor = context.system.scheduler.scheduleOnce(DelayedDeconstructionActor.periodicTest, self, DelayedDeconstructionActor.PeriodicTaskCulling) + } + + case _ => ; + } +} + +object DelayedDeconstructionActor { + /** + * Timer for the repeating executor. + */ + private final val periodicTest : FiniteDuration = 5000000000L nanoseconds //5s + + /** + * Queue a future vehicle deconstruction action. + * @param vehicle the `Vehicle` object + * @param zone the `Zone` that the vehicle currently occupies + * @param survivalTime how long until the vehicle will be deconstructed in seconds + */ + final case class ScheduleDeconstruction(vehicle : Vehicle, zone : Zone, survivalTime : Long) + + /** + * Dequeue a vehicle from being deconstructed. + * @param vehicle_guid the vehicle + */ + final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID) + + /** + * A message the `Actor` sends to itself. + * The trigger for the periodic deconstruction task. + */ + private final case class PeriodicTaskCulling() + + /** + * An entry that stores vehicle deconstruction tasks. + * @param vehicle the `Vehicle` object + * @param zone the `Zone` that the vehicle currently occupies + * @param survivalTime how long until the vehicle will be deconstructed in nanoseconds + * @param logTime when this deconstruction request was initially created in nanoseconds; + * initialized by default to a "now" + */ + private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, survivalTime : Long, logTime : Long = System.nanoTime()) +} diff --git a/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala b/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala index 756a31a34..ebc954aad 100644 --- a/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala +++ b/pslogin/src/main/scala/services/vehicle/support/VehicleContextActor.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package services.vehicle.support -import akka.actor.{Actor, Props} +import akka.actor.{Actor, ActorRef, Props} import net.psforever.objects.vehicles.VehicleControl import services.vehicle.VehicleServiceMessage @@ -15,15 +15,18 @@ import services.vehicle.VehicleServiceMessage *
* The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation. * It is also be allowed to be responsible for cleaning up that context. - * (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.) + * (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)
+ *
+ * This `Actor` is intended to sit on top of the event system that handles broadcast messaging. */ class VehicleContextActor() extends Actor { def receive : Receive = { case VehicleServiceMessage.GiveActorControl(vehicle, actorName) => - vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName") + vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName.${System.nanoTime()}") case VehicleServiceMessage.RevokeActorControl(vehicle) => vehicle.Actor ! akka.actor.PoisonPill + vehicle.Actor = ActorRef.noSender case _ => ; }